Bladeren bron

[animgraph] Begin working on BlendSpace2D ui

Clément Espeute 9 maanden geleden
bovenliggende
commit
95f76c2a9b
6 gewijzigde bestanden met toevoegingen van 321 en 0 verwijderingen
  1. 59 0
      bin/style.css
  2. 76 0
      bin/style.less
  3. 12 0
      hide/comp/SVG.hx
  4. 157 0
      hide/view/animgraph/BlendSpace2DEditor.hx
  5. 14 0
      hrt/animgraph/BlendSpace2D.hx
  6. 3 0
      hrt/animgraph/nodes/Input.hx

+ 59 - 0
bin/style.css

@@ -4320,3 +4320,62 @@ graph-editor-root properties-container graph-parameters > ul graph-parameter .ic
   transition: transform 0.25s;
   transform: rotate(0deg);
 }
+/** Blendspace2dEditor **/
+blend-space-2d-root {
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: row;
+  justify-items: stretch;
+  align-items: stretch;
+}
+blend-space-2d-root main-panel {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-items: stretch;
+  align-items: stretch;
+}
+blend-space-2d-root main-panel preview-container {
+  background-color: blue;
+  flex: 1;
+}
+blend-space-2d-root main-panel preview-container .scene-preview {
+  width: 100%;
+  height: 100%;
+}
+blend-space-2d-root main-panel graph-container {
+  height: 40%;
+  background-color: var(--bg-1);
+  margin: 16px;
+}
+blend-space-2d-root main-panel graph-container svg {
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  border: var(--basic-border);
+}
+blend-space-2d-root main-panel graph-container svg .tri {
+  stroke: #888;
+  stroke-width: 1;
+  fill: rgba(114, 180, 255, 0.226);
+}
+blend-space-2d-root main-panel graph-container svg .grid {
+  stroke: #666;
+  stroke-dasharray: 4;
+  stroke-width: 1;
+}
+blend-space-2d-root main-panel graph-container svg .bs-point {
+  fill: black;
+  stroke: white;
+  stroke-width: 2;
+}
+blend-space-2d-root properties-container {
+  padding: var(--basic-padding);
+  display: flex;
+  flex-direction: column;
+  flex-basis: 300px;
+  border-left: var(--basic-border);
+  background-color: var(--bg-1);
+}

+ 76 - 0
bin/style.less

@@ -5125,3 +5125,79 @@ graph-editor-root {
 	}
 }
 
+/** Blendspace2dEditor **/
+
+blend-space-2d-root {
+	width: 100%;
+	height: 100%;
+	box-sizing: border-box;
+
+	display: flex;
+	flex-direction: row;
+	justify-items: stretch;
+	align-items: stretch;
+
+	main-panel {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		justify-items: stretch;
+		align-items: stretch;
+
+		preview-container {
+			background-color: blue;
+			flex: 1;
+
+			.scene-preview {
+				width: 100%;
+				height: 100%;
+			}
+		}
+
+		graph-container {
+			height: 40%;
+
+			background-color: var(--bg-1);
+			margin: 16px;
+
+			svg {
+				width: 100%;
+				height: 100%;
+				box-sizing: border-box;
+
+				border: var(--basic-border);
+
+				.tri {
+					stroke: #888;
+					stroke-width: 1;
+					fill: rgba(114, 180, 255, 0.226);
+				}
+
+				.grid {
+					stroke: #666;
+					stroke-dasharray: 4;
+					stroke-width: 1;
+				}
+
+				.bs-point {
+					fill: black;
+					stroke: white;
+					stroke-width: 2;
+				}
+			}
+		}
+	}
+
+	properties-container {
+		padding: var(--basic-padding);
+
+		display: flex;
+		flex-direction: column;
+		flex-basis: 300px;
+
+		border-left: var(--basic-border);
+
+		background-color: var(--bg-1);
+	}
+
+}

+ 12 - 0
hide/comp/SVG.hx

@@ -85,6 +85,18 @@ class SVG extends Component {
 		return make(parent, "path", {d: lines.join("")}, style);
 	}
 
+	/**
+		Use the polygon element instead of path
+	**/
+	public function polygon2(?parent: Element, points: Array<h2d.col.Point>, ?style:Dynamic) {
+		// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon
+		var lines : Array<String> = [];
+		for(i in 0...points.length) {
+			lines.push('${points[i].x},${points[i].y}');
+		}
+		return make(parent, "polygon", {points: lines.join(" ")}, style);
+	}
+
 	public function group(?parent: Element, ?className: String, ?attr: Dynamic) {
 		var g = make(parent, "g", attr);
 		if(className != null)

+ 157 - 0
hide/view/animgraph/BlendSpace2DEditor.hx

@@ -0,0 +1,157 @@
+package hide.view.animgraph;
+
+@:access(hrt.animgraph.BlendSpace2D)
+class BlendSpace2DEditor extends hide.view.FileView {
+	var root : hide.Element;
+	var previewContainer : hide.Element;
+	var graphContainer : hide.Element;
+	var propertiesContainer : hide.Element;
+	var mainPanel : hide.Element;
+
+	var scenePreview : hide.comp.Scene;
+	var previewCamController : hide.comp.Scene.PreviewCamController;
+
+	var blendSpace2D: hrt.animgraph.BlendSpace2D;
+
+	var graph : hide.comp.SVG;
+
+	var graphPoints : Array<js.html.svg.GElement> = [];
+
+	static final pointRadius = 8;
+	var subdivs = 5;
+
+	override function onRebuild() {
+		blendSpace2D = cast hide.Ide.inst.loadPrefab(state.path, null,  true);
+		super.onRebuild();
+		element.html("");
+
+		root = new hide.Element("<blend-space-2d-root></blend-space-2d-root>").appendTo(element);
+		mainPanel = new hide.Element("<main-panel></main-panel>").appendTo(root);
+		{
+			previewContainer = new hide.Element("<preview-container></preview-container>").appendTo(mainPanel);
+			graphContainer = new hide.Element("<graph-container></graph-container>").appendTo(mainPanel);
+			var panel = new hide.comp.ResizablePanel(Vertical, graphContainer);
+			panel.saveDisplayKey = "graphPanel";
+			{
+				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 svg: js.html.svg.SVGElement = cast graph.element.get(0);
+
+				svg.onpointerdown = (e:js.html.PointerEvent) -> {
+					if (e.button != 0)
+						return;
+					var rect = svg.getBoundingClientRect();
+					var mouse = inline new h2d.col.Point((e.clientX - rect.x), e.clientY - rect.y);
+
+					for (id => point in blendSpace2D.points) {
+						var pt = inline new h2d.col.Point(point.x * rect.width, point.y * rect.height);
+						trace(mouse, pt);
+						if (mouse.distanceSq(pt) < pointRadius * pointRadius) {
+							movedPoint = id;
+							break;
+						}
+					}
+					if (movedPoint == -1)
+						return;
+
+					svg.setPointerCapture(e.pointerId);
+					e.preventDefault();
+				}
+
+				svg.onpointermove = (e:js.html.PointerEvent) -> {
+					if (movedPoint == -1)
+						return;
+
+					var rect = svg.getBoundingClientRect();
+					var mouse = inline new h2d.col.Point((e.clientX - rect.x)/rect.width, (e.clientY - rect.y)/rect.height);
+
+					blendSpace2D.points[movedPoint].x = hxd.Math.clamp(mouse.x);
+					blendSpace2D.points[movedPoint].y = hxd.Math.clamp(mouse.y);
+
+					if (!e.altKey) {
+						// Snap to grid
+						blendSpace2D.points[movedPoint].x = hxd.Math.round(blendSpace2D.points[movedPoint].x * (subdivs+1)) / (subdivs+1);
+						blendSpace2D.points[movedPoint].y = hxd.Math.round(blendSpace2D.points[movedPoint].y * (subdivs+1)) / (subdivs+1);
+					}
+
+					blendSpace2D.reTriangulate();
+
+					refreshGraph();
+				}
+
+				svg.onpointerup = (e:js.html.PointerEvent) -> {
+					if (movedPoint == -1)
+						return;
+					movedPoint = -1;
+				}
+
+			}
+			panel.onResize = refreshGraph;
+
+			scenePreview = new hide.comp.Scene(config, previewContainer, null);
+			scenePreview.element.addClass("scene-preview");
+
+			scenePreview.onReady = onScenePreviewReady;
+			scenePreview.onUpdate = onScenePreviewUpdate;
+		}
+
+		propertiesContainer = new hide.Element("<properties-container></properties-container>").appendTo(root);
+
+		refreshGraph();
+	}
+
+    function onScenePreviewReady() {
+        previewCamController = new hide.comp.Scene.PreviewCamController(scenePreview.s3d);
+    }
+
+    function onScenePreviewUpdate(dt: Float) {
+
+    }
+
+	function createPoint() {
+
+	}
+
+	function refreshGraph() {
+		graph.element.html("");
+
+		var width = graph.element.innerWidth();
+		var height = graph.element.innerHeight();
+
+
+		graph.element.attr("viewBox", '0 0 $width $height');
+		//graph.element.attr("preserveAspectRatio", "XMidYMid meet");
+
+		for (i in 1...subdivs+1) {
+			var posX = (i / (subdivs+1)) * width;
+			var posY = (i / (subdivs+1)) * height;
+			graph.line(graph.element, posX, 0, posX, height).addClass("grid");
+			graph.line(graph.element, 0, posY, width, posY).addClass("grid");
+		}
+
+		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 = blendSpace2D.points[point].x * width;
+				pts[id].y = blendSpace2D.points[point].y * height;
+			}
+			var g = graph.polygon2(graph.element, pts, {}).addClass("tri");
+		}
+
+		for (id => point in blendSpace2D.points) {
+			var g = graph.group(graph.element);
+			var svgPoint = graph.circle(g, 0,0, 8).addClass("bs-point");
+
+			g.attr("transform", 'translate(${point.x * width}, ${point.y * height})');
+
+			var move = false;
+			var elem : js.html.svg.CircleElement = cast svgPoint.get(0);
+		}
+	}
+
+    static var _ = FileTree.registerExtension(BlendSpace2DEditor,["blendspace2d"],{ icon : "arrows-alt", createNew: "Blend Space 2D" });
+}

+ 14 - 0
hrt/animgraph/BlendSpace2D.hx

@@ -16,6 +16,20 @@ class BlendSpace2D extends hrt.prefab.Prefab {
 		return instance ??= @:privateAccess new BlendSpace2DInstance(this);
 	}
 
+	function reTriangulate() {
+		triangles = [];
+
+		var h2dPoints : Array<h2d.col.Point> = [];
+		for (point in points) {
+			h2dPoints.push(new h2d.col.Point(point.x, point.y));
+		}
+
+		var triangulation = h2d.col.Delaunay.triangulate(h2dPoints);
+		for (triangle in triangulation) {
+			triangles.push([h2dPoints.indexOf(triangle.p1), h2dPoints.indexOf(triangle.p2), h2dPoints.indexOf(triangle.p3)]);
+		}
+	}
+
 	static var _ = hrt.prefab.Prefab.register("blendspace2d", BlendSpace2D, "blendspace2d");
 }
 

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

@@ -45,6 +45,7 @@ class Input extends AnimNode {
 		matrix.load(anim.getObjects()[id].targetObject.defaultTransform);
 	}
 
+	#if editor
 	override function getPropertiesHTML(width:Float):Array<hide.Element> {
 		var elts = super.getPropertiesHTML(width);
 
@@ -55,9 +56,11 @@ class Input extends AnimNode {
 		fileSelect.path = path;
 		fileSelect.onChange = () -> {
 			path = fileSelect.path;
+			getAnimEditor().refreshPreview();
 		}
 		elts.push(wrapper);
 
 		return elts;
 	}
+	#end
 }