소스 검색

Merge pull request #16040 from tentone/DracoExporter

Google draco exporter (.drc)
Mr.doob 6 년 전
부모
커밋
107f11b010
3개의 변경된 파일404개의 추가작업 그리고 0개의 파일을 삭제
  1. 206 0
      examples/js/exporters/DracoExporter.js
  2. 3 0
      examples/js/libs/draco/draco_encoder.js
  3. 195 0
      examples/misc_exporter_draco.html

+ 206 - 0
examples/js/exporters/DracoExporter.js

@@ -0,0 +1,206 @@
+'use strict';
+
+/**
+ * Export draco compressed files from threejs geometry objects.
+ *
+ * Draco files are compressed and usually are smaller than conventional 3D file formats.
+ *
+ * The exporter receives a options object containing
+ *  - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality)
+ *  - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality)
+ *  - encoderMethod
+ *  - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)
+ *  - exportUvs
+ *  - exportNormals
+ *
+ * @class DRACOExporter
+ * @author tentone
+ */
+
+THREE.DRACOExporter = function () {};
+
+THREE.DRACOExporter.prototype = {
+
+	constructor: THREE.DRACOExporter,
+
+	parse: function ( geometry, options ) {
+
+
+		if ( DracoEncoderModule === undefined ) {
+
+			throw new Error( 'THREE.DRACOExporter: required the draco_decoder to work.' );
+
+		}
+
+		if ( options === undefined ) {
+
+			options = {
+
+				decodeSpeed: 5,
+				encodeSpeed: 5,
+				encoderMethod: THREE.DRACOExporter.MESH_EDGEBREAKER_ENCODING,
+				quantization: [ 16, 8, 8, 8, 8 ],
+				exportUvs: true,
+				exportNormals: true,
+				exportColor: false,
+
+			};
+
+		}
+
+		var dracoEncoder = DracoEncoderModule();
+		var encoder = new dracoEncoder.Encoder();
+		var builder = new dracoEncoder.MeshBuilder();
+		var mesh = new dracoEncoder.Mesh();
+
+		if ( geometry.isGeometry === true ) {
+
+			var bufferGeometry = new THREE.BufferGeometry();
+			bufferGeometry.fromGeometry( geometry );
+			geometry = bufferGeometry;
+
+		}
+
+		if ( geometry.isBufferGeometry !== true ) {
+
+			throw new Error( 'THREE.DRACOExporter.parse(geometry, options): geometry is not a THREE.Geometry or THREE.BufferGeometry instance.' );
+
+		}
+
+		var vertices = geometry.getAttribute( 'position' );
+		builder.AddFloatAttributeToMesh( mesh, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
+
+		var faces = geometry.getIndex();
+
+		if ( faces !== null ) {
+
+			builder.AddFacesToMesh( mesh, faces.count, faces.array );
+
+		} else {
+
+			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array ) ( vertices.count );
+
+			for ( var i = 0; i < faces.length; i ++ ) {
+
+				faces[ i ] = i;
+
+			}
+
+			builder.AddFacesToMesh( mesh, vertices.count, faces );
+
+		}
+
+		if ( options.exportNormals === true ) {
+
+			var normals = geometry.getAttribute( 'normal' );
+
+			if ( normals !== undefined ) {
+
+				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
+
+			}
+
+		}
+
+		if ( options.exportUvs === true ) {
+
+			var uvs = geometry.getAttribute( 'uv' );
+
+			if ( uvs !== undefined ) {
+
+				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+
+			}
+
+		}
+
+		if ( options.exportColor === true ) {
+
+			var colors = geometry.getAttribute( 'color' );
+
+			if ( colors !== undefined ) {
+
+				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+
+			}
+
+		}
+
+		//Compress using draco encoder
+
+		var encodedData = new dracoEncoder.DracoInt8Array();
+
+		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
+
+		encoder.SetSpeedOptions( options.encodeSpeed || 5, options.decodeSpeed || 5 );
+
+		// Sets the desired encoding method for a given geometry.
+
+		if ( options.encoderMethod !== undefined ) {
+
+			encoder.SetEncodingMethod( options.encoderMethod );
+
+		}
+
+		// Sets the quantization (number of bits used to represent) compression options for a named attribute.
+		// The attribute values will be quantized in a box defined by the maximum extent of the attribute values.
+		if ( options.quantization !== undefined ) {
+
+			for ( var i = 0; i < 5; i ++ ) {
+
+				if ( options.quantization[ i ] !== undefined ) {
+
+					encoder.SetAttributeQuantization( i, options.quantization[ i ] );
+
+				}
+
+			}
+
+		}
+
+		var length = encoder.EncodeMeshToDracoBuffer( mesh, encodedData );
+		dracoEncoder.destroy( mesh );
+
+		if ( length === 0 ) {
+
+			throw new Error( 'THREE.DRACOExporter: Draco encoding failed.' );
+
+		}
+
+		//Copy encoded data to buffer.
+		var outputData = new Int8Array( new ArrayBuffer( length ) );
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			outputData[ i ] = encodedData.GetValue( i );
+
+		}
+
+		dracoEncoder.destroy( encodedData );
+		dracoEncoder.destroy( encoder );
+		dracoEncoder.destroy( builder );
+
+		return outputData;
+
+	}
+
+};
+
+// Encoder methods
+
+THREE.DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1;
+THREE.DRACOExporter.MESH_SEQUENTIAL_ENCODING = 0;
+
+// Geometry type
+
+THREE.DRACOExporter.POINT_CLOUD = 0;
+THREE.DRACOExporter.TRIANGULAR_MESH = 1;
+
+// Attribute type
+
+THREE.DRACOExporter.INVALID = - 1;
+THREE.DRACOExporter.POSITION = 0;
+THREE.DRACOExporter.NORMAL = 1;
+THREE.DRACOExporter.COLOR = 2;
+THREE.DRACOExporter.TEX_COORD = 3;
+THREE.DRACOExporter.GENERIC = 4;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 0
examples/js/libs/draco/draco_encoder.js


+ 195 - 0
examples/misc_exporter_draco.html

@@ -0,0 +1,195 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - exporter - draco</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000;
+				color: #fff;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+			#info a {
+				color: #046;
+				font-weight: bold;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - exporter - draco<br/><br/>
+			<button onclick="exportFile()">export</button>
+			<button onclick="createGeometry()">geometry</button>
+			<button onclick="createBufferGeometry()">buffer geometry</button>
+		</div>
+
+		<script src="../build/three.js"></script>
+		
+		<script src="js/libs/draco/draco_encoder.js"></script>
+
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/exporters/DRACOExporter.js"></script>
+
+		<script src="js/WebGL.js"></script>
+
+		<script>
+
+			if ( WEBGL.isWebGLAvailable() === false ) {
+
+				document.body.appendChild( WEBGL.getWebGLErrorMessage() );
+
+			}
+
+			var scene, camera, renderer, exporter, mesh;
+
+			init();
+			animate();
+
+			function createBufferGeometry() {
+
+				scene.remove( mesh );
+
+				var geometry = new THREE.TorusKnotBufferGeometry( 50, 15, 200, 30 );
+				var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
+				mesh = new THREE.Mesh( geometry, material );
+				mesh.castShadow = true;
+				mesh.position.y = 25;
+				scene.add( mesh );
+
+			}
+
+			function createGeometry() {
+
+				scene.remove( mesh );
+
+				var geometry = new THREE.TorusKnotGeometry( 50, 15, 200, 30 );
+				var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
+				mesh = new THREE.Mesh( geometry, material );
+				mesh.castShadow = true;
+				mesh.position.y = 25;
+				scene.add( mesh );
+			}
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set( 200, 100, 200 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xa0a0a0 );
+				scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 );
+
+				exporter = new THREE.DRACOExporter();
+
+				//
+
+				var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
+				hemiLight.position.set( 0, 200, 0 );
+				scene.add( hemiLight );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffffff );
+				directionalLight.position.set( 0, 200, 100 );
+				directionalLight.castShadow = true;
+				directionalLight.shadow.camera.top = 180;
+				directionalLight.shadow.camera.bottom = - 100;
+				directionalLight.shadow.camera.left = - 120;
+				directionalLight.shadow.camera.right = 120;
+				scene.add( directionalLight );
+
+				// ground
+
+				var ground = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
+				ground.rotation.x = - Math.PI / 2;
+				ground.receiveShadow = true;
+				scene.add( ground );
+
+				var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
+				grid.material.opacity = 0.2;
+				grid.material.transparent = true;
+				scene.add( grid );
+
+				// export mesh
+
+				var geometry = new THREE.TorusKnotBufferGeometry( 50, 15, 200, 30 );
+				var material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
+				mesh = new THREE.Mesh( geometry, material );
+				mesh.castShadow = true;
+				mesh.position.y = 25;
+				scene.add( mesh );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.shadowMap.enabled = true;
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 25, 0 );
+				controls.update();
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+				renderer.render( scene, camera );
+
+			}
+
+			function exportFile() {
+
+				var result = exporter.parse( mesh.geometry );
+				saveArrayBuffer( result, 'file.drc' );
+
+			}
+
+			var link = document.createElement( 'a' );
+			link.style.display = 'none';
+			document.body.appendChild( link );
+
+			function save( blob, filename ) {
+
+				link.href = URL.createObjectURL( blob );
+				link.download = filename;
+				link.click();
+
+			}
+
+			function saveArrayBuffer( buffer, filename ) {
+
+				save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
+
+			}
+
+		</script>
+
+	</body>
+</html>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.