Browse Source

add optional support for 4 bones of influence per vertex

trethaller 4 năm trước cách đây
mục cha
commit
44c4f57216

+ 1 - 0
h3d/scene/Skin.hx

@@ -240,6 +240,7 @@ class Skin extends MultiMaterial {
 				currentPalette[bid].multiply3x4inline(j.transPos, m);
 		}
 		skinShader.bonesMatrixes = currentPalette;
+		skinShader.fourBonesByVertex = skinData.bonesPerVertex == 4;
 		if( jointsAbsPosInv != null ) jointsAbsPosInv._44 = 0; // mark as invalid
 		jointsUpdated = false;
 	}

+ 10 - 2
h3d/shader/Skin.hx

@@ -18,10 +18,18 @@ class Skin extends SkinBase {
 				(relativePosition * bonesMatrixes[int(input.indexes.x)]) * input.weights.x +
 				(relativePosition * bonesMatrixes[int(input.indexes.y)]) * input.weights.y +
 				(relativePosition * bonesMatrixes[int(input.indexes.z)]) * input.weights.z;
-			transformedNormal = normalize(
+			transformedNormal = 
 				(input.normal * mat3(bonesMatrixes[int(input.indexes.x)])) * input.weights.x +
 				(input.normal * mat3(bonesMatrixes[int(input.indexes.y)])) * input.weights.y +
-				(input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * input.weights.z);
+				(input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * input.weights.z;
+
+			if(fourBonesByVertex) {
+				var w4 = 1 - (input.weights.x + input.weights.y + input.weights.z);
+				transformedPosition += (relativePosition * bonesMatrixes[int(input.indexes.w)]) * w4;
+				transformedNormal += (input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * w4;
+			}
+			
+			transformedNormal = normalize(transformedNormal);
 		}
 
 	};

+ 2 - 0
h3d/shader/SkinBase.hx

@@ -9,6 +9,8 @@ class SkinBase extends hxsl.Shader {
 		var transformedNormal : Vec3;
 
 		@const var MaxBones : Int;
+		@const @param var fourBonesByVertex = false;
+
 		@ignore @param var bonesMatrixes : Array<Mat3x4,MaxBones>;
 
 	};

+ 14 - 5
h3d/shader/SkinTangent.hx

@@ -19,15 +19,24 @@ class SkinTangent extends SkinBase {
 				(relativePosition * bonesMatrixes[int(input.indexes.x)]) * input.weights.x +
 				(relativePosition * bonesMatrixes[int(input.indexes.y)]) * input.weights.y +
 				(relativePosition * bonesMatrixes[int(input.indexes.z)]) * input.weights.z;
-			transformedNormal = normalize(
+			transformedNormal = 
 				(input.normal * mat3(bonesMatrixes[int(input.indexes.x)])) * input.weights.x +
 				(input.normal * mat3(bonesMatrixes[int(input.indexes.y)])) * input.weights.y +
-				(input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * input.weights.z);
-			transformedTangent = vec4(normalize(
+				(input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * input.weights.z;
+			transformedTangent.xyz =
 				(input.tangent.xyz * mat3(bonesMatrixes[int(input.indexes.x)])) * input.weights.x +
 				(input.tangent.xyz * mat3(bonesMatrixes[int(input.indexes.y)])) * input.weights.y +
-				(input.tangent.xyz * mat3(bonesMatrixes[int(input.indexes.z)])) * input.weights.z
-			), transformedTangent.w);
+				(input.tangent.xyz * mat3(bonesMatrixes[int(input.indexes.z)])) * input.weights.z;
+
+			if(fourBonesByVertex) {
+				var w4 = 1 - (input.weights.x + input.weights.y + input.weights.z);
+				transformedPosition += (relativePosition * bonesMatrixes[int(input.indexes.w)]) * w4;
+				transformedNormal += (input.normal * mat3(bonesMatrixes[int(input.indexes.z)])) * w4;
+				transformedTangent.xyz += (input.tangent.xyz * mat3(bonesMatrixes[int(input.indexes.w)])) * w4;
+			}
+			
+			transformedNormal = normalize(transformedNormal);
+			transformedTangent.xyz = normalize(transformedTangent.xyz);
 		}
 
 	};

+ 6 - 4
hxd/fmt/fbx/BaseLibrary.hx

@@ -121,9 +121,9 @@ class BaseLibrary {
 	public var skipObjects : Map<String,Bool>;
 
 	/**
-		Set how many bones per vertex should be created in skin data in makeObject(). Default is 3
+		Use 4 bones of influence per vertex instead of 3
 	**/
-	public var bonesPerVertex = 3;
+	public var fourBonesByVertex = false;
 
 	/**
 		If there are too many bones, the model will be split in separate render passes.
@@ -1297,7 +1297,9 @@ class BaseLibrary {
 		return keepJoints.get(j.name);
 	}
 
-	function createSkin( hskins : Map<Int,h3d.anim.Skin>, hgeom : Map<Int,{function vertexCount():Int;function setSkin(s:h3d.anim.Skin):Void;}>, rootJoints : Array<h3d.anim.Skin.Joint>, bonesPerVertex ) {
+	function createSkin( hskins : Map<Int,h3d.anim.Skin>, hgeom : Map<Int,{
+		function vertexCount():Int; function setSkin(s:h3d.anim.Skin):Void;
+	}>, rootJoints : Array<h3d.anim.Skin.Joint> ) {
 		var allJoints = [];
 		function collectJoints(j:h3d.anim.Skin.Joint) {
 			// collect subs first (allow easy removal of terminal unskinned joints)
@@ -1338,7 +1340,7 @@ class BaseLibrary {
 				if( skin != null )
 					return skin;
 				var geom = hgeom.get(getParent(def, "Geometry").getId());
-				skin = new h3d.anim.Skin(null, geom.vertexCount(), bonesPerVertex);
+				skin = new h3d.anim.Skin(null, geom.vertexCount(), fourBonesByVertex ? 4 : 3);
 				geom.setSkin(skin);
 				hskins.set(def.getId(), skin);
 			}

+ 9 - 16
hxd/fmt/fbx/HMDOut.hx

@@ -12,11 +12,6 @@ class HMDOut extends BaseLibrary {
 	public var absoluteTexturePath : Bool;
 	public var optimizeSkin = true;
 	public var generateNormals = false;
-	/*
-		Store the skin indexes as multiple premultiplied floats instead of as packed into a single 4 bytes ints.
-		This is necessary for GPUs that does not respect OpenGLES spec and does not allow non-constant indexing in vertex shader (Adreno 20X)
-	*/
-	public var floatSkinIndexes = #if floatSkinIndexes true #else false #end;
 
 	function int32tof( v : Int ) : Float {
 		tmp.set(0, v & 0xFF);
@@ -210,9 +205,10 @@ class HMDOut extends BaseLibrary {
 			g.vertexFormat.push(new GeometryFormat("color", DVec3));
 
 		if( skin != null ) {
-			if( bonesPerVertex <= 0 || bonesPerVertex > 4 ) throw "assert";
-			g.vertexFormat.push(new GeometryFormat("weights", [DFloat, DVec2, DVec3, DVec4][bonesPerVertex-1]));
-			g.vertexFormat.push(new GeometryFormat("indexes", floatSkinIndexes ? [DFloat, DVec2, DVec3, DVec4][bonesPerVertex-1] : DBytes4));
+			if(fourBonesByVertex)
+				g.props = [FourBonesByVertex];
+			g.vertexFormat.push(new GeometryFormat("weights", DVec3));  // Only 3 weights are necessary even in fourBonesByVertex since they sum-up to 1
+			g.vertexFormat.push(new GeometryFormat("indexes", DBytes4));
 		}
 
 		if( generateNormals )
@@ -304,15 +300,12 @@ class HMDOut extends BaseLibrary {
 				if( skin != null ) {
 					var k = vidx * skin.bonesPerVertex;
 					var idx = 0;
-					for( i in 0...skin.bonesPerVertex ) {
+					if(!(skin.bonesPerVertex == 3 || skin.bonesPerVertex == 4)) throw "assert";
+					for( i in 0...3 )  // Only 3 weights are necessary even in fourBonesByVertex since they sum-up to 1
 						tmpBuf[p++] = skin.vertexWeights[k + i];
+					for( i in 0...skin.bonesPerVertex )
 						idx = (skin.vertexJoints[k + i] << (8*i)) | idx;
-					}
-					if( floatSkinIndexes ) {
-						for( i in 0...skin.bonesPerVertex )
-							tmpBuf[p++] = skin.vertexJoints[k + i] * 3;
-					} else
-						tmpBuf[p++] = int32tof(idx);
+					tmpBuf[p++] = int32tof(idx);
 				}
 
 				if( generateNormals ) {
@@ -662,7 +655,7 @@ class HMDOut extends BaseLibrary {
 				for( c in o.skin.childs )
 					if( c.isJoint )
 						rootJoints.push(c.joint);
-				skin = createSkin(hskins, tmpGeom, rootJoints, bonesPerVertex);
+				skin = createSkin(hskins, tmpGeom, rootJoints);
 				if( skin.boundJoints.length > BaseLibrary.maxBonesPerSkin ) {
 					var g = new hxd.fmt.fbx.Geometry(this, g);
 					var idx = g.getIndexes();

+ 1 - 0
hxd/fmt/hmd/Data.hx

@@ -42,6 +42,7 @@ enum Property<T> {
 	CameraFOVY( v : Float ) : Property<Float>;
 	Unused_HasMaterialFlags; // TODO: Removing this will offset property indices
 	HasExtraTextures;
+	FourBonesByVertex;
 }
 
 typedef Properties = Null<Array<Property<Dynamic>>>;

+ 7 - 4
hxd/fmt/hmd/Library.hx

@@ -287,11 +287,11 @@ class Library {
 	}
 
 	@:access(h3d.anim.Skin)
-	function makeSkin( skin : Skin ) {
+	function makeSkin( skin : Skin, geom : Geometry ) {
 		var s = cachedSkin.get(skin.name);
 		if( s != null )
 			return s;
-		s = new h3d.anim.Skin(skin.name, 0, 3);
+		s = new h3d.anim.Skin(skin.name, 0, geom.props != null && geom.props.indexOf(FourBonesByVertex) >= 0 ? 4 : 3 );
 		s.namedJoints = new Map();
 		s.allJoints = [];
 		s.boundJoints = [];
@@ -352,7 +352,7 @@ class Library {
 			} else {
 				var prim = makePrimitive(m.geometry);
 				if( m.skin != null ) {
-					var skinData = makeSkin(m.skin);
+					var skinData = makeSkin(m.skin, header.geometries[m.geometry]);
 					skinData.primitive = prim;
 					obj = new h3d.scene.Skin(skinData, [for( mat in m.materials ) makeMaterial(m, mat, loadTexture)]);
 				} else if( m.materials.length == 1 )
@@ -584,7 +584,10 @@ class Library {
 			throw "assert";
 
 		@:privateAccess skin.vertexCount = geom.vertexCount;
-		var data = getBuffers(geom, [new hxd.fmt.hmd.Data.GeometryFormat("position",DVec3),new hxd.fmt.hmd.Data.GeometryFormat("weights",DVec3),new hxd.fmt.hmd.Data.GeometryFormat("indexes",DBytes4)]);
+		var data = getBuffers(geom, [
+			new hxd.fmt.hmd.Data.GeometryFormat("position",DVec3),
+			new hxd.fmt.hmd.Data.GeometryFormat("weights",DVec3),
+			new hxd.fmt.hmd.Data.GeometryFormat("indexes",DBytes4)]);
 		skin.vertexWeights = new haxe.ds.Vector(skin.vertexCount * skin.bonesPerVertex);
 		skin.vertexJoints = new haxe.ds.Vector(skin.vertexCount * skin.bonesPerVertex);
 

+ 2 - 0
hxd/fmt/hmd/Reader.hx

@@ -21,6 +21,8 @@ class Reader {
 			throw "Obsolete HasMaterialFlags";
 		case 2:
 			return HasExtraTextures;
+		case 3:
+			return FourBonesByVertex;
 		case unk:
 			throw "Unknown property #" + unk;
 		}

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

@@ -17,6 +17,7 @@ class Writer {
 			out.writeFloat(v);
 		case Unused_HasMaterialFlags:
 		case HasExtraTextures:
+		case FourBonesByVertex:
 		}
 	}
 

+ 3 - 1
hxd/fs/Convert.hx

@@ -81,8 +81,10 @@ class ConvertFBX2HMD extends Convert {
 		if( params != null ) {
 			if( params.normals )
 				hmdout.generateNormals = true;
-			if( params.precise )
+			if( params.precise ) {
 				hmdout.highPrecision = true;
+				hmdout.fourBonesByVertex = true;
+			}
 		}
 		hmdout.load(fbx);
 		var isAnim = StringTools.startsWith(originalFilename, "Anim_") || originalFilename.toLowerCase().indexOf("_anim_") > 0;