Bladeren bron

Blendshapes: chaging blendshape will not update all primitives

lviguier 3 weken geleden
bovenliggende
commit
c98c665612
2 gewijzigde bestanden met toevoegingen van 142 en 17 verwijderingen
  1. 115 17
      h3d/prim/Blendshape.hx
  2. 27 0
      h3d/shader/Blendshape.hx

+ 115 - 17
h3d/prim/Blendshape.hx

@@ -5,10 +5,16 @@ class Blendshape {
 
 	var hmdModel : HMDModel;
 	var shapes : Array<hxd.fmt.hmd.Data.BlendShape>;
-	var weights : Array<Float>;
 	var inputMapping : Array<Map<String, Int>> = [];
 	var shapesBytes = [];
 
+	#if editor
+	var weights : Array<Float>;
+	#else
+	var offsetsBuffer : hxd.FloatBuffer;
+	var weightsBuffer : hxd.FloatBuffer;
+	#end
+
 	public function new(hmdModel) {
 		this.hmdModel = hmdModel;
 
@@ -16,9 +22,7 @@ class Blendshape {
 			throw "Blend shape doesn't support low precision";
 
 		// Cache data for blendshapes
-		var is32 = hmdModel.data.vertexCount > 0x10000;
 		var vertexFormat = hmdModel.data.vertexFormat;
-		var size = hmdModel.data.vertexCount * vertexFormat.strideBytes;
 
 		var geoId = 0;
 		for (gIdx => g in hmdModel.lib.header.geometries)
@@ -27,7 +31,9 @@ class Blendshape {
 
 		shapes = [ for(s in hmdModel.lib.header.shapes) if (s.geom == geoId) s];
 
+		#if editor
 		weights = [];
+		#end
 
 		for ( s in 0...shapes.length ) {
 			var s = shapes[s];
@@ -43,7 +49,9 @@ class Blendshape {
 			shapesBytes.push({ vertexBytes : vertexBytes, remapBytes : remapBytes});
 			inputMapping.push(new Map());
 
+			#if editor
 			weights.push(0.0);
+			#end
 		}
 
 		// We want to remap inputs since inputs can be not exactly in the same
@@ -57,37 +65,127 @@ class Blendshape {
 				}
 			}
 		}
+
+		#if !editor
+		weightsBuffer = hxd.impl.Allocator.get().allocFloats(shapes.length);
+		offsetsBuffer = hxd.impl.Allocator.get().allocFloats(3 * hmdModel.data.vertexCount * shapes.length);
+
+		var flagOffset = 31;
+
+		// Apply blendshapes offsets to original vertex
+		var pos = 0;
+		for (sIdx in 0...shapes.length) {
+			var sp = shapesBytes[sIdx];
+			var offsetIdx = 0;
+			var idx = 0;
+
+			while (offsetIdx < shapes[sIdx].indexCount) {
+				var affectedVId = sp.remapBytes.getInt32(idx << 2);
+
+				var reachEnd = false;
+				while (!reachEnd) {
+					reachEnd = affectedVId >> flagOffset != 0;
+					if (reachEnd)
+						affectedVId = affectedVId ^ (1 << flagOffset);
+
+					var inputIdx = 0;
+					var offsetInput = 0;
+					for (input in shapes[sIdx].vertexFormat.getInputs()) {
+						for (sizeIdx in 0...input.type.getSize()) {
+							if (input.name != "position")
+								continue;
+
+							var offset = sp.vertexBytes.getFloat(offsetIdx * shapes[sIdx].vertexFormat.stride + offsetInput + sizeIdx << 2);
+							var pos = (sIdx * hmdModel.data.vertexCount * 3) + (affectedVId * 3) + sizeIdx;
+							offsetsBuffer[pos] = offset;
+						}
+
+						offsetInput += input.type.getSize();
+						inputIdx++;
+					}
+
+					idx++;
+
+					if (idx < hmdModel.data.vertexCount)
+						affectedVId = sp.remapBytes.getInt32(idx << 2);
+				}
+
+				offsetIdx++;
+			}
+		}
+		#end
 	}
 
-	public function setBlendshapeAmountByIndex(blendshapeIdx: Int, amount: Float, uploadBytes : Bool = true) {
-		if (blendshapeIdx >= this.weights.length)
-			throw 'Blendshape at index ${blendshapeIdx} doesn\'t exist (there is only ${this.weights.length} blendshapes).';
+	public function setBlendShapeWeight(mesh : h3d.scene.Mesh, blendShapeIdx : Int, amount : Float, upload : Bool = true) {
+		#if editor
+		// Ensure we got real weights in case this method is called several times on the same blenshape
+		var hmdModel = Std.downcast(mesh.primitive, HMDModel);
+
+		var cache = @:privateAccess hmdModel.lib.cachedPrimitives;
+		var cached = [];
+		for (m in hmdModel.lib.header.models) {
+			cached.push(cache[m.geometry]);
+			cache.remove(cache[m.geometry]);
+		}
+
+		var clonedPrim : HMDModel = cast @:privateAccess Std.downcast(hmdModel.lib.makeObject(), h3d.scene.Mesh).primitive;
+		clonedPrim.blendshape.weights = hmdModel.blendshape.weights.copy();
+		mesh.primitive = clonedPrim;
+		@:privateAccess clonedPrim.blendshape.weights[blendShapeIdx] = amount;
+		if (upload)
+			clonedPrim.blendshape.uploadBlendshapeBytes();
+
+		for (m in hmdModel.lib.header.models) {
+			cache.remove(cache[m.geometry]);
+			cache[m.geometry] = cached.shift();
+		}
+
+		#else
+		var alloc = hxd.impl.Allocator.get();
+		for (m in mesh.getMaterials()) {
+			var shader : h3d.shader.Blendshape = null;
+			for (s in m.mainPass.getShaders()) {
+				if (Std.isOfType(s, h3d.shader.Blendshape)) {
+					shader = cast s;
+					break;
+				}
+			}
 
-		this.weights[blendshapeIdx] = amount;
+			if (shader == null) {
+				shader = new h3d.shader.Blendshape();
 
-		if (uploadBytes)
-			uploadBlendshapeBytes();
+				shader.shapeCount = shapes.length;
+				shader.vcount = hmdModel.data.vertexCount;
+				shader.offsets = alloc.ofFloats(offsetsBuffer, hxd.BufferFormat.POS3D, UniformReadWrite);
+				shader.weights = alloc.ofFloats(weightsBuffer, hxd.BufferFormat.INDEX32, UniformReadWrite);
+
+				m.mainPass.addShader(shader);
+			}
+
+			weightsBuffer[blendShapeIdx] = amount;
+			shader.weights.uploadFloats(weightsBuffer, 0, shapes.length, 0);
+		}
+		#end
 	}
 
-	public function setBlendShapeAmountByName(name: String, amount: Float, uploadBytes : Bool = true) {
-		for (s in shapes) {
+	public function getBlendShapeIndex(name: String) : Int {
+		for (idx => s in shapes) {
 			if (s.name == name) {
-				this.weights[shapes.indexOf(s)] = amount;
-				break;
+				return idx;
 			}
 		}
 
-		if (uploadBytes)
-			uploadBlendshapeBytes();
+		return -1;
 	}
 
-	function getBlendshapeCount() {
+	public function getBlendshapeCount() {
 		if (hmdModel.lib.header.shapes == null)
 			return 0;
 
 		return shapes.length;
 	}
 
+	#if editor
 	public function uploadBlendshapeBytes() {
 		if (hmdModel.buffer == null || hmdModel.buffer.isDisposed())
 			hmdModel.alloc(Engine.getCurrent());
@@ -164,9 +262,9 @@ class Blendshape {
 			hmdModel.indexCount += n;
 		}
 
-
 		var size = (is32 ? 4 : 2) * hmdModel.indexCount;
 		var bytes = hmdModel.lib.resource.entry.fetchBytes(hmdModel.dataPosition + hmdModel.data.indexPosition, size);
 		hmdModel.indexes.uploadBytes(bytes, 0, hmdModel.indexCount);
 	}
+	#end
 }

+ 27 - 0
h3d/shader/Blendshape.hx

@@ -0,0 +1,27 @@
+package h3d.shader;
+
+class Blendshape extends hxsl.Shader {
+
+	static var SRC = {
+		@input var input : {
+			var position : Vec3;
+			var normal : Vec3;
+			var weights : Vec3;
+			var indexes : Bytes4;
+		};
+
+		var relativePosition : Vec3;
+
+		@param var shapeCount : Int;
+		@param var vcount : Int;
+		@param var offsets : StorageBuffer<Float>;
+		@param var weights : StorageBuffer<Float>;
+
+		function vertex() {
+			for (idx in 0...shapeCount) {
+				var offsetPos = vec3(offsets[(idx * vcount * 3) + (vertexID * 3)], offsets[(idx * vcount * 3) + (vertexID * 3) + 1], offsets[(idx * vcount * 3) + (vertexID * 3) + 2]);
+				relativePosition = relativePosition + offsetPos * weights[idx];
+			}
+		}
+	};
+}