2
0
Эх сурвалжийг харах

[animgraph] Blendspace somewhat working prototype

Clément Espeute 9 сар өмнө
parent
commit
848c07509e

+ 2 - 1
hide/view/animgraph/AnimGraphEditor.hx

@@ -17,6 +17,7 @@ class AnimGraphEditor extends GenericGraphEditor {
     var previewNode : hrt.animgraph.nodes.AnimNode = null;
 
     override function reloadView() {
+        previewNode = null;
         animGraph = cast hide.Ide.inst.loadPrefab(state.path, null,  true);
         super.reloadView();
 
@@ -187,7 +188,7 @@ class AnimGraphEditor extends GenericGraphEditor {
     override function onScenePreviewReady() {
         super.onScenePreviewReady();
 
-        previewModel = scenePreview.loadModel("character/Kobold01/Model.FBX");
+        previewModel = scenePreview.loadModel("Ogre/Ogre_Kobold.fbx");
         scenePreview.s3d.addChild(previewModel);
 
         setPreview(previewNode);

+ 3 - 3
hrt/animgraph/AnimGraphInstance.hx

@@ -120,15 +120,15 @@ class AnimGraphInstance extends h3d.anim.Animation {
 			switch (inputs[inputId].type) {
 				case TAnimation:
 					if (outputNode != null && Std.downcast(outputNode, hrt.animgraph.nodes.DefaultPose) == null /* use our default pose node instead of the one in the graph*/) {
-						Reflect.setField(node, inputs[inputId].name, outputNode);
+						Reflect.setProperty(node, inputs[inputId].name, outputNode);
 					} else {
-						Reflect.setField(node, inputs[inputId].name, defaultPoseNode);
+						Reflect.setProperty(node, inputs[inputId].name, defaultPoseNode);
 					}
 				case TFloat:
 					if (outputNode != null) {
 						var outputs = outputNode.getOutputs();
 						var output = outputs[edge.outputIndex];
-						Reflect.setField(node, inputs[inputId].name, Reflect.getProperty(outputNode, output.name));
+						Reflect.setProperty(node, inputs[inputId].name, Reflect.getProperty(outputNode, output.name));
 					}
 			}
 		}

+ 50 - 4
hrt/animgraph/BlendSpace2D.hx

@@ -7,19 +7,65 @@ typedef BlendSpacePoint = {
 };
 
 class BlendSpace2D extends hrt.prefab.Prefab {
-	@:s var points : Array<BlendSpacePoint>;
+	@:s var points : Array<BlendSpacePoint> = [];
+	@:s var triangles : Array<Array<Int>> = [];
 
-	public function getAnimation() {
+	var instance : BlendSpace2DInstance;
 
+	function getInstance() : BlendSpace2DInstance {
+		return instance ??= @:privateAccess new BlendSpace2DInstance(this);
 	}
+
+	static var _ = hrt.prefab.Prefab.register("blendspace2d", BlendSpace2D, "blendspace2d");
 }
 
 typedef BlendSpaceInstancePoint = {
 	x: Float,
 	y: Float,
-
+	?animation: h3d.anim.Animation, // Can be null if anim failed to load
+	?proxy: hrt.animgraph.nodes.Input.AnimProxy,
 }
 
-class BlendSpace2DIntance extends h3d.anim.Animation {
+@:access(hrt.animgraph.BlendSpace2D)
+class BlendSpace2DInstance extends h3d.anim.Animation {
+	var points : Array<BlendSpaceInstancePoint> = [];
+	var triangles : Array<Array<BlendSpaceInstancePoint>> = [];
+	var blendSpace : BlendSpace2D;
+
+	function new (blendSpace: BlendSpace2D) {
+		super(blendSpace.name, 1000, 1/60.0);
+		this.blendSpace = blendSpace;
+	}
+
+	override function bind(base: h3d.scene.Object) {
+		super.bind(base);
 
+		for (blendSpacePoint in blendSpace.points) {
+			var point : BlendSpaceInstancePoint = {x: blendSpacePoint.x, y: blendSpacePoint.y};
+			try
+			{
+				var animBase = hxd.res.Loader.currentInstance.load(blendSpacePoint.animPath).toModel().toHmd().loadAnimation();
+				point.proxy = new hrt.animgraph.nodes.Input.AnimProxy(null);
+				point.animation = animBase.createInstance(point.proxy);
+			} catch (e) {
+				trace('Couldn\'t load anim ${blendSpacePoint.animPath} : $e');
+			}
+			points.push(point);
+		}
+
+		for (blendSpaceTriangle in blendSpace.triangles) {
+			var ourTriangle : Array<BlendSpaceInstancePoint> = [];
+			for (point in blendSpaceTriangle) {
+				ourTriangle.push(points[point]);
+			}
+			triangles.push(ourTriangle);
+		}
+	}
+
+	override function clone(?target: h3d.anim.Animation) : h3d.anim.Animation {
+		if (target != null) throw "Unexpected";
+		var newBlendSpace2D : BlendSpace2D = cast blendSpace.clone();
+		var inst = super.clone(new BlendSpace2DInstance(newBlendSpace2D));
+		return inst;
+	}
 }

+ 14 - 1
hrt/animgraph/Macros.hx

@@ -74,8 +74,21 @@ class Macros {
 						Context.error('Unsupported type for field ${f.name}', f.pos, 0);
 					}
 				}
+			case FProp(_,_,t,_): {
+				switch (t) {
+					case TPath(p):
+						if (p.name ==  "AnimNode") {
+							return macro hrt.animgraph.Node.OutputType.TAnimation;
+						} else if (p.name == "Float") {
+							return macro hrt.animgraph.Node.OutputType.TFloat;
+						}
+						Context.error('Unsupported type ${p}', f.pos, 0);
+					default:
+						Context.error('Unsupported type for field ${f.name}', f.pos, 0);
+					}
+				}
 			default:
-				Context.error("Must be a var", f.pos);
+				Context.error("Must be a var, found " + f.kind, f.pos);
 		}
 		return null;
 	}

+ 1 - 2
hrt/animgraph/nodes/AnimNode.hx

@@ -23,7 +23,6 @@ class GetBoneTransformContext {
 
 	var targetObj : hrt.animgraph.AnimGraphInstance.AnimGraphAnimatedObject;
 
-	static var tmpWorkMatrix = new h3d.Matrix();
 	var tmpDefMatrix = new h3d.Matrix();
 	var defMatrix : h3d.Matrix = null;
 
@@ -38,7 +37,7 @@ class GetBoneTransformContext {
 		} else {
 			targetObj.targetObject.defaultTransform;
 		}
-		Tools.splitMatrix(m, tmpWorkMatrix);
+		Tools.splitMatrix(m, tmpDefMatrix);
 
 		defMatrix = tmpDefMatrix;
 		return defMatrix;

+ 197 - 0
hrt/animgraph/nodes/BlendSpace2D.hx

@@ -0,0 +1,197 @@
+package hrt.animgraph.nodes;
+using hrt.tools.MapUtils;
+
+typedef BlendSpaceInstancePoint = {
+	x: Float,
+	y: Float,
+	?animation: h3d.anim.Animation, // Can be null if anim failed to load
+	?proxy: hrt.animgraph.nodes.Input.AnimProxy,
+	indexRemap: Array<Int>,
+}
+
+
+@:access(hrt.animgraph.BlendSpace2D)
+class BlendSpace2DNode extends AnimNode {
+	@:input var bsX(default, set): Float = 0.5;
+	@:input var bsY(default, set): Float = 0.5;
+
+	@:s var path : String = "AnimGraph/blendSpaceOgreLocomotion.blendspace2d";
+
+	var dirtyPos: Bool = true;
+
+	function set_bsX(v: Float) : Float {
+		if (v != bsX)
+			currentTriangle = -1;
+		return bsX = v;
+	}
+
+	function set_bsY(v: Float) : Float {
+		if (v != bsY)
+			currentTriangle = -1;
+		return bsY = v;
+	}
+
+	var currentTriangle : Int = -1;
+	var weights : Array<Float> = [1.0,0.0,0.0];
+
+	var points : Array<BlendSpaceInstancePoint> = [];
+	var triangles : Array<Array<BlendSpaceInstancePoint>> = [];
+	var blendSpace : BlendSpace2D;
+
+	var workQuat = new h3d.Quat();
+	var workQuats : Array<h3d.Quat> = [new h3d.Quat(), new h3d.Quat(), new h3d.Quat()];
+	var refQuat = new h3d.Quat();
+
+	override function getBones(ctx: hrt.animgraph.nodes.AnimNode.GetBoneContext):Map<String, Int> {
+		var boneMap : Map<String, Int> = [];
+		var curOurBoneId = 0;
+
+		if (blendSpace == null) {
+			blendSpace = cast hxd.res.Loader.currentInstance.load(path).toPrefab().load();
+		}
+
+		for (blendSpacePoint in blendSpace.points) {
+			var point : BlendSpaceInstancePoint = {x: blendSpacePoint.x, y: blendSpacePoint.y, indexRemap: []};
+			try
+			{
+				var animBase = hxd.res.Loader.currentInstance.load(blendSpacePoint.animPath).toModel().toHmd().loadAnimation();
+				point.proxy = new hrt.animgraph.nodes.Input.AnimProxy(null);
+				point.animation = animBase.createInstance(point.proxy);
+
+				for (boneId => obj in point.animation.getObjects()) {
+					var ourId = boneMap.getOrPut(obj.objectName, curOurBoneId++);
+					point.indexRemap[ourId] = boneId;
+				}
+			} catch (e) {
+				trace('Couldn\'t load anim ${blendSpacePoint.animPath} : ${e.toString()}');
+			}
+			points.push(point);
+		}
+
+		for (blendSpaceTriangle in blendSpace.triangles) {
+			var triangle : Array<BlendSpaceInstancePoint> = [];
+			for (i => index in blendSpaceTriangle) {
+				triangle[i] = points[index];
+			}
+			triangles.push(triangle);
+		}
+
+		for (point in points) {
+			for (i in 0...curOurBoneId) {
+				if(point.indexRemap[i] == null) {
+					point.indexRemap[i] = -1;
+				}
+			}
+		}
+
+		return boneMap;
+	}
+
+	override function tick(dt:Float) {
+		super.tick(dt);
+
+		for (point in points) {
+			if (point.animation == null)
+				continue;
+			point.animation.update(dt);
+			@:privateAccess point.animation.isSync = false;
+		}
+	}
+
+	@:haxe.warning("-WInlineOptimizedField")
+	override function getBoneTransform(boneId:Int, outMatrix:h3d.Matrix, ctx:hrt.animgraph.nodes.AnimNode.GetBoneTransformContext) {
+		if (currentTriangle == -1) {
+			var curPos = inline new h2d.col.Point(bsX, bsY);
+
+			// find the triangle our curPos resides in
+			for (triIndex => tri in triangles) {
+				var colTri = inline new h2d.col.Triangle(inline new h2d.col.Point(tri[0].x, tri[0].y), inline new h2d.col.Point(tri[1].x, tri[1].y), inline new h2d.col.Point(tri[2].x, tri[2].y));
+				if (inline colTri.contains(curPos)) {
+					var bary = inline colTri.barycentric(curPos);
+					currentTriangle = triIndex;
+					weights[0] = bary.x;
+					weights[1] = bary.y;
+					weights[2] = bary.z;
+					break;
+				}
+			}
+
+			// We are outside all triangles, find the closest edge
+			if (currentTriangle == -1) {
+
+				var closestDistanceSq : Float = hxd.Math.POSITIVE_INFINITY;
+
+				for (triIndex => tri in triangles) {
+					for (i in 0...3) {
+						var i2 = (i+1) % 3;
+						var triSeg = inline new h2d.col.Line(inline new h2d.col.Point(tri[i].x, tri[i].y), inline new h2d.col.Point(tri[i2].x, tri[i2].y));
+
+						var dx = triSeg.p2.x - triSeg.p1.x;
+						var dy = triSeg.p2.y - triSeg.p1.y;
+						var k = ((curPos.x - triSeg.p1.x) * dx + (curPos.y - triSeg.p1.y) * dy) / (dx * dx + dy * dy);
+						var mx = dx * k + triSeg.p1.x - curPos.x;
+						var my = dy * k + triSeg.p1.y - curPos.y;
+						var dist2SegmentSq = mx * mx + my * my;
+
+						if (dist2SegmentSq < closestDistanceSq) {
+							closestDistanceSq = dist2SegmentSq;
+							currentTriangle = triIndex;
+
+							weights[i] = 1.0 - k;
+							weights[(i + 1) % 3] = k;
+							weights[(i + 2) % 3] = 0.0;
+						}
+					}
+				}
+			}
+
+			if (currentTriangle == -1)
+				throw "assert";
+		}
+
+		var blendedPos = inline new h3d.Vector();
+		var blendedRot = inline new h3d.Quat();
+		var blendedScale = inline new h3d.Vector();
+
+		var triangle = triangles[currentTriangle];
+		var def = ctx.getDefPose();
+		refQuat.set(def._12, def._13, def._21, def._23);
+		for (ptIndex => point in triangle) {
+			@:privateAccess
+			if (!point.animation.isSync) {
+				point.animation.sync(true);
+				point.animation.isSync = true;
+			}
+			var boneIndex = point.indexRemap[boneId];
+			var matrix = if (boneIndex == -1 || point.animation == null) {
+				def;
+			} else {
+				point.animation.getObjects()[boneIndex].targetObject.defaultTransform;
+			}
+
+			var w =  weights[ptIndex];
+			if (w == 0) {
+				continue;
+			}
+			blendedPos = inline blendedPos.add(inline new h3d.Vector(matrix.tx * w, matrix.ty * w, matrix.tz * w));
+			blendedScale = inline blendedScale.add(inline new h3d.Vector(matrix._11 * w, matrix._22 * w, matrix._33 * w));
+			workQuats[ptIndex].set(matrix._12, matrix._13, matrix._21, matrix._23);
+		}
+
+		Tools.weightedBlend(workQuats, refQuat, weights, workQuat);
+
+
+		outMatrix.tx = blendedPos.x;
+		outMatrix.ty = blendedPos.y;
+		outMatrix.tz = blendedPos.z;
+
+		outMatrix._11 = blendedScale.x;
+		outMatrix._22 = blendedScale.y;
+		outMatrix._33 = blendedScale.z;
+
+		outMatrix._12 = workQuat.x;
+		outMatrix._13 = workQuat.y;
+		outMatrix._21 = workQuat.z;
+		outMatrix._23 = workQuat.w;
+	}
+}

+ 0 - 1
hrt/animgraph/nodes/Input.hx

@@ -25,7 +25,6 @@ class Input extends AnimNode {
 
 		var map : Map<String, Int> = [];
 		for (id => obj in anim.getObjects()) {
-			trace(obj.objectName, id);
 			map.set(obj.objectName, id);
 		}
 		return map;