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

5 6 7 8 9
 //------------------------------------------------------------------------------
 // Constants
 //------------------------------------------------------------------------------
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
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 62 63 64 65 66
		var DEFAULT_OPTIONS = {
			trs: false,
			onlyVisible: true
		};

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

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

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

F
Fernando Serrano 已提交
71 72
				version: "2.0",
				generator: "THREE.JS GLTFExporter" // @QUESTION Does it support spaces?
F
Fernando Serrano 已提交
73

F
Fernando Serrano 已提交
74
		 	}
F
Fernando Serrano 已提交
75

F
Fernando Serrano 已提交
76 77 78 79
    };

		var byteOffset = 0;
		var dataViews = [];
F
Fernando Serrano 已提交
80

F
Fernando Serrano 已提交
81 82 83
		/**
		 * Compare two arrays
		 */
F
Fernando Serrano 已提交
84 85 86 87 88 89
		/**
		 * 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
		 */
90
		function equalArray ( array1, array2 ) {
F
Fernando Serrano 已提交
91

F
Fernando Serrano 已提交
92
			return ( array1.length === array2.length ) && array1.every( function( element, index ) {
F
Fernando Serrano 已提交
93

F
Fernando Serrano 已提交
94
    		return element === array2[ index ];
F
Fernando Serrano 已提交
95

F
Fernando Serrano 已提交
96
			});
F
Fernando Serrano 已提交
97

F
Fernando Serrano 已提交
98 99
		}

F
Fernando Serrano 已提交
100 101 102 103 104 105
		/**
		 * 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)
		 */
		function getMinMax ( attribute ) {
F
Fernando Serrano 已提交
106

F
Fernando Serrano 已提交
107
			var output = {
F
Fernando Serrano 已提交
108

F
Fernando Serrano 已提交
109 110
				min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
				max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
F
Fernando Serrano 已提交
111

F
Fernando Serrano 已提交
112 113 114
			};

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

F
Fernando Serrano 已提交
116
				for ( var a = 0; a < attribute.itemSize; a++ ) {
F
Fernando Serrano 已提交
117

F
Fernando Serrano 已提交
118
					var value = attribute.array[ i * attribute.itemSize + a ];
F
Fernando Serrano 已提交
119 120 121
					output.min[ a ] = Math.min( output.min[ a ], value );
					output.max[ a ] = Math.max( output.max[ a ], value );

F
Fernando Serrano 已提交
122
				}
F
Fernando Serrano 已提交
123

F
Fernando Serrano 已提交
124 125
			}

F
Fernando Serrano 已提交
126
			return output;
F
Fernando Serrano 已提交
127 128
		}

F
Fernando Serrano 已提交
129
		/**
F
Fernando Serrano 已提交
130 131 132 133
		 * 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 已提交
134 135
		 */
		function processBuffer ( attribute, componentType ) {
F
Fernando Serrano 已提交
136

F
Fernando Serrano 已提交
137
			if ( !outputJSON.buffers ) {
F
Fernando Serrano 已提交
138

F
Fernando Serrano 已提交
139
				outputJSON.buffers = [
F
Fernando Serrano 已提交
140

F
Fernando Serrano 已提交
141
					{
F
Fernando Serrano 已提交
142

F
Fernando Serrano 已提交
143 144
						byteLength: 0,
						uri: ''
F
Fernando Serrano 已提交
145

F
Fernando Serrano 已提交
146
					}
F
Fernando Serrano 已提交
147

F
Fernando Serrano 已提交
148
				];
F
Fernando Serrano 已提交
149

F
Fernando Serrano 已提交
150 151
			}

F
Fernando Serrano 已提交
152
			// Create a new dataview and dump the attribute's array into it
F
Fernando Serrano 已提交
153 154 155
			var dataView = new DataView( new ArrayBuffer( attribute.array.byteLength ) );

			var offset = 0;
156
			var offsetInc = componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4;
F
Fernando Serrano 已提交
157 158

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

F
Fernando Serrano 已提交
160
				for (var a = 0; a < attribute.itemSize; a++ ) {
F
Fernando Serrano 已提交
161

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

164
					if ( componentType === WEBGL_CONSTANTS.FLOAT ) {
F
Fernando Serrano 已提交
165

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

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

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

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

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

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

F
Fernando Serrano 已提交
178
					offset += offsetInc;
F
Fernando Serrano 已提交
179

F
Fernando Serrano 已提交
180
				}
F
Fernando Serrano 已提交
181

F
Fernando Serrano 已提交
182 183
			}

F
Fernando Serrano 已提交
184
			// We just use one buffer
F
Fernando Serrano 已提交
185
			dataViews.push( dataView );
F
Fernando Serrano 已提交
186

F
Fernando Serrano 已提交
187
			// Always using just one buffer
F
Fernando Serrano 已提交
188 189 190 191
			return 0;
		}

		/**
F
Fernando Serrano 已提交
192
		 * Process and generate a BufferView
F
Fernando Serrano 已提交
193 194 195 196
		 * @param  {[type]} data [description]
		 * @return {[type]}      [description]
		 */
		function processBufferView ( data, componentType ) {
F
Fernando Serrano 已提交
197

198
			var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
F
Fernando Serrano 已提交
199

F
Fernando Serrano 已提交
200
			if ( !outputJSON.bufferViews ) {
F
Fernando Serrano 已提交
201

F
Fernando Serrano 已提交
202
				outputJSON.bufferViews = [];
F
Fernando Serrano 已提交
203

F
Fernando Serrano 已提交
204 205 206
			}

			var gltfBufferView = {
F
Fernando Serrano 已提交
207

F
Fernando Serrano 已提交
208 209 210
				buffer: processBuffer( data, componentType ),
				byteOffset: byteOffset,
				byteLength: data.array.byteLength,
211 212
				byteStride: data.itemSize * ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4 ),
				target: isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER
F
Fernando Serrano 已提交
213

F
Fernando Serrano 已提交
214 215 216 217
			};

			byteOffset += data.array.byteLength;

F
Fernando Serrano 已提交
218
			outputJSON.bufferViews.push( gltfBufferView );
F
Fernando Serrano 已提交
219 220

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

F
Fernando Serrano 已提交
223 224
				id: outputJSON.bufferViews.length - 1,
				byteLength: 0
F
Fernando Serrano 已提交
225

F
Fernando Serrano 已提交
226
			};
F
Fernando Serrano 已提交
227

F
Fernando Serrano 已提交
228
			return output;
F
Fernando Serrano 已提交
229

F
Fernando Serrano 已提交
230 231 232
		}

		/**
F
Fernando Serrano 已提交
233 234 235
		 * 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 已提交
236 237
		 */
		function processAccessor ( attribute ) {
F
Fernando Serrano 已提交
238

F
Fernando Serrano 已提交
239
			if ( !outputJSON.accessors ) {
F
Fernando Serrano 已提交
240

F
Fernando Serrano 已提交
241
				outputJSON.accessors = [];
F
Fernando Serrano 已提交
242

F
Fernando Serrano 已提交
243 244 245
			}

			var types = [
F
Fernando Serrano 已提交
246

F
Fernando Serrano 已提交
247 248 249 250
				'SCALAR',
				'VEC2',
				'VEC3',
				'VEC4'
F
Fernando Serrano 已提交
251

F
Fernando Serrano 已提交
252 253
			];

254 255
			var componentType;

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

259
				componentType = WEBGL_CONSTANTS.FLOAT;
F
Fernando Serrano 已提交
260

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

263
				componentType = WEBGL_CONSTANTS.UNSIGNED_INT;
F
Fernando Serrano 已提交
264

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

267
				componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT;
F
Fernando Serrano 已提交
268

269
			} else {
F
Fernando Serrano 已提交
270

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

273
			}
F
Fernando Serrano 已提交
274 275 276

			var minMax = getMinMax( attribute );
			var bufferView = processBufferView( attribute, componentType );
F
Fernando Serrano 已提交
277

F
Fernando Serrano 已提交
278
			var gltfAccessor = {
F
Fernando Serrano 已提交
279

F
Fernando Serrano 已提交
280 281 282 283 284 285
				bufferView: bufferView.id,
				byteOffset: bufferView.byteOffset,
				componentType: componentType,
				count: attribute.count,
				max: minMax.max,
				min: minMax.min,
F
Fernando Serrano 已提交
286
				type: types[ attribute.itemSize - 1 ]
F
Fernando Serrano 已提交
287

F
Fernando Serrano 已提交
288 289 290 291 292
			};

			outputJSON.accessors.push( gltfAccessor );

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

F
Fernando Serrano 已提交
294 295 296
		}

		/**
F
Fernando Serrano 已提交
297 298 299 300 301
		 * Process image
		 * @param  {Texture} map Texture to process
		 * @return {Integer}     Index of the processed texture in the "images" array
		 */
		function processImage ( map ) {
F
Fernando Serrano 已提交
302

F
Fernando Serrano 已提交
303
			if ( !outputJSON.images ) {
F
Fernando Serrano 已提交
304

F
Fernando Serrano 已提交
305
				outputJSON.images = [];
F
Fernando Serrano 已提交
306

F
Fernando Serrano 已提交
307 308 309 310
			}

			var gltfImage = {};

F
Fernando Serrano 已提交
311
			if ( options.embedImages ) {
F
Fernando Serrano 已提交
312

F
Fernando Serrano 已提交
313
				// @TODO { bufferView, mimeType }
F
Fernando Serrano 已提交
314

F
Fernando Serrano 已提交
315
			} else {
F
Fernando Serrano 已提交
316

F
Fernando Serrano 已提交
317 318
				// @TODO base64 based on options
				gltfImage.uri = map.image.src;
F
Fernando Serrano 已提交
319

F
Fernando Serrano 已提交
320 321 322 323 324
			}

			outputJSON.images.push( gltfImage );

			return outputJSON.images.length - 1;
F
Fernando Serrano 已提交
325

F
Fernando Serrano 已提交
326 327 328 329 330 331 332 333
		}

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

F
Fernando Serrano 已提交
335
			if ( !outputJSON.samplers ) {
F
Fernando Serrano 已提交
336

F
Fernando Serrano 已提交
337
				outputJSON.samplers = [];
F
Fernando Serrano 已提交
338

F
Fernando Serrano 已提交
339 340 341
			}

			var gltfSampler = {
F
Fernando Serrano 已提交
342

343 344 345 346
				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 已提交
347

F
Fernando Serrano 已提交
348 349 350 351 352
			};

			outputJSON.samplers.push( gltfSampler );

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

F
Fernando Serrano 已提交
354 355 356 357 358 359 360 361
		}

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

F
Fernando Serrano 已提交
363
			if (!outputJSON.textures) {
F
Fernando Serrano 已提交
364

F
Fernando Serrano 已提交
365
				outputJSON.textures = [];
F
Fernando Serrano 已提交
366

F
Fernando Serrano 已提交
367 368 369
			}

			var gltfTexture = {
F
Fernando Serrano 已提交
370

F
Fernando Serrano 已提交
371 372
				sampler: processSampler( map ),
				source: processImage( map )
F
Fernando Serrano 已提交
373

F
Fernando Serrano 已提交
374 375 376 377 378
			};

			outputJSON.textures.push( gltfTexture );

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

F
Fernando Serrano 已提交
380 381 382 383 384 385
		}

		/**
		 * Process material
		 * @param  {THREE.Material} material Material to process
		 * @return {Integer}      Index of the processed material in the "materials" array
F
Fernando Serrano 已提交
386 387
		 */
		function processMaterial ( material ) {
F
Fernando Serrano 已提交
388

F
Fernando Serrano 已提交
389
			if ( !outputJSON.materials ) {
F
Fernando Serrano 已提交
390

F
Fernando Serrano 已提交
391
				outputJSON.materials = [];
F
Fernando Serrano 已提交
392

F
Fernando Serrano 已提交
393
			}
F
Fernando Serrano 已提交
394

395 396 397 398 399 400 401 402
			if ( material instanceof THREE.ShaderMaterial ) {

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

			}


403 404
			if ( !( material instanceof THREE.MeshStandardMaterial ) ) {

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

F
Fernando Serrano 已提交
407 408 409
			}

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

412
				pbrMetallicRoughness: {}
F
Fernando Serrano 已提交
413

414
			};
415

416 417
			// pbrMetallicRoughness.baseColorFactor
			var color = material.color.toArray().concat( [ material.opacity ] );
F
Fernando Serrano 已提交
418

419
			if ( !equalArray( color, [ 1, 1, 1, 1 ] ) ) {
420

421
				gltfMaterial.pbrMetallicRoughness.baseColorFactor = color;
422 423 424

			}

425
			if ( material instanceof THREE.MeshStandardMaterial ) {
426

427 428
				gltfMaterial.pbrMetallicRoughness.metallicFactor = material.metalness;
				gltfMaterial.pbrMetallicRoughness.roughnessFactor = material.roughness;
429

430
 			} else {
431

432 433
					gltfMaterial.pbrMetallicRoughness.metallicFactor = 0.5;
					gltfMaterial.pbrMetallicRoughness.roughnessFactor = 0.5;
F
Fernando Serrano 已提交
434

435
			}
436

437 438
			// pbrMetallicRoughness.baseColorTexture
			if ( material.map ) {
439

440
				gltfMaterial.pbrMetallicRoughness.baseColorTexture = {
F
Fernando Serrano 已提交
441

442 443
					index: processTexture( material.map ),
					texCoord: 0 // @FIXME
F
Fernando Serrano 已提交
444

445
				};
446

447
			}
448

F
Fernando Serrano 已提交
449 450 451
			if ( material instanceof THREE.MeshBasicMaterial ||
				material instanceof THREE.LineBasicMaterial ||
				material instanceof THREE.PointsMaterial ) {
452 453 454 455

			} else {

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

458 459 460 461
				if ( !equalArray( emissive, [ 0, 0, 0 ] ) ) {

					gltfMaterial.emissiveFactor = emissive;

F
Fernando Serrano 已提交
462
				}
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481

				// emissiveTexture
				if ( material.emissiveMap ) {

					gltfMaterial.emissiveTexture = {

						index: processTexture( material.emissiveMap ),
						texCoord: 0 // @FIXME

					};

				}

			}

			// normalTexture
			if ( material.normalMap ) {

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

483 484
					index: processTexture( material.normalMap ),
					texCoord: 0 // @FIXME
F
Fernando Serrano 已提交
485

486 487
				};

F
Fernando Serrano 已提交
488 489
			}

490 491 492 493
			// occlusionTexture
			if ( material.aoMap ) {

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

495 496
					index: processTexture( material.aoMap ),
					texCoord: 0 // @FIXME
F
Fernando Serrano 已提交
497

498 499 500 501 502 503 504 505 506 507 508 509
				};

			}

			// alphaMode
			if ( material.transparent ) {

				gltfMaterial.alphaMode = 'BLEND'; // @FIXME We should detect MASK or BLEND

			}

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

F
Fernando Serrano 已提交
512
				gltfMaterial.doubleSided = true;
513

F
Fernando Serrano 已提交
514 515
			}

F
Fernando Serrano 已提交
516
			if ( material.name ) {
517

F
Fernando Serrano 已提交
518
				gltfMaterial.name = material.name;
519

F
Fernando Serrano 已提交
520 521
			}

F
Fernando Serrano 已提交
522
			outputJSON.materials.push( gltfMaterial );
F
Fernando Serrano 已提交
523 524

			return outputJSON.materials.length - 1;
525

F
Fernando Serrano 已提交
526 527 528
		}

		/**
F
Fernando Serrano 已提交
529 530 531
		 * Process mesh
		 * @param  {THREE.Mesh} mesh Mesh to process
		 * @return {Integer}      Index of the processed mesh in the "meshes" array
F
Fernando Serrano 已提交
532 533
		 */
		function processMesh( mesh ) {
F
Fernando Serrano 已提交
534

F
Fernando Serrano 已提交
535
			if ( !outputJSON.meshes ) {
F
Fernando Serrano 已提交
536 537 538

				outputJSON.meshes = [];

F
Fernando Serrano 已提交
539 540 541
			}

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

543 544 545
			// Use the correct mode
			if ( mesh instanceof THREE.LineSegments ) {

546
				mode = WEBGL_CONSTANTS.LINES;
547 548 549

			} else if ( mesh instanceof THREE.LineLoop ) {

550
				mode = WEBGL_CONSTANTS.LINE_LOOP;
551 552 553

			} else if ( mesh instanceof THREE.Line ) {

554
				mode = WEBGL_CONSTANTS.LINE_STRIP;
555 556 557

			} else if ( mesh instanceof THREE.Points ) {

558
				mode = WEBGL_CONSTANTS.POINTS;
559 560 561

			} else {

562 563 564 565 566 567 568 569
				if ( !( geometry instanceof THREE.BufferGeometry) ) {

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

				}

570 571
				if ( mesh.drawMode === THREE.TriangleFanDrawMode ) {

F
Fernando Serrano 已提交
572
					console.warn( 'GLTFExporter: TriangleFanDrawMode and wireframe incompatible.' );
573
					mode = WEBGL_CONSTANTS.TRIANGLE_FAN;
574 575 576

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

577
					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINE_STRIP : WEBGL_CONSTANTS.TRIANGLE_STRIP;
578 579 580

				} else {

581
					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES;
582 583 584 585

				}

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

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

596 597 598 599 600 601 602 603
			var material = processMaterial( mesh.material );
			if ( material ) {

				gltfMesh.primitives[ 0 ].material = material;

			}


604 605 606 607 608 609
			if ( geometry.index ) {

				gltfMesh.primitives[ 0 ].indices = processAccessor( geometry.index );

			}

F
Fernando Serrano 已提交
610 611
			// We've just one primitive per mesh
			var gltfAttributes = gltfMesh.primitives[ 0 ].attributes;
F
Fernando Serrano 已提交
612 613
			var attributes = geometry.attributes;

F
Fernando Serrano 已提交
614 615
			// Conversion between attributes names in threejs and gltf spec
			var nameConversion = {
F
Fernando Serrano 已提交
616

F
Fernando Serrano 已提交
617 618
				uv: 'TEXCOORD_0',
				uv2: 'TEXCOORD_1',
619 620 621
				color: 'COLOR_0',
				skinWeight: 'WEIGHTS_0',
				skinIndex: 'JOINTS_0'
F
Fernando Serrano 已提交
622

F
Fernando Serrano 已提交
623 624
			};

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

F
Fernando Serrano 已提交
629
				var attribute = geometry.attributes[ attributeName ];
F
Fernando Serrano 已提交
630
				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
F
Fernando Serrano 已提交
631
				gltfAttributes[ attributeName ] = processAccessor( attribute );
F
Fernando Serrano 已提交
632

F
Fernando Serrano 已提交
633 634 635 636 637 638 639
			}

			outputJSON.meshes.push( gltfMesh );

			return outputJSON.meshes.length - 1;
		}

F
Fernando Serrano 已提交
640 641 642 643 644 645
		/**
		 * 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 已提交
646

F
Fernando Serrano 已提交
647
			if ( !outputJSON.cameras ) {
F
Fernando Serrano 已提交
648

F
Fernando Serrano 已提交
649
				outputJSON.cameras = [];
F
Fernando Serrano 已提交
650

F
Fernando Serrano 已提交
651 652 653 654 655
			}

			var isOrtho = camera instanceof THREE.OrthographicCamera;

			var gltfCamera = {
F
Fernando Serrano 已提交
656

F
Fernando Serrano 已提交
657
				type: isOrtho ? 'orthographic' : 'perspective'
F
Fernando Serrano 已提交
658

F
Fernando Serrano 已提交
659 660 661 662 663 664 665 666 667 668 669
			};

			if ( isOrtho ) {

				gltfCamera.orthographic = {

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

F
Fernando Serrano 已提交
670
				};
F
Fernando Serrano 已提交
671 672 673 674 675 676 677 678 679 680 681 682 683 684

			} else {

				gltfCamera.perspective = {

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

				};

			}

F
Fernando Serrano 已提交
685 686
			if ( camera.name ) {

F
Fernando Serrano 已提交
687
				gltfCamera.name = camera.type;
F
Fernando Serrano 已提交
688

F
Fernando Serrano 已提交
689 690 691 692 693 694 695
			}

			outputJSON.cameras.push( gltfCamera );

			return outputJSON.cameras.length - 1;
		}

F
Fernando Serrano 已提交
696 697 698 699 700 701 702
		/**
		 * Process Object3D node
		 * @param  {THREE.Object3D} node Object3D to processNode
		 * @return {Integer}      Index of the node in the nodes list
		 */
		function processNode ( object ) {

F
Fernando Serrano 已提交
703
			if ( !outputJSON.nodes ) {
F
Fernando Serrano 已提交
704

F
Fernando Serrano 已提交
705
				outputJSON.nodes = [];
F
Fernando Serrano 已提交
706

F
Fernando Serrano 已提交
707 708
			}

F
Fernando Serrano 已提交
709 710 711
			var gltfNode = {};

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

F
Fernando Serrano 已提交
713 714 715 716
				var rotation = object.quaternion.toArray();
				var position = object.position.toArray();
				var scale = object.scale.toArray();

717
				if ( !equalArray( rotation, [ 0, 0, 0, 1 ] ) ) {
F
Fernando Serrano 已提交
718

F
Fernando Serrano 已提交
719
					gltfNode.rotation = rotation;
F
Fernando Serrano 已提交
720

F
Fernando Serrano 已提交
721 722
				}

723
				if ( !equalArray( position, [ 0, 0, 0 ] ) ) {
F
Fernando Serrano 已提交
724

F
Fernando Serrano 已提交
725
					gltfNode.position = position;
F
Fernando Serrano 已提交
726

F
Fernando Serrano 已提交
727 728
				}

729
				if ( !equalArray( scale, [ 1, 1, 1 ] ) ) {
F
Fernando Serrano 已提交
730

F
Fernando Serrano 已提交
731
					gltfNode.scale = scale;
F
Fernando Serrano 已提交
732

F
Fernando Serrano 已提交
733 734 735
				}

			} else {
F
Fernando Serrano 已提交
736

F
Fernando Serrano 已提交
737
				object.updateMatrix();
738
				if (! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
F
Fernando Serrano 已提交
739

F
Fernando Serrano 已提交
740
					gltfNode.matrix = object.matrix.elements;
F
Fernando Serrano 已提交
741

F
Fernando Serrano 已提交
742
				}
F
Fernando Serrano 已提交
743

F
Fernando Serrano 已提交
744 745 746 747
			}

			if ( object.name ) {

F
Fernando Serrano 已提交
748
				gltfNode.name = object.name;
F
Fernando Serrano 已提交
749

F
Fernando Serrano 已提交
750 751
			}

752 753 754 755 756 757 758 759 760 761 762 763 764 765
			if ( object.userData && Object.keys( object.userData ).length > 0 ) {

				try {

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

				} catch (e) {

					throw new Error( 'GLTFExporter: userData can\'t be serialized' );

				}

			}

F
Fernando Serrano 已提交
766 767 768 769
			if ( object instanceof THREE.Mesh ||
				object instanceof THREE.Line ||
				object instanceof THREE.Points ) {

F
Fernando Serrano 已提交
770
				gltfNode.mesh = processMesh( object );
F
Fernando Serrano 已提交
771

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

F
Fernando Serrano 已提交
774
				gltfNode.camera = processCamera( object );
F
Fernando Serrano 已提交
775

F
Fernando Serrano 已提交
776 777 778
			}

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

780
				var children = [];
F
Fernando Serrano 已提交
781 782

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

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

786 787 788 789 790 791 792 793 794 795 796
					if ( child.visible || options.onlyVisible === false ) {

						if ( child instanceof THREE.Mesh ||
							child instanceof THREE.Camera ||
							child instanceof THREE.Group ||
							child instanceof THREE.Line ||
							child instanceof THREE.Points) {

							children.push( processNode( child ) );

						}
F
Fernando Serrano 已提交
797

F
Fernando Serrano 已提交
798
					}
F
Fernando Serrano 已提交
799

F
Fernando Serrano 已提交
800
				}
F
Fernando Serrano 已提交
801

802 803 804 805 806 807 808
				if ( children.length > 0 ) {

					gltfNode.children = children;

				}


F
Fernando Serrano 已提交
809 810 811 812 813
			}

			outputJSON.nodes.push( gltfNode );

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

F
Fernando Serrano 已提交
815 816 817
		}

		/**
F
Fernando Serrano 已提交
818
		 * Process Scene
F
Fernando Serrano 已提交
819 820 821
		 * @param  {THREE.Scene} node Scene to process
		 */
		function processScene( scene ) {
F
Fernando Serrano 已提交
822

823
			if ( !outputJSON.scenes ) {
F
Fernando Serrano 已提交
824

F
Fernando Serrano 已提交
825 826
				outputJSON.scenes = [];
				outputJSON.scene = 0;
F
Fernando Serrano 已提交
827

F
Fernando Serrano 已提交
828 829 830
			}

			var gltfScene = {
F
Fernando Serrano 已提交
831

F
Fernando Serrano 已提交
832
				nodes: []
F
Fernando Serrano 已提交
833

F
Fernando Serrano 已提交
834 835
			};

F
Fernando Serrano 已提交
836 837
			if ( scene.name ) {

F
Fernando Serrano 已提交
838
				gltfScene.name = scene.name;
F
Fernando Serrano 已提交
839

F
Fernando Serrano 已提交
840 841
			}

F
Fernando Serrano 已提交
842
			outputJSON.scenes.push( gltfScene );
F
Fernando Serrano 已提交
843

844 845
			var nodes = [];

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

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

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

852 853 854 855 856 857 858 859 860 861
					// @TODO We don't process lights yet
					if ( child instanceof THREE.Mesh ||
						child instanceof THREE.Camera ||
						child instanceof THREE.Group ||
						child instanceof THREE.Line ||
						child instanceof THREE.Points) {

						nodes.push( processNode( child ) );

					}
862 863 864

				}

865
			}
866

867
			if ( nodes.length > 0 ) {
F
Fernando Serrano 已提交
868

869
				gltfScene.nodes = nodes;
F
Fernando Serrano 已提交
870

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

F
Fernando Serrano 已提交
873 874
		}

875 876 877 878 879 880 881
		/**
		 * Creates a THREE.Scene to hold a list of objects and parse it
		 * @param  {Array} objects List of objects to process
		 */
		function processObjects ( objects ) {

			var scene = new THREE.Scene();
882
			scene.name = 'AuxScene';
883 884 885

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

886 887 888
				// 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 ] );
889 890 891 892 893 894 895

			}

			processScene( scene );

		}

896
		function processInput( input ) {
897

898
			input = input instanceof Array ? input : [ input ];
F
Fernando Serrano 已提交
899

900
			var objectsWithoutScene = [];
F
Fernando Serrano 已提交
901
			for ( i = 0; i < input.length; i++ ) {
F
Fernando Serrano 已提交
902

903
				if ( input[ i ] instanceof THREE.Scene ) {
904 905 906

					processScene( input[ i ] );

907
				} else {
908

909
					objectsWithoutScene.push( input[ i ] );
910

911
				}
F
Fernando Serrano 已提交
912

F
Fernando Serrano 已提交
913
			}
F
Fernando Serrano 已提交
914

915
			if ( objectsWithoutScene.length > 0 ) {
916

917
				processObjects( objectsWithoutScene );
918 919

			}
F
Fernando Serrano 已提交
920

F
Fernando Serrano 已提交
921
		}
F
Fernando Serrano 已提交
922

923 924
		processInput( input );

F
Fernando Serrano 已提交
925
		// Generate buffer
F
Fernando Serrano 已提交
926
		// Create a new blob with all the dataviews from the buffers
927
		var blob = new Blob( dataViews, { type: 'application/octet-stream' } );
F
Fernando Serrano 已提交
928 929

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

932 933 934 935 936 937
			outputJSON.buffers[ 0 ].byteLength = blob.size;
			objectURL = URL.createObjectURL( blob );

			var reader = new window.FileReader();
			 reader.readAsDataURL( blob );
			 reader.onloadend = function() {
F
Fernando Serrano 已提交
938

939 940 941
				 base64data = reader.result;
				 outputJSON.buffers[ 0 ].uri = base64data;
				 onDone( outputJSON );
F
Fernando Serrano 已提交
942 943 944

			 };

945
		} else {
F
Fernando Serrano 已提交
946

947
			onDone ( outputJSON );
F
Fernando Serrano 已提交
948

949
		}
F
Fernando Serrano 已提交
950 951
	}
};