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

F
Fernando Serrano 已提交
5 6 7
//------------------------------------------------------------------------------
// GLTF Exporter
//------------------------------------------------------------------------------
F
Fernando Serrano 已提交
8
THREE.GLTFExporter = function ( renderer ) {
F
Fernando Serrano 已提交
9

F
Fernando Serrano 已提交
10
	this.renderer = renderer;
F
Fernando Serrano 已提交
11

F
Fernando Serrano 已提交
12
};
F
Fernando Serrano 已提交
13 14 15 16

THREE.GLTFExporter.prototype = {

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

F
Fernando Serrano 已提交
18 19 20
	/**
	 * Parse scenes and generate GLTF output
	 * @param  {THREE.Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
F
Fernando Serrano 已提交
21 22
	 * @param  {Function} onDone  Callback on completed
	 * @param  {Object} options options
F
Fernando Serrano 已提交
23 24
	 *                          trs: Exports position, rotation and scale instead of matrix
	 */
F
Fernando Serrano 已提交
25 26
	parse: function ( input, onDone, options ) {

F
Fernando Serrano 已提交
27
		options = options || {};
F
Fernando Serrano 已提交
28

29 30
		var glUtils = new THREE.WebGLUtils( this.renderer.context, this.renderer.extensions );
		var gl = this.renderer.context;
F
Fernando Serrano 已提交
31

F
Fernando Serrano 已提交
32
		var outputJSON = {
F
Fernando Serrano 已提交
33

F
Fernando Serrano 已提交
34
			asset: {
F
Fernando Serrano 已提交
35

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

F
Fernando Serrano 已提交
39
		 	}
F
Fernando Serrano 已提交
40

F
Fernando Serrano 已提交
41 42 43 44
    };

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

F
Fernando Serrano 已提交
46 47 48
		/**
		 * Compare two arrays
		 */
F
Fernando Serrano 已提交
49 50 51 52 53 54
		/**
		 * 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
		 */
55
		function equalArray ( array1, array2 ) {
F
Fernando Serrano 已提交
56

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

F
Fernando Serrano 已提交
59
    		return element === array2[ index ];
F
Fernando Serrano 已提交
60

F
Fernando Serrano 已提交
61
			});
F
Fernando Serrano 已提交
62

F
Fernando Serrano 已提交
63 64
		}

F
Fernando Serrano 已提交
65 66 67 68 69 70
		/**
		 * 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 已提交
71

F
Fernando Serrano 已提交
72
			var output = {
F
Fernando Serrano 已提交
73

F
Fernando Serrano 已提交
74 75
				min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
				max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
F
Fernando Serrano 已提交
76

F
Fernando Serrano 已提交
77 78 79
			};

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

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

F
Fernando Serrano 已提交
83
					var value = attribute.array[ i * attribute.itemSize + a ];
F
Fernando Serrano 已提交
84 85 86
					output.min[ a ] = Math.min( output.min[ a ], value );
					output.max[ a ] = Math.max( output.max[ a ], value );

F
Fernando Serrano 已提交
87
				}
F
Fernando Serrano 已提交
88

F
Fernando Serrano 已提交
89 90
			}

F
Fernando Serrano 已提交
91
			return output;
F
Fernando Serrano 已提交
92 93
		}

F
Fernando Serrano 已提交
94
		/**
F
Fernando Serrano 已提交
95 96 97 98
		 * 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 已提交
99 100
		 */
		function processBuffer ( attribute, componentType ) {
F
Fernando Serrano 已提交
101

F
Fernando Serrano 已提交
102
			if ( !outputJSON.buffers ) {
F
Fernando Serrano 已提交
103

F
Fernando Serrano 已提交
104
				outputJSON.buffers = [
F
Fernando Serrano 已提交
105

F
Fernando Serrano 已提交
106
					{
F
Fernando Serrano 已提交
107

F
Fernando Serrano 已提交
108 109
						byteLength: 0,
						uri: ''
F
Fernando Serrano 已提交
110

F
Fernando Serrano 已提交
111
					}
F
Fernando Serrano 已提交
112

F
Fernando Serrano 已提交
113
				];
F
Fernando Serrano 已提交
114

F
Fernando Serrano 已提交
115 116
			}

F
Fernando Serrano 已提交
117
			// Create a new dataview and dump the attribute's array into it
F
Fernando Serrano 已提交
118 119 120
			var dataView = new DataView( new ArrayBuffer( attribute.array.byteLength ) );

			var offset = 0;
121
			var offsetInc = componentType === gl.UNSIGNED_SHORT ? 2 : 4;
F
Fernando Serrano 已提交
122 123

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

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

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

129
					if ( componentType === gl.FLOAT ) {
F
Fernando Serrano 已提交
130

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

133
					} else if ( componentType === gl.UNSIGNED_INT ) {
F
Fernando Serrano 已提交
134

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

137
					} else if ( componentType === gl.UNSIGNED_SHORT ) {
F
Fernando Serrano 已提交
138

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

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

F
Fernando Serrano 已提交
143
					offset += offsetInc;
F
Fernando Serrano 已提交
144

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

F
Fernando Serrano 已提交
147 148
			}

F
Fernando Serrano 已提交
149
			// We just use one buffer
F
Fernando Serrano 已提交
150
			dataViews.push( dataView );
F
Fernando Serrano 已提交
151

F
Fernando Serrano 已提交
152
			// Always using just one buffer
F
Fernando Serrano 已提交
153 154 155 156
			return 0;
		}

		/**
F
Fernando Serrano 已提交
157
		 * Process and generate a BufferView
F
Fernando Serrano 已提交
158 159 160 161
		 * @param  {[type]} data [description]
		 * @return {[type]}      [description]
		 */
		function processBufferView ( data, componentType ) {
F
Fernando Serrano 已提交
162

163
			var isVertexAttributes = componentType === gl.FLOAT;
F
Fernando Serrano 已提交
164

F
Fernando Serrano 已提交
165
			if ( !outputJSON.bufferViews ) {
F
Fernando Serrano 已提交
166

F
Fernando Serrano 已提交
167
				outputJSON.bufferViews = [];
F
Fernando Serrano 已提交
168

F
Fernando Serrano 已提交
169 170 171
			}

			var gltfBufferView = {
F
Fernando Serrano 已提交
172

F
Fernando Serrano 已提交
173 174 175
				buffer: processBuffer( data, componentType ),
				byteOffset: byteOffset,
				byteLength: data.array.byteLength,
176 177
				byteStride: data.itemSize * ( componentType === gl.UNSIGNED_SHORT ? 2 : 4 ),
				target: isVertexAttributes ? gl.ARRAY_BUFFER : gl.ELEMENT_ARRAY_BUFFER
F
Fernando Serrano 已提交
178

F
Fernando Serrano 已提交
179 180 181 182
			};

			byteOffset += data.array.byteLength;

F
Fernando Serrano 已提交
183
			outputJSON.bufferViews.push( gltfBufferView );
F
Fernando Serrano 已提交
184 185

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

F
Fernando Serrano 已提交
188 189
				id: outputJSON.bufferViews.length - 1,
				byteLength: 0
F
Fernando Serrano 已提交
190

F
Fernando Serrano 已提交
191
			};
F
Fernando Serrano 已提交
192

F
Fernando Serrano 已提交
193
			return output;
F
Fernando Serrano 已提交
194

F
Fernando Serrano 已提交
195 196 197
		}

		/**
F
Fernando Serrano 已提交
198 199 200
		 * 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 已提交
201 202
		 */
		function processAccessor ( attribute ) {
F
Fernando Serrano 已提交
203

F
Fernando Serrano 已提交
204
			if ( !outputJSON.accessors ) {
F
Fernando Serrano 已提交
205

F
Fernando Serrano 已提交
206
				outputJSON.accessors = [];
F
Fernando Serrano 已提交
207

F
Fernando Serrano 已提交
208 209 210
			}

			var types = [
F
Fernando Serrano 已提交
211

F
Fernando Serrano 已提交
212 213 214 215
				'SCALAR',
				'VEC2',
				'VEC3',
				'VEC4'
F
Fernando Serrano 已提交
216

F
Fernando Serrano 已提交
217 218
			];

219 220
			var componentType;

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

224
				componentType = gl.FLOAT;
F
Fernando Serrano 已提交
225

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

228
				componentType = gl.UNSIGNED_INT;
F
Fernando Serrano 已提交
229

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

232
				componentType = gl.UNSIGNED_SHORT;
F
Fernando Serrano 已提交
233

234
			} else {
F
Fernando Serrano 已提交
235

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

238
			}
F
Fernando Serrano 已提交
239 240 241

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

F
Fernando Serrano 已提交
243
			var gltfAccessor = {
F
Fernando Serrano 已提交
244

F
Fernando Serrano 已提交
245 246 247 248 249 250
				bufferView: bufferView.id,
				byteOffset: bufferView.byteOffset,
				componentType: componentType,
				count: attribute.count,
				max: minMax.max,
				min: minMax.min,
F
Fernando Serrano 已提交
251
				type: types[ attribute.itemSize - 1 ]
F
Fernando Serrano 已提交
252

F
Fernando Serrano 已提交
253 254 255 256 257
			};

			outputJSON.accessors.push( gltfAccessor );

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

F
Fernando Serrano 已提交
259 260 261
		}

		/**
F
Fernando Serrano 已提交
262 263 264 265 266
		 * 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 已提交
267

F
Fernando Serrano 已提交
268
			if ( !outputJSON.images ) {
F
Fernando Serrano 已提交
269

F
Fernando Serrano 已提交
270
				outputJSON.images = [];
F
Fernando Serrano 已提交
271

F
Fernando Serrano 已提交
272 273 274 275
			}

			var gltfImage = {};

F
Fernando Serrano 已提交
276
			if ( options.embedImages ) {
F
Fernando Serrano 已提交
277

F
Fernando Serrano 已提交
278
				// @TODO { bufferView, mimeType }
F
Fernando Serrano 已提交
279

F
Fernando Serrano 已提交
280
			} else {
F
Fernando Serrano 已提交
281

F
Fernando Serrano 已提交
282 283
				// @TODO base64 based on options
				gltfImage.uri = map.image.src;
F
Fernando Serrano 已提交
284

F
Fernando Serrano 已提交
285 286 287 288 289
			}

			outputJSON.images.push( gltfImage );

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

F
Fernando Serrano 已提交
291 292 293 294 295 296 297 298
		}

		/**
		 * 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 已提交
299

F
Fernando Serrano 已提交
300
			if ( !outputJSON.samplers ) {
F
Fernando Serrano 已提交
301

F
Fernando Serrano 已提交
302
				outputJSON.samplers = [];
F
Fernando Serrano 已提交
303

F
Fernando Serrano 已提交
304 305 306
			}

			var gltfSampler = {
F
Fernando Serrano 已提交
307

F
Fernando Serrano 已提交
308 309 310 311
				magFilter: glUtils.convert( map.magFilter ),
				minFilter: glUtils.convert( map.minFilter ),
				wrapS: glUtils.convert( map.wrapS ),
				wrapT: glUtils.convert( map.wrapT )
F
Fernando Serrano 已提交
312

F
Fernando Serrano 已提交
313 314 315 316 317
			};

			outputJSON.samplers.push( gltfSampler );

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

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

		/**
		 * 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 已提交
327

F
Fernando Serrano 已提交
328
			if (!outputJSON.textures) {
F
Fernando Serrano 已提交
329

F
Fernando Serrano 已提交
330
				outputJSON.textures = [];
F
Fernando Serrano 已提交
331

F
Fernando Serrano 已提交
332 333 334
			}

			var gltfTexture = {
F
Fernando Serrano 已提交
335

F
Fernando Serrano 已提交
336 337
				sampler: processSampler( map ),
				source: processImage( map )
F
Fernando Serrano 已提交
338

F
Fernando Serrano 已提交
339 340 341 342 343
			};

			outputJSON.textures.push( gltfTexture );

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

F
Fernando Serrano 已提交
345 346 347 348 349 350
		}

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

F
Fernando Serrano 已提交
354
			if ( !outputJSON.materials ) {
F
Fernando Serrano 已提交
355

F
Fernando Serrano 已提交
356
				outputJSON.materials = [];
F
Fernando Serrano 已提交
357

F
Fernando Serrano 已提交
358
			}
F
Fernando Serrano 已提交
359

360 361 362 363
			if ( !( material instanceof THREE.MeshStandardMaterial ) ) {

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

F
Fernando Serrano 已提交
364 365 366
			}

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

369
				pbrMetallicRoughness: {}
F
Fernando Serrano 已提交
370

371
			};
372

373 374
			// pbrMetallicRoughness.baseColorFactor
			var color = material.color.toArray().concat( [ material.opacity ] );
F
Fernando Serrano 已提交
375

376
			if ( !equalArray( color, [ 1, 1, 1, 1 ] ) ) {
377

378
				gltfMaterial.pbrMetallicRoughness.baseColorFactor = color;
379 380 381

			}

382
			if ( material instanceof THREE.MeshStandardMaterial ) {
383

384 385
				gltfMaterial.pbrMetallicRoughness.metallicFactor = material.metalness;
				gltfMaterial.pbrMetallicRoughness.roughnessFactor = material.roughness;
386

387
 			} else {
388

389 390
					gltfMaterial.pbrMetallicRoughness.metallicFactor = 0.5;
					gltfMaterial.pbrMetallicRoughness.roughnessFactor = 0.5;
F
Fernando Serrano 已提交
391

392
			}
393

394 395
			// pbrMetallicRoughness.baseColorTexture
			if ( material.map ) {
396

397
				gltfMaterial.pbrMetallicRoughness.baseColorTexture = {
F
Fernando Serrano 已提交
398

399 400
					index: processTexture( material.map ),
					texCoord: 0 // @FIXME
F
Fernando Serrano 已提交
401

402
				};
403

404
			}
405

F
Fernando Serrano 已提交
406 407 408
			if ( material instanceof THREE.MeshBasicMaterial ||
				material instanceof THREE.LineBasicMaterial ||
				material instanceof THREE.PointsMaterial ) {
409 410 411 412

			} else {

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

415 416 417 418
				if ( !equalArray( emissive, [ 0, 0, 0 ] ) ) {

					gltfMaterial.emissiveFactor = emissive;

F
Fernando Serrano 已提交
419
				}
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

				// emissiveTexture
				if ( material.emissiveMap ) {

					gltfMaterial.emissiveTexture = {

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

					};

				}

			}

			// normalTexture
			if ( material.normalMap ) {

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

440 441
					index: processTexture( material.normalMap ),
					texCoord: 0 // @FIXME
F
Fernando Serrano 已提交
442

443 444
				};

F
Fernando Serrano 已提交
445 446
			}

447 448 449 450
			// occlusionTexture
			if ( material.aoMap ) {

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

452 453
					index: processTexture( material.aoMap ),
					texCoord: 0 // @FIXME
F
Fernando Serrano 已提交
454

455 456 457 458 459 460 461 462 463 464 465 466
				};

			}

			// alphaMode
			if ( material.transparent ) {

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

			}

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

F
Fernando Serrano 已提交
469
				gltfMaterial.doubleSided = true;
470

F
Fernando Serrano 已提交
471 472
			}

F
Fernando Serrano 已提交
473
			if ( material.name ) {
474

F
Fernando Serrano 已提交
475
				gltfMaterial.name = material.name;
476

F
Fernando Serrano 已提交
477 478
			}

F
Fernando Serrano 已提交
479
			outputJSON.materials.push( gltfMaterial );
F
Fernando Serrano 已提交
480 481

			return outputJSON.materials.length - 1;
482

F
Fernando Serrano 已提交
483 484 485
		}

		/**
F
Fernando Serrano 已提交
486 487 488
		 * Process mesh
		 * @param  {THREE.Mesh} mesh Mesh to process
		 * @return {Integer}      Index of the processed mesh in the "meshes" array
F
Fernando Serrano 已提交
489 490
		 */
		function processMesh( mesh ) {
F
Fernando Serrano 已提交
491

F
Fernando Serrano 已提交
492
			if ( !outputJSON.meshes ) {
F
Fernando Serrano 已提交
493 494 495

				outputJSON.meshes = [];

F
Fernando Serrano 已提交
496 497 498
			}

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

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
			// Use the correct mode
			if ( mesh instanceof THREE.LineSegments ) {

				mode = gl.LINES;

			} else if ( mesh instanceof THREE.LineLoop ) {

				mode = gl.LINE_LOOP;

			} else if ( mesh instanceof THREE.Line ) {

				mode = gl.LINE_STRIP;

			} else if ( mesh instanceof THREE.Points ) {

				mode = gl.POINTS;

			} else {

519 520 521 522 523 524 525 526
				if ( !( geometry instanceof THREE.BufferGeometry) ) {

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

				}

527 528
				if ( mesh.drawMode === THREE.TriangleFanDrawMode ) {

F
Fernando Serrano 已提交
529
					console.warn( 'GLTFExporter: TriangleFanDrawMode and wireframe incompatible.' );
530
					mode = gl.TRIANGLE_FAN;
531 532 533

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

F
Fernando Serrano 已提交
534
					mode = mesh.material.wireframe ? gl.LINE_STRIP : gl.TRIANGLE_STRIP;
535 536 537

				} else {

F
Fernando Serrano 已提交
538
					mode = mesh.material.wireframe ? gl.LINES : gl.TRIANGLES;
539 540 541 542

				}

			}
F
Fernando Serrano 已提交
543 544 545 546 547 548

			var gltfMesh = {
				primitives: [
					{
						mode: mode,
						attributes: {},
549
						material: processMaterial( mesh.material )
F
Fernando Serrano 已提交
550 551 552 553
					}
				]
			};

554 555 556 557 558 559
			if ( geometry.index ) {

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

			}

F
Fernando Serrano 已提交
560 561
			// We've just one primitive per mesh
			var gltfAttributes = gltfMesh.primitives[ 0 ].attributes;
F
Fernando Serrano 已提交
562 563
			var attributes = geometry.attributes;

F
Fernando Serrano 已提交
564 565
			// Conversion between attributes names in threejs and gltf spec
			var nameConversion = {
F
Fernando Serrano 已提交
566

F
Fernando Serrano 已提交
567 568
				uv: 'TEXCOORD_0',
				uv2: 'TEXCOORD_1',
569 570 571
				color: 'COLOR_0',
				skinWeight: 'WEIGHTS_0',
				skinIndex: 'JOINTS_0'
F
Fernando Serrano 已提交
572

F
Fernando Serrano 已提交
573 574
			};

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

F
Fernando Serrano 已提交
579
				var attribute = geometry.attributes[ attributeName ];
F
Fernando Serrano 已提交
580
				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
F
Fernando Serrano 已提交
581
				gltfAttributes[ attributeName ] = processAccessor( attribute );
F
Fernando Serrano 已提交
582

F
Fernando Serrano 已提交
583 584 585 586 587 588 589
			}

			outputJSON.meshes.push( gltfMesh );

			return outputJSON.meshes.length - 1;
		}

F
Fernando Serrano 已提交
590 591 592 593 594 595
		/**
		 * 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 已提交
596

F
Fernando Serrano 已提交
597
			if ( !outputJSON.cameras ) {
F
Fernando Serrano 已提交
598

F
Fernando Serrano 已提交
599
				outputJSON.cameras = [];
F
Fernando Serrano 已提交
600

F
Fernando Serrano 已提交
601 602 603 604 605
			}

			var isOrtho = camera instanceof THREE.OrthographicCamera;

			var gltfCamera = {
F
Fernando Serrano 已提交
606

F
Fernando Serrano 已提交
607
				type: isOrtho ? 'orthographic' : 'perspective'
F
Fernando Serrano 已提交
608

F
Fernando Serrano 已提交
609 610 611 612 613 614 615 616 617 618 619
			};

			if ( isOrtho ) {

				gltfCamera.orthographic = {

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

F
Fernando Serrano 已提交
620
				};
F
Fernando Serrano 已提交
621 622 623 624 625 626 627 628 629 630 631 632 633 634

			} else {

				gltfCamera.perspective = {

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

				};

			}

F
Fernando Serrano 已提交
635 636
			if ( camera.name ) {

F
Fernando Serrano 已提交
637
				gltfCamera.name = camera.type;
F
Fernando Serrano 已提交
638

F
Fernando Serrano 已提交
639 640 641 642 643 644 645
			}

			outputJSON.cameras.push( gltfCamera );

			return outputJSON.cameras.length - 1;
		}

F
Fernando Serrano 已提交
646 647 648 649 650 651 652
		/**
		 * 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 已提交
653
			if ( !outputJSON.nodes ) {
F
Fernando Serrano 已提交
654

F
Fernando Serrano 已提交
655
				outputJSON.nodes = [];
F
Fernando Serrano 已提交
656

F
Fernando Serrano 已提交
657 658
			}

F
Fernando Serrano 已提交
659 660 661
			var gltfNode = {};

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

F
Fernando Serrano 已提交
663 664 665 666
				var rotation = object.quaternion.toArray();
				var position = object.position.toArray();
				var scale = object.scale.toArray();

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

F
Fernando Serrano 已提交
669
					gltfNode.rotation = rotation;
F
Fernando Serrano 已提交
670

F
Fernando Serrano 已提交
671 672
				}

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

F
Fernando Serrano 已提交
675
					gltfNode.position = position;
F
Fernando Serrano 已提交
676

F
Fernando Serrano 已提交
677 678
				}

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

F
Fernando Serrano 已提交
681
					gltfNode.scale = scale;
F
Fernando Serrano 已提交
682

F
Fernando Serrano 已提交
683 684 685
				}

			} else {
F
Fernando Serrano 已提交
686

F
Fernando Serrano 已提交
687
				object.updateMatrix();
688
				if (! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
F
Fernando Serrano 已提交
689

F
Fernando Serrano 已提交
690
					gltfNode.matrix = object.matrix.elements;
F
Fernando Serrano 已提交
691

F
Fernando Serrano 已提交
692
				}
F
Fernando Serrano 已提交
693

F
Fernando Serrano 已提交
694 695 696 697
			}

			if ( object.name ) {

F
Fernando Serrano 已提交
698
				gltfNode.name = object.name;
F
Fernando Serrano 已提交
699

F
Fernando Serrano 已提交
700 701
			}

702 703 704 705 706 707 708 709 710 711 712 713 714 715
			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 已提交
716 717 718 719
			if ( object instanceof THREE.Mesh ||
				object instanceof THREE.Line ||
				object instanceof THREE.Points ) {

F
Fernando Serrano 已提交
720
				gltfNode.mesh = processMesh( object );
F
Fernando Serrano 已提交
721

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

F
Fernando Serrano 已提交
724
				gltfNode.camera = processCamera( object );
F
Fernando Serrano 已提交
725

F
Fernando Serrano 已提交
726 727 728
			}

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

F
Fernando Serrano 已提交
730
				gltfNode.children = [];
F
Fernando Serrano 已提交
731 732

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

F
Fernando Serrano 已提交
734
					var child = object.children[ i ];
F
Fernando Serrano 已提交
735 736 737 738 739 740
					if ( child instanceof THREE.Mesh ||
						child instanceof THREE.Camera ||
						child instanceof THREE.Group ||
						child instanceof THREE.Line ||
						child instanceof THREE.Points) {

F
Fernando Serrano 已提交
741
						gltfNode.children.push( processNode( child ) );
F
Fernando Serrano 已提交
742

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

F
Fernando Serrano 已提交
745
				}
F
Fernando Serrano 已提交
746

F
Fernando Serrano 已提交
747 748 749 750 751
			}

			outputJSON.nodes.push( gltfNode );

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

F
Fernando Serrano 已提交
753 754 755
		}

		/**
F
Fernando Serrano 已提交
756
		 * Process Scene
F
Fernando Serrano 已提交
757 758 759
		 * @param  {THREE.Scene} node Scene to process
		 */
		function processScene( scene ) {
F
Fernando Serrano 已提交
760

761
			if ( !outputJSON.scenes ) {
F
Fernando Serrano 已提交
762

F
Fernando Serrano 已提交
763 764
				outputJSON.scenes = [];
				outputJSON.scene = 0;
F
Fernando Serrano 已提交
765

F
Fernando Serrano 已提交
766 767 768
			}

			var gltfScene = {
F
Fernando Serrano 已提交
769

F
Fernando Serrano 已提交
770
				nodes: []
F
Fernando Serrano 已提交
771

F
Fernando Serrano 已提交
772 773
			};

F
Fernando Serrano 已提交
774 775
			if ( scene.name ) {

F
Fernando Serrano 已提交
776
				gltfScene.name = scene.name;
F
Fernando Serrano 已提交
777

F
Fernando Serrano 已提交
778 779
			}

F
Fernando Serrano 已提交
780
			outputJSON.scenes.push( gltfScene );
F
Fernando Serrano 已提交
781 782

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

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

F
Fernando Serrano 已提交
786 787 788 789 790 791 792
				// @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) {

F
Fernando Serrano 已提交
793
					gltfScene.nodes.push( processNode( child ) );
F
Fernando Serrano 已提交
794

F
Fernando Serrano 已提交
795
				}
F
Fernando Serrano 已提交
796

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

F
Fernando Serrano 已提交
799 800
		}

801 802 803 804 805 806 807
		/**
		 * 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();
808
			scene.name = 'AuxScene';
809 810 811

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

812 813 814
				// 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 ] );
815 816 817 818 819 820 821

			}

			processScene( scene );

		}

822
		function processInput( input ) {
823

824
			input = input instanceof Array ? input : [ input ];
F
Fernando Serrano 已提交
825

826
			var objectsWithoutScene = [];
F
Fernando Serrano 已提交
827
			for ( i = 0; i < input.length; i++ ) {
F
Fernando Serrano 已提交
828

829
				if ( input[ i ] instanceof THREE.Scene ) {
830 831 832

					processScene( input[ i ] );

833
				} else {
834

835
					objectsWithoutScene.push( input[ i ] );
836

837
				}
F
Fernando Serrano 已提交
838

F
Fernando Serrano 已提交
839
			}
F
Fernando Serrano 已提交
840

841
			if ( objectsWithoutScene.length > 0 ) {
842

843
				processObjects( objectsWithoutScene );
844 845

			}
F
Fernando Serrano 已提交
846

F
Fernando Serrano 已提交
847
		}
F
Fernando Serrano 已提交
848

849 850
		processInput( input );

F
Fernando Serrano 已提交
851
		// Generate buffer
F
Fernando Serrano 已提交
852 853 854 855
		// Create a new blob with all the dataviews from the buffers
		var blob = new Blob( dataViews, { type: 'application/octet-binary' } );

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

858 859 860 861 862 863
			outputJSON.buffers[ 0 ].byteLength = blob.size;
			objectURL = URL.createObjectURL( blob );

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

865 866 867
				 base64data = reader.result;
				 outputJSON.buffers[ 0 ].uri = base64data;
				 onDone( outputJSON );
F
Fernando Serrano 已提交
868 869 870

			 };

871
		} else {
F
Fernando Serrano 已提交
872

873
			onDone ( outputJSON );
F
Fernando Serrano 已提交
874

875
		}
F
Fernando Serrano 已提交
876 877
	}
};