Browse Source

Convert: add localParams for collide per mat in hmd (#1269)

Yuxiao Mao 7 months ago
parent
commit
ece366c093
4 changed files with 138 additions and 19 deletions
  1. 3 1
      h3d/mat/PbrMaterial.hx
  2. 12 1
      hxd/fmt/fbx/HMDOut.hx
  3. 101 2
      hxd/fs/Convert.hx
  4. 22 15
      hxd/fs/FileConverter.hx

+ 3 - 1
h3d/mat/PbrMaterial.hx

@@ -95,6 +95,7 @@ typedef PbrProps = {
 	@:optional var drawOrder : String;
 	@:optional var depthPrepass : Bool;
 	@:optional var flipBackFaceNormal : Bool;
+	@:optional var ignoreCollide : Bool;
 }
 
 class PbrMaterial extends Material {
@@ -437,7 +438,7 @@ class PbrMaterial extends Material {
 		if ( props.flipBackFaceNormal && sh == null )
 			mainPass.addShader(new h3d.shader.FlipBackFaceNormal());
 		else if ( !props.flipBackFaceNormal && sh != null )
-			mainPass.removeShader(sh); 
+			mainPass.removeShader(sh);
 	}
 
 	function setColorMask() {
@@ -625,6 +626,7 @@ class PbrMaterial extends Material {
 				</dd>
 				<dt>Depth prepass</dt><dd><input type="checkbox" field="depthPrepass"/></dd>
 				<dt>Flip back face normal</dt><dd><input type="checkbox" field="flipBackFaceNormal"/></dd>
+				<dt>Ignore collide</dt><dd><input type="checkbox" field="ignoreCollide"/></dd>
 			</dl>
 		');
 	}

+ 12 - 1
hxd/fmt/fbx/HMDOut.hx

@@ -24,6 +24,8 @@ class HMDOut extends BaseLibrary {
 	public var generateNormals = false;
 	public var generateTangents = false;
 	public var generateCollides : CollideParams;
+	public var ignoreCollides : Array<String>;
+	var ignoreCollidesCache : Map<Int,Bool> = [];
 	public var lowPrecConfig : Map<String,Precision>;
 	public var lodsDecimation : Array<Float>;
 
@@ -845,9 +847,18 @@ class HMDOut extends BaseLibrary {
 			}
 			var triangleCount = 0;
 			for ( i in 0...Std.int(index.length / 3) ) {
-				var mat = mats == null ? 0 : mats[i];
+				var mat = (mats == null || i >= mats.length) ? 0 : mats[i];
 				if ( mat >= d.materials.length )
 					continue;
+				if( ignoreCollides != null ) {
+					var b = ignoreCollidesCache.get(mat);
+					if( b == null ) {
+						b = ignoreCollides.contains(d.materials[mat].name);
+						ignoreCollidesCache.set(mat, b);
+					}
+					if( b == true )
+						continue;
+				}
 				cb(unpackIndex(index[3*i]));
 				cb(unpackIndex(index[3*i+1]));
 				cb(unpackIndex(index[3*i+2]));

+ 101 - 2
hxd/fs/Convert.hx

@@ -11,9 +11,11 @@ class Convert {
 	public var version(default, null):Int;
 
 	public var params:Dynamic;
+	public var localParams:Dynamic;
 
 	public var srcPath:String;
 	public var dstPath:String;
+	public var baseDir:String;
 	public var originalFilename:String;
 	public var srcBytes:haxe.io.Bytes;
 	/*
@@ -21,16 +23,39 @@ class Convert {
 	*/
 	public var hash : String;
 
-	public function new(sourceExts, destExt) {
+	public function new(sourceExts:String, destExt:String) {
 		this.sourceExts = sourceExts == null ? null : sourceExts.split(",");
 		this.destExt = destExt;
 		this.version = 0;
 	}
 
+	public function cleanup() {
+		params = null;
+		localParams = null;
+		srcPath = null;
+		dstPath = null;
+		baseDir = null;
+		originalFilename = null;
+		srcBytes = null;
+		hash = null;
+	}
+
 	public function convert() {
 		throw "Not implemented";
 	}
 
+	/**
+		A function that should return quickly if the convert might have local params or not.
+		Do not have access to: srcBytes, hash.
+	**/
+	public function hasLocalParams():Bool {
+		return false;
+	}
+
+	public function computeLocalParams():Dynamic {
+		return null;
+	}
+
 	function hasParam(name:String) {
 		var f:Dynamic = Reflect.field(params, name);
 		return f != null && f != false;
@@ -72,12 +97,81 @@ class Convert {
 
 #if (sys || nodejs)
 class ConvertFBX2HMD extends Convert {
+	var fbx : hxd.fmt.fbx.Data.FbxNode;
+
 	public function new() {
 		super("fbx", "hmd");
 	}
 
+	override function cleanup() {
+		super.cleanup();
+		fbx = null;
+	}
+
+	override function hasLocalParams():Bool {
+		return (params != null && params.collide != null);
+	}
+
+	override function computeLocalParams():Dynamic {
+		// Parse fbx to find used materials
+		fbx = try hxd.fmt.fbx.Parser.parse(srcBytes) catch (e:Dynamic) throw Std.string(e) + " in " + srcPath;
+		var matNodes = hxd.fmt.fbx.Data.FbxTools.getAll(fbx, "Objects.Material");
+		var matNames = [];
+		for( o in matNodes ) {
+			var name = hxd.fmt.fbx.Data.FbxTools.getName(o);
+			matNames.push(name);
+		}
+		// Parse material.props to find material config
+		var ignoredMaterials = [];
+		var dirPath = srcPath.substring(0, srcPath.lastIndexOf("/"));
+		var matPropsPath = dirPath + "/materials.props";
+		var matProps = null;
+		try {
+			var res = hxd.File.getBytes(matPropsPath).toString();
+			matProps = haxe.Json.parse(res).materials;
+		} catch( e ) {
+		}
+		if( matProps == null )
+			return null;
+		var modelLibCache = new Map<String, Array<Dynamic>>();
+		for( config in Reflect.fields(matProps) ) {
+			var configProps = Reflect.field(matProps, config);
+			for( matName in matNames ) {
+				var m = Reflect.field(configProps, matName);
+				if( m == null )
+					continue;
+				if( m.ignoreCollide == true ) {
+					ignoredMaterials.push(matName);
+					continue;
+				}
+				// Parse model library
+				if( m.__ref != null && m.name != null ) {
+					var libchildren = modelLibCache.get(m.__ref);
+					if( libchildren == null ) {
+						var lib = try haxe.Json.parse(hxd.File.getBytes(baseDir + m.__ref).toString()) catch( e ) null;
+						if( lib == null || lib.children == null )
+							continue;
+						libchildren = lib.children;
+						modelLibCache.set(m.__ref, libchildren);
+					}
+					for( c in libchildren ) {
+						if( c.type == "material" && c.name == m.name ) {
+							if( c.props?.PBR?.ignoreCollide == true ) {
+								ignoredMaterials.push(matName);
+							}
+							break;
+						}
+					}
+				}
+			}
+		}
+		return { ignoreCollideMaterials : ignoredMaterials };
+	}
+
 	override function convert() {
-		var fbx = try hxd.fmt.fbx.Parser.parse(srcBytes) catch (e:Dynamic) throw Std.string(e) + " in " + srcPath;
+		if( fbx == null ) {
+			fbx = try hxd.fmt.fbx.Parser.parse(srcBytes) catch (e:Dynamic) throw Std.string(e) + " in " + srcPath;
+		}
 		var hmdout = new hxd.fmt.fbx.HMDOut(srcPath);
 		if (params != null) {
 			if (params.normals)
@@ -115,6 +209,11 @@ class ConvertFBX2HMD extends Convert {
 				hmdout.lodsDecimation = [for(lod in config) lod];
 			}
 		}
+		if( localParams != null ) {
+			if( localParams.ignoreCollideMaterials != null ) {
+				hmdout.ignoreCollides = localParams.ignoreCollideMaterials;
+			}
+		}
 		hmdout.load(fbx);
 		var isAnim = StringTools.startsWith(originalFilename, "Anim_") || originalFilename.toLowerCase().indexOf("_anim_") > 0;
 		var hmd = hmdout.toHMD(null, !isAnim);

+ 22 - 15
hxd/fs/FileConverter.hx

@@ -35,7 +35,7 @@ class FileConverter {
 	var tmpDir : String;
 	var configs : Map<String,ConvertConfig> = new Map();
 	var defaultConfig : ConvertConfig;
-	var cache : Map<String,Array<{ out : String, time : Int, hash : String, ver : Null<Int>, milliseconds : Null<Int> }>>;
+	var cache : Map<String,Array<{ out : String, time : Int, hash : String, ver : Null<Int>, milliseconds : Null<Int>, localParamsHash : Null<String> }>>;
 	var cacheTime : Float;
 
 	static var extraConfigs:Array<Dynamic> = [];
@@ -306,7 +306,8 @@ class FileConverter {
 				time : 0,
 				hash : "",
 				ver: conv.version,
-				milliseconds : #if js 0 #else null #end
+				milliseconds : #if js 0 #else null #end,
+				localParamsHash: null
 			};
 			entry.push(match);
 		}
@@ -324,12 +325,27 @@ class FileConverter {
 		#end
 		var alreadyGen = sys.FileSystem.exists(fullOutPath) && match.ver == conv.version #if disable_res_cache && false #end;
 
-		if( alreadyGen && match.time == time #if js && (match.milliseconds == null || match.milliseconds == milliseconds ) #end )
+		conv.params = params;
+		conv.srcPath = fullPath;
+		conv.dstPath = fullOutPath;
+		conv.baseDir = baseDir;
+		conv.originalFilename = e.name;
+		var hasLocalParams = conv.hasLocalParams();
+
+		if( alreadyGen && !hasLocalParams && match.time == time #if js && (match.milliseconds == null || match.milliseconds == milliseconds ) #end ) {
+			conv.cleanup();
 			return; // not changed (time stamp)
+		}
 
 		var content = hxd.File.getBytes(fullPath);
 		var hash = haxe.crypto.Sha1.make(content).toHex();
-		if( alreadyGen && match.hash == hash ) {
+		conv.srcBytes = content;
+		conv.hash = hash;
+		var localParams = hasLocalParams ? conv.computeLocalParams() : null;
+		conv.localParams = localParams;
+		var localParamsHash = localParams == null ? null : haxe.crypto.Sha1.make(haxe.io.Bytes.ofString(formatValue(localParams))).toHex();
+		if( alreadyGen && match.hash == hash && match.localParamsHash == localParamsHash ) {
+			conv.cleanup();
 			match.time = time;
 			match.milliseconds = milliseconds;
 			saveCache();
@@ -338,19 +354,9 @@ class FileConverter {
 
 		sys.FileSystem.createDirectory(fullOutPath.substr(0, fullOutPath.lastIndexOf("/")));
 
-		conv.srcPath = fullPath;
-		conv.dstPath = fullOutPath;
-		conv.srcBytes = content;
-		conv.originalFilename = e.name;
-		conv.params = params;
-		conv.hash = hash;
 		onConvert(conv);
 		executeConvert(conv);
-		conv.hash = null;
-		conv.srcPath = null;
-		conv.dstPath = null;
-		conv.srcBytes = null;
-		conv.originalFilename = null;
+		conv.cleanup();
 
 		if( !sys.FileSystem.exists(fullOutPath) )
 			throw "Converted output file "+fullOutPath+" was not created";
@@ -359,6 +365,7 @@ class FileConverter {
 		match.time = time;
 		match.milliseconds = milliseconds;
 		match.hash = hash;
+		match.localParamsHash = localParamsHash;
 		saveCache();
 	}