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

F
Fernando Serrano 已提交
42 43 44
//------------------------------------------------------------------------------
// GLTF Exporter
//------------------------------------------------------------------------------
45
THREE.GLTFExporter = function () {};
F
Fernando Serrano 已提交
46 47 48 49

THREE.GLTFExporter.prototype = {

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

F
Fernando Serrano 已提交
51 52 53
	/**
	 * Parse scenes and generate GLTF output
	 * @param  {THREE.Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
F
Fernando Serrano 已提交
54 55
	 * @param  {Function} onDone  Callback on completed
	 * @param  {Object} options options
F
Fernando Serrano 已提交
56 57
	 *                          trs: Exports position, rotation and scale instead of matrix
	 */
F
Fernando Serrano 已提交
58 59
	parse: function ( input, onDone, options ) {

60 61
		var DEFAULT_OPTIONS = {
			trs: false,
62 63
			onlyVisible: true,
			truncateDrawRange: true
64 65 66 67
		};

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

F
Fernando Serrano 已提交
68
		var outputJSON = {
F
Fernando Serrano 已提交
69

F
Fernando Serrano 已提交
70
			asset: {
F
Fernando Serrano 已提交
71

F
Fernando Serrano 已提交
72
				version: "2.0",
M
Mr.doob 已提交
73
				generator: "THREE.GLTFExporter"
F
Fernando Serrano 已提交
74

M
Mr.doob 已提交
75
			}
F
Fernando Serrano 已提交
76

M
Mr.doob 已提交
77
		};
F
Fernando Serrano 已提交
78 79 80

		var byteOffset = 0;
		var dataViews = [];
81 82 83 84 85 86
		var cachedData = {

			images: {},
			materials: {}

		};
F
Fernando Serrano 已提交
87

F
Fernando Serrano 已提交
88 89 90
		/**
		 * Compare two arrays
		 */
F
Fernando Serrano 已提交
91 92 93 94 95 96
		/**
		 * 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 已提交
97
		function equalArray( array1, array2 ) {
F
Fernando Serrano 已提交
98

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

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

M
Mugen87 已提交
103
			} );
F
Fernando Serrano 已提交
104

F
Fernando Serrano 已提交
105 106
		}

F
Fernando Serrano 已提交
107 108 109 110 111
		/**
		 * Get the min and he max vectors from the given attribute
		 * @param  {THREE.WebGLAttribute} attribute Attribute to find the min/max
		 * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
		 */
M
Mr.doob 已提交
112
		function getMinMax( attribute ) {
F
Fernando Serrano 已提交
113

F
Fernando Serrano 已提交
114
			var output = {
F
Fernando Serrano 已提交
115

F
Fernando Serrano 已提交
116 117
				min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
				max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
F
Fernando Serrano 已提交
118

F
Fernando Serrano 已提交
119 120
			};

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

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

F
Fernando Serrano 已提交
125
					var value = attribute.array[ i * attribute.itemSize + a ];
F
Fernando Serrano 已提交
126 127 128
					output.min[ a ] = Math.min( output.min[ a ], value );
					output.max[ a ] = Math.max( output.max[ a ], value );

F
Fernando Serrano 已提交
129
				}
F
Fernando Serrano 已提交
130

F
Fernando Serrano 已提交
131 132
			}

F
Fernando Serrano 已提交
133
			return output;
M
Mugen87 已提交
134

F
Fernando Serrano 已提交
135 136
		}

F
Fernando Serrano 已提交
137
		/**
F
Fernando Serrano 已提交
138 139 140 141
		 * 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 已提交
142
		 */
M
Mr.doob 已提交
143
		function processBuffer( attribute, componentType, start, count ) {
F
Fernando Serrano 已提交
144

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

F
Fernando Serrano 已提交
147
				outputJSON.buffers = [
F
Fernando Serrano 已提交
148

F
Fernando Serrano 已提交
149
					{
F
Fernando Serrano 已提交
150

F
Fernando Serrano 已提交
151 152
						byteLength: 0,
						uri: ''
F
Fernando Serrano 已提交
153

F
Fernando Serrano 已提交
154
					}
F
Fernando Serrano 已提交
155

F
Fernando Serrano 已提交
156
				];
F
Fernando Serrano 已提交
157

F
Fernando Serrano 已提交
158 159
			}

160 161 162
			var offset = 0;
			var componentSize = componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4;

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

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

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

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

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

174
					if ( componentType === WEBGL_CONSTANTS.FLOAT ) {
F
Fernando Serrano 已提交
175

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

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

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

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

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

F
Fernando Serrano 已提交
186
					}
F
Fernando Serrano 已提交
187

188
					offset += componentSize;
F
Fernando Serrano 已提交
189

F
Fernando Serrano 已提交
190
				}
F
Fernando Serrano 已提交
191

F
Fernando Serrano 已提交
192 193
			}

F
Fernando Serrano 已提交
194
			// We just use one buffer
F
Fernando Serrano 已提交
195
			dataViews.push( dataView );
F
Fernando Serrano 已提交
196

F
Fernando Serrano 已提交
197
			// Always using just one buffer
F
Fernando Serrano 已提交
198
			return 0;
M
Mugen87 已提交
199

F
Fernando Serrano 已提交
200 201 202
		}

		/**
F
Fernando Serrano 已提交
203
		 * Process and generate a BufferView
F
Fernando Serrano 已提交
204 205 206
		 * @param  {[type]} data [description]
		 * @return {[type]}      [description]
		 */
M
Mr.doob 已提交
207
		function processBufferView( data, componentType, start, count ) {
F
Fernando Serrano 已提交
208

209
			var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
F
Fernando Serrano 已提交
210

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

F
Fernando Serrano 已提交
213
				outputJSON.bufferViews = [];
F
Fernando Serrano 已提交
214

F
Fernando Serrano 已提交
215 216
			}

217 218 219 220 221
			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 已提交
222
			var gltfBufferView = {
F
Fernando Serrano 已提交
223

224
				buffer: processBuffer( data, componentType, start, count ),
F
Fernando Serrano 已提交
225
				byteOffset: byteOffset,
226 227
				byteLength: byteLength,
				byteStride: data.itemSize * componentSize,
228
				target: isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER
F
Fernando Serrano 已提交
229

F
Fernando Serrano 已提交
230 231
			};

232
			byteOffset += byteLength;
F
Fernando Serrano 已提交
233

F
Fernando Serrano 已提交
234
			outputJSON.bufferViews.push( gltfBufferView );
F
Fernando Serrano 已提交
235 236

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

F
Fernando Serrano 已提交
239 240
				id: outputJSON.bufferViews.length - 1,
				byteLength: 0
F
Fernando Serrano 已提交
241

F
Fernando Serrano 已提交
242
			};
F
Fernando Serrano 已提交
243

F
Fernando Serrano 已提交
244
			return output;
F
Fernando Serrano 已提交
245

F
Fernando Serrano 已提交
246 247 248
		}

		/**
F
Fernando Serrano 已提交
249 250 251
		 * Process attribute to generate an accessor
		 * @param  {THREE.WebGLAttribute} attribute Attribute to process
		 * @return {Integer}           Index of the processed accessor on the "accessors" array
F
Fernando Serrano 已提交
252
		 */
M
Mr.doob 已提交
253
		function processAccessor( attribute, geometry ) {
F
Fernando Serrano 已提交
254

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

F
Fernando Serrano 已提交
257
				outputJSON.accessors = [];
F
Fernando Serrano 已提交
258

F
Fernando Serrano 已提交
259 260 261
			}

			var types = [
F
Fernando Serrano 已提交
262

F
Fernando Serrano 已提交
263 264 265 266
				'SCALAR',
				'VEC2',
				'VEC3',
				'VEC4'
F
Fernando Serrano 已提交
267

F
Fernando Serrano 已提交
268 269
			];

270 271
			var componentType;

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

275
				componentType = WEBGL_CONSTANTS.FLOAT;
F
Fernando Serrano 已提交
276

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

279
				componentType = WEBGL_CONSTANTS.UNSIGNED_INT;
F
Fernando Serrano 已提交
280

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

283
				componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT;
F
Fernando Serrano 已提交
284

285
			} else {
F
Fernando Serrano 已提交
286

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

289
			}
F
Fernando Serrano 已提交
290 291

			var minMax = getMinMax( attribute );
292 293 294 295 296 297

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

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

299 300
				start = geometry.drawRange.start;
				count = geometry.drawRange.count !== Infinity ? geometry.drawRange.count : attribute.count;
M
Mugen87 已提交
301

302 303 304
			}

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

F
Fernando Serrano 已提交
306
			var gltfAccessor = {
F
Fernando Serrano 已提交
307

F
Fernando Serrano 已提交
308 309 310
				bufferView: bufferView.id,
				byteOffset: bufferView.byteOffset,
				componentType: componentType,
311
				count: count,
F
Fernando Serrano 已提交
312 313
				max: minMax.max,
				min: minMax.min,
F
Fernando Serrano 已提交
314
				type: types[ attribute.itemSize - 1 ]
F
Fernando Serrano 已提交
315

F
Fernando Serrano 已提交
316 317 318 319 320
			};

			outputJSON.accessors.push( gltfAccessor );

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

F
Fernando Serrano 已提交
322 323 324
		}

		/**
F
Fernando Serrano 已提交
325 326 327 328
		 * Process image
		 * @param  {Texture} map Texture to process
		 * @return {Integer}     Index of the processed texture in the "images" array
		 */
M
Mr.doob 已提交
329
		function processImage( map ) {
F
Fernando Serrano 已提交
330

331 332 333 334 335 336
			if ( cachedData.images[ map.uuid ] ) {

				return cachedData.images[ map.uuid ];

			}

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

F
Fernando Serrano 已提交
339
				outputJSON.images = [];
F
Fernando Serrano 已提交
340

F
Fernando Serrano 已提交
341 342 343 344
			}

			var gltfImage = {};

F
Fernando Serrano 已提交
345
			if ( options.embedImages ) {
F
Fernando Serrano 已提交
346

F
Fernando Serrano 已提交
347
				// @TODO { bufferView, mimeType }
F
Fernando Serrano 已提交
348

F
Fernando Serrano 已提交
349
			} else {
F
Fernando Serrano 已提交
350

F
Fernando Serrano 已提交
351 352
				// @TODO base64 based on options
				gltfImage.uri = map.image.src;
F
Fernando Serrano 已提交
353

F
Fernando Serrano 已提交
354 355 356 357
			}

			outputJSON.images.push( gltfImage );

358 359 360 361
			var index = outputJSON.images.length - 1;
			cachedData.images[ map.uuid ] = index;

			return index;
F
Fernando Serrano 已提交
362

F
Fernando Serrano 已提交
363 364 365 366 367 368 369
		}

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

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

F
Fernando Serrano 已提交
374
				outputJSON.samplers = [];
F
Fernando Serrano 已提交
375

F
Fernando Serrano 已提交
376 377 378
			}

			var gltfSampler = {
F
Fernando Serrano 已提交
379

380 381 382 383
				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 已提交
384

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

			outputJSON.samplers.push( gltfSampler );

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

F
Fernando Serrano 已提交
391 392 393 394 395 396 397
		}

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

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

F
Fernando Serrano 已提交
402
				outputJSON.textures = [];
F
Fernando Serrano 已提交
403

F
Fernando Serrano 已提交
404 405 406
			}

			var gltfTexture = {
F
Fernando Serrano 已提交
407

F
Fernando Serrano 已提交
408 409
				sampler: processSampler( map ),
				source: processImage( map )
F
Fernando Serrano 已提交
410

F
Fernando Serrano 已提交
411 412 413 414 415
			};

			outputJSON.textures.push( gltfTexture );

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

F
Fernando Serrano 已提交
417 418 419 420 421 422
		}

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

426 427 428 429 430 431
			if ( cachedData.materials[ material.uuid ] ) {

				return cachedData.materials[ material.uuid ];

			}

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

F
Fernando Serrano 已提交
434
				outputJSON.materials = [];
F
Fernando Serrano 已提交
435

F
Fernando Serrano 已提交
436
			}
F
Fernando Serrano 已提交
437

438 439 440 441 442 443 444 445
			if ( material instanceof THREE.ShaderMaterial ) {

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

			}


M
Mugen87 已提交
446
			if ( ! ( material instanceof THREE.MeshStandardMaterial ) ) {
447

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

F
Fernando Serrano 已提交
450 451 452
			}

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

455
				pbrMetallicRoughness: {}
F
Fernando Serrano 已提交
456

457
			};
458

459 460
			// pbrMetallicRoughness.baseColorFactor
			var color = material.color.toArray().concat( [ material.opacity ] );
F
Fernando Serrano 已提交
461

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

464
				gltfMaterial.pbrMetallicRoughness.baseColorFactor = color;
465 466 467

			}

468
			if ( material instanceof THREE.MeshStandardMaterial ) {
469

470 471
				gltfMaterial.pbrMetallicRoughness.metallicFactor = material.metalness;
				gltfMaterial.pbrMetallicRoughness.roughnessFactor = material.roughness;
472

M
Mr.doob 已提交
473
			} else {
474

M
Mugen87 已提交
475 476
				gltfMaterial.pbrMetallicRoughness.metallicFactor = 0.5;
				gltfMaterial.pbrMetallicRoughness.roughnessFactor = 0.5;
F
Fernando Serrano 已提交
477

478
			}
479

480 481
			// pbrMetallicRoughness.baseColorTexture
			if ( material.map ) {
482

483
				gltfMaterial.pbrMetallicRoughness.baseColorTexture = {
F
Fernando Serrano 已提交
484

485
					index: processTexture( material.map )
F
Fernando Serrano 已提交
486

487
				};
488

489
			}
490

F
Fernando Serrano 已提交
491 492 493
			if ( material instanceof THREE.MeshBasicMaterial ||
				material instanceof THREE.LineBasicMaterial ||
				material instanceof THREE.PointsMaterial ) {
494 495 496 497

			} else {

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

M
Mugen87 已提交
500
				if ( ! equalArray( emissive, [ 0, 0, 0 ] ) ) {
501 502 503

					gltfMaterial.emissiveFactor = emissive;

F
Fernando Serrano 已提交
504
				}
505 506 507 508 509 510

				// emissiveTexture
				if ( material.emissiveMap ) {

					gltfMaterial.emissiveTexture = {

511
						index: processTexture( material.emissiveMap )
512 513 514 515 516 517 518 519 520 521 522

					};

				}

			}

			// normalTexture
			if ( material.normalMap ) {

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

524
					index: processTexture( material.normalMap )
F
Fernando Serrano 已提交
525

526 527
				};

M
Mugen87 已提交
528
				if ( material.normalScale.x !== - 1 ) {
529 530 531

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

M
Mugen87 已提交
532
						console.warn( 'THREE.GLTFExporter: Normal scale components are different, ignoring Y and exporting X.' );
533 534 535 536 537 538 539

					}

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

				}

F
Fernando Serrano 已提交
540 541
			}

542 543 544 545
			// occlusionTexture
			if ( material.aoMap ) {

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

547
					index: processTexture( material.aoMap )
F
Fernando Serrano 已提交
548

549 550
				};

551 552 553 554 555 556
				if ( material.aoMapIntensity !== 1.0 ) {

					gltfMaterial.occlusionTexture.strength = material.aoMapIntensity;

				}

557 558 559 560 561
			}

			// alphaMode
			if ( material.transparent ) {

562 563 564 565 566 567 568
				gltfMaterial.alphaMode = 'MASK'; // @FIXME We should detect MASK or BLEND

				if ( material.alphaTest !== 0.5 ) {

					gltfMaterial.alphaCutoff = material.alphaTest;

				}
569 570 571 572

			}

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

F
Fernando Serrano 已提交
575
				gltfMaterial.doubleSided = true;
576

F
Fernando Serrano 已提交
577 578
			}

F
Fernando Serrano 已提交
579
			if ( material.name ) {
580

F
Fernando Serrano 已提交
581
				gltfMaterial.name = material.name;
582

F
Fernando Serrano 已提交
583 584
			}

F
Fernando Serrano 已提交
585
			outputJSON.materials.push( gltfMaterial );
F
Fernando Serrano 已提交
586

587 588 589 590
			var index = outputJSON.materials.length - 1;
			cachedData.materials[ material.uuid ] = index;

			return index;
591

F
Fernando Serrano 已提交
592 593 594
		}

		/**
F
Fernando Serrano 已提交
595 596 597
		 * Process mesh
		 * @param  {THREE.Mesh} mesh Mesh to process
		 * @return {Integer}      Index of the processed mesh in the "meshes" array
F
Fernando Serrano 已提交
598 599
		 */
		function processMesh( mesh ) {
F
Fernando Serrano 已提交
600

M
Mugen87 已提交
601
			if ( ! outputJSON.meshes ) {
F
Fernando Serrano 已提交
602 603 604

				outputJSON.meshes = [];

F
Fernando Serrano 已提交
605 606 607
			}

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

M
Mr.doob 已提交
609 610
			var mode;

611 612 613
			// Use the correct mode
			if ( mesh instanceof THREE.LineSegments ) {

614
				mode = WEBGL_CONSTANTS.LINES;
615 616 617

			} else if ( mesh instanceof THREE.LineLoop ) {

618
				mode = WEBGL_CONSTANTS.LINE_LOOP;
619 620 621

			} else if ( mesh instanceof THREE.Line ) {

622
				mode = WEBGL_CONSTANTS.LINE_STRIP;
623 624 625

			} else if ( mesh instanceof THREE.Points ) {

626
				mode = WEBGL_CONSTANTS.POINTS;
627 628 629

			} else {

M
Mugen87 已提交
630
				if ( ! geometry.isBufferGeometry ) {
631 632 633 634 635 636 637

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

				}

638 639
				if ( mesh.drawMode === THREE.TriangleFanDrawMode ) {

F
Fernando Serrano 已提交
640
					console.warn( 'GLTFExporter: TriangleFanDrawMode and wireframe incompatible.' );
641
					mode = WEBGL_CONSTANTS.TRIANGLE_FAN;
642 643 644

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

645
					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINE_STRIP : WEBGL_CONSTANTS.TRIANGLE_STRIP;
646 647 648

				} else {

649
					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES;
650 651 652 653

				}

			}
F
Fernando Serrano 已提交
654 655 656 657 658 659 660 661 662 663

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

664
			var material = processMaterial( mesh.material );
665
			if ( material !== null ) {
666 667 668 669 670 671

				gltfMesh.primitives[ 0 ].material = material;

			}


672 673
			if ( geometry.index ) {

674
				gltfMesh.primitives[ 0 ].indices = processAccessor( geometry.index, geometry );
675 676 677

			}

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

F
Fernando Serrano 已提交
681 682
			// Conversion between attributes names in threejs and gltf spec
			var nameConversion = {
F
Fernando Serrano 已提交
683

F
Fernando Serrano 已提交
684 685
				uv: 'TEXCOORD_0',
				uv2: 'TEXCOORD_1',
686 687 688
				color: 'COLOR_0',
				skinWeight: 'WEIGHTS_0',
				skinIndex: 'JOINTS_0'
F
Fernando Serrano 已提交
689

F
Fernando Serrano 已提交
690 691
			};

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

F
Fernando Serrano 已提交
696
				var attribute = geometry.attributes[ attributeName ];
F
Fernando Serrano 已提交
697
				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
698
				gltfAttributes[ attributeName ] = processAccessor( attribute, geometry );
F
Fernando Serrano 已提交
699

F
Fernando Serrano 已提交
700 701 702 703 704
			}

			outputJSON.meshes.push( gltfMesh );

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

F
Fernando Serrano 已提交
706 707
		}

F
Fernando Serrano 已提交
708 709 710 711 712 713
		/**
		 * 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 已提交
714

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

F
Fernando Serrano 已提交
717
				outputJSON.cameras = [];
F
Fernando Serrano 已提交
718

F
Fernando Serrano 已提交
719 720 721 722 723
			}

			var isOrtho = camera instanceof THREE.OrthographicCamera;

			var gltfCamera = {
F
Fernando Serrano 已提交
724

F
Fernando Serrano 已提交
725
				type: isOrtho ? 'orthographic' : 'perspective'
F
Fernando Serrano 已提交
726

F
Fernando Serrano 已提交
727 728 729 730 731 732 733 734 735 736 737
			};

			if ( isOrtho ) {

				gltfCamera.orthographic = {

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

F
Fernando Serrano 已提交
738
				};
F
Fernando Serrano 已提交
739 740 741 742 743 744 745 746 747 748 749 750 751 752

			} else {

				gltfCamera.perspective = {

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

				};

			}

F
Fernando Serrano 已提交
753 754
			if ( camera.name ) {

F
Fernando Serrano 已提交
755
				gltfCamera.name = camera.type;
F
Fernando Serrano 已提交
756

F
Fernando Serrano 已提交
757 758 759 760 761
			}

			outputJSON.cameras.push( gltfCamera );

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

F
Fernando Serrano 已提交
763 764
		}

F
Fernando Serrano 已提交
765 766 767 768 769
		/**
		 * Process Object3D node
		 * @param  {THREE.Object3D} node Object3D to processNode
		 * @return {Integer}      Index of the node in the nodes list
		 */
M
Mr.doob 已提交
770
		function processNode( object ) {
F
Fernando Serrano 已提交
771

772 773 774
			if ( object instanceof THREE.Light ) {

				console.warn( 'GLTFExporter: Unsupported node type:', object.constructor.name );
775
				return null;
776 777 778

			}

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

F
Fernando Serrano 已提交
781
				outputJSON.nodes = [];
F
Fernando Serrano 已提交
782

F
Fernando Serrano 已提交
783 784
			}

F
Fernando Serrano 已提交
785 786 787
			var gltfNode = {};

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

F
Fernando Serrano 已提交
789 790 791 792
				var rotation = object.quaternion.toArray();
				var position = object.position.toArray();
				var scale = object.scale.toArray();

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

F
Fernando Serrano 已提交
795
					gltfNode.rotation = rotation;
F
Fernando Serrano 已提交
796

F
Fernando Serrano 已提交
797 798
				}

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

F
Fernando Serrano 已提交
801
					gltfNode.position = position;
F
Fernando Serrano 已提交
802

F
Fernando Serrano 已提交
803 804
				}

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

F
Fernando Serrano 已提交
807
					gltfNode.scale = scale;
F
Fernando Serrano 已提交
808

F
Fernando Serrano 已提交
809 810 811
				}

			} else {
F
Fernando Serrano 已提交
812

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

F
Fernando Serrano 已提交
816
					gltfNode.matrix = object.matrix.elements;
F
Fernando Serrano 已提交
817

F
Fernando Serrano 已提交
818
				}
F
Fernando Serrano 已提交
819

F
Fernando Serrano 已提交
820 821 822 823
			}

			if ( object.name ) {

F
Fernando Serrano 已提交
824
				gltfNode.name = object.name;
F
Fernando Serrano 已提交
825

F
Fernando Serrano 已提交
826 827
			}

828 829 830 831 832 833
			if ( object.userData && Object.keys( object.userData ).length > 0 ) {

				try {

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

M
Mugen87 已提交
834
				} catch ( e ) {
835

M
Mugen87 已提交
836
					throw new Error( 'THREE.GLTFExporter: userData can\'t be serialized' );
837 838 839 840 841

				}

			}

F
Fernando Serrano 已提交
842 843 844 845
			if ( object instanceof THREE.Mesh ||
				object instanceof THREE.Line ||
				object instanceof THREE.Points ) {

F
Fernando Serrano 已提交
846
				gltfNode.mesh = processMesh( object );
F
Fernando Serrano 已提交
847

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

F
Fernando Serrano 已提交
850
				gltfNode.camera = processCamera( object );
F
Fernando Serrano 已提交
851

F
Fernando Serrano 已提交
852 853 854
			}

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

856
				var children = [];
F
Fernando Serrano 已提交
857 858

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

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

862 863
					if ( child.visible || options.onlyVisible === false ) {

864 865
						var node = processNode( child );

866
						if ( node !== null ) {
867

868
							children.push( node );
869 870

						}
F
Fernando Serrano 已提交
871

F
Fernando Serrano 已提交
872
					}
F
Fernando Serrano 已提交
873

F
Fernando Serrano 已提交
874
				}
F
Fernando Serrano 已提交
875

876 877 878 879 880 881 882
				if ( children.length > 0 ) {

					gltfNode.children = children;

				}


F
Fernando Serrano 已提交
883 884 885 886 887
			}

			outputJSON.nodes.push( gltfNode );

			return outputJSON.nodes.length - 1;
F
Fernando Serrano 已提交
888

F
Fernando Serrano 已提交
889 890 891
		}

		/**
F
Fernando Serrano 已提交
892
		 * Process Scene
F
Fernando Serrano 已提交
893 894 895
		 * @param  {THREE.Scene} node Scene to process
		 */
		function processScene( scene ) {
F
Fernando Serrano 已提交
896

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

F
Fernando Serrano 已提交
899 900
				outputJSON.scenes = [];
				outputJSON.scene = 0;
F
Fernando Serrano 已提交
901

F
Fernando Serrano 已提交
902 903 904
			}

			var gltfScene = {
F
Fernando Serrano 已提交
905

F
Fernando Serrano 已提交
906
				nodes: []
F
Fernando Serrano 已提交
907

F
Fernando Serrano 已提交
908 909
			};

F
Fernando Serrano 已提交
910 911
			if ( scene.name ) {

F
Fernando Serrano 已提交
912
				gltfScene.name = scene.name;
F
Fernando Serrano 已提交
913

F
Fernando Serrano 已提交
914 915
			}

F
Fernando Serrano 已提交
916
			outputJSON.scenes.push( gltfScene );
F
Fernando Serrano 已提交
917

918 919
			var nodes = [];

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

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

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

926 927
					var node = processNode( child );

928
					if ( node !== null ) {
929

930
						nodes.push( node );
931 932

					}
933 934 935

				}

936
			}
937

938
			if ( nodes.length > 0 ) {
F
Fernando Serrano 已提交
939

940
				gltfScene.nodes = nodes;
F
Fernando Serrano 已提交
941

F
Fernando Serrano 已提交
942
			}
F
Fernando Serrano 已提交
943

F
Fernando Serrano 已提交
944 945
		}

946 947 948 949
		/**
		 * Creates a THREE.Scene to hold a list of objects and parse it
		 * @param  {Array} objects List of objects to process
		 */
M
Mr.doob 已提交
950
		function processObjects( objects ) {
951 952

			var scene = new THREE.Scene();
953
			scene.name = 'AuxScene';
954

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

957 958 959
				// 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 ] );
960 961 962 963 964 965 966

			}

			processScene( scene );

		}

967
		function processInput( input ) {
968

969
			input = input instanceof Array ? input : [ input ];
F
Fernando Serrano 已提交
970

971
			var objectsWithoutScene = [];
M
Mr.doob 已提交
972

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

975
				if ( input[ i ] instanceof THREE.Scene ) {
976 977 978

					processScene( input[ i ] );

979
				} else {
980

981
					objectsWithoutScene.push( input[ i ] );
982

983
				}
F
Fernando Serrano 已提交
984

F
Fernando Serrano 已提交
985
			}
F
Fernando Serrano 已提交
986

987
			if ( objectsWithoutScene.length > 0 ) {
988

989
				processObjects( objectsWithoutScene );
990 991

			}
F
Fernando Serrano 已提交
992

F
Fernando Serrano 已提交
993
		}
F
Fernando Serrano 已提交
994

995 996
		processInput( input );

F
Fernando Serrano 已提交
997
		// Generate buffer
F
Fernando Serrano 已提交
998
		// Create a new blob with all the dataviews from the buffers
999
		var blob = new Blob( dataViews, { type: 'application/octet-stream' } );
F
Fernando Serrano 已提交
1000 1001

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

1004
			outputJSON.buffers[ 0 ].byteLength = blob.size;
F
Fernando Serrano 已提交
1005
			var objectURL = URL.createObjectURL( blob );
1006 1007

			var reader = new window.FileReader();
M
Mr.doob 已提交
1008 1009
			reader.readAsDataURL( blob );
			reader.onloadend = function () {
F
Fernando Serrano 已提交
1010

M
Mr.doob 已提交
1011 1012 1013
				var base64data = reader.result;
				outputJSON.buffers[ 0 ].uri = base64data;
				onDone( outputJSON );
F
Fernando Serrano 已提交
1014

M
Mr.doob 已提交
1015
			};
F
Fernando Serrano 已提交
1016

1017
		} else {
F
Fernando Serrano 已提交
1018

M
Mugen87 已提交
1019
			onDone( outputJSON );
F
Fernando Serrano 已提交
1020

1021
		}
M
Mr.doob 已提交
1022

F
Fernando Serrano 已提交
1023
	}
M
Mr.doob 已提交
1024

F
Fernando Serrano 已提交
1025
};