Browse Source

Updated meshoptimizer.

Бранимир Караџић 6 years ago
parent
commit
1d2ea72c70
2 changed files with 216 additions and 150 deletions
  1. 116 116
      3rdparty/meshoptimizer/demo/GLTFLoader.js
  2. 100 34
      3rdparty/meshoptimizer/tools/gltfpack.cpp

+ 116 - 116
3rdparty/meshoptimizer/demo/GLTFLoader.js

@@ -14,6 +14,7 @@ THREE.GLTFLoader = ( function () {
 
 		this.dracoLoader = null;
 		this.ddsLoader = null;
+		this.basisLoader = null;
 		this.meshoptDecoder = null;
 
 	}
@@ -111,6 +112,13 @@ THREE.GLTFLoader = ( function () {
 
 		},
 
+		setBasisLoader: function ( basisLoader ) {
+
+			this.basisLoader = basisLoader;
+			return this;
+
+		},
+
 		setMeshoptDecoder: function ( decoder ) {
 
 			this.meshoptDecoder = decoder;
@@ -222,7 +230,9 @@ THREE.GLTFLoader = ( function () {
 
 				path: path || this.resourcePath || '',
 				crossOrigin: this.crossOrigin,
-				manager: this.manager
+				manager: this.manager,
+				ddsLoader: this.ddsLoader,
+				basisLoader: this.basisLoader,
 
 			} );
 
@@ -1390,95 +1400,9 @@ THREE.GLTFLoader = ( function () {
 			var morphPositions = accessors[ 0 ];
 			var morphNormals = accessors[ 1 ];
 
-			// Clone morph target accessors before modifying them.
-
-			for ( var i = 0, il = morphPositions.length; i < il; i ++ ) {
-
-				if ( geometry.attributes.position === morphPositions[ i ] ) continue;
-
-				morphPositions[ i ] = cloneBufferAttribute( morphPositions[ i ] );
-
-			}
-
-			for ( var i = 0, il = morphNormals.length; i < il; i ++ ) {
-
-				if ( geometry.attributes.normal === morphNormals[ i ] ) continue;
-
-				morphNormals[ i ] = cloneBufferAttribute( morphNormals[ i ] );
-
-			}
-
-			for ( var i = 0, il = targets.length; i < il; i ++ ) {
-
-				var target = targets[ i ];
-				var attributeName = 'morphTarget' + i;
-
-				if ( hasMorphPosition ) {
-
-					// Three.js morph position is absolute value. The formula is
-					//   basePosition
-					//     + weight0 * ( morphPosition0 - basePosition )
-					//     + weight1 * ( morphPosition1 - basePosition )
-					//     ...
-					// while the glTF one is relative
-					//   basePosition
-					//     + weight0 * glTFmorphPosition0
-					//     + weight1 * glTFmorphPosition1
-					//     ...
-					// then we need to convert from relative to absolute here.
-
-					if ( target.POSITION !== undefined ) {
-
-						var positionAttribute = morphPositions[ i ];
-						positionAttribute.name = attributeName;
-
-						var position = geometry.attributes.position;
-
-						for ( var j = 0, jl = positionAttribute.count; j < jl; j ++ ) {
-
-							positionAttribute.setXYZ(
-								j,
-								positionAttribute.getX( j ) + position.getX( j ),
-								positionAttribute.getY( j ) + position.getY( j ),
-								positionAttribute.getZ( j ) + position.getZ( j )
-							);
-
-						}
-
-					}
-
-				}
-
-				if ( hasMorphNormal ) {
-
-					// see target.POSITION's comment
-
-					if ( target.NORMAL !== undefined ) {
-
-						var normalAttribute = morphNormals[ i ];
-						normalAttribute.name = attributeName;
-
-						var normal = geometry.attributes.normal;
-
-						for ( var j = 0, jl = normalAttribute.count; j < jl; j ++ ) {
-
-							normalAttribute.setXYZ(
-								j,
-								normalAttribute.getX( j ) + normal.getX( j ),
-								normalAttribute.getY( j ) + normal.getY( j ),
-								normalAttribute.getZ( j ) + normal.getZ( j )
-							);
-
-						}
-
-					}
-
-				}
-
-			}
-
 			if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions;
 			if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals;
+			geometry.morphTargetsRelative = true;
 
 			return geometry;
 
@@ -1566,31 +1490,6 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
-	function cloneBufferAttribute( attribute ) {
-
-		if ( attribute.isInterleavedBufferAttribute ) {
-
-			var count = attribute.count;
-			var itemSize = attribute.itemSize;
-			var array = attribute.array.slice( 0, count * itemSize );
-
-			for ( var i = 0, j = 0; i < count; ++ i ) {
-
-				array[ j ++ ] = attribute.getX( i );
-				if ( itemSize >= 2 ) array[ j ++ ] = attribute.getY( i );
-				if ( itemSize >= 3 ) array[ j ++ ] = attribute.getZ( i );
-				if ( itemSize >= 4 ) array[ j ++ ] = attribute.getW( i );
-
-			}
-
-			return new THREE.BufferAttribute( array, itemSize, attribute.normalized );
-
-		}
-
-		return attribute.clone();
-
-	}
-
 	/* GLTF PARSER */
 
 	function GLTFParser( json, extensions, options ) {
@@ -2081,11 +1980,43 @@ THREE.GLTFLoader = ( function () {
 
 			var loader = options.manager.getHandler( sourceURI );
 
+			if ( ! loader && textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] ) {
+
+				loader = parser.extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].ddsLoader
+
+			}
+
+			if ( ! loader && options.ddsLoader ) {
+
+				if ( source.uri !== undefined && source.uri.toLowerCase().endsWith(".dds") ) {
+
+					loader = options.ddsLoader;
+
+				} else if ( source.bufferView !== undefined && source.mimeType == "image/vnd-ms.dds" ) {
+
+					loader = options.ddsLoader;
+
+				}
+
+			}
+
+			if ( ! loader && options.basisLoader ) {
+
+				if ( source.uri !== undefined && source.uri.toLowerCase().endsWith(".basis") ) {
+
+					loader = options.basisLoader;
+
+				} else if ( source.bufferView !== undefined && source.mimeType == "image/basis" ) {
+
+					loader = options.basisLoader;
+
+				}
+
+			}
+
 			if ( ! loader ) {
 
-				loader = textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ]
-					? parser.extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].ddsLoader
-					: textureLoader;
+				loader = textureLoader;
 
 			}
 
@@ -2456,6 +2387,73 @@ THREE.GLTFLoader = ( function () {
 
 	};
 
+	/**
+	 * @param {THREE.BufferGeometry} geometry
+	 * @param {GLTF.Primitive} primitiveDef
+	 * @param {GLTFParser} parser
+	 */
+	function computeBounds( geometry, primitiveDef, parser ) {
+
+		var attributes = primitiveDef.attributes;
+
+		var box = new THREE.Box3();
+
+		if ( attributes.POSITION !== undefined ) {
+
+			var accessor = parser.json.accessors[ attributes.POSITION ];
+			var min = accessor.min;
+			var max = accessor.max;
+
+			box.set(
+				new THREE.Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ),
+				new THREE.Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) );
+
+		} else {
+
+			return;
+
+		}
+
+		var targets = primitiveDef.targets;
+
+		if ( targets !== undefined ) {
+
+			var vector = new THREE.Vector3();
+
+			for ( var i = 0, il = targets.length; i < il; i ++ ) {
+
+				var target = targets[ i ];
+
+				if ( target.POSITION !== undefined ) {
+
+					var accessor = parser.json.accessors[ target.POSITION ];
+					var min = accessor.min;
+					var max = accessor.max;
+
+					// we need to get max of absolute components because target weight is [-1,1]
+					vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) );
+					vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) );
+					vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) );
+
+					box.expandByVector( vector );
+
+				}
+
+			}
+
+		}
+
+		geometry.boundingBox = box;
+
+		var sphere = new THREE.Sphere();
+
+		box.getCenter( sphere.center );
+		sphere.radius = box.min.distanceTo( box.max ) / 2;
+
+		geometry.boundingSphere = sphere;
+
+	}
+
 	/**
 	 * @param {THREE.BufferGeometry} geometry
 	 * @param {GLTF.Primitive} primitiveDef
@@ -2504,6 +2502,8 @@ THREE.GLTFLoader = ( function () {
 
 		assignExtrasToUserData( geometry, primitiveDef );
 
+		computeBounds( geometry, primitiveDef, parser );
+
 		return Promise.all( pending ).then( function () {
 
 			return primitiveDef.targets !== undefined

+ 100 - 34
3rdparty/meshoptimizer/tools/gltfpack.cpp

@@ -34,7 +34,9 @@
 #include <stdio.h>
 #include <string.h>
 
-#ifndef _WIN32
+#ifdef _WIN32
+#include <io.h>
+#else
 #include <unistd.h>
 #endif
 
@@ -90,6 +92,8 @@ struct Settings
 	bool texture_embed;
 	bool texture_basis;
 
+	int texture_quality;
+
 	bool compress;
 	bool fallback;
 
@@ -1525,11 +1529,7 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua
 		{
 			const Attr& a = stream.data[i];
 
-			float v[4] = {
-			    a.f[0],
-			    a.f[1],
-			    a.f[2],
-			    a.f[3]};
+			float v[4] = {a.f[0], a.f[1], a.f[2], a.f[3]};
 			bin.append(reinterpret_cast<const char*>(v), sizeof(v));
 		}
 
@@ -1656,19 +1656,37 @@ StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type typ
 		StreamFormat format = {cgltf_type_scalar, cgltf_component_type_r_8u, true, 1};
 		return format;
 	}
-	else
+	else if (type == cgltf_animation_path_type_translation || type == cgltf_animation_path_type_scale)
 	{
+		int bits = 15;
+
 		for (size_t i = 0; i < data.size(); ++i)
 		{
 			const Attr& a = data[i];
 
-			float v[3] = {a.f[0], a.f[1], a.f[2]};
+			float v[3] = {
+			    meshopt_quantizeFloat(a.f[0], bits),
+			    meshopt_quantizeFloat(a.f[1], bits),
+			    meshopt_quantizeFloat(a.f[2], bits)};
 			bin.append(reinterpret_cast<const char*>(v), sizeof(v));
 		}
 
 		StreamFormat format = {cgltf_type_vec3, cgltf_component_type_r_32f, false, 12};
 		return format;
 	}
+	else
+	{
+		for (size_t i = 0; i < data.size(); ++i)
+		{
+			const Attr& a = data[i];
+
+			float v[4] = {a.f[0], a.f[1], a.f[2], a.f[3]};
+			bin.append(reinterpret_cast<const char*>(v), sizeof(v));
+		}
+
+		StreamFormat format = {cgltf_type_vec4, cgltf_component_type_r_32f, false, 16};
+		return format;
+	}
 }
 
 void compressVertexStream(std::string& bin, const std::string& data, size_t count, size_t stride)
@@ -2724,9 +2742,52 @@ bool writeFile(const char* path, const std::string& data)
 	return result == data.size();
 }
 
-bool encodeBasisFile(const char* input, const char* output, bool normal_map)
+struct TempFile
 {
-	std::string cmd = "basisu";
+	std::string path;
+	int fd;
+
+	TempFile(const char* suffix)
+	    : fd(-1)
+	{
+#ifdef _WIN32
+		const char* temp_dir = getenv("TEMP");
+		path = temp_dir ? temp_dir : ".";
+		path += "\\gltfpack-XXXXXX";
+		(void)_mktemp(&path[0]);
+		path += suffix;
+#else
+		path = "/tmp/gltfpack-XXXXXX";
+		path += suffix;
+		fd = mkstemps(&path[0], strlen(suffix));
+#endif
+	}
+
+	~TempFile()
+	{
+		unlink(path.c_str());
+#ifndef _WIN32
+		close(fd);
+#endif
+	}
+};
+
+bool encodeBasis(const std::string& data, std::string& result, bool normal_map, int quality)
+{
+	TempFile temp_input(".raw");
+	TempFile temp_output(".basis");
+
+	if (!writeFile(temp_input.path.c_str(), data))
+		return false;
+
+	const char* basisu_path = getenv("BASISU_PATH");
+	std::string cmd = basisu_path ? basisu_path : "basisu";
+
+	char ql[16];
+	sprintf(ql, "%d", (quality * 255 + 50) / 100);
+
+	cmd += " -q ";
+	cmd += ql;
 
 	cmd += " -mipmap";
 	if (normal_map)
@@ -2736,9 +2797,9 @@ bool encodeBasisFile(const char* input, const char* output, bool normal_map)
 	}
 
 	cmd += " -file ";
-	cmd += input;
+	cmd += temp_input.path;
 	cmd += " -output_file ";
-	cmd += output;
+	cmd += temp_output.path;
 
 #ifdef _WIN32
 	cmd += " >nul";
@@ -2746,23 +2807,9 @@ bool encodeBasisFile(const char* input, const char* output, bool normal_map)
 	cmd += " >/dev/null";
 #endif
 
-	return system(cmd.c_str()) == 0;
-}
+	int rc = system(cmd.c_str());
 
-bool encodeBasisData(const std::string& data, std::string& result, bool normal_map, const char* output_path)
-{
-	std::string temp_name = getFileName(output_path) + ".temp";
-	std::string temp_input = getFullPath(temp_name.c_str(), output_path) + ".png";
-	std::string temp_output = getFullPath(temp_name.c_str(), output_path) + ".basis";
-
-	bool ok =
-	    writeFile(temp_input.c_str(), data) &&
-	    encodeBasisFile(temp_input.c_str(), temp_output.c_str(), normal_map) &&
-	    readFile(temp_output.c_str(), result);
-
-	unlink(temp_input.c_str());
-	unlink(temp_output.c_str());
-	return ok;
+	return rc == 0 && readFile(temp_output.path.c_str(), result);
 }
 
 void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_image& image, const ImageInfo& info, size_t index, const char* input_path, const char* output_path, const Settings& settings)
@@ -2799,7 +2846,7 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
 		{
 			std::string encoded;
 
-			if (encodeBasisData(img_data, encoded, info.normal_map, output_path))
+			if (encodeBasis(img_data, encoded, info.normal_map, settings.texture_quality))
 			{
 				writeEmbeddedImage(json, views, encoded.c_str(), encoded.size(), "image/basis");
 			}
@@ -2821,15 +2868,28 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
 			std::string basis_path = getFileName(image.uri) + ".basis";
 			std::string basis_full_path = getFullPath(basis_path.c_str(), output_path);
 
-			if (encodeBasisFile(full_path.c_str(), basis_full_path.c_str(), info.normal_map))
+			if (!readFile(full_path.c_str(), img_data))
 			{
-				append(json, "\"uri\":\"");
-				append(json, basis_path);
-				append(json, "\"");
+				fprintf(stderr, "Warning: unable to read image %s, skipping\n", image.uri);
 			}
 			else
 			{
-				fprintf(stderr, "Warning: unable to encode image %s with Basis, skipping\n", image.uri);
+				std::string encoded;
+
+				if (!encodeBasis(img_data, encoded, info.normal_map, settings.texture_quality))
+				{
+					fprintf(stderr, "Warning: unable to encode image %s with Basis, skipping\n", image.uri);
+				}
+				else if (!writeFile(basis_full_path.c_str(), encoded))
+				{
+					fprintf(stderr, "Warning: unable to save Basis image %s, skipping\n", image.uri);
+				}
+				else
+				{
+					append(json, "\"uri\":\"");
+					append(json, basis_path);
+					append(json, "\"");
+				}
 			}
 		}
 		else
@@ -4139,6 +4199,7 @@ int main(int argc, char** argv)
 	settings.nrm_bits = 8;
 	settings.anim_freq = 30;
 	settings.simplify_threshold = 1.f;
+	settings.texture_quality = 50;
 
 	const char* input = 0;
 	const char* output = 0;
@@ -4193,6 +4254,10 @@ int main(int argc, char** argv)
 		{
 			settings.texture_basis = true;
 		}
+		else if (strcmp(arg, "-tq") == 0 && i + 1 < argc && isdigit(argv[i + 1][0]))
+		{
+			settings.texture_quality = atoi(argv[++i]);
+		}
 		else if (strcmp(arg, "-i") == 0 && i + 1 < argc && !input)
 		{
 			input = argv[++i];
@@ -4263,6 +4328,7 @@ int main(int argc, char** argv)
 		fprintf(stderr, "-sa: aggressively simplify to the target ratio disregarding quality\n");
 		fprintf(stderr, "-te: embed all textures into main buffer\n");
 		fprintf(stderr, "-tb: convert all textures to Basis Universal format (with basisu executable)\n");
+		fprintf(stderr, "-tq N: set texture encoding quality (default: 50; N should be between 1 and 100\n");
 		fprintf(stderr, "-c: produce compressed gltf/glb files\n");
 		fprintf(stderr, "-cf: produce compressed gltf/glb files with fallback for loaders that don't support compression\n");
 		fprintf(stderr, "-v: verbose output\n");