|
@@ -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;
|
|
|
+ }
|
|
|
+}
|