ソースを参照

GLTFLoader: Implement KHR_draco_mesh_compression extension.

Don McCurdy 7 年 前
コミット
056ff3bf00

+ 11 - 0
docs/examples/loaders/GLTFLoader.html

@@ -35,6 +35,9 @@
 			<li>
 				KHR_lights (experimental)
 			</li>
+			<li>
+				KHR_draco_mesh_compression (experimental)
+			</li>
 		</ul>
 
 		<h2>Example</h2>
@@ -43,6 +46,9 @@
 			// Instantiate a loader
 			var loader = new THREE.GLTFLoader();
 
+			// Optional: Provide a DRACOLoader instance to decode compressed mesh data
+			loader.setDRACOLoader( new THREE.DRACOLoader( undefined, {type: 'js'} ) );
+
 			// Load a glTF resource
 			loader.load(
 				// resource URL
@@ -124,6 +130,11 @@
 		[page:String value] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS.
 		</div>
 
+		<h3>[method:null setDRACOLoader]( [page:DRACOLoader dracoLoader] )</h3>
+		<div>
+		[page:DRACOLoader dracoLoader] — Instance of THREE.DRACOLoader, to be used for decoding assets compressed with the KHR_draco_mesh_compression extension.
+		</div>
+
 		<h3>[method:null parse]( [page:ArrayBuffer data], [page:String path], [page:Function onLoad], [page:Function onError] )</h3>
 		<div>
 		[page:ArrayBuffer data] — glTF asset to parse, as an ArrayBuffer or <em>JSON</em> string.<br />

+ 119 - 45
examples/js/loaders/GLTFLoader.js

@@ -11,6 +11,7 @@ THREE.GLTFLoader = ( function () {
 	function GLTFLoader( manager ) {
 
 		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+		this.dracoLoader = null;
 
 	}
 
@@ -68,6 +69,12 @@ THREE.GLTFLoader = ( function () {
 
 		},
 
+		setDRACOLoader: function ( dracoLoader ) {
+
+			this.dracoLoader = dracoLoader;
+
+		},
+
 		parse: function ( data, path, onLoad, onError ) {
 
 			var content;
@@ -127,6 +134,12 @@ THREE.GLTFLoader = ( function () {
 
 				}
 
+				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ) >= 0 ) {
+
+					extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] = new GLTFDracoMeshCompressionExtension( this.dracoLoader );
+
+				}
+
 			}
 
 			console.time( 'GLTFLoader' );
@@ -201,6 +214,7 @@ THREE.GLTFLoader = ( function () {
 
 	var EXTENSIONS = {
 		KHR_BINARY_GLTF: 'KHR_binary_glTF',
+		KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
 		KHR_LIGHTS: 'KHR_lights',
 		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness'
 	};
@@ -357,6 +371,46 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
+	/**
+	 * DRACO Mesh Compression Extension
+	 *
+	 * Specification: https://github.com/KhronosGroup/glTF/pull/874
+	 *
+	 * TODO:
+	 * - Support additional (uncompressed) attributes in addition to those provided by Draco.
+	 * - Support fallback to uncompressed data if decoder is not unavailable.
+	 * - `primitive.attributes` should be passed to DRACOLoader, but isn't yet supported.
+	 */
+	function GLTFDracoMeshCompressionExtension ( dracoLoader ) {
+
+		if ( ! dracoLoader ) {
+
+			throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' );
+
+		}
+
+		this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
+		this.dracoLoader = dracoLoader;
+
+	}
+
+	GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) {
+
+		var dracoLoader = this.dracoLoader;
+		var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
+
+		return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
+
+			return new Promise( function ( resolve ) {
+
+				dracoLoader.decodeDracoFile( bufferView, resolve );
+
+			} );
+
+		} );
+
+	};
+
 	/**
 	 * Specular-Glossiness Extension
 	 *
@@ -1480,6 +1534,15 @@ THREE.GLTFLoader = ( function () {
 
 		var accessorDef = this.json.accessors[ accessorIndex ];
 
+		if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) {
+
+			// Ignore empty accessors, which may be used to declare runtime
+			// information about attributes coming from another source (e.g. Draco
+			// compression extension).
+			return null;
+
+		}
+
 		var pendingBufferViews = [];
 
 		if ( accessorDef.bufferView !== undefined ) {
@@ -1880,6 +1943,8 @@ THREE.GLTFLoader = ( function () {
 	 */
 	GLTFParser.prototype.loadGeometries = function ( primitives ) {
 
+		var parser = this;
+		var extensions = this.extensions;
 		var cache = this.primitiveCache;
 
 		return this.getDependencies( 'accessor' ).then( function ( accessors ) {
@@ -1893,88 +1958,97 @@ THREE.GLTFLoader = ( function () {
 				// See if we've already created this geometry
 				var cached = getCachedGeometry( cache, primitive );
 
+				var geometry;
+
 				if ( cached ) {
 
 					// Use the cached geometry if it exists
 					geometries.push( cached );
 
-				} else {
+					continue;
+
+				} else if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) {
+
+					// Use DRACO geometry if available
+					geometry = extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ].decodePrimitive( primitive, parser );
+
+				} else  {
 
 					// Otherwise create a new geometry
-					var geometry = new THREE.BufferGeometry();
+					geometry = new THREE.BufferGeometry();
 
-					var attributes = primitive.attributes;
+				}
 
-					for ( var attributeId in attributes ) {
+				var attributes = primitive.attributes;
 
-						var attributeEntry = attributes[ attributeId ];
+				for ( var attributeId in attributes ) {
 
-						var bufferAttribute = accessors[ attributeEntry ];
+					var attributeEntry = attributes[ attributeId ];
 
-						switch ( attributeId ) {
+					var bufferAttribute = accessors[ attributeEntry ];
 
-							case 'POSITION':
+					switch ( attributeId ) {
 
-								geometry.addAttribute( 'position', bufferAttribute );
-								break;
+						case 'POSITION':
 
-							case 'NORMAL':
+							geometry.addAttribute( 'position', bufferAttribute );
+							break;
 
-								geometry.addAttribute( 'normal', bufferAttribute );
-								break;
+						case 'NORMAL':
 
-							case 'TEXCOORD_0':
-							case 'TEXCOORD0':
-							case 'TEXCOORD':
+							geometry.addAttribute( 'normal', bufferAttribute );
+							break;
 
-								geometry.addAttribute( 'uv', bufferAttribute );
-								break;
+						case 'TEXCOORD_0':
+						case 'TEXCOORD0':
+						case 'TEXCOORD':
 
-							case 'TEXCOORD_1':
+							geometry.addAttribute( 'uv', bufferAttribute );
+							break;
 
-								geometry.addAttribute( 'uv2', bufferAttribute );
-								break;
+						case 'TEXCOORD_1':
 
-							case 'COLOR_0':
-							case 'COLOR0':
-							case 'COLOR':
+							geometry.addAttribute( 'uv2', bufferAttribute );
+							break;
 
-								geometry.addAttribute( 'color', bufferAttribute );
-								break;
+						case 'COLOR_0':
+						case 'COLOR0':
+						case 'COLOR':
 
-							case 'WEIGHTS_0':
-							case 'WEIGHT': // WEIGHT semantic deprecated.
+							geometry.addAttribute( 'color', bufferAttribute );
+							break;
 
-								geometry.addAttribute( 'skinWeight', bufferAttribute );
-								break;
+						case 'WEIGHTS_0':
+						case 'WEIGHT': // WEIGHT semantic deprecated.
 
-							case 'JOINTS_0':
-							case 'JOINT': // JOINT semantic deprecated.
+							geometry.addAttribute( 'skinWeight', bufferAttribute );
+							break;
 
-								geometry.addAttribute( 'skinIndex', bufferAttribute );
-								break;
+						case 'JOINTS_0':
+						case 'JOINT': // JOINT semantic deprecated.
 
-						}
+							geometry.addAttribute( 'skinIndex', bufferAttribute );
+							break;
 
 					}
 
-					if ( primitive.indices !== undefined ) {
+				}
 
-						geometry.setIndex( accessors[ primitive.indices ] );
+				if ( primitive.indices !== undefined ) {
 
-					}
+					geometry.setIndex( accessors[ primitive.indices ] );
 
-					// Cache this geometry
-					cache.push( {
+				}
 
-						primitive: primitive,
-						geometry: geometry
+				// Cache this geometry
+				cache.push( {
 
-					} );
+					primitive: primitive,
+					geometry: geometry
 
-					geometries.push( geometry );
+				} );
 
-				}
+				geometries.push( geometry );
 
 			}
 

+ 7 - 1
examples/webgl_loader_gltf_extensions.html

@@ -90,11 +90,13 @@
 				<option value="glTF-Embedded">None (Embedded)</option>
 				<option value="glTF-Binary">None (Binary)</option>
 				<option value="glTF-pbrSpecularGlossiness">Specular-Glossiness (PBR)</option>
+				<option value="glTF-Draco">Draco (Compressed)</option>
 			</select>
 		</div>
 	</div>
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/draco/DRACOLoader.js"></script>
 		<script src="js/loaders/GLTFLoader.js"></script>
 
 		<script>
@@ -204,6 +206,10 @@
 
 				loader = new THREE.GLTFLoader();
 
+				var dracoLoader = new THREE.DRACOLoader();
+				dracoLoader.setDecoderPath( 'js/loaders/draco/' );
+				loader.setDRACOLoader( dracoLoader );
+
 				for (var i = 0; i < extensionSelect.children.length; i++) {
 					var child = extensionSelect.children[i];
 					child.disabled = sceneInfo.extensions.indexOf(child.value) === -1;
@@ -475,7 +481,7 @@
 					addLights:true,
 					addGround:true,
 					shadows:true,
-					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
+					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary', 'glTF-Draco']
 				},
 				{
 					name : "Rigged Simple",