Browse Source

Merge pull request #18799 from LeonYuanYao/BufferGeometry-Compression-Example-Improvements

BufferGeometry compression example improved and codes optimized
Mr.doob 5 years ago
parent
commit
47495ba321

+ 3 - 3
examples/jsm/utils/GeometryCompressionUtils.d.ts

@@ -2,8 +2,8 @@ import { Mesh } from '../../../src/Three';
 
 export namespace GeometryCompressionUtils {
 
-	export function compressNormals( mesh: Mesh, encodeMethod: String );
-	export function compressPositions( mesh: Mesh );
-	export function compressUvs( mesh: Mesh );
+	export function compressNormals( mesh: Mesh, encodeMethod: String ): void;
+	export function compressPositions( mesh: Mesh ): void;
+	export function compressUvs( mesh: Mesh ): void;
 
 }

+ 14 - 8
examples/jsm/utils/GeometryCompressionUtils.js

@@ -58,6 +58,7 @@ var GeometryCompressionUtils = {
 		let result;
 		if ( encodeMethod == "DEFAULT" ) {
 
+			// TODO: Add 1 byte to the result, making the encoded length to be 4 bytes. 
 			result = new Uint8Array( count * 3 );
 
 			for ( let idx = 0; idx < array.length; idx += 3 ) {
@@ -77,6 +78,12 @@ var GeometryCompressionUtils = {
 
 		} else if ( encodeMethod == "OCT1Byte" ) {
 
+			/**
+			* It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
+			* As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
+			* Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
+			*/
+
 			result = new Int8Array( count * 2 );
 
 			for ( let idx = 0; idx < array.length; idx += 3 ) {
@@ -261,7 +268,7 @@ var GeometryCompressionUtils = {
 
 		let array = uvs.array;
 
-		for ( let i = 0; i < array.length; i ++ ) {
+		for ( let i = 0; i < array.length; i++ ) {
 
 			range.min = Math.min( range.min, array[ i ] );
 			range.max = Math.max( range.max, array[ i ] );
@@ -387,7 +394,7 @@ var GeometryCompressionUtils = {
 
 		},
 
-		// for `OCT` encoding
+		// for `Octahedron` encoding
 		octEncodeBest: function ( x, y, z, bytes ) {
 
 			var oct, dec, best, currentCos, bestCos;
@@ -396,7 +403,7 @@ var GeometryCompressionUtils = {
 			// to minimize rounding errors
 			best = oct = octEncodeVec3( x, y, z, "floor", "floor" );
 			dec = octDecodeVec2( oct );
-			currentCos = bestCos = dot( x, y, z, dec );
+			bestCos = dot( x, y, z, dec );
 
 			oct = octEncodeVec3( x, y, z, "ceil", "floor" );
 			dec = octDecodeVec2( oct );
@@ -427,7 +434,6 @@ var GeometryCompressionUtils = {
 			if ( currentCos > bestCos ) {
 
 				best = oct;
-				bestCos = currentCos;
 
 			}
 
@@ -440,10 +446,9 @@ var GeometryCompressionUtils = {
 
 				if ( z < 0 ) {
 
-					var tempx = x;
-					var tempy = y;
-					tempx = ( 1 - Math.abs( y ) ) * ( x >= 0 ? 1 : - 1 );
-					tempy = ( 1 - Math.abs( x ) ) * ( y >= 0 ? 1 : - 1 );
+					var tempx = tempx = ( 1 - Math.abs( y ) ) * ( x >= 0 ? 1 : - 1 );
+					var tempy = tempy = ( 1 - Math.abs( x ) ) * ( y >= 0 ? 1 : - 1 );
+
 					x = tempx;
 					y = tempy;
 
@@ -833,6 +838,7 @@ function PackedPhongMaterial( parameters ) {
 		"}",
 	].join( "\n" );
 
+	// Use the original MeshPhongMaterial's fragmentShader. 
 	this.fragmentShader = [
 		"#define PHONG",
 

+ 45 - 69
examples/webgl_buffergeometry_compression.html

@@ -17,9 +17,10 @@
 			import * as THREE from '../build/three.module.js';
 
 			import Stats from './jsm/libs/stats.module.js';
-
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { GeometryCompressionUtils } from './jsm/utils/GeometryCompressionUtils.js';
+			import { BufferGeometryUtils } from './jsm/utils/BufferGeometryUtils.js';
+			import { TeapotBufferGeometry } from './jsm/geometries/TeapotBufferGeometry.js';
 			import { GUI } from './jsm/libs/dat.gui.module.js';
 
 			var statsEnabled = true;
@@ -32,19 +33,15 @@
 
 			// options
 			var data = {
+				"model": "Icosahedron",
 				"wireframe": false,
 				"texture": false,
 				"detail": 4,
 				"rotationSpeed": 0.1,
 
-				"quantizeEncodePos": false,
-
-				"defaultEncodeNormal": false,
-				"anglesEncodeNormal": false,
-				"oct1bytesEncode": false,
-				"oct2bytesEncode": false,
-
-				"defaultEncodeUV": false,
+				"QuantizePosEncoding": false,
+				"NormEncodingMethods": "None", // for normal encodings
+				"DefaultUVEncoding": false,
 
 				"totalGPUMemory": "0 bytes"
 			};
@@ -109,36 +106,43 @@
 
 				//
 
-				var ballGeom = newGeometry();
+				var geom = newGeometry(data);
 
-				var ballMesh = new THREE.Mesh( ballGeom, meshMaterial );
-				var ballLineSegments = new THREE.LineSegments( new THREE.WireframeGeometry( ballGeom ), lineMaterial );
-				ballLineSegments.visible = data.wireframe;
+				var mesh = new THREE.Mesh( geom, meshMaterial );
+				var lineSegments = new THREE.LineSegments( new THREE.WireframeGeometry( geom ), lineMaterial );
+				lineSegments.visible = data.wireframe;
 
-				scene.add( ballMesh );
-				scene.add( ballLineSegments );
+				scene.add( mesh );
+				scene.add( lineSegments );
 
 				//
 
 				gui = new GUI();
 				gui.width = 350;
 
-				function newGeometry() {
+				function newGeometry(data) {
 
-					var geom = new THREE.IcosahedronBufferGeometry( radius, data.detail );
-					// var geom = new THREE.CylinderBufferGeometry( 5, 5, 20, 32 );
-					// var geom = new THREE.OctahedronBufferGeometry( radius, data.detail );
-					// var geom = new THREE.BoxBufferGeometry(radius, radius, radius, data.detail, data.detail, data.detail);
-					return geom;
+					switch (data.model) {
+						case "Icosahedron": 
+							return new THREE.IcosahedronBufferGeometry( radius, data.detail );
+						case "Cylinder": 
+							return new THREE.CylinderBufferGeometry( radius, radius, radius * 2, data.detail * 6 );
+						case "Teapot": 
+							return new TeapotBufferGeometry( radius, data.detail * 3, true, true, true, true, true );
+						case "TorusKnot": 
+							return new THREE.TorusKnotBufferGeometry( radius, 10, data.detail * 20, data.detail * 6, 3, 4 );
+					}
 
 				}
 
 				function generateGeometry() {
 
+					geom = newGeometry(data);
+
 					updateGroupGeometry(
-						ballMesh,
-						ballLineSegments,
-						newGeometry(),
+						mesh,
+						lineSegments,
+						geom,
 						data );
 
 				}
@@ -146,36 +150,34 @@
 				// updateLineSegments
 				function updateLineSegments() {
 
-					ballLineSegments.visible = data.wireframe;
+					lineSegments.visible = data.wireframe;
 
 				}
 
 				var folder = gui.addFolder( 'Scene' );
-				folder.open();
+				folder.add( data, 'model', ["Icosahedron", "Cylinder", "TorusKnot", "Teapot"] ).onChange( generateGeometry );
 				folder.add( data, 'wireframe', false ).onChange( updateLineSegments );
 				folder.add( data, 'texture', false ).onChange( generateGeometry );
-				folder.add( data, 'detail', 0, 6, 1 ).onChange( generateGeometry );
+				folder.add( data, 'detail', 1, 8, 1 ).onChange( generateGeometry );
 				folder.add( data, 'rotationSpeed', 0, 0.5, 0.1 );
+				folder.open();
 
 				folder = gui.addFolder( 'Position Compression' );
+				folder.add( data, 'QuantizePosEncoding', false ).onChange( generateGeometry );
 				folder.open();
-				folder.add( data, 'quantizeEncodePos', false ).onChange( generateGeometry );
 
 				folder = gui.addFolder( 'Normal Compression' );
+				folder.add( data, 'NormEncodingMethods', ["None", "DEFAULT", "OCT1Byte", "OCT2Byte", "ANGLES"] ).onChange( generateGeometry );
 				folder.open();
-				folder.add( data, 'defaultEncodeNormal', false ).onChange( generateGeometry );
-				folder.add( data, 'anglesEncodeNormal', false ).onChange( generateGeometry );
-				folder.add( data, 'oct1bytesEncode', false ).onChange( generateGeometry );
-				folder.add( data, 'oct2bytesEncode', false ).onChange( generateGeometry );
 
 				folder = gui.addFolder( 'UV Compression' );
+				folder.add( data, 'DefaultUVEncoding', false ).onChange( generateGeometry );
 				folder.open();
-				folder.add( data, 'defaultEncodeUV', false ).onChange( generateGeometry );
 
 				folder = gui.addFolder( 'Memory Info' );
 				folder.open();
 				memoryDisplay = folder.add( data, 'totalGPUMemory', "0 bytes" );
-				computeGPUMemory( ballMesh );
+				computeGPUMemory( mesh );
 
 				//
 
@@ -237,10 +239,11 @@
 
 					geometry = new THREE.BufferGeometry().fromGeometry( geometry );
 
-					console.warn( 'THREE.GeometryBrowser: Converted Geometry to BufferGeometry.' );
+					console.log( 'THREE.GeometryCompression: Converted Geometry to BufferGeometry.' );
 
 				}
 
+				// dispose first
 				lineSegments.geometry.dispose();
 				mesh.geometry.dispose();
 
@@ -249,44 +252,19 @@
 				mesh.material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x111111 } );
 				mesh.material.map = data.texture ? texture : null;
 
-				var normalEncode = "";
-
-				if ( data.oct1bytesEncode ) {
-
-					/**
-					 * It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
-					 * As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
-					 * Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
-					 */
-					normalEncode = "OCT1Byte";
-
-				} else if ( data.oct2bytesEncode ) {
+				if ( data["QuantizePosEncoding"] ) {
 
-					normalEncode = "OCT2Byte";
-
-				} else if ( data.anglesEncodeNormal ) {
-
-					normalEncode = "ANGLES";
-
-				} else if ( data.defaultEncodeNormal ) {
-
-					normalEncode = "DEFAULT";
-
-				}
-
-				if ( normalEncode != "" ) {
-
-					GeometryCompressionUtils.compressNormals( mesh, normalEncode );
+					GeometryCompressionUtils.compressPositions( mesh );
 
 				}
 
-				if ( data.quantizeEncodePos ) {
+				if ( data["NormEncodingMethods"] !== "None" ) {
 
-					GeometryCompressionUtils.compressPositions( mesh );
+					GeometryCompressionUtils.compressNormals( mesh, data["NormEncodingMethods"] );
 
 				}
 
-				if ( data.defaultEncodeUV ) {
+				if ( data["DefaultUVEncoding"] ) {
 
 					GeometryCompressionUtils.compressUvs( mesh );
 
@@ -299,10 +277,8 @@
 
 			function computeGPUMemory( mesh ) {
 
-				let posBytes = mesh.geometry.attributes.position.bytes || mesh.geometry.attributes.position.array.length * 4;
-				let normBytes = mesh.geometry.attributes.normal.bytes || mesh.geometry.attributes.normal.array.length * 4;
-				let uvBytes = mesh.geometry.attributes.uv.bytes || mesh.geometry.attributes.uv.array.length * 4;
-				memoryDisplay.setValue( posBytes + normBytes + uvBytes + " bytes" );
+				// Use BufferGeometryUtils to do memory calculation
+				memoryDisplay.setValue( BufferGeometryUtils.estimateBytesUsed(mesh.geometry) + " bytes");
 
 			}