Ver Fonte

[animgraph] Exported BlendSpace2D to heaps

Clément Espeute há 7 meses atrás
pai
commit
8466592b87

+ 38 - 57
hide/view/animgraph/BlendSpace2DEditor.hx

@@ -7,6 +7,7 @@ class BlendSpacePreviewState {
 }
 }
 
 
 @:access(hrt.animgraph.BlendSpace2D)
 @:access(hrt.animgraph.BlendSpace2D)
+@:access(h3d.anim.BlendSpace2D)
 class BlendSpace2DEditor extends hide.view.FileView {
 class BlendSpace2DEditor extends hide.view.FileView {
 	var root : hide.Element;
 	var root : hide.Element;
 	var previewContainer : hide.Element;
 	var previewContainer : hide.Element;
@@ -20,6 +21,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
 	var propsEditor : hide.comp.PropsEditor;
 	var propsEditor : hide.comp.PropsEditor;
 
 
 	var blendSpace2D: hrt.animgraph.BlendSpace2D;
 	var blendSpace2D: hrt.animgraph.BlendSpace2D;
+	var animPreview : h3d.anim.BlendSpace2D;
 
 
 	var graph : hide.comp.SVG;
 	var graph : hide.comp.SVG;
 
 
@@ -36,7 +38,6 @@ class BlendSpace2DEditor extends hide.view.FileView {
 	static final pointRadius = 8;
 	static final pointRadius = 8;
 	var subdivs = 5;
 	var subdivs = 5;
 
 
-	var animPreview : hrt.animgraph.anim.BlendSpace2D;
 
 
 	var previewBlendPos : hide.Element;
 	var previewBlendPos : hide.Element;
 
 
@@ -45,7 +46,6 @@ class BlendSpace2DEditor extends hide.view.FileView {
 		var y = hxd.Math.clamp(graphYToLocal(clientY), blendSpace2D.minY, blendSpace2D.maxY);
 		var y = hxd.Math.clamp(graphYToLocal(clientY), blendSpace2D.minY, blendSpace2D.maxY);
 
 
 		if (snap) {
 		if (snap) {
-			// Snap to grid
 			var dx = (x - blendSpace2D.minX) / (blendSpace2D.maxX - blendSpace2D.minX);
 			var dx = (x - blendSpace2D.minX) / (blendSpace2D.maxX - blendSpace2D.minX);
 			dx = hxd.Math.round(dx * (subdivs+1)) / (subdivs+1);
 			dx = hxd.Math.round(dx * (subdivs+1)) / (subdivs+1);
 			x = hxd.Math.lerp(blendSpace2D.minX, blendSpace2D.maxX, dx);
 			x = hxd.Math.lerp(blendSpace2D.minX, blendSpace2D.maxX, dx);
@@ -100,9 +100,6 @@ class BlendSpace2DEditor extends hide.view.FileView {
 			{
 			{
 				graph = new hide.comp.SVG(graphContainer);
 				graph = new hide.comp.SVG(graphContainer);
 
 
-				//graph.rect(graph.element, -1,-1,1,1, {fill: "red"});
-				//graph.circle(graph.element, 0, 0, 0.2, {fill: "blue"});
-
 				var movedPoint = -1;
 				var movedPoint = -1;
 				var movingPreview = false;
 				var movingPreview = false;
 				var svg: js.html.svg.SVGElement = cast graph.element.get(0);
 				var svg: js.html.svg.SVGElement = cast graph.element.get(0);
@@ -175,7 +172,6 @@ class BlendSpace2DEditor extends hide.view.FileView {
 						blendSpace2D.points[movedPoint].x = pt.x;
 						blendSpace2D.points[movedPoint].x = pt.x;
 						blendSpace2D.points[movedPoint].y = pt.y;
 						blendSpace2D.points[movedPoint].y = pt.y;
 
 
-						blendSpace2D.triangulate();
 						refreshPreviewAnimation();
 						refreshPreviewAnimation();
 					}
 					}
 
 
@@ -207,10 +203,8 @@ class BlendSpace2DEditor extends hide.view.FileView {
 								blendSpace2D.points[ptId].y = old.y;
 								blendSpace2D.points[ptId].y = old.y;
 							}
 							}
 
 
-							blendSpace2D.triangulate();
-							refreshGraph();
+							reTriangulate();
 							refreshPropertiesPannel();
 							refreshPropertiesPannel();
-							refreshPreviewAnimation();
 						}
 						}
 
 
 						undo.change(Custom(exec));
 						undo.change(Custom(exec));
@@ -380,37 +374,11 @@ class BlendSpace2DEditor extends hide.view.FileView {
 				}
 				}
 			}
 			}
 
 
-			var blendSpace = new hrt.animgraph.anim.BlendSpace2D(blendSpace2D, set, new h3d.prim.ModelCache());
+			var blendSpace = blendSpace2D.makeAnimation(set, new h3d.prim.ModelCache());
 			animPreview = cast previewModel.playAnimation(blendSpace);
 			animPreview = cast previewModel.playAnimation(blendSpace);
-			// else @:privateAccess {
-			// 	var root : hrt.animgraph.nodes.BlendSpace2D.BlendSpace2D = cast @:privateAccess animPreview.rootNode;
-			// 	var old = root.points[0]?.animInfo?.anim.frame;
-
-			// 	var resolver = null;
-			// 	if (hrt.animgraph.AnimGraph.customEditorResolverProvider != null) {
-			// 		var resolvers = hrt.animgraph.AnimGraph.customEditorResolverProvider({animDirectory: blendSpace2D.animFolder, assetPath: state.path});
-			// 		if (resolvers != null) {
-			// 			resolver = resolvers[previewState.providerIndex]?.resolver;
-			// 		}
-			// 	}
-			// 	animPreview.resolver = resolver;
-
-			// 	// if the anim or the mesh changed between the last refreshPreviewAnimation
-			// 	if (previewModel.currentAnimation == animPreview) {
-			// 		animPreview.bind(previewModel);
-			// 	} else {
-			// 		previewModel.playAnimation(animPreview);
-			// 	}
-			// 	if (old != null) {
-			// 		for (point in root.points) {
-			// 			if (point.animInfo != null) {
-			// 				point.animInfo.anim.setFrame(old);
-			// 			}
-			// 		}
-			// 	}
-			// }
 
 
 			updatePreviewAxis();
 			updatePreviewAxis();
+			animPreview.resetSmooth();
 		}
 		}
 	}
 	}
 
 
@@ -435,7 +403,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
 			</dl>
 			</dl>
 		</div>
 		</div>
 		'), blendSpace2D, (_) -> {
 		'), blendSpace2D, (_) -> {
-			refreshGraph();
+			refreshPreviewAnimation();
 		});
 		});
 
 
 		if (selectedPoint != -1) {
 		if (selectedPoint != -1) {
@@ -451,9 +419,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
 			');
 			');
 
 
 			propsEditor.add(editor, blendSpace2D.points[selectedPoint], (_) -> {
 			propsEditor.add(editor, blendSpace2D.points[selectedPoint], (_) -> {
-				blendSpace2D.triangulate();
-				refreshGraph();
-				refreshPreviewAnimation();
+				reTriangulate();
 			});
 			});
 
 
 			var div = new Element("<div></div>").appendTo(editor.find("dl"));
 			var div = new Element("<div></div>").appendTo(editor.find("dl"));
@@ -470,9 +436,11 @@ class BlendSpace2DEditor extends hide.view.FileView {
 		var preview = new hide.Element('
 		var preview = new hide.Element('
 		<div class="group" name="Preview">
 		<div class="group" name="Preview">
 				<dl>
 				<dl>
-					<dt>X</dt><dd><input type="range" min="0.0" max="1.0" field="x"/></dd>
-					<dt>Y</dt><dd><input type="range" min="0.0" max="1.0" field="y"/></dd>
+					<dt>X</dt><dd><input type="range" min="${blendSpace2D.minX}" max="${blendSpace2D.maxX}" field="x"/></dd>
+					<dt>Y</dt><dd><input type="range" min="${blendSpace2D.minY}" max="${blendSpace2D.maxY}" field="y"/></dd>
 				</dl>
 				</dl>
+				<p><i>(You can also Ctrl+Click on the preview graph to set the preview point)</i></p>
+
 			</div>
 			</div>
 		');
 		');
 
 
@@ -487,6 +455,17 @@ class BlendSpace2DEditor extends hide.view.FileView {
 		});
 		});
 	}
 	}
 
 
+	function reTriangulate() {
+		try {
+			animPreview.triangulate();
+			refreshGraph();
+			refreshPreviewAnimation();
+		}
+		catch (e) {
+
+		}
+	}
+
 	override function save() {
 	override function save() {
 		var content = ide.toJSON(blendSpace2D.save());
 		var content = ide.toJSON(blendSpace2D.save());
 		currentSign = ide.makeSignature(content);
 		currentSign = ide.makeSignature(content);
@@ -532,8 +511,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
 			} else {
 			} else {
 				blendSpace2D.points.insert(index, point);
 				blendSpace2D.points.insert(index, point);
 			}
 			}
-			blendSpace2D.triangulate();
-			refreshGraph();
+			reTriangulate();
 		}
 		}
 		exec(false);
 		exec(false);
 		undo.change(Custom(exec));
 		undo.change(Custom(exec));
@@ -545,12 +523,12 @@ class BlendSpace2DEditor extends hide.view.FileView {
 		function exec(isUndo: Bool) {
 		function exec(isUndo: Bool) {
 			if (!isUndo) {
 			if (!isUndo) {
 				blendSpace2D.points.insert(index, point);
 				blendSpace2D.points.insert(index, point);
-				blendSpace2D.triangulate();
+				reTriangulate();
 				if (select)
 				if (select)
 					setSelection(index);
 					setSelection(index);
 			} else {
 			} else {
 				blendSpace2D.points.splice(index, 1);
 				blendSpace2D.points.splice(index, 1);
-				blendSpace2D.triangulate();
+				reTriangulate();
 				if (select || selectedPoint == index)
 				if (select || selectedPoint == index)
 					setSelection(prevSelection);
 					setSelection(prevSelection);
 			}
 			}
@@ -569,8 +547,8 @@ class BlendSpace2DEditor extends hide.view.FileView {
 	function onRequestAnimationFrame(dt: Float) {
 	function onRequestAnimationFrame(dt: Float) {
 		@:privateAccess
 		@:privateAccess
 		if (previewBlendPos != null && animPreview != null) {
 		if (previewBlendPos != null && animPreview != null) {
-			var rx = @:privateAccess animPreview.realX;
-			var ry = @:privateAccess animPreview.realY;
+			var rx = animPreview.xSmoothed;
+			var ry = animPreview.ySmoothed;
 			previewBlendPos.attr("transform", 'translate(${localXToGraph(rx)}, ${localYToGraph(ry)})');
 			previewBlendPos.attr("transform", 'translate(${localXToGraph(rx)}, ${localYToGraph(ry)})');
 
 
 			queuedRequest = js.Browser.window.requestAnimationFrame(onRequestAnimationFrame);
 			queuedRequest = js.Browser.window.requestAnimationFrame(onRequestAnimationFrame);
@@ -662,13 +640,16 @@ class BlendSpace2DEditor extends hide.view.FileView {
 			graph.text(graph.element, posX, cachedRect.height - 10, '$partRoundedX').addClass("grid-label");
 			graph.text(graph.element, posX, cachedRect.height - 10, '$partRoundedX').addClass("grid-label");
 		}
 		}
 
 
-		var pts = [new h2d.col.Point(), new h2d.col.Point(), new h2d.col.Point()];
-		for (triangle in blendSpace2D.triangles) {
-			for (id => point in triangle) {
-				pts[id].x = localXToGraph(blendSpace2D.points[point].x);
-				pts[id].y = localYToGraph(blendSpace2D.points[point].y);
+		if (animPreview != null) {
+
+			var pts = [new h2d.col.Point(), new h2d.col.Point(), new h2d.col.Point()];
+			for (triangle in animPreview.triangles) {
+				for (id => point in triangle) {
+					pts[id].x = localXToGraph(point.x);
+					pts[id].y = localYToGraph(point.y);
+				}
+				var g = graph.polygon2(graph.element, pts, {}).addClass("tri");
 			}
 			}
-			var g = graph.polygon2(graph.element, pts, {}).addClass("tri");
 		}
 		}
 
 
 		for (id => point in blendSpace2D.points) {
 		for (id => point in blendSpace2D.points) {
@@ -697,8 +678,8 @@ class BlendSpace2DEditor extends hide.view.FileView {
 			graph.line(g, -size, size, size, -size).addClass("preview-axis");
 			graph.line(g, -size, size, size, -size).addClass("preview-axis");
 
 
 			if (animPreview != null) {
 			if (animPreview != null) {
-				var rx = @:privateAccess animPreview.realX;
-				var ry = @:privateAccess animPreview.realY;
+				var rx = @:privateAccess animPreview.xSmoothed;
+				var ry = @:privateAccess animPreview.ySmoothed;
 
 
 				previewBlendPos = graph.group(graph.element);
 				previewBlendPos = graph.group(graph.element);
 				previewBlendPos.attr("transform", 'translate(${localXToGraph(rx)}, ${localYToGraph(ry)})');
 				previewBlendPos.attr("transform", 'translate(${localXToGraph(rx)}, ${localYToGraph(ry)})');

+ 15 - 79
hrt/animgraph/BlendSpace2D.hx

@@ -12,100 +12,36 @@ class BlendSpacePoint {
 
 
 class BlendSpace2D extends hrt.prefab.Prefab {
 class BlendSpace2D extends hrt.prefab.Prefab {
 	@:s var points : Array<BlendSpacePoint> = [];
 	@:s var points : Array<BlendSpacePoint> = [];
-	@:s var triangles : Array<Array<Int>> = [];
 	@:s var animFolder : String = null; // The folder to use as a base for the animation selection/loading
 	@:s var animFolder : String = null; // The folder to use as a base for the animation selection/loading
 
 
-	@:s var minX = 0.0;
+	@:s var minX = -1.0;
 	@:s var maxX = 1.0;
 	@:s var maxX = 1.0;
 	@:s var smoothX = 0.0;
 	@:s var smoothX = 0.0;
 
 
-	@:s var minY = 0.0;
+	@:s var minY = -1.0;
 	@:s var maxY = 1.0;
 	@:s var maxY = 1.0;
 	@:s var smoothY = 0.0;
 	@:s var smoothY = 0.0;
 
 
 	@:s var scaleSpeedOutOfBound: Bool = false;
 	@:s var scaleSpeedOutOfBound: Bool = false;
 
 
-
-	var instance : BlendSpace2DInstance;
-
-	function getInstance() : BlendSpace2DInstance {
-		return instance ??= @:privateAccess new BlendSpace2DInstance(this);
-	}
-
-	function triangulate() {
-		triangles = [];
-
-		var h2dPoints : Array<h2d.col.Point> = [];
+	public function makeAnimation(animSet: Map<String, String>, animCache: h3d.prim.ModelCache) : h3d.anim.BlendSpace2D {
+		var instPoints : Array<h3d.anim.BlendSpace2D.BlendSpace2DPoint> = [];
 		for (point in points) {
 		for (point in points) {
-			// normalize x / y in range 0/1 so the triangulation is done in a square
-			// this avoid the triangulation failing to create triangles when one axis is far larger than the other
-
-			var x = (point.x - minX) / (maxX - minX);
-			var y = (point.y - minY) / (maxY - minY);
-
-
-			h2dPoints.push(new h2d.col.Point(x, y));
-		}
-
-		var triangulation = h2d.col.Delaunay.triangulate(h2dPoints);
-		if (triangulation == null)
-			return;
-
-		for (triangle in triangulation) {
-			triangles.push([h2dPoints.indexOf(triangle.p1), h2dPoints.indexOf(triangle.p2), h2dPoints.indexOf(triangle.p3)]);
+			var path = point.animPath;
+			path = animSet[path] ?? path;
+			var anim = animCache.loadAnimation(hxd.res.Loader.currentInstance.load(path).toModel());
+			anim.speed = point.speed;
+			instPoints.push(new h3d.anim.BlendSpace2D.BlendSpace2DPoint(point.x, point.y, anim, point.keepSync));
 		}
 		}
-	}
 
 
-	static var _ = hrt.prefab.Prefab.register("blendspace2d", BlendSpace2D, "bs2d");
-}
-
-typedef BlendSpaceInstancePoint = {
-	x: Float,
-	y: Float,
-	?animation: h3d.anim.Animation, // Can be null if anim failed to load
-	?proxy: hrt.animgraph.nodes.Input.AnimProxy,
-}
-
-@: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);
+		var blendSpace = new h3d.anim.BlendSpace2D(name, instPoints);
 
 
-		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);
-		}
+		blendSpace.xSmooth = smoothX;
+		blendSpace.ySmooth = smoothY;
+		blendSpace.scaleSpeedOutsideOfBounds = scaleSpeedOutOfBound;
 
 
-		for (blendSpaceTriangle in blendSpace.triangles) {
-			var ourTriangle : Array<BlendSpaceInstancePoint> = [];
-			for (point in blendSpaceTriangle) {
-				ourTriangle.push(points[point]);
-			}
-			triangles.push(ourTriangle);
-		}
+		return blendSpace;
 	}
 	}
 
 
-	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;
-	}
+	static var _ = hrt.prefab.Prefab.register("blendspace2d", BlendSpace2D, "bs2d");
 }
 }

+ 0 - 521
hrt/animgraph/anim/BlendSpace2D.hx

@@ -1,521 +0,0 @@
-package hrt.animgraph.anim;
-import hrt.tools.MapUtils;
-
-typedef BlendSpaceInstancePoint = {
-	x: Float,
-	y: Float,
-	speed: Float,
-	?animInfo: AnimInfo, // can be null if no animation could be loaded
-}
-
-typedef AnimInfo = {
-	anim: h3d.anim.Animation,
-	proxy: hrt.animgraph.nodes.Input.AnimProxy,
-	keepSync: Bool,
-	selfSpeed: Float,
-	selfTime: Float,
-	objects: Array<BlendSpaceObject>,
-}
-
-class BlendSpaceObject extends h3d.anim.Animation.AnimatedObject {
-	public var matrices : Array<h3d.Matrix> = [];
-	public var outMatrix = new h3d.Matrix();
-	public var defaultMatrix = new h3d.Matrix();
-	public var touchedThisFrame = false;
-}
-
-@:access(hrt.animgraph.BlendSpace2D)
-class BlendSpace2D extends h3d.anim.Animation {
-	public var x(default, set): Float = 0.5;
-	var realX : Float = 0.5;
-	var vX : Float = 0.0;
-
-	public var y(default, set): Float = 0.5;
-	var realY : Float = 0.5;
-	var vY : Float = 0.0;
-
-	var blendSpace : hrt.animgraph.BlendSpace2D;
-	var animSet : Map<String, String>;
-
-	var dirtyPos: Bool = true;
-
-	var syncAnimTime: Float;
-
-	var prevAnimEventBind : h3d.anim.Animation;
-	static var tmpMatrix = new h3d.Matrix();
-
-	function set_x(v: Float) : Float {
-		if (v != x)
-			currentTriangle = -1;
-		return x = v;
-	}
-
-	function set_y(v: Float) : Float {
-		if (v != y)
-			currentTriangle = -1;
-		return y = v;
-	}
-
-	function getBSObjects() : Array<BlendSpaceObject> {
-		return cast objects;
-	}
-
-	var currentTriangle : Int = -1;
-	var weights : Array<Float> = [1.0,0.0,0.0];
-	var currentAnimLenght = 1.0;
-
-	var animInfos: Array<AnimInfo> = [];
-	var points : Array<BlendSpaceInstancePoint> = [];
-	var triangles : Array<Array<BlendSpaceInstancePoint>> = [];
-
-	var workQuat = new h3d.Quat();
-	var workQuats : Array<h3d.Quat> = [new h3d.Quat(), new h3d.Quat(), new h3d.Quat()];
-	var refQuat = new h3d.Quat();
-	var modelCache : h3d.prim.ModelCache;
-
-	var outsideSpeedScale : Float = 1.0;
-
-	public function new(blendSpace: hrt.animgraph.BlendSpace2D, animSet: Map<String, String>, modelCache: h3d.prim.ModelCache) {
-		this.blendSpace = blendSpace;
-		this.animSet = animSet;
-		this.modelCache = modelCache;
-		super(blendSpace.name, 100, 1/60.0);
-	}
-
-	public function resetSmooth() {
-		realX = x;
-		realY = y;
-		vX = 0.0;
-		vY = 0.0;
-	}
-
-	override function bind(object: h3d.scene.Object) {
-		animInfos = [];
-		points = [];
-		triangles = [];
-		currentTriangle = -1;
-		objects = [];
-
-		resetSmooth();
-
-		var curOurBoneId = 0;
-
-		if (blendSpace == null)
-			throw "Can't bind with a null blendSpace";
-
-		// only one animation is created per anim path, so if multiple points use the same anim, only one instance is created
-		var animMap : Map<String, Int> = [];
-		var allObjects : Map<String, BlendSpaceObject> = [];
-
-		for (blendSpacePoint in blendSpace.points) {
-			var point : BlendSpaceInstancePoint = {x: blendSpacePoint.x, y: blendSpacePoint.y, speed: blendSpacePoint.speed};
-			if (blendSpacePoint.animPath != null && blendSpacePoint.animPath.length > 0) {
-				try
-				{
-					var path = animSet.get(blendSpacePoint.animPath);
-					if (path == null) {
-						if (StringTools.endsWith(blendSpacePoint.animPath, ".fbx")) {
-							path = blendSpacePoint.animPath;
-						}
-					}
-					if (path != null) {
-
-						function makeAnim() : Int {
-							// Create a new animation
-							var index = animInfos.length;
-							var animModel = hxd.res.Loader.currentInstance.load(path).toModel();
-							var animBase = modelCache.loadAnimation(animModel);
-							var proxy = new hrt.animgraph.nodes.Input.AnimProxy(null);
-							var animInstance = animBase.createInstance(proxy);
-
-							animInstance.bind(object);
-
-							var animObjects = [];
-							for (obj in animInstance.getObjects()) {
-								var o = MapUtils.getOrPut(allObjects, obj.objectName, {
-									var o = new BlendSpaceObject(obj.objectName);
-									o.targetJoint = obj.targetJoint;
-									o.targetSkin = obj.targetSkin;
-									o.targetObject = obj.targetObject;
-
-									@:privateAccess
-									if (o.targetSkin != null) {
-										Tools.decomposeMatrix(o.targetSkin.skinData.allJoints[o.targetJoint].defMat, o.defaultMatrix);
-									} else {
-										o.defaultMatrix = h3d.anim.SmoothTransition.MZERO;
-									}
-									objects.push(o);
-									o;
-								});
-
-								animObjects.push(o);
-							}
-
-							animInfos.push({anim: animInstance, proxy: proxy, selfSpeed: 1.0, keepSync: blendSpacePoint.keepSync, selfTime: 0, objects: animObjects});
-							return index;
-						}
-
-						var animIndex = if (blendSpacePoint.keepSync) {
-							MapUtils.getOrPut(animMap, path, makeAnim());
-						} else {
-							// All anims not kept in sync are unique, so we bypass the animMap
-							var i = makeAnim();
-							animInfos[i].selfSpeed = blendSpacePoint.speed;
-							i;
-						}
-
-						point.animInfo = animInfos[animIndex];
-					}
-
-					points.push(point);
-				} catch (e) {
-					trace('Couldn\'t load anim ${blendSpacePoint.animPath} : ${e.toString()}');
-				}
-			}
-		}
-
-		trace(allObjects);
-
-		triangulate();
-	}
-
-	function triangulate() : Void {
-		triangles = [];
-
-		var h2dPoints : Array<h2d.col.Point> = [];
-		for (point in points) {
-			// normalize x / y in range 0/1 so the triangulation is done in a square
-			// this avoid the triangulation failing to create triangles when one axis is far larger than the other
-
-			var x = (point.x - blendSpace.minX) / (blendSpace.maxX - blendSpace.minX);
-			var y = (point.y - blendSpace.minY) / (blendSpace.maxY - blendSpace.minY);
-
-
-			h2dPoints.push(new h2d.col.Point(x, y));
-		}
-
-		var triangulation = h2d.col.Delaunay.triangulate(h2dPoints);
-		if (triangulation == null) {
-			// todo : put blend space into "1d mode" if triangulation failed
-			throw "triangulation failed";
-			return;
-		}
-
-		for (triTriangle in triangulation) {
-			var triangle : Array<BlendSpaceInstancePoint> = [];
-			triangle[0] = points[h2dPoints.indexOf(triTriangle.p1)];
-			triangle[1] = points[h2dPoints.indexOf(triTriangle.p2)];
-			triangle[2] = points[h2dPoints.indexOf(triTriangle.p3)];
-			triangles.push(triangle);
-		}
-	}
-
-	override function clone(?a:h3d.anim.Animation):h3d.anim.Animation {
-		var a : BlendSpace2D = cast a;
-		if (a == null)
-			a = new BlendSpace2D(blendSpace, animSet, modelCache);
-		a.blendSpace = blendSpace;
-		a.animSet = animSet;
-		a.modelCache = a.modelCache;
-		return super.clone(a);
-	}
-
-	override function update(dt:Float):Float {
-		var dt2 = super.update(dt);
-
-		if (blendSpace.smoothX > 0) {
-			var r = criticalSpringDamper(realX, vX, x, 0, blendSpace.smoothX, dt);
-			realX = r.x;
-			vX = r.v;
-
-			currentTriangle = -1;
-		} else {
-			realX = x;
-		}
-
-		if (blendSpace.smoothX > 0) {
-			var r = criticalSpringDamper(realY, vY, y, 0, blendSpace.smoothY, dt);
-			realY = r.x;
-			vY = r.v;
-
-			currentTriangle = -1;
-		} else {
-			realY = y;
-		}
-
-		var scale = 1.0 / currentAnimLenght;
-		if (blendSpace.scaleSpeedOutOfBound)
-		{
-			scale *= outsideSpeedScale;
-		}
-		syncAnimTime = (syncAnimTime + dt * scale) % 1.0;
-
-		updateCurrentTriangle();
-
-		if (currentTriangle < 0)
-			return dt2;
-
-		var triangle = triangles[currentTriangle];
-
-		// update our anim infos
-		for (animInfo in animInfos) {
-			var skip = false;
-
-			var newTime = if (animInfo.keepSync) {
-				animInfo.anim.getDuration() * syncAnimTime;
-			} else {
-				animInfo.anim.frame / (animInfo.anim.speed * animInfo.anim.sampling) + dt * animInfo.selfSpeed;
-			}
-
-			// Check if the anim info is in one of our triangle points, and if so
-			// tick it normaly,
-
-			for (p in triangle) {
-
-				if (p.animInfo == animInfo) {
-					skip = true;
-					//var scale = animInfo.selfSpeed;
-					var delta = newTime - animInfo.anim.frame / (animInfo.anim.speed * animInfo.anim.sampling);
-					animInfo.anim.update(delta);
-					break;
-				}
-			}
-
-			if (skip)
-				continue;
-
-			animInfo.anim.setFrame(newTime * (animInfo.anim.speed * animInfo.anim.sampling));
-		}
-		return dt2;
-	}
-
-	function updateCurrentTriangle() {
-		if (triangles.length < 1)
-			return;
-
-		if (currentTriangle == -1) {
-			var curPos = inline new h2d.col.Point(realX, realY);
-
-			// find the triangle our curPos resides in
-			var collided = false;
-			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;
-					collided = true;
-					break;
-				}
-			}
-
-			var debugk = 0.0;
-			// We are outside all triangles, find the closest edge
-			if (currentTriangle == -1) {
-
-				var closestDistanceSq : Float = hxd.Math.POSITIVE_INFINITY;
-				var closestX : Float = 0.0;
-				var closestY : Float = 0.0;
-
-				for (triIndex => tri in triangles) {
-					for (i in 0...3) {
-						var i2 = (i+1) % 3;
-						var p1 = tri[i];
-						var p2 = tri[i2];
-
-						var dx = p2.x - p1.x;
-						var dy = p2.y - p1.y;
-						var k = ((curPos.x - p1.x) * dx + (curPos.y - p1.y) * dy) / (dx * dx + dy * dy);
-						k = hxd.Math.clamp(k, 0, 1);
-						var mx = dx * k + p1.x - curPos.x;
-						var my = dy * k + p1.y - curPos.y;
-						var dist2SegmentSq = mx * mx + my * my;
-
-						if (dist2SegmentSq < closestDistanceSq) {
-							closestDistanceSq = dist2SegmentSq;
-							currentTriangle = triIndex;
-							closestX = mx;
-							closestY = my;
-
-							debugk = k;
-
-							weights[i] = 1.0 - k;
-							weights[(i + 1) % 3] = k;
-							weights[(i + 2) % 3] = 0.0;
-						}
-					}
-				}
-
-				var centerX = (blendSpace.maxX + blendSpace.minX) / 2.0;
-				var centerY = (blendSpace.maxY + blendSpace.minY) / 2.0;
-
-				trace(closestX, closestY);
-				closestX += (curPos.x);
-				closestY += (curPos.y);
-				trace(closestX, closestY);
-
-				var distClosesetToCenter = hxd.Math.distance(closestX - centerX, closestY - centerY) + hxd.Math.EPSILON;
-				var distToCenter = curPos.distance(inline new h2d.col.Point(centerX, centerY)) + hxd.Math.EPSILON;
-
-				outsideSpeedScale = distToCenter / distClosesetToCenter;
-				trace(outsideSpeedScale, distToCenter, distClosesetToCenter, "====");
-			} else {
-				outsideSpeedScale = 1.0;
-			}
-
-			if (currentTriangle == -1)
-				throw "assert";
-
-			var max = 0;
-			for (i in 1...3) {
-				if (weights[i] > weights[max]) {
-					max = i;
-				}
-			}
-
-			var strongestAnim = triangles[currentTriangle][max].animInfo?.anim;
-			if (prevAnimEventBind != strongestAnim) {
-				if (prevAnimEventBind != null)
-					prevAnimEventBind.onEvent = null;
-				if (strongestAnim != null)
-					strongestAnim.onEvent = animEventHander;
-				prevAnimEventBind = strongestAnim;
-			}
-
-			currentAnimLenght = 0.0;
-
-			// Compensate for null animations that don't have length
-			var nulls = 0;
-			var nullWeights: Float = 0;
-			for (i => pt in triangles[currentTriangle]) {
-				if (pt.animInfo == null || !pt.animInfo.keepSync) {
-					nulls ++;
-					nullWeights += weights[i];
-				}
-			}
-
-			if (nulls < 3) {
-				nullWeights /= (3 - nulls);
-			}
-
-			for (i => pt in triangles[currentTriangle]) {
-				if(pt.animInfo != null && pt.animInfo.keepSync) {
-					var blendLength = pt.animInfo.anim.getDuration()/pt.speed * (weights[i] + nullWeights);
-					currentAnimLenght += blendLength;
-				}
-			}
-		}
-	}
-
-	@:haxe.warning("-WInlineOptimizedField")
-	override function sync(decompose:Bool = false) {
-		updateCurrentTriangle();
-
-		if (currentTriangle < 0)
-			return;
-
-		var triangle = triangles[currentTriangle];
-
-		// Reset tmpMatrices to the default matrix
-		for (object in getBSObjects()) {
-			for (i => _ in triangle) {
-				object.matrices[i] = object.defaultMatrix;
-			}
-			object.touchedThisFrame = false;
-		}
-
-		for (ptIndex => point in triangle) {
-			point.animInfo.anim.isSync = false;
-			point.animInfo.anim.sync(true);
-
-			// copy modified matrices references
-			@:privateAccess
-			for (object in point.animInfo.objects) {
-				object.matrices[ptIndex] = (if( object.targetSkin != null ) object.targetSkin.currentRelPose[object.targetJoint] else object.targetObject.defaultTransform) ?? object.matrices[ptIndex];
-			}
-		}
-
-
-		for (object in getBSObjects()) {
-			var outMatrix = object.outMatrix;
-
-			var blendedPos = inline new h3d.Vector();
-			var blendedRot = inline new h3d.Quat();
-			var blendedScale = inline new h3d.Vector();
-
-			var triangle = triangles[currentTriangle];
-			var def = object.defaultMatrix;
-			refQuat.set(def._12, def._13, def._21, def._23);
-
-			for (ptIndex => point in triangle) {
-				var w =  weights[ptIndex];
-				if (w == 0) {
-					continue;
-				}
-
-				var matrix = object.matrices[ptIndex];
-
-				if (matrix == null)
-					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;
-
-			if (!decompose) {
-				Tools.recomposeMatrix(outMatrix, tmpMatrix);
-				outMatrix.load(tmpMatrix);
-			}
-
-			@:privateAccess if( object.targetSkin != null ) object.targetSkin.currentRelPose[object.targetJoint] = outMatrix else object.targetObject.defaultTransform = outMatrix;
-		}
-
-	}
-
-	function animEventHander(name: String) {
-		onEvent(name);
-	}
-
-	function setupAnimEvents() {
-		// handled by the triangle setup
-	}
-
-	static function halfLifeToDamping(halfLife: Float) {
-    	return (4.0 * 0.69314718056) / (halfLife + 1e-5);
-	}
-
-	static function fastNegexp(x: Float) : Float
-	{
-		return 1.0 / (1.0 + x + 0.48*x*x + 0.235*x*x*x);
-	}
-
-
-	inline static function criticalSpringDamper(x: Float, v: Float, xGloal: Float, vGoal: Float, halfLife: Float, dt: Float) : {x: Float, v: Float} {
-		final damping = halfLifeToDamping(halfLife);
-		final c = xGloal + (damping * vGoal) / (damping * damping ) / 4.0;
-		final half_damping = damping / 2.0;
-		final j0 = x - c;
-		final j1 = v + j0 * half_damping;
-		final eydt = fastNegexp(half_damping * dt);
-
-		return {x: eydt * (j0 + j1 * dt) + c, v: eydt *(v - j1*half_damping*dt)};
-	}
-}

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

@@ -58,351 +58,7 @@ class BlendSpace2D extends AnimNode {
 	var workQuats : Array<h3d.Quat> = [new h3d.Quat(), new h3d.Quat(), new h3d.Quat()];
 	var workQuats : Array<h3d.Quat> = [new h3d.Quat(), new h3d.Quat(), new h3d.Quat()];
 	var refQuat = new h3d.Quat();
 	var refQuat = new h3d.Quat();
 
 
-
-	static function halfLifeToDamping(halfLife: Float) {
-    	return (4.0 * 0.69314718056) / (halfLife + 1e-5);
-	}
-
-	static function fastNegexp(x: Float) : Float
-	{
-    	return 1.0 / (1.0 + x + 0.48*x*x + 0.235*x*x*x);
-	}
-
-
-	inline static function criticalSpringDamper(x: Float, v: Float, xGloal: Float, vGoal: Float, halfLife: Float, dt: Float) : {x: Float, v: Float} {
-		final damping = halfLifeToDamping(halfLife);
-		final c = xGloal + (damping * vGoal) / (damping * damping ) / 4.0;
-		final half_damping = damping / 2.0;
-		final j0 = x - c;
-		final j1 = v + j0 * half_damping;
-		final eydt = fastNegexp(half_damping * dt);
-
-		return {x: eydt * (j0 + j1 * dt) + c, v: eydt *(v - j1*half_damping*dt)};
-	}
-
-	override function resetSmoothedValues() {
-		realX = bsX;
-		realY = bsY;
-		vX = 0.0;
-		vY = 0.0;
-	}
-
-	override function getBones(ctx: hrt.animgraph.nodes.AnimNode.GetBoneContext):Map<String, Int> {
-		var boneMap : Map<String, Int> = [];
-		animInfos = [];
-		points = [];
-		triangles = [];
-		currentTriangle = -1;
-
-		realX = bsX;
-		realY = bsY;
-
-		var curOurBoneId = 0;
-
-		if (blendSpace == null) {
-			if (!hxd.res.Loader.currentInstance.exists(path))
-				return boneMap;
-			blendSpace = cast hxd.res.Loader.currentInstance.load(path).toPrefab().load();
-		}
-
-		// only one animation is created per anim path, so if multiple points use the same anim, only one instance is created
-		var animMap : Map<String, Int> = [];
-
-		for (blendSpacePoint in blendSpace.points) {
-			var point : BlendSpaceInstancePoint = {x: blendSpacePoint.x, y: blendSpacePoint.y, speed: blendSpacePoint.speed};
-			if (blendSpacePoint.animPath != null && blendSpacePoint.animPath.length > 0) {
-				try
-				{
-					var path = ctx.resolver(blendSpacePoint.animPath);
-					if (path != null) {
-
-						function makeAnim() : Int {
-							// Create a new animation
-							var index = animInfos.length;
-							var animModel = hxd.res.Loader.currentInstance.load(path).toModel();
-							var animBase = ctx.modelCache.loadAnimation(animModel);
-							var proxy = new hrt.animgraph.nodes.Input.AnimProxy(null);
-							var animInstance = animBase.createInstance(proxy);
-
-							var indexRemap : Array<Null<Int>> = [];
-
-							for (boneId => obj in animInstance.getObjects()) {
-								var ourId = MapUtils.getOrPut(boneMap, obj.objectName, curOurBoneId++);
-								indexRemap[ourId] = boneId;
-							}
-
-							animInfos.push({anim: animInstance, proxy: proxy, indexRemap: indexRemap, selfSpeed: 1.0, keepSync: blendSpacePoint.keepSync});
-							return index;
-						}
-
-						var animIndex = if (blendSpacePoint.keepSync) {
-							MapUtils.getOrPut(animMap, path, makeAnim());
-						} else {
-							// All anims not kept in sync are unique, so we bypass the animMap
-							var i = makeAnim();
-							animInfos[i].selfSpeed = blendSpacePoint.speed;
-							i;
-						}
-
-						point.animInfo = animInfos[animIndex];
-					}
-
-				} 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 (info in animInfos) {
-			for (i in 0...curOurBoneId) {
-				if(info.indexRemap[i] == null) {
-					info.indexRemap[i] = -1;
-				}
-			}
-		}
-
-		return boneMap;
-	}
-
-	override function tick(dt:Float) {
-		super.tick(dt);
-		if (blendSpace == null)
-			return;
-
-		if (blendSpace.smoothX > 0) {
-			var r = criticalSpringDamper(realX, vX, bsX, 0, blendSpace.smoothX, dt);
-			realX = r.x;
-			vX = r.v;
-
-			currentTriangle = -1;
-		} else {
-			realX = bsX;
-		}
-
-		if (blendSpace.smoothX > 0) {
-			var r = criticalSpringDamper(realY, vY, bsY, 0, blendSpace.smoothY, dt);
-			realY = r.x;
-			vY = r.v;
-
-			currentTriangle = -1;
-		} else {
-			realY = bsY;
-		}
-
-		for (animInfo in animInfos) {
-			// keep all the animations in sync
-			var scale = animInfo.selfSpeed;
-
-			if (animInfo.keepSync) {
-				if (currentAnimLenght <= 0) {
-					continue;
-				}
-				scale *= (animInfo.anim.getDuration()) / currentAnimLenght;
-			}
-
-			animInfo.anim.update(dt * scale);
-			@:privateAccess animInfo.anim.isSync = false;
-		}
-	}
-
-	@:haxe.warning("-WInlineOptimizedField")
-	override function getBoneTransform(boneId:Int, outMatrix:h3d.Matrix, ctx:hrt.animgraph.nodes.AnimNode.GetBoneTransformContext) {
-		if (triangles.length < 1)
-			return;
-
-		if (currentTriangle == -1) {
-			var curPos = inline new h2d.col.Point(realX, realY);
-
-			// find the triangle our curPos resides in
-			var collided = false;
-			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;
-					collided = true;
-					break;
-				}
-			}
-
-			var debugk = 0.0;
-			// 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 p1 = tri[i];
-						var p2 = tri[i2];
-
-						var dx = p2.x - p1.x;
-						var dy = p2.y - p1.y;
-						var k = ((curPos.x - p1.x) * dx + (curPos.y - p1.y) * dy) / (dx * dx + dy * dy);
-						k = hxd.Math.clamp(k, 0, 1);
-						var mx = dx * k + p1.x - curPos.x;
-						var my = dy * k + p1.y - curPos.y;
-						var dist2SegmentSq = mx * mx + my * my;
-
-						if (dist2SegmentSq < closestDistanceSq) {
-							closestDistanceSq = dist2SegmentSq;
-							currentTriangle = triIndex;
-
-							debugk = k;
-
-							weights[i] = 1.0 - k;
-							weights[(i + 1) % 3] = k;
-							weights[(i + 2) % 3] = 0.0;
-						}
-					}
-				}
-			}
-
-			if (currentTriangle == -1)
-				throw "assert";
-
-			var max = 0;
-			for (i in 1...3) {
-				if (weights[i] > weights[max]) {
-					max = i;
-				}
-			}
-
-			var strongestAnim = triangles[currentTriangle][max].animInfo?.anim;
-			if (prevAnimEventBind != strongestAnim) {
-				if (prevAnimEventBind != null)
-					prevAnimEventBind.onEvent = null;
-				if (strongestAnim != null)
-					strongestAnim.onEvent = animEventHander;
-				prevAnimEventBind = strongestAnim;
-			}
-
-			currentAnimLenght = 0.0;
-
-			// Compensate for null animations that don't have length
-			var nulls = 0;
-			var nullWeights: Float = 0;
-			for (i => pt in triangles[currentTriangle]) {
-				if (pt.animInfo == null || !pt.animInfo.keepSync) {
-					nulls ++;
-					nullWeights += weights[i];
-				}
-			}
-
-			if (nulls < 3) {
-				nullWeights /= (3 - nulls);
-			}
-
-			for (i => pt in triangles[currentTriangle]) {
-				if(pt.animInfo != null && pt.animInfo.keepSync) {
-					var blendLength = pt.animInfo.anim.getDuration()/pt.speed * (weights[i] + nullWeights);
-					currentAnimLenght += blendLength;
-				}
-			}
-		}
-
-		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) {
-			var w =  weights[ptIndex];
-			if (w == 0) {
-				continue;
-			}
-
-			var matrix : h3d.Matrix;
-
-			if (point.animInfo == null) {
-				matrix = ctx.getDefPose();
-			}
-			else {
-				@:privateAccess
-				if (!point.animInfo.anim.isSync) {
-					point.animInfo.anim.sync(true);
-					point.animInfo.anim.isSync = true;
-				}
-				var boneIndex = point.animInfo.indexRemap[boneId];
-				matrix = if (boneIndex == -1 || point.animInfo.anim == null) {
-					def;
-				} else {
-					point.animInfo.anim.getObjects()[boneIndex].targetObject.defaultTransform;
-				}
-			}
-
-			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;
-	}
-
-	function animEventHander(name: String) {
-		onEvent(name);
-	}
-
 	function setupAnimEvents() {
 	function setupAnimEvents() {
-		// handled by the triangle setup
-	}
-
-	#if editor
-	override function getPropertiesHTML(width:Float):Array<hide.Element> {
-		var elts = super.getPropertiesHTML(width);
 
 
-		var wrapper = new hide.Element("<input-wrapper></input-wrapper>");
-		wrapper.height("20px");
-
-		var fileSelect = new hide.comp.FileSelect(["bs2d"], wrapper);
-		fileSelect.path = path;
-		fileSelect.onChange = () -> {
-			var prev = path;
-			var curr = fileSelect.path;
-			function exec(isUndo : Bool) {
-				path = !isUndo ? curr : prev;
-				fileSelect.path = path;
-				getAnimEditor().refreshPreview();
-			}
-			exec(false);
-			getAnimEditor().undo.change(Custom(exec));
-		}
-		elts.push(wrapper);
-		return elts;
-	}
-
-	override function getSize():Int {
-		return Node.SIZE_BIG;
 	}
 	}
-
-	#end
 }
 }