Bläddra i källkod

[shgraph] Can load shader graph

Clément Espeute 1 år sedan
förälder
incheckning
7b10eaffc1

+ 5 - 4
hide/view/Graph.hx → hide/view/GraphEditor.hx

@@ -23,7 +23,7 @@ typedef UndoFn = (isUndo : Bool) -> Void;
 typedef UndoBuffer = Array<UndoFn>;
 
 @:access(hide.view.shadereditor.Box)
-class Graph extends hide.comp.Component {
+class GraphEditor extends hide.comp.Component {
 	var heapsScene : JQuery;
 	var editor : hide.view.GraphInterface.IGraphEditor;
 	var editorDisplay : SVG;
@@ -239,6 +239,9 @@ class Graph extends hide.comp.Component {
 		}
 
 		var edges = editor.getEdges();
+		for (edge in edges) {
+			createEdge(edge);
+		}
 	}
 
 	function openAddMenu(x : Int = 0, y : Int = 0) {
@@ -368,14 +371,12 @@ class Graph extends hide.comp.Component {
 			var key = Std.parseInt(this.selectedNode.attr("node"));
 			var posCursor = new Point(lX(ide.mouseX - 25), lY(ide.mouseY - 10));
 
-			var instance : IGraphNode = null; // For the lambda capture
-			var instance = nodes[key].onAdd();
+			var instance = nodes[key].onConstructNode();
 
 			var createLinkInput = edgeCreationInput;
 			var createLinkOutput = edgeCreationOutput;
 			var fromInput = createLinkInput != null;
 
-			instance = nodes[key].onAdd();
 
 			if (createLinkInput != null) {
 				createLinkOutput = packIO(instance.getId(), 0);

+ 3 - 3
hide/view/GraphInterface.hx

@@ -54,7 +54,7 @@ typedef AddNodeMenuEntry = {
         You should generate a unique ID for the new IGraphNode. Don't add the node to your graph datastructure yet,
         the Graph editor will call addNode() with this node at the right time.
     **/
-    onAdd: () -> IGraphNode,
+    onConstructNode: () -> IGraphNode,
 };
 
 
@@ -78,8 +78,8 @@ interface IGraphNode {
 }
 
 interface IGraphEditor {
-    public function getNodes() : Array<IGraphNode>;
-    public function getEdges() : Array<Edge>;
+    public function getNodes() : Iterator<IGraphNode>;
+    public function getEdges() : Iterator<Edge>;
     public function getAddNodesMenu() : Array<AddNodeMenuEntry>;
 
     public function addNode(node : IGraphNode) : Void;

+ 5 - 5
hide/view/shadereditor/Box.hx

@@ -4,7 +4,7 @@ import hide.comp.SVG;
 import js.jquery.JQuery;
 import hide.view.GraphInterface;
 
-@:access(hide.view.Graph)
+@:access(hide.view.GraphEditor)
 class Box {
 
 	static final boolColor = "#cc0505";
@@ -44,7 +44,7 @@ class Box {
 	static final resizeBorder : Int = 8;
 	static final halfResizeBorder : Int = resizeBorder >> 1;
 
-	public function new(editor : Graph, parent : JQuery, node : IGraphNode) {
+	public function new(editor : GraphEditor, parent : JQuery, node : IGraphNode) {
 		this.node = node;
 		info = node.getInfo();
 		width = info.width ?? 150;
@@ -273,7 +273,7 @@ class Box {
 		closePreviewBtn.find(".ico").toggleClass("ico-angle-up", viz);
 	}
 
-	public function addInput(editor : Graph, name : String, valueDefault : String = null, color : Int) {
+	public function addInput(editor : GraphEditor, name : String, valueDefault : String = null, color : Int) {
 		var node = editor.editorDisplay.group(element).addClass("input-node-group");
 		var nodeHeight = HEADER_HEIGHT + NODE_MARGIN * (inputs.length+1) + NODE_RADIUS * inputs.length;
 		var style = {fill : '#${StringTools.hex(color, 6)}'};
@@ -327,7 +327,7 @@ class Box {
 		}
 	}
 
-	public function addOutput(editor : Graph, name : String, color : Int) {
+	public function addOutput(editor : GraphEditor, name : String, color : Int) {
 		var node = editor.editorDisplay.group(element).addClass("output-node-group");
 		var nodeHeight = getNodeHeight(outputs.length);
 		var style = {fill : '#${StringTools.hex(color, 6)}'};
@@ -348,7 +348,7 @@ class Box {
 		return HEADER_HEIGHT + NODE_MARGIN * (id+1) + NODE_RADIUS * (id);
 	}
 
-	public function generateProperties(editor : Graph) {
+	public function generateProperties(editor : GraphEditor) {
 		var props = node.getPropertiesHTML(this.width);
 
 		if (props.length == 0) return;

+ 50 - 16
hide/view/shadereditor/ShaderEditor.hx

@@ -202,35 +202,69 @@ class TestNode implements IGraphNode {
 }
 
 class ShaderEditor extends hide.view.FileView implements GraphInterface.IGraphEditor {
-	var graph : hide.view.Graph;
+	var graphEditor : hide.view.GraphEditor;
+	var shaderGraph : hrt.shgraph.ShaderGraph;
+	var currentGraph : hrt.shgraph.ShaderGraph.Graph;
 	
 	override function onDisplay() {
 		super.onDisplay();
-		if (graph != null)
-			graph.remove();
-		graph = new hide.view.Graph(this, this.element);
-		graph.onDisplay();
+ 		shaderGraph = cast hide.Ide.inst.loadPrefab(state.path, null,  true);
+		currentGraph = shaderGraph.getGraph(Fragment);
+
+		if (graphEditor != null)
+			graphEditor.remove();
+		graphEditor = new hide.view.GraphEditor(this, this.element);
+		graphEditor.onDisplay();
 	}
 
 	/** IGraphEditor interface **/
-	public function getNodes() : Array<IGraphNode> {
-		return [];
+	public function getNodes() : Iterator<IGraphNode> {
+		return currentGraph.getNodes().iterator();
 	}
 
-	public function getEdges() : Array<Edge> {
-		return [];
+	public function getEdges() : Iterator<Edge> {
+		var edges : Array<Edge> = [];
+		for (id => node in currentGraph.getNodes()) {
+			for (inputId => connection in node.connections) {
+				if (connection != null) {
+					edges.push(
+						{
+							nodeFromId: connection.from.getId(),
+							outputFromId: connection.outputId,
+							nodeToId: id,
+							inputToId: inputId,
+						});
+				}
+			}
+		}
+		return edges.iterator();
 	}
 
 	public function getAddNodesMenu() : Array<AddNodeMenuEntry> {
+		var entries : Array<AddNodeMenuEntry> = [];
+
 		var id = 0;
-		return [
-			{
-				name: "Test",
-				description: "Just a test",
-				group: "Test",
-				onAdd: () -> new TestNode(id++),
+		for (i => node in ShaderNode.registeredNodes) {
+			var metas = haxe.rtti.Meta.getType(node);
+			if (metas.group == null) {
+				continue;
 			}
-		];
+
+			entries.push(
+				{
+					name: metas.name != null ? metas.name[0] : "unknown",
+					group: metas.group[0],
+					description: metas.description != null ? metas.description[0] : "",
+					onConstructNode: () -> {
+						@:privateAccess var id = currentGraph.current_node_id++;
+						var inst = std.Type.createInstance(node, []);
+						inst.setId(id);
+						return inst;
+					},
+				}
+			);
+		}
+		return entries;
 	}
 
 	public function addNode(node: IGraphNode) : Void {

+ 4 - 4
hrt/shgraph/NodeGenContext.hx

@@ -267,9 +267,9 @@ class NodeGenContext {
 	/**
 		API used by ShaderGraphGenContext
 	**/
-	function initForNode(node: ShaderGraph.Node, nodeInputExprs: Array<TExpr>) {
-		nodeInputInfo = node.instance.getInputs();
-		nodeOutputInfo = node.instance.getOutputs();
+	function initForNode(node: ShaderNode, nodeInputExprs: Array<TExpr>) {
+		nodeInputInfo = node.getInputs();
+		nodeOutputInfo = node.getOutputs();
 		this.node = node;
 		this.nodeInputExprs = nodeInputExprs;
 
@@ -297,7 +297,7 @@ class NodeGenContext {
 		}
 	}
 
-	var node : ShaderGraph.Node = null;
+	var node : ShaderNode = null;
 
 	var currentPreviewId: Int = -1;
 	var expressions: Array<TExpr> = [];

+ 24 - 56
hrt/shgraph/ShaderGraph.hx

@@ -135,15 +135,6 @@ typedef ShaderNodeDef = {
 	?functions: Array<TFunction>,
 };
 
-typedef Node = {
-	x : Float,
-	y : Float,
-	id : Int,
-	type : String,
-	?properties : Dynamic,
-	?instance : ShaderNode,
-};
-
 typedef Edge = {
 	?outputNodeId : Int,
 	nameOutput : String, // Fallback if name has changed
@@ -154,7 +145,7 @@ typedef Edge = {
 };
 
 typedef Connection = {
-	from : Node,
+	from : ShaderNode,
 	outputId : Int,
 };
 
@@ -173,14 +164,6 @@ enum Domain {
 	Fragment;
 }
 
-
-typedef GenNodeInfo = {
-	outputToInputMap: Map<String, Array<{node: Node, inputName: String}>>,
-	inputTypes: Array<Type>,
-	?outputs: Map<String, TVar>,
-	?def: ShaderGraph.ShaderNodeDef,
-}
-
 @:structInit @:publicFields
 class
 ExternVarDef {
@@ -203,7 +186,7 @@ class ShaderGraphGenContext {
 	var nodes : Array<{
 		var outputs: Array<Array<{to: Int, input: Int}>>;
 		var inputs : Array<TExpr>;
-		var node : Node;
+		var node : ShaderNode;
 	}>;
 
 	var inputNodes : Array<Int> = [];
@@ -227,7 +210,7 @@ class ShaderGraphGenContext {
 			var node = nodes[nodeId];
 			genContext.initForNode(node.node, node.inputs);
 
-			node.node.instance.generate(genContext);
+			node.node.generate(genContext);
 
 			for (outputId => expr in genContext.outputs) {
 				if (expr == null) throw "null expr for output " + outputId;
@@ -280,7 +263,7 @@ class ShaderGraphGenContext {
 
 		for (id => node in nodes) {
 			if (node == null) continue;
-			var inst = node.node.instance;
+			var inst = node.node;
 			var empty = true;
 			var inputs = inst.getInputs();
 
@@ -290,7 +273,7 @@ class ShaderGraphGenContext {
 				if (connection == null)
 					continue;
 				empty = false;
-				var nodeOutputs = connection.from.instance.getOutputs();
+				var nodeOutputs = connection.from.getOutputs();
 				var outputs = nodes[connection.from.id].outputs;
 				if (outputs == null) {
 					outputs = [];
@@ -647,7 +630,7 @@ class Graph {
 	var cachedGen : ShaderNodeDef = null;
 	var allParamDefaultValue = [];
 	var current_node_id = 0;
-	var nodes : Map<Int, Node> = [];
+	var nodes : Map<Int, ShaderNode> = [];
 
 	public var parent : ShaderGraph = null;
 
@@ -664,23 +647,18 @@ class Graph {
 		generate(Reflect.getProperty(json, "nodes"), Reflect.getProperty(json, "edges"));
 	}
 
-	public function generate(nodes : Array<Node>, edges : Array<Edge>) {
+	public function generate(nodes : Array<Dynamic>, edges : Array<Edge>) {
+		current_node_id = 0;
 		for (n in nodes) {
-			var cl = std.Type.resolveClass(n.type);
-			if( cl == null ) throw "Missing shader node "+n.type;
-			n.instance = std.Type.createInstance(cl, []);
-			n.instance.setId(n.id);
-			n.instance.loadProperties(n.properties);
-			this.nodes.set(n.id, n);
-
-			var shaderParam = Std.downcast(n.instance, ShaderParam);
+			var node = ShaderNode.createFromDynamic(n);
+			this.nodes.set(node.id, node);
+			var shaderParam = Std.downcast(node, ShaderParam);
 			if (shaderParam != null) {
 				var paramShader = getParameter(shaderParam.parameterId);
 				shaderParam.variable = paramShader.variable;
 			}
+			current_node_id = hxd.Math.imax(current_node_id, node.id+1);
 		}
-		if (nodes[nodes.length-1] != null)
-			this.current_node_id = nodes[nodes.length-1].id+1;
 
 		// Migration patch
 		for (e in edges) {
@@ -699,9 +677,8 @@ class Graph {
 		var node = this.nodes.get(edge.inputNodeId);
 		var output = this.nodes.get(edge.outputNodeId);
 
-		var inputs = node.instance.getInputs();
-		var outputs = output.instance.getOutputs();
-
+		var inputs = node.getInputs();
+		var outputs = output.getOutputs();
 
 		var outputId = edge.outputId;
 		var inputId = edge.inputId;
@@ -732,7 +709,7 @@ class Graph {
 			}
 		}
 
-		node.instance.connections[inputId] = {from: output, outputId: outputId};
+		node.connections[inputId] = {from: output, outputId: outputId};
 
 		#if editor
 		if (hasCycle()){
@@ -755,6 +732,10 @@ class Graph {
 		return true;
 	}
 
+	public function addNode(shNode : ShaderNode) {
+		this.nodes.set(shNode.id, shNode);
+	}
+
 	public function areTypesCompatible(input: SgType, output: SgType) : Bool {
 		return switch (input) {
 			case SgFloat(_):
@@ -773,9 +754,9 @@ class Graph {
 
 	public function removeEdge(idNode, inputId, update = true) {
 		var node = this.nodes.get(idNode);
-		if (node.instance.connections[inputId] == null) return;
+		if (node.connections[inputId] == null) return;
 
-		node.instance.connections[inputId] = null;
+		node.connections[inputId] = null;
 	}
 
 	public function setPosition(idNode : Int, x : Float, y : Float) {
@@ -796,19 +777,6 @@ class Graph {
 		return parent.getParameter(id);
 	}
 
-
-	public function addNode(x : Float, y : Float, nameClass : Class<ShaderNode>, args: Array<Dynamic>) {
-		var node : Node = { x : x, y : y, id : current_node_id, type: std.Type.getClassName(nameClass) };
-
-		node.instance = std.Type.createInstance(nameClass, args);
-		node.instance.setId(current_node_id);
-
-		this.nodes.set(node.id, node);
-		current_node_id++;
-
-		return node.instance;
-	}
-
 	public function hasCycle() : Bool {
 		var ctx = new ShaderGraphGenContext(this, false);
 		@:privateAccess ctx.initNodes();
@@ -823,15 +791,15 @@ class Graph {
 	public function saveToDynamic() : Dynamic {
 		var edgesJson : Array<Edge> = [];
 		for (n in nodes) {
-			for (inputId => connection in n.instance.connections) {
+			for (inputId => connection in n.connections) {
 				if (connection == null) continue;
 				var outputId = connection.outputId;
-				edgesJson.push({ outputNodeId: connection.from.id, nameOutput: connection.from.instance.getOutputs()[outputId].name, inputNodeId: n.id, nameInput: n.instance.getInputs()[inputId].name, inputId: inputId, outputId: outputId });
+				edgesJson.push({ outputNodeId: connection.from.id, nameOutput: connection.from.getOutputs()[outputId].name, inputNodeId: n.id, nameInput: n.getInputs()[inputId].name, inputId: inputId, outputId: outputId });
 			}
 		}
 		var json = {
 			nodes: [
-				for (n in nodes) { x : Std.int(n.x), y : Std.int(n.y), id: n.id, type: n.type, properties : n.instance.saveProperties() }
+				for (n in nodes) n.serializeToDynamic(),
 			],
 			edges: edgesJson
 		};

+ 75 - 5
hrt/shgraph/ShaderNode.hx

@@ -11,6 +11,10 @@ import hrt.shgraph.AstTools.*;
 import hrt.shgraph.ShaderGraph;
 import hrt.shgraph.SgHxslVar.ShaderDefInput;
 
+#if editor
+import hide.view.GraphInterface; 
+#end
+
 
 class AlphaPreview extends hxsl.Shader {
 	static var SRC = {
@@ -39,13 +43,83 @@ typedef AliasInfo = {?nameSearch: String, ?nameOverride : String, ?description :
 @:autoBuild(hrt.shgraph.Macros.autoRegisterNode())
 @:keepSub
 @:keep
-class ShaderNode {
+class ShaderNode 
+#if editor
+implements hide.view.GraphInterface.IGraphNode 
+#end
+{
 
 	public var id : Int;
+	public var x : Float;
+	public var y : Float;
 	public var showPreview : Bool = true;
 	@prop public var nameOverride : String;
 
 
+	#if editor
+	// IGraphNode Interface
+	public function getInfo() : GraphNodeInfo {
+		var metas = haxe.rtti.Meta.getType(HaxeType.getClass(this));
+		return {
+			name: nameOverride ?? (metas.name != null ? metas.name[0] : "undefined"),
+			inputs: [
+				for (i in getInputs()) {
+					{
+						name: i.name,
+						color: 0xFF0000,
+					}
+				}
+			],
+			outputs: [
+				for (o in getOutputs()) {
+					{
+						name: o.name,
+						color: 0xFF0000,
+					}
+				}
+			]
+		};
+	}
+
+	public function getId() : Int {
+		return id;
+	}
+
+	public function getPos(p : h2d.col.Point) : Void {
+		p.set(x,y);
+	}
+
+	public function setPos(p : h2d.col.Point) : Void {
+		x = p.x;
+		y = p.y;
+	}
+
+	public function getPropertiesHTML(width : Float) : Array<hide.Element> {
+		return [];
+	}
+	#end
+
+	public function serializeToDynamic() : Dynamic {
+		return {
+			x: x,
+			y: y,
+			id: id,
+			type: std.Type.getClassName(std.Type.getClass(this)),
+			properties: saveProperties(),
+		};
+	}
+
+	public static function createFromDynamic(data: Dynamic) : ShaderNode {
+		var type = std.Type.resolveClass(data.type);
+		var inst = std.Type.createInstance(type, []);
+		inst.x = data.x;
+		inst.y = data.y;
+		inst.id = data.id;
+		inst.connections = [];
+		inst.loadProperties(data.properties);
+		return inst;
+	}
+
 	public var defaults : Dynamic = {};
 
 	/**
@@ -159,10 +233,6 @@ class ShaderNode {
 		return props;
 	}
 
-	function getPropertiesHTML(width : Float) : Array<hide.Element> {
-		return [];
-	}
-
 	static public var registeredNodes = new Map<String, Class<ShaderNode>>();
 
 	static public function register(key : String, cl : Class<ShaderNode>) : Bool {