浏览代码

allow to setup hmd per input precision

Nicolas Cannasse 2 年之前
父节点
当前提交
54b8222857
共有 6 个文件被更改,包括 178 次插入57 次删除
  1. 105 11
      hxd/fmt/fbx/HMDOut.hx
  2. 39 37
      hxd/fmt/hmd/Library.hx
  3. 5 1
      hxd/fmt/hmd/Reader.hx
  4. 1 1
      hxd/fmt/hmd/Writer.hx
  5. 12 1
      hxd/fs/Convert.hx
  6. 16 6
      hxd/fs/FileConverter.hx

+ 105 - 11
hxd/fmt/fbx/HMDOut.hx

@@ -2,6 +2,7 @@ package hxd.fmt.fbx;
 using hxd.fmt.fbx.Data;
 import hxd.fmt.fbx.BaseLibrary;
 import hxd.fmt.hmd.Data;
+import hxd.BufferFormat;
 
 class HMDOut extends BaseLibrary {
 
@@ -13,6 +14,7 @@ class HMDOut extends BaseLibrary {
 	public var optimizeSkin = true;
 	public var generateNormals = false;
 	public var generateTangents = false;
+	public var lowPrecConfig : Map<String,Precision>;
 
 	function int32tof( v : Int ) : Float {
 		tmp.set(0, v & 0xFF);
@@ -178,6 +180,37 @@ class HMDOut extends BaseLibrary {
 		}
 	}
 
+	inline function writePrec( v : Float, p : Precision ) {
+		switch( p ) {
+		case F32: writeFloat(v);
+		case F16: dataOut.writeUInt16(hxd.BufferFormat.float32to16(v,true));
+		case S8: dataOut.writeByte(hxd.BufferFormat.float32toS8(v));
+		case U8: dataOut.writeByte(BufferFormat.float32toU8(v));
+		}
+	}
+
+	inline function precisionSize(p:Precision) {
+		return switch( p ) {
+		case F32: 4;
+		case F16: 2;
+		case U8, S8: 1;
+		}
+	}
+
+	inline function flushPrec( p : Precision, count : Int ) {
+		var b = (count * precisionSize(p)) & 3;
+		switch( b ) {
+		case 0:
+		case 1:
+			dataOut.writeUInt16(0);
+			dataOut.writeByte(0);
+		case 2:
+			dataOut.writeUInt16(0);
+		case 3:
+			dataOut.writeByte(0);
+		}
+	}
+
 	function buildGeom( geom : hxd.fmt.fbx.Geometry, skin : h3d.anim.Skin, dataOut : haxe.io.BytesOutput, genTangents : Bool ) {
 		var g = new Geometry();
 
@@ -202,28 +235,41 @@ class HMDOut extends BaseLibrary {
 		// generate tangents
 		var tangents = genTangents ? buildTangents(geom) : null;
 
+		inline function getPrec(n) {
+			var p = lowPrecConfig == null ? null : lowPrecConfig.get(n);
+			if( p == null ) p = F32;
+			return p;
+		}
+		var ppos = getPrec("position");
+		var pnormal = getPrec("normal");
+		var pcolor = getPrec("color");
+		var puv = getPrec("uv");
+		var pweight = getPrec("weights");
+
 		// build format
-		var format = [
-			new GeometryFormat("position", DVec3),
-		];
+		var format = [];
+		inline function addFormat(name,type,prec) {
+			format.push(new hxd.BufferFormat.BufferInput(name,type,prec));
+		}
+		addFormat("position", DVec3, ppos);
 		if( normals != null )
-			format.push(new GeometryFormat("normal", DVec3));
+			addFormat("normal", DVec3, pnormal);
 		if( tangents != null )
-			format.push(new GeometryFormat("tangent", DVec3));
+			addFormat("tangent", DVec3, pnormal);
 		for( i in 0...uvs.length )
-			format.push(new GeometryFormat("uv" + (i == 0 ? "" : "" + (i + 1)), DVec2));
+			addFormat("uv"+(i == 0 ? "" : ""+(i+1)), DVec2, puv);
 		if( colors != null )
-			format.push(new GeometryFormat("color", DVec3));
+			addFormat("color", DVec3, pcolor);
 
 		if( skin != null ) {
 			if(fourBonesByVertex)
 				g.props = [FourBonesByVertex];
-			format.push(new GeometryFormat("weights", DVec3));  // Only 3 weights are necessary even in fourBonesByVertex since they sum-up to 1
+			addFormat("weights", DVec3, pweight);  // Only 3 weights are necessary even in fourBonesByVertex since they sum-up to 1
 			format.push(new GeometryFormat("indexes", DBytes4));
 		}
 
 		if( generateNormals )
-			format.push(new GeometryFormat("logicNormal", DVec3));
+			addFormat("logicNormal", DVec3, pnormal);
 		g.vertexFormat = hxd.BufferFormat.make(format);
 		g.vertexCount = 0;
 
@@ -396,8 +442,56 @@ class HMDOut extends BaseLibrary {
 
 		// write data
 		g.vertexPosition = dataOut.length;
-		for( i in 0...vbuf.length )
-			writeFloat(vbuf[i]);
+		if( lowPrecConfig == null ) {
+			for( i in 0...vbuf.length )
+				writeFloat(vbuf[i]);
+		} else {
+			for( index in 0...Std.int(vbuf.length / stride) ) {
+				var i = index * stride;
+				writePrec(vbuf[i++], ppos);
+				writePrec(vbuf[i++], ppos);
+				writePrec(vbuf[i++], ppos);
+				flushPrec(ppos,3);
+				if( normals != null ) {
+					writePrec(vbuf[i++], pnormal);
+					writePrec(vbuf[i++], pnormal);
+					writePrec(vbuf[i++], pnormal);
+					flushPrec(pnormal,3);
+				}
+				if( tangents != null ) {
+					writePrec(vbuf[i++], pnormal);
+					writePrec(vbuf[i++], pnormal);
+					writePrec(vbuf[i++], pnormal);
+					flushPrec(pnormal,3);
+				}
+				for( k in 0...uvs.length ) {
+					writePrec(vbuf[i++], puv);
+					writePrec(vbuf[i++], puv);
+					flushPrec(puv,2);
+				}
+				if( colors != null ) {
+					writePrec(vbuf[i++], pcolor);
+					writePrec(vbuf[i++], pcolor);
+					writePrec(vbuf[i++], pcolor);
+					flushPrec(pcolor,3);
+				}
+				if( skin != null ) {
+					writePrec(vbuf[i++], pweight);
+					writePrec(vbuf[i++], pweight);
+					writePrec(vbuf[i++], pweight);
+					flushPrec(pweight,3);
+					writeFloat(vbuf[i++]);
+				}
+				if( generateNormals ) {
+					writePrec(vbuf[i++], pnormal);
+					writePrec(vbuf[i++], pnormal);
+					writePrec(vbuf[i++], pnormal);
+					flushPrec(pnormal,3);
+				}
+				if( i != (index + 1) * stride )
+					throw "assert";
+			}
+		}
 		g.indexPosition = dataOut.length;
 		g.indexCounts = [];
 

+ 39 - 37
hxd/fmt/hmd/Library.hx

@@ -4,13 +4,13 @@ import hxd.fmt.hmd.Data;
 private class FormatMap {
 	public var size : Int;
 	public var offset : Int;
+	public var precision : hxd.BufferFormat.Precision;
 	public var def : h3d.Vector;
-	public var next : FormatMap;
-	public function new(size, offset, def, next) {
+	public function new(size, offset, def, prec) {
 		this.size = size;
 		this.offset = offset;
+		this.precision = prec;
 		this.def = def;
-		this.next = next;
 	}
 }
 
@@ -107,37 +107,45 @@ class Library {
 		if( material == 0 && geom.indexCounts.length == 1 )
 			material = null;
 
-		var map = null, stride = 0;
-		for( i in 0...@:privateAccess format.inputs.length ) {
-			var i = @:privateAccess format.inputs.length - 1 - i;
-			var input = @:privateAccess format.inputs[i];
-			var size  = input.type.getSize();
-			var offset = 0;
-			var found = false;
-			for( f2 in geom.vertexFormat.getInputs() ) {
-				if( f2.name == input.name ) {
-					if( f2.type.getSize() < size )
-						throw 'Requested ${input.name} data has only ${f2.type.getSize()} regs instead of $size';
-					found = true;
-					break;
-				}
-				offset += f2.type.getSize();
-			}
-			if( found ) {
-				map = new FormatMap(size, offset, null, map);
-			} else {
-				var def = defaults == null ? null : defaults[i];
+		var maps = [];
+		var index = 0, stride = 0, lowPrec = false;
+		for( i in format.getInputs() ) {
+			var i2 = geom.vertexFormat.getInput(i.name);
+			var map;
+			if( i2 == null ) {
+				var def = defaults == null ? null : defaults[index];
 				if( def == null )
-					throw 'Missing required ${input.name}';
-				map = new FormatMap(size, 0, def, map);
+					throw 'Missing required ${i.name}';
+				map = new FormatMap(i.type.getSize(), 0, def, F32);
+			} else {
+				if( i2.type != i.type )
+					throw 'Requested ${i.name} ${i.type} but found ${i2.type}';
+				map = new FormatMap(i.type.getSize(), geom.vertexFormat.calculateInputOffset(i2.name), null, i2.precision);
+				if( i2.precision != F32 ) lowPrec = true;
 			}
-			stride += size;
+			maps.push(map);
+			stride += i.type.getSize();
+			index++;
 		}
 
-		var vsize = geom.vertexCount * geom.vertexFormat.stride * 4;
+		var geomStride = geom.vertexFormat.strideBytes;
+		var vsize = geom.vertexCount * geomStride;
 		var vbuf = haxe.io.Bytes.alloc(vsize);
 		var entry = resource.entry;
 
+		inline function readBuffer( vid : Int, index : Int, map : FormatMap ) {
+			if( lowPrec ) {
+				return switch( map.precision ) {
+				case F32: vbuf.getFloat(vid * geomStride + (index<<2) + map.offset);
+				case F16: hxd.BufferFormat.float16to32(vbuf.getUInt16(vid * geomStride + (index<<1) + map.offset));
+				case S8: hxd.BufferFormat.floatS8to32(vbuf.get(vid * geomStride + index + map.offset));
+				case U8: hxd.BufferFormat.floatU8to32(vbuf.get(vid * geomStride + index + map.offset));
+				}
+			} else {
+				return vbuf.getFloat(vid * geomStride + (index<<2) + map.offset);
+			}
+		}
+
 		entry.readFull(vbuf, header.dataPosition + geom.vertexPosition, vsize);
 
 		var dataPos = header.dataPosition + geom.indexPosition;
@@ -163,12 +171,10 @@ class Library {
 			buf.indexes = new haxe.ds.Vector(geom.indexCount);
 			var w = 0;
 			for( vid in 0...geom.vertexCount ) {
-				var m = map;
-				while( m != null ) {
+				for( m in maps ) {
 					if( m.def == null ) {
-						var r = vid * geom.vertexFormat.stride;
 						for( i in 0...m.size )
-							buf.vertexes[w++] = vbuf.getFloat((r + m.offset + i) << 2);
+							buf.vertexes[w++] = readBuffer(vid,i,m);
 					} else {
 						switch( m.size ) {
 						case 1:
@@ -187,7 +193,6 @@ class Library {
 							buf.vertexes[w++] = m.def.w;
 						}
 					}
-					m = m.next;
 				}
 			}
 			if( isSmall ) {
@@ -210,12 +215,10 @@ class Library {
 				if( rid == 0 ) {
 					rid = ++vcount;
 					vmap[vid] = rid;
-					var m = map;
-					while( m != null ) {
+					for( m in maps ) {
 						if( m.def == null ) {
-							var r = vid * geom.vertexFormat.stride;
 							for( i in 0...m.size )
-								vertexes.push(vbuf.getFloat((r + m.offset + i) << 2));
+								vertexes.push(readBuffer(vid,i,m));
 						} else {
 							switch( m.size ) {
 							case 1:
@@ -234,7 +237,6 @@ class Library {
 								vertexes.push(m.def.w);
 							}
 						}
-						m = m.next;
 					}
 				}
 				buf.indexes[i] = rid - 1;

+ 5 - 1
hxd/fmt/hmd/Reader.hx

@@ -147,7 +147,11 @@ class Reader {
 			g.props = readProps();
 			g.vertexCount = i.readInt32();
 			var stride = i.readByte();
-			g.vertexFormat = hxd.BufferFormat.make([for( k in 0...i.readByte() ) new GeometryFormat(readCachedName(), @:privateAccess GeometryDataFormat.fromInt(i.readByte()))]);
+			g.vertexFormat = hxd.BufferFormat.make([for( k in 0...i.readByte() ) {
+				var name = readCachedName();
+				var type = i.readByte();
+				new GeometryFormat(name, @:privateAccess GeometryDataFormat.fromInt(type&15), @:privateAccess Precision.fromInt(type>>4));
+			}]);
 			if( stride != g.vertexFormat.stride ) throw "assert";
 			g.vertexPosition = i.readInt32();
 			var subCount = i.readByte();

+ 1 - 1
hxd/fmt/hmd/Writer.hx

@@ -118,7 +118,7 @@ class Writer {
 			out.writeByte(@:privateAccess g.vertexFormat.inputs.length);
 			for( f in g.vertexFormat.getInputs() ) {
 				writeName(f.name);
-				out.writeByte(f.type.toInt());
+				out.writeByte(f.type.toInt() | (f.precision.toInt() << 4));
 			}
 			out.writeInt32(g.vertexPosition);
 			if( g.indexCounts.length >= 0xFF ) {

+ 12 - 1
hxd/fs/Convert.hx

@@ -89,8 +89,19 @@ class ConvertFBX2HMD extends Convert {
 			}
 			if( params.maxBones != null)
 				hmdout.maxBonesPerSkin = params.maxBones;
-			if ( params.tangents != null)
+			if( params.tangents != null)
 				hmdout.generateTangents = true;
+			if( params.lowp != null ) {
+				var m : haxe.DynamicAccess<String> = params.lowp;
+				hmdout.lowPrecConfig = [];
+				for( k in m.keys() )
+					hmdout.lowPrecConfig.set(k, switch( m.get(k) ) {
+					case "f16": F16;
+					case "u8": U8;
+					case "s8": S8;
+					case x: throw "Invalid precision '"+x+"' should be u8|s8|f16";
+				});
+			}
 		}
 		hmdout.load(fbx);
 		var isAnim = StringTools.startsWith(originalFilename, "Anim_") || originalFilename.toLowerCase().indexOf("_anim_") > 0;

+ 16 - 6
hxd/fs/FileConverter.hx

@@ -134,18 +134,28 @@ class FileConverter {
 			case "priority": priority = value;
 			default:
 				if( cmd.params == null ) cmd.params = {};
-				if( Reflect.isObject(value) && !hxd.impl.Api.isOfType(value,String) ) throw "Invalid parameter value "+f+"="+value;
 				Reflect.setField(cmd.params, f, value);
 			}
 		}
-		if( cmd.params != null ) {
-			var fl = Reflect.fields(cmd.params);
-			fl.sort(Reflect.compare);
-			cmd.paramsStr = [for( f in fl ) f+"_"+Reflect.field(cmd.params,f)].join("_");
-		}
+		if( cmd.params != null )
+			cmd.paramsStr = formatValue(cmd.params);
 		return { cmd : cmd, priority : priority };
 	}
 
+	function formatValue( v : Dynamic ) : String {
+		if( !Reflect.isObject(v) )
+			return Std.string(v);
+		if( Std.isOfType(v,String) )
+			return v;
+		if( Std.isOfType(v,Array) ) {
+			var a : Array<Dynamic> = v;
+			return [for( v in a ) formatValue(v)].toString();
+		}
+		var fl = Reflect.fields(v);
+		fl.sort(Reflect.compare);
+		return [for( f in fl ) f+"_"+formatValue(Reflect.field(v,f))].join("_");
+	}
+
 	function mergeRec( a : Dynamic, b : Dynamic ) {
 		if( b == null ) return a;
 		if( a == null ) return b;