Explorar o código

Shadergraph Codegen Refacto (#247)

* [shgraph] Refactored code generation part 1

* [shgraph] removed old generate2 func

* [shgraph] fix graph dynamic typing

* [shgraph] New codegen base

* [shgraph] new codegen path

* shader param

* Refactored Generate function to use more of ctx

* New input/output declaration and type resolution

* SubShaders almost working

* Fixed subgraphs inclusion

* Sampler test

* Restored cycles detection in shadergraph

* Editor uses ids everywhere for node input/outputs now

* Fix visual glitch when connecting nodes backwards

* Hxsl nodes working again almost

* Shader inputs/outputs working

* Fixed generation of default values and parameter sorting

* Cleanup

* Fix previews and alpha output

* Use fragment shader for vertex previews

* Fix compilation in game and subgraphs

* [shgraph] hxsl node with unbound inputs now output 0 to all their outputs

* [shgraph] renamed compile3 to compile as other functions were removed
Valden hai 1 ano
pai
achega
2385c3bb0e

+ 52 - 101
hide/view/Graph.hx

@@ -15,7 +15,7 @@ import hrt.shgraph.ShaderType.SType;
 
 
 enum EdgeState { None; FromInput; FromOutput; }
 enum EdgeState { None; FromInput; FromOutput; }
 
 
-typedef Edge = { from : Box, nodeFrom : JQuery, to : Box, nodeTo : JQuery, elt : JQuery };
+typedef Edge = { from : Box, outputFrom : Int, to : Box, inputTo : Int, elt : JQuery };
 
 
 @:access(hide.view.shadereditor.Box)
 @:access(hide.view.shadereditor.Box)
 class Graph extends FileView {
 class Graph extends FileView {
@@ -55,8 +55,8 @@ class Graph extends FileView {
 	var edgeStyle = {stroke : ""};
 	var edgeStyle = {stroke : ""};
 	var startLinkBox : Box;
 	var startLinkBox : Box;
 	var endLinkBox : Box;
 	var endLinkBox : Box;
-	var startLinkGrNode : JQuery;
-	var endLinkNode : JQuery;
+	var startLinkNodeId : Int;
+	var endLinkNodeId : Int;
 	var currentLink : JQuery; // draft of edge
 	var currentLink : JQuery; // draft of edge
 
 
 	// used for deleting
 	// used for deleting
@@ -169,11 +169,11 @@ class Graph extends FileView {
 		}
 		}
 		// Moving edge
 		// Moving edge
 		if (currentEdge != null) {
 		if (currentEdge != null) {
-			var distOutput = distanceToElement(currentEdge.nodeFrom, clientX, clientY);
-			var distInput = distanceToElement(currentEdge.nodeTo, clientX, clientY);
+			var distOutput = distanceToElement(currentEdge.from.outputs[currentEdge.outputFrom], clientX, clientY);
+			var distInput = distanceToElement(currentEdge.to.inputs[currentEdge.inputTo], clientX, clientY);
 
 
 			if (distOutput > distInput) {
 			if (distOutput > distInput) {
-				replaceEdge(FromOutput, currentEdge.nodeTo, clientX, clientY);
+				replaceEdge(FromOutput, currentEdge.to.inputs[currentEdge.inputTo], clientX, clientY);
 			} else {
 			} else {
 				replaceEdge(FromInput, currentEdge, clientX, clientY);
 				replaceEdge(FromInput, currentEdge, clientX, clientY);
 			}
 			}
@@ -242,8 +242,8 @@ class Graph extends FileView {
 		for (edge in listOfEdges) {
 		for (edge in listOfEdges) {
 			if (edge.from == b || edge.to == b) {
 			if (edge.from == b || edge.to == b) {
 				edge.elt.remove();
 				edge.elt.remove();
-				edgeStyle.stroke = edge.nodeFrom.css("fill");
-				edge.elt = createCurve(edge.nodeFrom, edge.nodeTo);
+				edgeStyle.stroke = edge.from.outputs[edge.outputFrom].css("fill");
+				edge.elt = createCurve( edge.from.outputs[edge.outputFrom], edge.to.inputs[edge.inputTo]);
 
 
 				edge.elt.on("mousedown", function(e) {
 				edge.elt.on("mousedown", function(e) {
 					e.stopPropagation();
 					e.stopPropagation();
@@ -342,17 +342,17 @@ class Graph extends FileView {
 		});
 		});
 		listOfBoxes.push(box);
 		listOfBoxes.push(box);
 
 
-		for (inputName => inputVar in box.getInstance().getInputs2(domain)) {
+		for (inputId => input in box.getInstance().getInputs()) {
 			var defaultValue : String = null;
 			var defaultValue : String = null;
-			switch (inputVar.def) {
+			switch (input.def) {
 				case Const(defValue):
 				case Const(defValue):
-					defaultValue= Reflect.getProperty(box.getInstance().defaults, '${inputName}');
+					defaultValue= Reflect.getProperty(box.getInstance().defaults, '${input.name}');
 					if (defaultValue == null) {
 					if (defaultValue == null) {
 						defaultValue = '$defValue';
 						defaultValue = '$defValue';
 					}
 					}
 				default:
 				default:
 			}
 			}
-			var grNode = box.addInput(this, inputName, defaultValue, inputVar.v.type);
+			var grNode = box.addInput(this, input.name, defaultValue, input.type);
 			if (defaultValue != null) {
 			if (defaultValue != null) {
 				var fieldEditInput = grNode.find("input");
 				var fieldEditInput = grNode.find("input");
 				fieldEditInput.on("change", function(ev) {
 				fieldEditInput.on("change", function(ev) {
@@ -361,13 +361,13 @@ class Graph extends FileView {
 						fieldEditInput.addClass("error");
 						fieldEditInput.addClass("error");
 					} else {
 					} else {
 						// Store the value as a string anyway
 						// Store the value as a string anyway
-						Reflect.setField(box.getInstance().defaults, '${inputName}', '$tmpValue');
+						Reflect.setField(box.getInstance().defaults, '${input.name}', '$tmpValue');
 						fieldEditInput.val(tmpValue);
 						fieldEditInput.val(tmpValue);
 						fieldEditInput.removeClass("error");
 						fieldEditInput.removeClass("error");
 					}
 					}
 				});
 				});
 			}
 			}
-			grNode.find(".node").attr("field", inputName);
+			grNode.find(".node").attr("field", inputId);
 			grNode.on("mousedown", function(e : js.jquery.Event) {
 			grNode.on("mousedown", function(e : js.jquery.Event) {
 				e.stopPropagation();
 				e.stopPropagation();
 				var node = grNode.find(".node");
 				var node = grNode.find(".node");
@@ -376,23 +376,21 @@ class Graph extends FileView {
 					return;
 					return;
 				}
 				}
 				isCreatingLink = FromInput;
 				isCreatingLink = FromInput;
-				startLinkGrNode = grNode;
+				startLinkNodeId = inputId;
 				startLinkBox = box;
 				startLinkBox = box;
 				edgeStyle.stroke = node.css("fill");
 				edgeStyle.stroke = node.css("fill");
-				setAvailableOutputNodes(box, grNode.find(".node").attr("field"));
 			});
 			});
 		}
 		}
-		for (outputName => outputVar in box.getInstance().getOutputs2(domain)) {
-			var grNode = box.addOutput(this, outputName, outputVar.v.type);
-			grNode.find(".node").attr("field", outputName);
+		for (outputId => info in box.getInstance().getOutputs()) {
+			var grNode = box.addOutput(this, info.name, info.type);
+			grNode.find(".node").attr("field", outputId);
 			grNode.on("mousedown", function(e) {
 			grNode.on("mousedown", function(e) {
 				e.stopPropagation();
 				e.stopPropagation();
 				var node = grNode.find(".node");
 				var node = grNode.find(".node");
 				isCreatingLink = FromOutput;
 				isCreatingLink = FromOutput;
-				startLinkGrNode = grNode;
+				startLinkNodeId = outputId;
 				startLinkBox = box;
 				startLinkBox = box;
 				edgeStyle.stroke = node.css("fill");
 				edgeStyle.stroke = node.css("fill");
-				setAvailableInputNodes(box, startLinkGrNode.find(".node").attr("field"));
 			});
 			});
 		}
 		}
 
 
@@ -419,8 +417,8 @@ class Graph extends FileView {
 
 
 	function removeEdge(edge : Edge) {
 	function removeEdge(edge : Edge) {
 		edge.elt.remove();
 		edge.elt.remove();
-		edge.nodeTo.removeAttr("hasLink");
-		edge.nodeTo.parent().removeClass("hasLink");
+		edge.to.inputs[edge.inputTo].removeAttr("hasLink");
+		edge.to.inputs[edge.inputTo].parent().removeClass("hasLink");
 		listOfEdges.remove(edge);
 		listOfEdges.remove(edge);
 	}
 	}
 
 
@@ -428,12 +426,11 @@ class Graph extends FileView {
 		switch (state) {
 		switch (state) {
 			case FromOutput:
 			case FromOutput:
 				for (e in listOfEdges) {
 				for (e in listOfEdges) {
-					if (e.nodeTo.is(node)) {
+					if (e.to.inputs[e.inputTo].is(node)) {
 						isCreatingLink = FromOutput;
 						isCreatingLink = FromOutput;
-						startLinkGrNode = e.nodeFrom.parent();
+						startLinkNodeId = e.outputFrom;
 						startLinkBox = e.from;
 						startLinkBox = e.from;
-						edgeStyle.stroke = e.nodeFrom.css("fill");
-						setAvailableInputNodes(e.from, e.nodeFrom.attr("field"));
+						edgeStyle.stroke = e.from.outputs[e.outputFrom].css("fill");
 						removeEdge(e);
 						removeEdge(e);
 						createLink(x, y);
 						createLink(x, y);
 						return;
 						return;
@@ -441,12 +438,11 @@ class Graph extends FileView {
 				}
 				}
 			case FromInput:
 			case FromInput:
 				for (e in listOfEdges) {
 				for (e in listOfEdges) {
-					if (e.nodeTo.is(edge.nodeTo) && e.nodeFrom.is(edge.nodeFrom)) {
+					if (e.to == edge.to && e.inputTo == edge.inputTo && e.from == edge.from && e.outputFrom == edge.outputFrom) {
 						isCreatingLink = FromInput;
 						isCreatingLink = FromInput;
-						startLinkGrNode = e.nodeTo.parent();
+						startLinkNodeId = e.inputTo;
 						startLinkBox = e.to;
 						startLinkBox = e.to;
-						edgeStyle.stroke = e.nodeFrom.css("fill");
-						setAvailableOutputNodes(e.to, e.nodeTo.attr("field"));
+						edgeStyle.stroke = e.from.outputs[e.outputFrom].css("fill");
 						removeEdge(e);
 						removeEdge(e);
 						createLink(x, y);
 						createLink(x, y);
 						return;
 						return;
@@ -457,37 +453,6 @@ class Graph extends FileView {
 		}
 		}
 	}
 	}
 
 
-	// TODO(ces) : nuke SType from orbit
-	function setAvailableInputNodes(boxOutput : Box, field : String) {
-		var type = boxOutput.getInstance().getOutputs2(domain)[field].v.type;
-		var sType : SType;
-
-		for (box in listOfBoxes) {
-			for (input in box.inputs) {
-				if (box.getInstance().checkTypeAndCompatibilyInput(input.attr("field"), type)) {
-					input.addClass("nodeMatch");
-				}
-			}
-		}
-	}
-
-	function setAvailableOutputNodes(boxInput : Box, field : String) {
-		for (box in listOfBoxes) {
-			for (output in box.outputs) {
-				var outputField = output.attr("field");
-				var type = box.getInstance().getOutputs2(domain)[outputField].v.type;
-				var sType = ShaderType.getSType(type);
-				if (boxInput.getInstance().checkTypeAndCompatibilyInput(field, type)) {
-					output.addClass("nodeMatch");
-				}
-			}
-		}
-	}
-
-	function clearAvailableNodes() {
-		editor.element.find(".nodeMatch").removeClass("nodeMatch");
-	}
-
 	function error(str : String, ?idBox : Int) {
 	function error(str : String, ?idBox : Int) {
 		statusBar.html(str);
 		statusBar.html(str);
 		statusClose.show();
 		statusClose.show();
@@ -509,8 +474,8 @@ class Graph extends FileView {
 
 
 	function createEdgeInEditorGraph(edge) {
 	function createEdgeInEditorGraph(edge) {
 		listOfEdges.push(edge);
 		listOfEdges.push(edge);
-		edge.nodeTo.attr("hasLink", "true");
-		edge.nodeTo.parent().addClass("hasLink");
+		edge.to.inputs[edge.inputTo].attr("hasLink", "true");
+		edge.to.inputs[edge.inputTo].parent().addClass("hasLink");
 
 
 		edge.elt.on("mousedown", function(e) {
 		edge.elt.on("mousedown", function(e) {
 			e.stopPropagation();
 			e.stopPropagation();
@@ -522,7 +487,7 @@ class Graph extends FileView {
 
 
 	function createLink(clientX : Int, clientY : Int) {
 	function createLink(clientX : Int, clientY : Int) {
 
 
-		var nearestNode = null;
+		var nearestId = -1;
 		var minDistNode = NODE_TRIGGER_NEAR;
 		var minDistNode = NODE_TRIGGER_NEAR;
 
 
 		// checking nearest box
 		// checking nearest box
@@ -542,61 +507,47 @@ class Graph extends FileView {
 
 
 		// checking nearest node in the nearest box
 		// checking nearest node in the nearest box
 		if (isCreatingLink == FromInput) {
 		if (isCreatingLink == FromInput) {
-			var startIndex = 0;
-			while (startIndex < nearestBox.outputs.length && !nearestBox.outputs[startIndex].hasClass("nodeMatch")) {
-				startIndex++;
-			}
-			if (startIndex < nearestBox.outputs.length) {
-				nearestNode = nearestBox.outputs[startIndex];
-				minDistNode = distanceToElement(nearestNode, clientX, clientY);
-				for (i in startIndex+1...nearestBox.outputs.length) {
-					if (!nearestBox.outputs[i].hasClass("nodeMatch"))
-						continue;
-					var tmpDist = distanceToElement(nearestBox.outputs[i], clientX, clientY);
-					if (tmpDist < minDistNode) {
-						minDistNode = tmpDist;
-						nearestNode = nearestBox.outputs[i];
-					}
+			for (id => o in nearestBox.outputs) {
+				var newMin = distanceToElement(o, clientX, clientY);
+				if (newMin < minDistNode) {
+					nearestId = id;
+					minDistNode = newMin;
 				}
 				}
 			}
 			}
 		} else {
 		} else {
 			// input has one edge at most
 			// input has one edge at most
-			var startIndex = 0;
-			while (startIndex < nearestBox.inputs.length && !nearestBox.inputs[startIndex].hasClass("nodeMatch")) {
-				startIndex++;
-			}
-			if (startIndex < nearestBox.inputs.length) {
-				nearestNode = nearestBox.inputs[startIndex];
-				minDistNode = distanceToElement(nearestNode, clientX, clientY);
-				for (i in startIndex+1...nearestBox.inputs.length) {
-					if (!nearestBox.inputs[i].hasClass("nodeMatch"))
-						continue;
-					var tmpDist = distanceToElement(nearestBox.inputs[i], clientX, clientY);
-					if (tmpDist < minDistNode) {
-						minDistNode = tmpDist;
-						nearestNode = nearestBox.inputs[i];
-					}
+			for (id => i in nearestBox.inputs) {
+				var newMin = distanceToElement(i, clientX, clientY);
+				if (newMin < minDistNode) {
+					nearestId = id;
+					minDistNode = newMin;
 				}
 				}
 			}
 			}
 		}
 		}
-		if (minDistNode < NODE_TRIGGER_NEAR) {
-			endLinkNode = nearestNode;
+
+		if (minDistNode < NODE_TRIGGER_NEAR && nearestId >= 0) {
+			endLinkNodeId = nearestId;
 			endLinkBox = nearestBox;
 			endLinkBox = nearestBox;
 		} else {
 		} else {
-			endLinkNode = null;
+			endLinkNodeId = -1;
 			endLinkBox = null;
 			endLinkBox = null;
+			minDistNode = null;
 		}
 		}
 
 
 		// create edge
 		// create edge
 		if (currentLink != null) currentLink.remove();
 		if (currentLink != null) currentLink.remove();
-		currentLink = createCurve(startLinkGrNode.find(".node"), nearestNode, minDistNode, clientX, clientY, true);
-
+		if (isCreatingLink == FromInput) {
+			currentLink = createCurve(startLinkBox.inputs[startLinkNodeId], endLinkBox?.outputs[endLinkNodeId], minDistNode, clientX, clientY, true);
+		}
+		else {
+			currentLink = createCurve(startLinkBox.outputs[startLinkNodeId], endLinkBox?.inputs[endLinkNodeId], minDistNode, clientX, clientY, true);
+		}
 	}
 	}
 
 
 	function createCurve(start : JQuery, end : JQuery, ?distance : Float, ?x : Float, ?y : Float, ?isDraft : Bool) {
 	function createCurve(start : JQuery, end : JQuery, ?distance : Float, ?x : Float, ?y : Float, ?isDraft : Bool) {
 		var offsetEnd;
 		var offsetEnd;
 		var offsetStart = start.offset();
 		var offsetStart = start.offset();
-		if (distance == null || distance < NODE_TRIGGER_NEAR) {
+		if (end != null) {
 			offsetEnd = end.offset();
 			offsetEnd = end.offset();
 		} else {
 		} else {
 			offsetEnd = { top : y, left : x };
 			offsetEnd = { top : y, left : x };

+ 34 - 54
hide/view/shadereditor/Box.hx

@@ -7,15 +7,15 @@ import hrt.shgraph.ShaderNode;
 @:access(hide.view.Graph)
 @:access(hide.view.Graph)
 class Box {
 class Box {
 
 
-	var boolColor = "#cc0505";
-	var numberColor = "#00ffea";
-	var floatColor = "#00ff73";
-	var intColor = "#00ffea";
-	var vec2Color = "#5eff00";
-	var vec3Color = "#eeff00";
-	var vec4Color = "#fc6703";
-	var samplerColor = "#600aff";
-	var defaultColor = "#c8c8c8";
+	static final boolColor = "#cc0505";
+	static final numberColor = "#00ffea";
+	static final floatColor = "#00ff73";
+	static final intColor = "#00ffea";
+	static final vec2Color = "#5eff00";
+	static final vec3Color = "#eeff00";
+	static final vec4Color = "#fc6703";
+	static final samplerColor = "#600aff";
+	static final defaultColor = "#c8c8c8";
 
 
 	var nodeInstance : ShaderNode;
 	var nodeInstance : ShaderNode;
 
 
@@ -297,32 +297,11 @@ class Box {
 		closePreviewBtn.find(".ico").toggleClass("ico-angle-up", nodeInstance.showPreview);
 		closePreviewBtn.find(".ico").toggleClass("ico-angle-up", nodeInstance.showPreview);
 	}
 	}
 
 
-	public function addInput(editor : Graph, name : String, valueDefault : String = null, type : hxsl.Ast.Type) {
+	public function addInput(editor : Graph, name : String, valueDefault : String = null, type : hrt.shgraph.ShaderGraph.SgType) {
 		var node = editor.editor.group(element).addClass("input-node-group");
 		var node = editor.editor.group(element).addClass("input-node-group");
 		var nodeHeight = HEADER_HEIGHT + NODE_MARGIN * (inputs.length+1) + NODE_RADIUS * inputs.length;
 		var nodeHeight = HEADER_HEIGHT + NODE_MARGIN * (inputs.length+1) + NODE_RADIUS * inputs.length;
 		var style = {fill : ""}
 		var style = {fill : ""}
-		style.fill = defaultColor;
-
-		if (type != null) {
-			switch (type) {
-				case TBool:
-					style.fill = boolColor;
-				case TFloat:
-					style.fill = floatColor;
-				case TVec(size, _):
-					switch (size) {
-						case 2:
-							style.fill = vec2Color;
-						case 3:
-							style.fill = vec3Color;
-						case 4:
-							style.fill = vec4Color;
-					}
-				case TSampler(_):
-					style.fill = samplerColor;
-				default:
-			}
-		}
+		style.fill = getTypeColor(type);
 
 
 		var nodeCircle = editor.editor.circle(node, 0, nodeHeight, NODE_RADIUS, style).addClass("node input-node");
 		var nodeCircle = editor.editor.circle(node, 0, nodeHeight, NODE_RADIUS, style).addClass("node input-node");
 
 
@@ -352,32 +331,33 @@ class Box {
 		return node;
 		return node;
 	}
 	}
 
 
-	public function addOutput(editor : Graph, name : String, ?type : hxsl.Ast.Type) {
+	public static function getTypeColor(type : hrt.shgraph.ShaderGraph.SgType) {
+		return switch (type) {
+			case SgBool:
+				boolColor;
+			case SgInt:
+				intColor;
+			case SgFloat(1):
+				floatColor;
+			case SgFloat(2):
+				vec2Color;
+			case SgFloat(3):
+				vec3Color;
+			case SgFloat(_):
+				vec4Color;
+			case SgGeneric(_, _):
+				vec4Color;
+			case SgSampler:
+				samplerColor;
+		}
+	}
+
+	public function addOutput(editor : Graph, name : String, ?type : hrt.shgraph.ShaderGraph.SgType) {
 		var node = editor.editor.group(element).addClass("output-node-group");
 		var node = editor.editor.group(element).addClass("output-node-group");
 		var nodeHeight = HEADER_HEIGHT + NODE_MARGIN * (outputs.length+1) + NODE_RADIUS * outputs.length;
 		var nodeHeight = HEADER_HEIGHT + NODE_MARGIN * (outputs.length+1) + NODE_RADIUS * outputs.length;
 		var style = {fill : ""}
 		var style = {fill : ""}
 
 
-		style.fill = defaultColor;
-		if (type != null) {
-			switch (type) {
-				case TBool:
-					style.fill = boolColor;
-				case TInt:
-					style.fill = intColor;
-				case TFloat:
-					style.fill = floatColor;
-				case TVec(size, t):
-					if (size == 2)
-						style.fill = vec2Color;
-					else if (size == 3)
-						style.fill = vec3Color;
-					else if (size == 4)
-						style.fill = vec4Color;
-				case TSampler(_):
-					style.fill = samplerColor;
-				default:
-			}
-		}
+		style.fill = getTypeColor(type);
 
 
 		var nodeCircle = editor.editor.circle(node, width, nodeHeight, NODE_RADIUS, style).addClass("node output-node");
 		var nodeCircle = editor.editor.circle(node, width, nodeHeight, NODE_RADIUS, style).addClass("node output-node");
 
 

+ 67 - 119
hide/view/shadereditor/ShaderEditor.hx

@@ -25,7 +25,7 @@ typedef SavedClipboard = {
 		nodeType : Class<ShaderNode>,
 		nodeType : Class<ShaderNode>,
 		props : Dynamic,
 		props : Dynamic,
 	}>,
 	}>,
-	edges : Array<{ fromIdx : Int, fromName : String, toIdx : Int, toName : String }>,
+	edges : Array<{ fromIdx : Int, fromOutputId : Int, toIdx : Int, toInputId : Int }>,
 }
 }
 
 
 class PreviewShaderBase extends hxsl.Shader {
 class PreviewShaderBase extends hxsl.Shader {
@@ -56,8 +56,6 @@ class PreviewShaderBase extends hxsl.Shader {
 			projectedPosition = vec4(input.position, 0.0, 0.0);
 			projectedPosition = vec4(input.position, 0.0, 0.0);
 			fakeNormal = vec3(0,0,-1);
 			fakeNormal = vec3(0,0,-1);
 			transformedNormal = vec3(0,0,-1);
 			transformedNormal = vec3(0,0,-1);
-
-
 		}
 		}
 
 
 
 
@@ -230,7 +228,6 @@ class ShaderEditor extends hide.view.Graph {
 									<input id="displayGlsl" type="button" value="Glsl" />
 									<input id="displayGlsl" type="button" value="Glsl" />
 									<input id="displayHlsl" type="button" value="Hlsl" />
 									<input id="displayHlsl" type="button" value="Hlsl" />
 									<input id="display2" type="button" value="2" />
 									<input id="display2" type="button" value="2" />
-
 								</div>
 								</div>
 								<input id="togglelight" type="button" value="Toggle Default Lights" />
 								<input id="togglelight" type="button" value="Toggle Default Lights" />
 								<input id="refreshGraph" type="button" value="Refresh Shader Graph" />
 								<input id="refreshGraph" type="button" value="Refresh Shader Graph" />
@@ -244,7 +241,6 @@ class ShaderEditor extends hide.view.Graph {
 			node.variable = paramShader.variable;
 			node.variable = paramShader.variable;
 			node.setName(paramShader.name);
 			node.setName(paramShader.name);
 			setDisplayValue(node, paramShader.type, paramShader.defaultValue);
 			setDisplayValue(node, paramShader.type, paramShader.defaultValue);
-			node.computeOutputs();
 			addBox(posCursor, ShaderParam, node);
 			addBox(posCursor, ShaderParam, node);
 		});
 		});
 
 
@@ -315,7 +311,8 @@ class ShaderEditor extends hide.view.Graph {
 			if (e.button == 0) {
 			if (e.button == 0) {
 				// Stop link creation
 				// Stop link creation
 				if (isCreatingLink != None) {
 				if (isCreatingLink != None) {
-					if (startLinkBox != null && endLinkBox != null && createEdgeInShaderGraph()) {
+					if (startLinkBox != null && endLinkBox != null) {
+						createEdgeInShaderGraph();
 						cleanupLinkCreation();
 						cleanupLinkCreation();
 					} else {
 					} else {
 						openAddMenu();
 						openAddMenu();
@@ -435,8 +432,10 @@ class ShaderEditor extends hide.view.Graph {
 		element.find("#displayGlsl").on("click", () -> displayCompiled("glsl"));
 		element.find("#displayGlsl").on("click", () -> displayCompiled("glsl"));
 		element.find("#displayHlsl").on("click", () -> displayCompiled("hlsl"));
 		element.find("#displayHlsl").on("click", () -> displayCompiled("hlsl"));
 
 
-		element.find("#display2").on("click", () -> {@:privateAccess info(hxsl.Printer.shaderToString(shaderGraph.compile2(domain).shader.data, true));});
-
+		element.find("#display2").on("click", () -> {
+			@:privateAccess info(
+				hxsl.Printer.shaderToString(shaderGraph.compile(domain).shader.data, true)
+			);});
 
 
 		editorMatrix.on("click", "input, select", function(ev) {
 		editorMatrix.on("click", "input, select", function(ev) {
 			beforeChange();
 			beforeChange();
@@ -525,9 +524,8 @@ class ShaderEditor extends hide.view.Graph {
 
 
 	function cleanupLinkCreation() {
 	function cleanupLinkCreation() {
 		startLinkBox = endLinkBox = null;
 		startLinkBox = endLinkBox = null;
-		startLinkGrNode = endLinkNode = null;
+		startLinkNodeId = endLinkNodeId = -1;
 		isCreatingLink = None;
 		isCreatingLink = None;
-		clearAvailableNodes();
 
 
 		if (currentLink != null) currentLink.remove();
 		if (currentLink != null) currentLink.remove();
 		currentLink = null;
 		currentLink = null;
@@ -580,8 +578,10 @@ class ShaderEditor extends hide.view.Graph {
 	}
 	}
 
 
 	function onRefresh() {
 	function onRefresh() {
-		if (sceneEditor.scene.s3d == null)
-			throw "Scene 3d is not ready yet";
+		if (sceneEditor.scene.s3d == null) {
+			Timer.delay(onRefresh, 250);
+			return;
+		}
 		var saveCustomModel = getDisplayState("customModel");
 		var saveCustomModel = getDisplayState("customModel");
 		if (saveCustomModel != null)
 		if (saveCustomModel != null)
 			loadPreviewPrefab(saveCustomModel);
 			loadPreviewPrefab(saveCustomModel);
@@ -703,12 +703,13 @@ class ShaderEditor extends hide.view.Graph {
 	function generateEdgesFromBox(box : Box) {
 	function generateEdgesFromBox(box : Box) {
 
 
 		for (b in listOfBoxes) {
 		for (b in listOfBoxes) {
-			for (inputName => connection in b.getInstance().connections) {
+			for (inputId => connection in b.getInstance().connections) {
+				if (connection == null) continue;
 				if (connection.from.id == box.getId()) {
 				if (connection.from.id == box.getId()) {
-					var nodeFrom = box.getElement().find('[field=${connection.fromName}]');
-					var nodeTo = b.getElement().find('[field=${inputName}]');
+					var nodeFrom = box.outputs[connection.outputId];
+					var nodeTo = b.inputs[inputId];
 					edgeStyle.stroke = nodeFrom.css("fill");
 					edgeStyle.stroke = nodeFrom.css("fill");
-					createEdgeInEditorGraph({from: box, nodeFrom: nodeFrom, to : b, nodeTo: nodeTo, elt : createCurve(nodeFrom, nodeTo) });
+					createEdgeInEditorGraph({from: box, outputFrom: connection.outputId, to : b, inputTo: inputId, elt : createCurve(nodeFrom, nodeTo) });
 				}
 				}
 
 
 			}
 			}
@@ -716,7 +717,8 @@ class ShaderEditor extends hide.view.Graph {
 	}
 	}
 
 
 	function generateEdgesToBox(box : Box) {
 	function generateEdgesToBox(box : Box) {
-		for (inputName => connection in box.getInstance().connections) {
+		for (inputId => connection in box.getInstance().connections) {
+			if (connection == null) continue;
 			var fromBox : Box = null;
 			var fromBox : Box = null;
 			for (boxFrom in listOfBoxes) {
 			for (boxFrom in listOfBoxes) {
 				if (boxFrom.getId() == connection.from.id) {
 				if (boxFrom.getId() == connection.from.id) {
@@ -724,10 +726,10 @@ class ShaderEditor extends hide.view.Graph {
 					break;
 					break;
 				}
 				}
 			}
 			}
-			var nodeFrom = fromBox.getElement().find('[field=${connection.fromName}]');
-			var nodeTo = box.getElement().find('[field=${inputName}]');
+			var nodeFrom = fromBox.outputs[connection.outputId];
+			var nodeTo = box.inputs[inputId];
 			edgeStyle.stroke = nodeFrom.css("fill");
 			edgeStyle.stroke = nodeFrom.css("fill");
-			createEdgeInEditorGraph({from: fromBox, nodeFrom: nodeFrom, to : box, nodeTo: nodeTo, elt : createCurve(nodeFrom, nodeTo) });
+			createEdgeInEditorGraph({from: fromBox, outputFrom: connection.outputId, to : box, inputTo: inputId, elt : createCurve(nodeFrom, nodeTo) });
 		}
 		}
 	}
 	}
 
 
@@ -745,24 +747,13 @@ class ShaderEditor extends hide.view.Graph {
 				super.removeEdge(edge);
 				super.removeEdge(edge);
 			}
 			}
 		}
 		}
-		var indexInputStartLink = -1;
-		if (startLinkBox == box) {
-			var nodeInputJQuery = startLinkGrNode.find(".node");
-			for (i in 0...box.inputs.length) {
-				if (box.inputs[i].is(nodeInputJQuery)) {
-					indexInputStartLink = i;
-					break;
-				}
-			}
-		}
 		var newBox : Box = addBox(new Point(box.getX(), box.getY()), std.Type.getClass(box.getInstance()), box.getInstance());
 		var newBox : Box = addBox(new Point(box.getX(), box.getY()), std.Type.getClass(box.getInstance()), box.getInstance());
 		box.dispose();
 		box.dispose();
 		listOfBoxes.remove(box);
 		listOfBoxes.remove(box);
 		generateEdgesToBox(newBox);
 		generateEdgesToBox(newBox);
 		generateEdgesFromBox(newBox);
 		generateEdgesFromBox(newBox);
-		if (indexInputStartLink >= 0) {
+		if (startLinkBox == box) {
 			startLinkBox = newBox;
 			startLinkBox = newBox;
-			startLinkGrNode = newBox.inputs[indexInputStartLink].parent();
 		}
 		}
 		return newBox;
 		return newBox;
 	}
 	}
@@ -1140,7 +1131,7 @@ class ShaderEditor extends hide.view.Graph {
 	function displayCompiled(type : String) {
 	function displayCompiled(type : String) {
 		var text = "\n";
 		var text = "\n";
 
 
-		var def = shaderGraph.compile2(null);
+		var def = shaderGraph.compile(null);
 
 
 		if( def != null) {
 		if( def != null) {
 			text += switch( type ) {
 			text += switch( type ) {
@@ -1164,7 +1155,7 @@ class ShaderEditor extends hide.view.Graph {
 				for (m in obj.getMaterials())
 				for (m in obj.getMaterials())
 					m.mainPass.removeShader(currentShader);
 					m.mainPass.removeShader(currentShader);
 
 
-			var shaderGraphDef = shaderGraph.compile2(null);
+			var shaderGraphDef = shaderGraph.compile(null);
 			newShader = new hxsl.DynamicShader(shaderGraphDef.shader);
 			newShader = new hxsl.DynamicShader(shaderGraphDef.shader);
 			for (init in shaderGraphDef.inits) {
 			for (init in shaderGraphDef.inits) {
 				setParamValue(newShader, init.variable, init.value);
 				setParamValue(newShader, init.variable, init.value);
@@ -1184,44 +1175,8 @@ class ShaderEditor extends hide.view.Graph {
 			if (Std.isOfType(e, String)) {
 			if (Std.isOfType(e, String)) {
 				var str : String = e;
 				var str : String = e;
 				trace(str);
 				trace(str);
-				if (str.split(":")[0] == "An error occurred compiling the shaders") {
-					var strSplitted = str.split("(output_");
-					if (strSplitted.length >= 2) {
-						var idBox = strSplitted[1].split("_")[0];
-						var idBoxParsed = Std.parseInt(idBox);
-						if (Std.string(idBoxParsed) == idBox) {
-							error("Compilation of shader failed > Invalid inputs", idBoxParsed);
-						} else {
-							error("Compilation of shader failed > " + str);
-						}
-					} else {
-						var nameOutput = str.split("(")[1].split(" =")[0];
-						var errorSent = false;
-						for (b in listOfBoxes) {
-							var shaderOutput = Std.downcast(b.getInstance(), hrt.shgraph.ShaderOutput);
-							if (shaderOutput != null) {
-								var v = shaderOutput.getVariable();
-								if (v.name == nameOutput) {
-									error("Compilation of shader failed > Invalid inputs", shaderOutput.id);
-									errorSent = true;
-									break;
-								}
-							}
-							if (!errorSent) {
-								error("Compilation of shader failed > " + str);
-							}
-						}
-					}
-					if (newShader != null)
-						for (m in obj.getMaterials())
-							m.mainPass.removeShader(newShader);
-					if (currentShader != null) {
-						for (m in obj.getMaterials()) {
-							m.mainPass.addShader(currentShader);
-						}
-					}
-					return;
-				}
+
+				error("Compilation of shader failed > " + str);
 			} else if (Std.isOfType(e, ShaderException)) {
 			} else if (Std.isOfType(e, ShaderException)) {
 				error(e.msg, e.idBox);
 				error(e.msg, e.idBox);
 				return;
 				return;
@@ -1238,7 +1193,7 @@ class ShaderEditor extends hide.view.Graph {
 			}
 			}
 		}
 		}
 
 
-		currentShaderPreviewsDef = shaderGraph.compile2(domain);
+		currentShaderPreviewsDef = shaderGraph.compile(domain);
 	}
 	}
 
 
 	function updateParam(id : Int) {
 	function updateParam(id : Int) {
@@ -1325,7 +1280,7 @@ class ShaderEditor extends hide.view.Graph {
 
 
 		var select = null;
 		var select = null;
 		if (currentShaderPreviewsDef != null) {
 		if (currentShaderPreviewsDef != null) {
-			select = currentShaderPreviewsDef.inits.find((e) -> e.variable.name == "__sg_PREVIEW_output_select");
+			select = currentShaderPreviewsDef.inits.find((e) -> e.variable.name == hrt.shgraph.Variables.previewSelectName);
 		}
 		}
 		for (box => preview in boxToPreview) {
 		for (box => preview in boxToPreview) {
 			preview.visible = box.getInstance().shouldShowPreview();
 			preview.visible = box.getInstance().shouldShowPreview();
@@ -1374,19 +1329,13 @@ class ShaderEditor extends hide.view.Graph {
 		if (isCreatingLink != None) {
 		if (isCreatingLink != None) {
 			if (startLinkBox != null) {
 			if (startLinkBox != null) {
 				endLinkBox = box;
 				endLinkBox = box;
-				var node = isCreatingLink == FromInput ? box.outputs[0] : box.inputs[0];
-				if (node != null) {
-					endLinkNode = node;
-					createEdgeInShaderGraph();
-				}
+				endLinkNodeId = 0;
+				createEdgeInShaderGraph();
 			}
 			}
 			else if (endLinkBox != null) {
 			else if (endLinkBox != null) {
 				startLinkBox = box;
 				startLinkBox = box;
-				var node = box.outputs[0];
-				if (node != null) {
-					startLinkGrNode = node;
-					createEdgeInShaderGraph();
-				}
+				startLinkNodeId = 0;
+				createEdgeInShaderGraph();
 			}
 			}
 		}
 		}
 
 
@@ -1402,34 +1351,46 @@ class ShaderEditor extends hide.view.Graph {
 	}
 	}
 
 
 	function createEdgeInShaderGraph() : Bool {
 	function createEdgeInShaderGraph() : Bool {
-		var startLinkNode = startLinkGrNode.find(".node");
 		if (isCreatingLink == FromInput) {
 		if (isCreatingLink == FromInput) {
 			var tmpBox = startLinkBox;
 			var tmpBox = startLinkBox;
 			startLinkBox = endLinkBox;
 			startLinkBox = endLinkBox;
 			endLinkBox = tmpBox;
 			endLinkBox = tmpBox;
 
 
-			var tmpNode = startLinkNode;
-			startLinkNode = endLinkNode;
-			endLinkNode = tmpNode;
+			var tmpNodeId = startLinkNodeId;
+			startLinkNodeId = endLinkNodeId;
+			endLinkNodeId = tmpNodeId;
 		}
 		}
 
 
-		var newEdge = { from: startLinkBox, nodeFrom : startLinkNode, to : endLinkBox, nodeTo : endLinkNode, elt : currentLink };
-		if (endLinkNode.attr("hasLink") != null) {
-			for (edge in listOfEdges) {
-				if (edge.nodeTo.is(endLinkNode)) {
-					super.removeEdge(edge);
-					removeShaderGraphEdge(edge);
-					break;
-				}
+		var newEdge = { from: startLinkBox, outputFrom : startLinkNodeId, to : endLinkBox, inputTo : endLinkNodeId, elt : currentLink };
+		for (edge in listOfEdges) {
+			if (edge.to == newEdge.to && edge.inputTo == newEdge.inputTo) {
+				super.removeEdge(edge);
+				removeShaderGraphEdge(edge);
+				break;
 			}
 			}
 		}
 		}
 		try {
 		try {
 			beforeChange();
 			beforeChange();
-			if (currentGraph.addEdge({ outputNodeId: startLinkBox.getId(), nameOutput: startLinkNode.attr("field"), inputNodeId: endLinkBox.getId(), nameInput: endLinkNode.attr("field") })) {
+			var outputId = newEdge.outputFrom;
+			var inputId = newEdge.inputTo;
+
+			var edge = {
+				outputNodeId: startLinkBox.getId(),
+				nameOutput: startLinkBox.getInstance().getOutputs()[outputId].name,
+				outputId: outputId,
+				inputNodeId: endLinkBox.getId(),
+				inputId: inputId,
+				nameInput: endLinkBox.getInstance().getInputs()[inputId].name
+			};
+			trace(edge);
+
+			if (currentGraph.addEdge(edge))
+			{
 				afterChange();
 				afterChange();
 				createEdgeInEditorGraph(newEdge);
 				createEdgeInEditorGraph(newEdge);
 				currentLink.removeClass("draft");
 				currentLink.removeClass("draft");
 				currentLink = null;
 				currentLink = null;
+				isCreatingLink = None;
 				launchCompileShader();
 				launchCompileShader();
 				refreshBox(endLinkBox);
 				refreshBox(endLinkBox);
 				return true;
 				return true;
@@ -1666,27 +1627,13 @@ class ShaderEditor extends hide.view.Graph {
 	}
 	}
 
 
 	function removeShaderGraphEdge(edge : Graph.Edge) {
 	function removeShaderGraphEdge(edge : Graph.Edge) {
-		currentGraph.removeEdge(edge.to.getId(), edge.nodeTo.attr("field"));
+		currentGraph.removeEdge(edge.to.getId(), edge.inputTo);
 	}
 	}
 
 
 	function removeEdgeSubGraphUpdate(edge : Graph.Edge) {
 	function removeEdgeSubGraphUpdate(edge : Graph.Edge) {
 		var subGraph = Std.downcast(edge.to.getInstance(), hrt.shgraph.nodes.SubGraph);
 		var subGraph = Std.downcast(edge.to.getInstance(), hrt.shgraph.nodes.SubGraph);
 		if (subGraph != null) {
 		if (subGraph != null) {
-			var field = "";
-			if (isCreatingLink == FromInput) {
-				field = edge.nodeTo.attr("field");
-			} else {
-				field = edge.nodeFrom.attr("field");
-			}
 			var newBox = refreshBox(edge.to);
 			var newBox = refreshBox(edge.to);
-			// subGraph.loadGraphShader();
-
-			clearAvailableNodes();
-			if (isCreatingLink == FromInput) {
-				setAvailableOutputNodes(newBox, field);
-			} else {
-				setAvailableInputNodes(edge.from, field);
-			}
 		}
 		}
 	}
 	}
 
 
@@ -1770,7 +1717,7 @@ class ShaderEditor extends hide.view.Graph {
 				}
 				}
 		];
 		];
 
 
-		var edges : Array<{ fromIdx : Int, fromName : String, toIdx : Int, toName : String }> = [];
+		var edges : Array<{ fromIdx : Int, fromOutputId : Int, toIdx : Int, toInputId : Int }> = [];
 
 
 		for( edge in listOfEdges ) {
 		for( edge in listOfEdges ) {
 			for( fromIdx in 0...boxes.length ) {
 			for( fromIdx in 0...boxes.length ) {
@@ -1779,9 +1726,9 @@ class ShaderEditor extends hide.view.Graph {
 						if( boxes[toIdx] == edge.to ) {
 						if( boxes[toIdx] == edge.to ) {
 							edges.push({
 							edges.push({
 								fromIdx : fromIdx,
 								fromIdx : fromIdx,
-								fromName : edge.nodeFrom.attr("field"),
+								fromOutputId : edge.outputFrom,
 								toIdx : toIdx,
 								toIdx : toIdx,
-								toName : edge.nodeTo.attr("field"),
+								toInputId :  edge.inputTo,
 							});
 							});
 						}
 						}
 					}
 					}
@@ -1820,7 +1767,6 @@ class ShaderEditor extends hide.view.Graph {
 				shaderParam.variable = paramShader.variable;
 				shaderParam.variable = paramShader.variable;
 				shaderParam.setName(paramShader.name);
 				shaderParam.setName(paramShader.name);
 				setDisplayValue(shaderParam, paramShader.type, paramShader.defaultValue);
 				setDisplayValue(shaderParam, paramShader.type, paramShader.defaultValue);
-				shaderParam.computeOutputs();
 			}
 			}
 			var box = addBox(offset.add(n.pos), n.nodeType, node);
 			var box = addBox(offset.add(n.pos), n.nodeType, node);
 			instancedBoxes.push(box);
 			instancedBoxes.push(box);
@@ -1828,11 +1774,13 @@ class ShaderEditor extends hide.view.Graph {
 		for( edge in val.edges ) {
 		for( edge in val.edges ) {
 			if( instancedBoxes[edge.fromIdx] == null || instancedBoxes[edge.toIdx] == null )
 			if( instancedBoxes[edge.fromIdx] == null || instancedBoxes[edge.toIdx] == null )
 				continue;
 				continue;
-			var toCreate = {
+			var toCreate : hrt.shgraph.ShaderGraph.Edge = {
 				outputNodeId: instancedBoxes[edge.fromIdx].getId(),
 				outputNodeId: instancedBoxes[edge.fromIdx].getId(),
-				nameOutput: edge.fromName,
+				outputId: edge.fromOutputId,
+				nameOutput: instancedBoxes[edge.fromIdx].getInstance().getOutputs()[edge.fromOutputId].name,
 				inputNodeId: instancedBoxes[edge.toIdx].getId(),
 				inputNodeId: instancedBoxes[edge.toIdx].getId(),
-				nameInput: edge.toName,
+				inputId: edge.toInputId,
+				nameInput: instancedBoxes[edge.toIdx].getInstance().getInputs()[edge.toInputId].name,
 			}
 			}
 			if( !currentGraph.addEdge(toCreate) ) {
 			if( !currentGraph.addEdge(toCreate) ) {
 				error("A pasted edge creates a cycle");
 				error("A pasted edge creates a cycle");

+ 6 - 1
hrt/prefab/DynamicShader.hx

@@ -11,6 +11,11 @@ class DynamicShader extends Shader {
 		super(parent, shared);
 		super(parent, shared);
 	}
 	}
 
 
+	override function copy(other:Prefab) {
+		super.copy(other);
+		var shaderDef = Std.downcast(other, DynamicShader)?.getShaderDefinition();
+	}
+
 	override function setShaderParam(shader:hxsl.Shader, v:hxsl.Ast.TVar, value:Dynamic) {
 	override function setShaderParam(shader:hxsl.Shader, v:hxsl.Ast.TVar, value:Dynamic) {
 		if( isInstance && !isShadergraph ) {
 		if( isInstance && !isShadergraph ) {
 			super.setShaderParam(shader,v,value);
 			super.setShaderParam(shader,v,value);
@@ -76,7 +81,7 @@ class DynamicShader extends Shader {
 				var shgraph = Std.downcast(hxd.res.Loader.currentInstance.load(source).toPrefab().load(), hrt.shgraph.ShaderGraph);
 				var shgraph = Std.downcast(hxd.res.Loader.currentInstance.load(source).toPrefab().load(), hrt.shgraph.ShaderGraph);
 				if (shgraph == null)
 				if (shgraph == null)
 					throw source + " is not a valid shadergraph";
 					throw source + " is not a valid shadergraph";
-				shaderDef = shgraph.compile2(null);
+				shaderDef = shgraph.compile(null);
 			}
 			}
 			else if( isInstance && !isShadergraph ) {
 			else if( isInstance && !isShadergraph ) {
 				shaderClass = loadShaderClass();
 				shaderClass = loadShaderClass();

+ 1 - 1
hrt/prefab/rfx/ScreenShaderGraph.hx

@@ -69,7 +69,7 @@ class ScreenShaderGraph extends RendererFX {
 	public function loadShaderDef() {
 	public function loadShaderDef() {
 		if (shaderGraph == null)
 		if (shaderGraph == null)
 			resolveRef();
 			resolveRef();
-		shaderDef = shaderGraph.compile2(null);
+		shaderDef = shaderGraph.compile(null);
 		if(shaderDef == null)
 		if(shaderDef == null)
 			return;
 			return;
 
 

+ 32 - 1
hrt/shgraph/AstTools.hx

@@ -31,6 +31,10 @@ class AstTools {
 		return makeExpr(TConst(CInt(int)), TInt);
 		return makeExpr(TConst(CInt(int)), TInt);
 	}
 	}
 
 
+	public inline static function makeFloat(float: Float) : TExpr {
+		return makeExpr(TConst(CFloat(float)), TFloat);
+	}
+
 	public inline static function makeVec(values : Array<Float>) : TExpr {
 	public inline static function makeVec(values : Array<Float>) : TExpr {
 		var ctor = switch(values.length) {
 		var ctor = switch(values.length) {
 			case 2: Vec2;
 			case 2: Vec2;
@@ -39,18 +43,45 @@ class AstTools {
 			default: throw "Can't create a vector of size " + values.length;
 			default: throw "Can't create a vector of size " + values.length;
 		}
 		}
 		var params = [for (v in values) makeExpr(TConst(CFloat(v)), TFloat)];
 		var params = [for (v in values) makeExpr(TConst(CFloat(v)), TFloat)];
-		return makeExpr(TCall(makeExpr(TGlobal(ctor), TVoid), params), TVec(values.length, VFloat));
+		return makeGlobalCall(ctor, params, TVec(values.length, VFloat));
+	}
+
+	public inline static function makeVecExpr(values: Array<TExpr>, ?ctor: TGlobal) : TExpr {
+		var ctor = ctor ?? switch(values.length) {
+			case 2: Vec2;
+			case 3: Vec3;
+			case 4: Vec4;
+			default: throw "Can't create a vector of size " + values.length;
+		}
+		return makeGlobalCall(ctor, values, TVec(values.length, VFloat));
 	}
 	}
 
 
 	public inline static function makeVar(v: TVar, ?pos: Position) : TExpr {
 	public inline static function makeVar(v: TVar, ?pos: Position) : TExpr {
 		return makeExpr(TVar(v), v.type, pos);
 		return makeExpr(TVar(v), v.type, pos);
 	}
 	}
 
 
+	public inline static function makeSwizzle(e: TExpr, components: Array<Component>) {
+		return makeExpr(TSwiz(e, components), components.length == 1 ? TFloat : TVec(components.length, VFloat));
+	}
+
+	public inline static function makeVarDecl(v: TVar, ?init: TExpr) : TExpr {
+		return makeExpr(TVarDecl(v, init), v.type);
+	}
+
 	public inline static function makeExpr(def: TExprDef, type: Type, ?pos: Position) : TExpr {
 	public inline static function makeExpr(def: TExprDef, type: Type, ?pos: Position) : TExpr {
 		return {e: def, p: (pos ?? defPos), t: type}
 		return {e: def, p: (pos ?? defPos), t: type}
 	}
 	}
 
 
+	// Expect a.t == b.t
+	public inline static function makeBinop(a: TExpr, op: Binop, b: TExpr) : TExpr {
+		return makeExpr(TBinop(op, a, b), a.t);
+	}
+
 	public inline static function makeEq(a: TExpr, b: TExpr) : TExpr {
 	public inline static function makeEq(a: TExpr, b: TExpr) : TExpr {
 		return makeExpr(TBinop(OpEq, a, b), TBool);
 		return makeExpr(TBinop(OpEq, a, b), TBool);
 	}
 	}
+
+	public inline static function makeGlobalCall(global: TGlobal, args: Array<TExpr>, retType: Type) : TExpr {
+		return makeExpr(TCall(makeExpr(TGlobal(global), TVoid), args), retType);
+	}
 }
 }

+ 41 - 32
hrt/shgraph/Macros.hx

@@ -29,28 +29,24 @@ class Macros {
 
 
 							// expr.map()
 							// expr.map()
 
 
-							var inVars : Array<String> = [];
-							var outVars : Array<String> = [];
-							var constVars : Array<String> = [];
-							var defValues : Array<String> = [];
-							var dynamicValues : Array<String> = [];
+							var varmap : Map<String, hrt.shgraph.SgHxslVar> = [];
 
 
 							function iter(e: haxe.macro.Expr) : Void {
 							function iter(e: haxe.macro.Expr) : Void {
 								switch(e.expr) {
 								switch(e.expr) {
 									case EMeta(meta, subexpr):
 									case EMeta(meta, subexpr):
 										switch (meta.name) {
 										switch (meta.name) {
 											case "sginput":
 											case "sginput":
-												var defValue = null;
+												var defValue : hrt.shgraph.SgHxslVar.ShaderDefInput = null;
 												if (meta.params != null && meta.params.length > 0) {
 												if (meta.params != null && meta.params.length > 0) {
 													switch (meta.params[0].expr) {
 													switch (meta.params[0].expr) {
 														case EConst(v):
 														case EConst(v):
 															switch(v) {
 															switch(v) {
 																case CIdent(name):
 																case CIdent(name):
-																	defValue = name;
+																	defValue = Var(name);
 																case CString(val):
 																case CString(val):
-																	defValue = val;
+																	defValue = Var(val);
 																case CFloat(val), CInt(val):
 																case CFloat(val), CInt(val):
-																	defValue = '$val';
+																	defValue = Const(Std.parseFloat(val));
 																default:
 																default:
 																	throw "sginput default param must be an identifier or a integer";
 																	throw "sginput default param must be an identifier or a integer";
 															}
 															}
@@ -63,18 +59,14 @@ class Macros {
 												switch(subexpr.expr) {
 												switch(subexpr.expr) {
 													case EVars(vars):
 													case EVars(vars):
 														for (v in vars) {
 														for (v in vars) {
-																inVars.push(v.name);
-																defValues.push(defValue);
-
+																var isDynamic = false;
 																switch (v.type) {
 																switch (v.type) {
-																	case TPath(p): {
-																		if (p.name == "Dynamic") {
-																			dynamicValues.push(v.name);
-																			p.name = "Vec4"; // Convert dynamic value back to vec4 as a hack
-																		}
-																	}
+																	case TPath(p) if (p.name == "Dynamic"):
+																		isDynamic = true;
+																		p.name = "Vec4"; // Convert dynamic value back to vec4 as a hack
 																	default:
 																	default:
 																}
 																}
+																varmap.set(v.name, SgInput(isDynamic, defValue));
 															}
 															}
 															e.expr = subexpr.expr;
 															e.expr = subexpr.expr;
 													default:
 													default:
@@ -84,16 +76,15 @@ class Macros {
 												switch(subexpr.expr) {
 												switch(subexpr.expr) {
 													case EVars(vars):
 													case EVars(vars):
 														for (v in vars) {
 														for (v in vars) {
-															outVars.push(v.name);
+															var isDynamic = false;
 															switch (v.type) {
 															switch (v.type) {
-																case TPath(p): {
-																	if (p.name == "Dynamic") {
-																		dynamicValues.push(v.name);
-																		p.name = "Vec4"; // Convert dynamic value back to vec4 as a hack
-																	}
-																}
+																case TPath(p) if (p.name == "Dynamic"):
+																	isDynamic = true;
+																	p.name = "Vec4"; // Convert dynamic value back to vec4 as a hack
 																default:
 																default:
 															}
 															}
+
+															varmap.set(v.name, SgOutput(isDynamic));
 														}
 														}
 														e.expr = subexpr.expr;
 														e.expr = subexpr.expr;
 													default:
 													default:
@@ -103,7 +94,7 @@ class Macros {
 												switch(subexpr.expr) {
 												switch(subexpr.expr) {
 													case EVars(vars):
 													case EVars(vars):
 														for (v in vars) {
 														for (v in vars) {
-															constVars.push(v.name);
+															varmap.set(v.name, SgConst);
 														}
 														}
 														e.expr = subexpr.expr;
 														e.expr = subexpr.expr;
 													default:
 													default:
@@ -130,7 +121,8 @@ class Macros {
 							var shader = check.check(name, shader);
 							var shader = check.check(name, shader);
 							//trace(shader);
 							//trace(shader);
 							//Printer.check(shader);
 							//Printer.check(shader);
-							var str = Context.defined("display") ? "" : hxsl.Serializer.run(shader);
+							var serializer = new hxsl.Serializer();
+							var str = Context.defined("display") ? "" : serializer.serialize(shader);
 							f.kind = FVar(null, { expr : EConst(CString(str)), pos : pos } );
 							f.kind = FVar(null, { expr : EConst(CString(str)), pos : pos } );
 							f.meta.push({
 							f.meta.push({
 								name : ":keep",
 								name : ":keep",
@@ -151,11 +143,28 @@ class Macros {
 								};
 								};
 							}
 							}
 
 
-							fields.push(makeField("_inVars", inVars));
-							fields.push(makeField("_outVars", outVars));
-							fields.push(makeField("_defValues", defValues));
-							fields.push(makeField("_dynamicValues", dynamicValues));
-							fields.push(makeField("_constVars", constVars));
+							var finalMap : Map<Int, hrt.shgraph.SgHxslVar> = [];
+
+							for (v in shader.vars) {
+								var info = varmap.get(v.name);
+								if (info != null) {
+									var serId = @:privateAccess serializer.idMap.get(v.id);
+									finalMap.set(serId, info);
+								}
+							}
+
+							fields.push(
+							{
+								name: "_variablesInfos",
+								access: [APublic, AStatic],
+								kind: FVar(macro : Map<Int, hrt.shgraph.SgHxslVar>, macro $v{finalMap}),
+								pos: f.pos,
+								meta: [{
+										name : ":keep",
+										pos : pos,}
+								],
+							}
+							);
 
 
 
 
 						} catch( e : hxsl.Ast.Error ) {
 						} catch( e : hxsl.Ast.Error ) {

+ 311 - 0
hrt/shgraph/NodeGenContext.hx

@@ -0,0 +1,311 @@
+package hrt.shgraph;
+
+using hxsl.Ast;
+using Lambda;
+using hrt.shgraph.Utils;
+import hrt.shgraph.AstTools.*;
+import hrt.shgraph.ShaderGraph;
+import hrt.shgraph.ShaderNode;
+
+class NodeGenContextSubGraph extends NodeGenContext {
+	public function new(parentCtx : NodeGenContext) {
+		super(parentCtx?.domain ?? Fragment);
+		this.parentCtx = parentCtx;
+	}
+
+	override function getGlobalInput(id: Variables.Global) : TExpr {
+		var global = Variables.Globals[id];
+		var info = globalInVars.getOrPut(global.name, {type: global.type, id: inputCount++});
+		return parentCtx?.nodeInputExprs[info.id] ?? parentCtx?.getGlobalInput(id) ?? super.getGlobalInput(id);
+	}
+
+	override  function setGlobalOutput(id: Variables.Global, expr: TExpr) : Void {
+		var global = Variables.Globals[id];
+		if (outputCount == 0 && parentCtx != null) {
+			parentCtx.addPreview(expr);
+		}
+		var info = globalOutVars.getOrPut(global.name, {type: global.type, id: outputCount ++});
+		if (parentCtx != null) {
+			parentCtx.setOutput(info.id, expr);
+		} else {
+			super.setGlobalOutput(id, expr);
+		}
+	}
+
+	override  function getGlobalParam(name: String, type: Type) : TExpr {
+		var info = globalInVars.getOrPut(name, {type: type, id: inputCount ++});
+		return parentCtx?.nodeInputExprs[info.id] ?? parentCtx?.getGlobalParam(name, type) ?? super.getGlobalParam(name, type);
+	}
+
+	override function setGlobalCustomOutput(name: String, expr: TExpr) : Void {
+		if (outputCount == 0 && parentCtx != null) {
+			parentCtx.addPreview(expr);
+		}
+		var info = globalOutVars.getOrPut(name, {type : expr.t, id: outputCount ++});
+		if (parentCtx != null) {
+			parentCtx.setOutput(info.id, expr);
+		} else {
+			super.setGlobalCustomOutput(name, expr);
+		}
+	}
+
+	override function addExpr(expr: TExpr) : Void {
+		if (parentCtx != null) {
+			parentCtx.addExpr(expr);
+		}
+	}
+
+	var parentCtx : NodeGenContext;
+
+	var globalInVars: Map<String, {type: Type, id: Int}> = [];
+	var globalOutVars: Map<String, {type: Type, id: Int}> = [];
+	var inputCount = 0;
+	var outputCount = 0;
+}
+
+@:allow(hrt.shgraph.ShaderGraph)
+class NodeGenContext {
+	// Pour les rares nodes qui ont besoin de differencier entre vertex et fragment
+	public var domain : ShaderGraph.Domain;
+	public var previewDomain: ShaderGraph.Domain = null;
+
+	public function new(domain: ShaderGraph.Domain) {
+		this.domain = domain;
+	}
+
+	// For general input/output of the shader graph. Allocate a new global var if name is not found,
+	// else return the previously allocated variable and assert that v.type == type and devValue == v.defValue
+	public function getGlobalInput(id: Variables.Global) : TExpr {
+		var global = Variables.Globals[id];
+		switch (global.varkind) {
+			case KVar(_,_,_):
+				var v = getOrAllocateGlobal(id);
+				return makeVar(v);
+			case KSwizzle(id, swiz):
+				var v = getOrAllocateGlobal(id);
+				return makeSwizzle(makeVar(v), swiz);
+		}
+	}
+
+	public function setGlobalOutput(id: Variables.Global, expr: TExpr) : Void {
+		var global = Variables.Globals[id];
+		switch (global.varkind) {
+			case KVar(_,_,_):
+				var v = getOrAllocateGlobal(id);
+				expressions.push(makeAssign(makeVar(v), expr));
+			case KSwizzle(otherId, swiz):
+				var v = getOrAllocateGlobal(otherId);
+				expressions.push(makeAssign(makeSwizzle(makeVar(v), swiz), expr));
+		}
+	}
+
+	public function getGlobalParam(name: String, type: Type) : TExpr {
+		return makeVar(globalVars.getOrPut(name, {v: {id: hxsl.Tools.allocVarId(), name: name, type: type, kind: Param}, defValue:null, __init__: null}).v);
+	}
+
+	public function setGlobalCustomOutput(name: String, expr: TExpr) : Void {
+		var v = makeVar(globalVars.getOrPut(name, {v: {id: hxsl.Tools.allocVarId(), name: name, type: expr.t, kind: Param}, defValue:null, __init__: null}).v);
+		expressions.push(makeAssign(v, expr));
+	}
+
+	function getOrAllocateGlobal(id: Variables.Global) : TVar {
+		// Remap id for certains variables
+		switch (id) {
+			case Normal if (previewDomain == domain):
+				id = FakeNormal;
+			default:
+		}
+
+		var global = Variables.Globals[id];
+		var def : ShaderGraph.ExternVarDef = globalVars.get(global.name);
+
+		switch (global.varkind)
+		{
+			case KVar(kind, parent, defValue):
+				var def : ShaderGraph.ExternVarDef = globalVars.get(global.name);
+				if (def == null) {
+					var v : TVar = {id: hxsl.Tools.allocVarId(), name: global.name, type: global.type, kind: kind};
+					def = {v: v, defValue: defValue, __init__: null};
+					if (parent != null) {
+						var p = Variables.Globals[parent];
+						v.parent = globalVars.getOrPut(p.name, {v : {id : hxsl.Tools.allocVarId(), name: p.name, type: TStruct([]), kind: kind}, defValue: null, __init__: null}).v;
+						switch(v.parent.type) {
+							case TStruct(arr):
+								arr.push(v);
+							default: throw "parent must be a TStruct";
+						}
+					}
+
+					// Post process certain variables
+					switch (id) {
+						case CalculatedUV:
+							var uv = getOrAllocateGlobal(UV);
+							var expr = makeAssign(makeVar(v), makeVar(uv));
+							def.__init__ = expr;
+						default:
+					}
+					globalVars.set(global.name, def);
+				}
+				return def.v;
+			default: throw "id must be a global Var";
+		}
+	}
+
+	// Generate a preview block that displays the content of expr
+	// in the preview box of the node. Expr must be a type that
+	// can be casted a Vec4
+	public function addPreview(expr: TExpr) {
+		if (previewDomain != domain) return;
+		var selector = getGlobalInput(PreviewSelect);
+		var outputColor = getOrAllocateGlobal(PreviewColor);
+
+		var previewExpr = makeAssign(makeVar(outputColor), convertToType(TVec(4, VFloat), expr));
+		var ifExpr = makeIf(makeEq(selector, makeInt(currentPreviewId)), previewExpr);
+		preview = ifExpr;
+	}
+
+	public static function convertToType(targetType: hxsl.Ast.Type, sourceExpr: TExpr) : TExpr {
+		var sourceType = sourceExpr.t;
+
+		if (sourceType.equals(targetType))
+			return sourceExpr;
+
+		var sourceSize = switch (sourceType) {
+			case TFloat: 1;
+			case TVec(size, VFloat): size;
+			default:
+				throw "Unsupported source type " + sourceType;
+		}
+
+		var targetSize = switch (targetType) {
+			case TFloat: 1;
+			case TVec(size, VFloat): size;
+			default:
+				throw "Unsupported target type " + targetType;
+		}
+
+		var delta = targetSize - sourceSize;
+		if (delta == 0)
+			return sourceExpr;
+		if (delta > 0) {
+			var args = [];
+			if (sourceSize == 1) {
+				for (_ in 0...targetSize) {
+					args.push(sourceExpr);
+				}
+			}
+			else {
+
+				args.push(sourceExpr);
+				for (i in 0...delta) {
+					// Set alpha to 1.0 by default on upcasts casts
+					var value = i == delta - 1 ? 1.0 : 0.0;
+					args.push({e : TConst(CFloat(value)), p: sourceExpr.p, t: TFloat});
+				}
+			}
+			var global : TGlobal = switch (targetSize) {
+				case 2: Vec2;
+				case 3: Vec3;
+				case 4: Vec4;
+				default: throw "unreachable";
+			}
+			return {e: TCall({e: TGlobal(global), p: sourceExpr.p, t:targetType}, args), p: sourceExpr.p, t: targetType};
+		}
+		if (delta < 0) {
+			var swizz : Array<hxsl.Ast.Component> = [X,Y,Z,W];
+			swizz.resize(targetSize);
+			return {e: TSwiz(sourceExpr, swizz), p: sourceExpr.p, t: targetType};
+		}
+		throw "unreachable";
+	}
+
+	public function addExpr(e: TExpr) {
+		expressions.push(e);
+	}
+
+	public function setOutput(id: Int, e: TExpr) {
+		var expectedType = getType(nodeOutputInfo[id].type);
+		if (!expectedType.equals(e.t))
+			throw "Output " + id + " has different type than declared";
+		outputs[id]=e;
+	}
+
+	public function getType(type: SgType) : Type {
+		switch (type) {
+			case SgGeneric(id, consDtraint):
+				return getGenericType(id);
+			default:
+				return inline sgTypeToType(type);
+		}
+	}
+
+	public inline function getGenericType(id: Int) {
+		return genericTypes[id];
+	}
+
+	public function getInput(id: Int, ?defValue: SgHxslVar.ShaderDefInput) : Null<TExpr> {
+		var input = nodeInputExprs[id];
+		var inputType = getType(nodeInputInfo[id].type);
+		if (input != null) {
+			return convertToType(inputType, input);
+		}
+
+		if (defValue != null) {
+			switch(defValue) {
+				case Const(f):
+					return convertToType(inputType, makeFloat(f));
+				default:
+					throw "def value not handled yet";
+			}
+		}
+		return null;
+	}
+
+	/**
+		API used by ShaderGraphGenContext
+	**/
+	function initForNode(node: ShaderGraph.Node, nodeInputExprs: Array<TExpr>) {
+		nodeInputInfo = node.instance.getInputs();
+		nodeOutputInfo = node.instance.getOutputs();
+		this.node = node;
+		this.nodeInputExprs = nodeInputExprs;
+
+		outputs.resize(0);
+		genericTypes.resize(0);
+		preview = null;
+
+		for (inputId => input in nodeInputInfo) {
+			switch(input.type) {
+				case SgGeneric(id, constraint):
+					genericTypes[id] = constraint(nodeInputExprs[inputId]?.t, genericTypes[id]);
+				default:
+			}
+		}
+
+		currentPreviewId = node.id + 1;
+	}
+
+	function finishNode() {
+		if (nodeOutputInfo.length != outputs.length) {
+			throw "Missing outputs for node";
+		}
+		if (preview != null) {
+			addExpr(preview);
+		}
+	}
+
+	var node : ShaderGraph.Node = null;
+
+	var currentPreviewId: Int = -1;
+	var expressions: Array<TExpr> = [];
+	var outputs: Array<TExpr> = [];
+	var preview : TExpr = null;
+	var nodeOutputInfo: Array<OutputInfo>;
+
+
+	var genericTypes: Array<Type> = [];
+	var nodeInputExprs : Array<TExpr>;
+
+	var nodeInputInfo : Array<InputInfo>;
+	var globalVars: Map<String, ShaderGraph.ExternVarDef> = [];
+}

+ 13 - 0
hrt/shgraph/SgHxslVar.hx

@@ -0,0 +1,13 @@
+package hrt.shgraph;
+
+enum ShaderDefInput {
+	Var(name: String);
+	Const(intialValue: Float);
+	ConstBool(initialValue: Bool);
+}
+
+enum SgHxslVar {
+	SgInput(isDynamic: Bool, defaultValue: ShaderDefInput);
+	SgConst;
+	SgOutput(isDynamic: Bool);
+}

+ 2 - 2
hrt/shgraph/ShaderConst.hx

@@ -10,8 +10,8 @@ class ShaderConst extends ShaderNode {
 	// 	return getOutputTExpr(key).t;
 	// 	return getOutputTExpr(key).t;
 	// }
 	// }
 
 
-	override public function build(key : String) : TExpr {
-		return null;
+	override function generate(ctx:NodeGenContext) {
+
 	}
 	}
 
 
 	#if editor
 	#if editor

+ 23 - 16
hrt/shgraph/ShaderGlobalInput.hx

@@ -10,16 +10,33 @@ class ShaderGlobalInput extends ShaderNode {
 
 
 	@prop("Variable") public var variableIdx : Int = 0;
 	@prop("Variable") public var variableIdx : Int = 0;
 
 
-	static public var globalInputs = [	{display: "Time", v: { parent: null, id: 0, kind: Global, name: "global.time", type: TFloat }},
-										{display: "Pixel Size", v: { parent: null, id: 0, kind: Global, name: "global.pixelSize", type: TVec(2, VFloat) }},
-										//{display: "Model View", v: { parent: null, id: 0, kind: Global, name: "global.modelView", type: TMat4 }},
-										//{display: "Model View Inverse", v: { parent: null, id: 0, kind: Global, name: "global.modelViewInverse", type: TMat4 }}
-									];
+	static public var globalInputs : Array<{display: String, g: Variables.Global}> =
+		[
+			{display: "Time", g: Time},
+			{display: "Pixel Size", g: PixelSize},
+		];
 
 
 	public function new(idx: Int) {
 	public function new(idx: Int) {
 		variableIdx = idx;
 		variableIdx = idx;
 	}
 	}
 
 
+	var outputs : Array<ShaderNode.OutputInfo>;
+	override public function getOutputs() {
+		if (outputs == null) {
+			var global = globalInputs[variableIdx].g;
+			var info = Variables.Globals[global];
+			outputs = [{name: "output", type: ShaderGraph.typeToSgType(info.type)}];
+		}
+		return outputs;
+	}
+
+	override function generate(ctx: NodeGenContext) {
+		var input = ctx.getGlobalInput(globalInputs[variableIdx].g);
+
+		ctx.setOutput(0, input);
+		ctx.addPreview(input);
+	}
+
 	override public function getAliases(name: String, group: String, description: String) {
 	override public function getAliases(name: String, group: String, description: String) {
 		var aliases = super.getAliases(name, group, description);
 		var aliases = super.getAliases(name, group, description);
 		for (i => input in globalInputs) {
 		for (i => input in globalInputs) {
@@ -33,17 +50,6 @@ class ShaderGlobalInput extends ShaderNode {
 		return aliases;
 		return aliases;
 	}
 	}
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var inVar : TVar = Reflect.copy(globalInputs[variableIdx].v);
-		inVar.id = getNewIdFn();
-		var output : TVar = {name: "output", id: getNewIdFn(), type: inVar.type, kind: Local, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TVar(inVar), p: pos, t: output.type}), p: pos, t: output.type};
-
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false, isDynamic: false}], externVars: [inVar], inits: []};
-	}
-
 	#if editor
 	#if editor
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
 		var elements = [];
 		var elements = [];
@@ -60,6 +66,7 @@ class ShaderGlobalInput extends ShaderNode {
 		}
 		}
 		input.on("change", function(e) {
 		input.on("change", function(e) {
 			var value = input.val();
 			var value = input.val();
+			outputs = null;
 			this.variableIdx = value;
 			this.variableIdx = value;
 		});
 		});
 
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 343 - 882
hrt/shgraph/ShaderGraph.hx


+ 52 - 134
hrt/shgraph/ShaderInput.hx

@@ -2,27 +2,49 @@ package hrt.shgraph;
 
 
 using hxsl.Ast;
 using hxsl.Ast;
 
 
+enum InputKind {
+	IGlobal(g: Variables.Global);
+	ICustom(gen:(ctx:NodeGenContext) -> TExpr, t: hxsl.Ast.Type);
+}
+
 @name("Inputs")
 @name("Inputs")
 @description("Shader inputs of Heaps, it's dynamic")
 @description("Shader inputs of Heaps, it's dynamic")
 @group("Property")
 @group("Property")
 @color("#0e8826")
 @color("#0e8826")
 class ShaderInput extends ShaderNode {
 class ShaderInput extends ShaderNode {
-
-
 	@prop("Variable") public var variable : String = "pixelColor";
 	@prop("Variable") public var variable : String = "pixelColor";
 
 
-	// override public function getOutput(key : String) : TVar {
-	// 	return variable;
-	// }
-
-	// override public function build(key : String) : TExpr {
-	// 	return null;
-	// }
-
 	public function new(variable = "pixelColor") {
 	public function new(variable = "pixelColor") {
 		this.variable = variable;
 		this.variable = variable;
 	}
 	}
 
 
+	var outputs : Array<ShaderNode.OutputInfo>;
+	override function getOutputs() {
+		if (outputs == null) {
+			var global = availableInputs[variable].k;
+			var t = switch(global) {
+				case IGlobal(g):
+					Variables.Globals[g].type;
+				case ICustom(_,t):
+					t;
+			}
+			outputs = [{name: "output", type: ShaderGraph.typeToSgType(t)}];
+		}
+		return outputs;
+	}
+
+	override function generate(ctx: NodeGenContext) {
+		var expr = switch(availableInputs[variable].k) {
+			case IGlobal(g):
+				ctx.getGlobalInput(g);
+			case ICustom(gen, _):
+				gen(ctx);
+		}
+
+		ctx.setOutput(0, expr);
+		ctx.addPreview(expr);
+	}
+
 	override public function getAliases(name: String, group: String, description: String) {
 	override public function getAliases(name: String, group: String, description: String) {
 		var aliases = super.getAliases(name, group, description);
 		var aliases = super.getAliases(name, group, description);
 		for (key => input in hrt.shgraph.ShaderInput.availableInputs) {
 		for (key => input in hrt.shgraph.ShaderInput.availableInputs) {
@@ -36,85 +58,9 @@ class ShaderInput extends ShaderNode {
 		return aliases;
 		return aliases;
 	}
 	}
 
 
-
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var variable : ShaderNode.VariableDecl = availableInputs.get(this.variable);
-		if (variable == null)
-			throw "Unknown input variable " + this.variable;
-
-		var inVar : TVar = Reflect.copy(variable.v);
-		inVar.id = getNewIdFn();
-		var output : TVar = {name: "output", id: getNewIdFn(), type: variable.v.type, kind: Local, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TVar(inVar), p: pos, t: output.type}), p: pos, t: output.type};
-
-		if (variable.v.name == "pixelColor") {
-			var vec3 = TVec(3, VFloat);
-			output.type = vec3;
-			finalExpr =
-				{
-					e: TBinop(
-					OpAssign,
-						{
-							e: TVar(output),
-							p: pos,
-							t: vec3
-						},
-						{
-							e: TSwiz(
-									{
-										e: TVar(inVar),
-										p: pos,
-										t: vec3,
-									},
-									[X,Y,Z]
-								),
-							p:pos,
-							t:vec3
-						}
-					),
-					p: pos,
-					t: vec3
-				};
-		}
-		else if (variable.v.name == "alpha") {
-			var flt = TFloat;
-			output.type = flt;
-			inVar.name = "pixelColor";
-			finalExpr =
-				{
-					e: TBinop(
-					OpAssign,
-						{
-							e: TVar(output),
-							p: pos,
-							t: flt
-						},
-						{
-							e: TSwiz(
-									{
-										e: TVar(inVar),
-										p: pos,
-										t: flt,
-									},
-									[W]
-								),
-							p:pos,
-							t:flt
-						}
-					),
-					p: pos,
-					t: flt
-				};
-		}
-
-		return {expr: finalExpr, inVars: [{v:inVar, internal: true, isDynamic: false}], outVars:[{v:output, internal: false, isDynamic: false}], externVars: [], inits: []};
-	}
-
 	override function loadProperties(props:Dynamic) {
 	override function loadProperties(props:Dynamic) {
 		super.loadProperties(props);
 		super.loadProperties(props);
-		var ivar : ShaderNode.VariableDecl = availableInputs.get(this.variable);
+		var ivar = availableInputs.get(this.variable);
 		if (ivar == null) {
 		if (ivar == null) {
 			for (k => v in availableInputs) {
 			for (k => v in availableInputs) {
 				variable = k;
 				variable = k;
@@ -123,54 +69,25 @@ class ShaderInput extends ShaderNode {
 		}
 		}
 	}
 	}
 
 
-	public static var availableInputs : Map<String, ShaderNode.VariableDecl> = [
-		"pixelColor" => {display: "Pixel Color", v: { parent: null, id: 0, kind: Local, name: "pixelColor", type: TVec(4, VFloat) }},
-		"alpha" => {display: "Alpha", v: { parent: null, id: 0, kind: Local, name: "alpha", type: TVec(4, VFloat) }},
-		"calculatedUV" => {display: "UV", v: { parent: null, id: 0, kind: Local, name: "calculatedUV", type: TVec(2, VFloat) }},
-		"relativePosition" => {display: "Position (Object Space)", v: { parent: null, id: 0, kind: Local, name: "relativePosition", type: TVec(3, VFloat) }},
-		"transformedPosition" => {display: "Position (World Space)", v: { parent: null, id: 0, kind: Local, name: "transformedPosition", type: TVec(3, VFloat) }},
-		"projectedPosition" => {display: "Position (View Space)", v: { parent: null, id: 0, kind: Local, name: "projectedPosition", type: TVec(4, VFloat) }},
-		"normal" => {display: "Normal (Object Space)", v: { parent: null, id: 0, kind: Local, name: "input.normal", type: TVec(3, VFloat) }},
-		"transformedNormal" => {display: "Normal (World Space)", v: { parent: null, id: 0, kind: Local, name: "transformedNormal", type: TVec(3, VFloat) }},
-		"depth" => {display: "Depth", v: {parent: null, id: 0, kind: Local, name: "depth", type: TFloat}},
-
-		"metalness" => {display: "Metalness", v: {parent: null,id: 0,kind: Local,name: "metalness",type: TFloat}},
-		"roughness" => {display: "Roughness", v: {parent: null, id: 0, kind: Local, name: "roughness", type: TFloat}},
-		"emissive" => {display: "Emissive", v: {parent: null, id: 0, kind: Local, name: "emissive", type: TFloat}},
-		"occlusion" => {display: "Occlusion", v: {parent: null, id: 0, kind: Local, name: "occlusion", type: TFloat}},
-
-		// "position" => {display: "Source Position", v: { parent: null, id: 0, kind: Input, name: "input.position", type: TVec(3, VFloat) }},
-		// "color" => 	{display: "Source Vertex Color", v: { parent: null, id: 0, kind: Input, name: "input.color", type: TVec(3, VFloat) }},
-		"uv" => {display: "Source UV", v: { parent: null, id: 0, kind: Input, name: "input.uv", type: TVec(2, VFloat) }},
-		// "normal" => {display: "Source Normal", v: { parent: null, id: 0, kind: Input, name: "input.normal", type: TVec(3, VFloat) }},
-		// "tangent" => {display: "Source Tangent", v: { parent: null, id: 0, kind: Input, name: "input.tangent", type: TVec(3, VFloat) }},
-	];
 
 
-	/*public static var availableInputs : Array<TVar> = [
-									{ parent: null, id: 0, kind: Input, name: "position", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Input, name: "color", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Input, name: "uv", type: TVec(2, VFloat) },
-									{ parent: null, id: 0, kind: Input, name: "normal", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Input, name: "tangent", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Local, name: "relativePosition", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Local, name: "transformedPosition", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Local, name: "projectedPosition", type: TVec(4, VFloat) },
-									{ parent: null, id: 0, kind: Local, name: "transformedNormal", type: TVec(3, VFloat) },
-									{ parent: null, id: 0, kind: Local, name: "screenUV", type: TVec(2, VFloat) },
-									{ parent: null, id: 0, kind: Local, name: "calculatedUV", type: TVec(2, VFloat) },
-								];*/
-
-	// override public function loadProperties(props : Dynamic) {
-	// 	variable = Reflect.field(props, "Variable");
-	// }
-
-	// override public function saveProperties() : Dynamic {
-	// 	var parameters = {
-	// 		variable: variable;
-	// 	};
-
-	// 	return parameters;
-	// }
+	public static var availableInputs : Map<String, {display: String, k: InputKind}> = [
+		"pixelColor" => {display: "Pixel Color", k: ICustom((ctx:NodeGenContext) -> makeSwizzle(ctx.getGlobalInput(PixelColor), [X,Y,Z]), TVec(3, VFloat))},
+		"alpha" => {display: "Alpha", k: ICustom((ctx:NodeGenContext) -> makeSwizzle(ctx.getGlobalInput(PixelColor), [W]), TFloat)},
+		"calculatedUV" => {display: "UV", k: IGlobal(CalculatedUV)},
+		"relativePosition" => {display: "Position (Object Space)", k: IGlobal(RelativePosition)},
+		"transformedPosition" => {display: "Position (World Space)", k: IGlobal(TransformedPosition)},
+		"projectedPosition" => {display: "Position (View Space)", k: IGlobal(ProjectedPosition)},
+		"normal" => {display: "Normal (Object Space)", k: IGlobal(Normal)},
+		"transformedNormal" => {display: "Normal (World Space)", k: IGlobal(TransformedNormal)},
+
+		"depth" => {display: "Depth", k: IGlobal(Depth)},
+		"metalness" => {display: "Metalness", k: IGlobal(Metalness)},
+		"roughness" => {display: "Roughness", k: IGlobal(Roughness)},
+		"emissive" => {display: "Emissive", k: IGlobal(Emissive)},
+		"occlusion" => {display: "Occlusion", k: IGlobal(Occlusion)},
+
+		"uv" => {display: "Source UV", k: IGlobal(UV)},
+	];
 
 
 	#if editor
 	#if editor
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
@@ -191,6 +108,7 @@ class ShaderInput extends ShaderNode {
 
 
 		input.on("change", function(e) {
 		input.on("change", function(e) {
 			variable = input.val();
 			variable = input.val();
+			outputs = null;
 		});
 		});
 
 
 		elements.push(element);
 		elements.push(element);

+ 37 - 125
hrt/shgraph/ShaderNode.hx

@@ -6,6 +6,11 @@ using hxsl.Ast;
 import h3d.scene.Mesh;
 import h3d.scene.Mesh;
 
 
 using Lambda;
 using Lambda;
+using hrt.shgraph.Utils;
+import hrt.shgraph.AstTools.*;
+import hrt.shgraph.ShaderGraph;
+import hrt.shgraph.SgHxslVar.ShaderDefInput;
+
 
 
 class AlphaPreview extends hxsl.Shader {
 class AlphaPreview extends hxsl.Shader {
 	static var SRC = {
 	static var SRC = {
@@ -27,6 +32,8 @@ class AlphaPreview extends hxsl.Shader {
 	}
 	}
 }
 }
 
 
+typedef InputInfo = {name: String, type: SgType, ?def: ShaderDefInput};
+typedef OutputInfo = {name: String, type: SgType};
 typedef VariableDecl = {v: TVar, display: String, ?vertexOnly: Bool};
 typedef VariableDecl = {v: TVar, display: String, ?vertexOnly: Bool};
 typedef AliasInfo = {?nameSearch: String, ?nameOverride : String, ?description : String, ?args : Array<Dynamic>, ?group: String};
 typedef AliasInfo = {?nameSearch: String, ?nameOverride : String, ?description : String, ?args : Array<Dynamic>, ?group: String};
 @:autoBuild(hrt.shgraph.Macros.autoRegisterNode())
 @:autoBuild(hrt.shgraph.Macros.autoRegisterNode())
@@ -41,6 +48,35 @@ class ShaderNode {
 
 
 	public var defaults : Dynamic = {};
 	public var defaults : Dynamic = {};
 
 
+	/**
+		Declare all the inputs this node uses
+	**/
+	public function getInputs() : Array<InputInfo> {
+		return [];
+	}
+
+	/**
+		Declare all the outputs this node uses
+	**/
+	public function getOutputs() : Array<OutputInfo> {
+		return [];
+	}
+
+	/**
+		Generate the hxsl expressions and outputs of the node
+	**/
+	public function generate(ctx: NodeGenContext) : Void {
+		throw "generate is not defined for class " + std.Type.getClassName(std.Type.getClass(this));
+	}
+
+	function getDef(name: String, def: Float) {
+		var defaultValue = Reflect.getProperty(defaults, name);
+		if (defaultValue != null) {
+			def = Std.parseFloat(defaultValue) ?? def;
+		}
+		return def;
+	}
+
 	public function getAliases(name: String, group: String, description: String) : Array<AliasInfo> {
 	public function getAliases(name: String, group: String, description: String) : Array<AliasInfo> {
 		var cl = HaxeType.getClass(this);
 		var cl = HaxeType.getClass(this);
 		var meta = haxe.rtti.Meta.getType(cl);
 		var meta = haxe.rtti.Meta.getType(cl);
@@ -53,136 +89,12 @@ class ShaderNode {
 		}
 		}
 		return aliases;
 		return aliases;
 	}
 	}
-
-	static var availableVariables = [
-					{
-						parent: null,
-						id: 0,
-						kind: Global,
-						name: "_sg_out_color",
-						type: TVec(3, VFloat)
-					},
-					{
-						parent: null,
-						id: 0,
-						kind: Global,
-						name: "_sg_out_alpha",
-						type: TFloat
-					},
-				];
-
-
-	public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
-		throw "getShaderDef is not defined for class " + std.Type.getClassName(std.Type.getClass(this));
-		return {expr: null, inVars: [], outVars: [], inits: [], externVars: []};
-	}
-
-	public var connections : Map<String, ShaderGraph.Connection> = [];
-
-	public var outputCompiled : Map<String, Bool> = []; // todo: put with outputs variable
-
-	// TODO(ces) : caching
-	public function getOutputs2(domain: ShaderGraph.Domain, ?inputTypes: Array<Type>) : Map<String, {v: TVar, index: Int}> {
-		var def = getShaderDef(domain, () -> 0);
-		var map : Map<String, {v: TVar, index: Int}> = [];
-		var count = 0;
-		for (i => tvar in def.outVars) {
-			if (!tvar.internal) {
-				map.set(tvar.v.name, {v: tvar.v, index: count});
-				count += 1;
-			}
-		}
-		return map;
-	}
-
-	// TODO(ces) : caching
-	public function getInputs2(domain: ShaderGraph.Domain) : Map<String, {v: TVar, ?def: hrt.shgraph.ShaderGraph.ShaderDefInput, index: Int}> {
-		var def = getShaderDef(domain, () -> 0);
-		var map : Map<String, {v: TVar, ?def: hrt.shgraph.ShaderGraph.ShaderDefInput, index: Int}> = [];
-		var count = 0;
-		for (tvar in def.inVars) {
-			if (!tvar.internal) {
-				map.set(tvar.v.name, {v: tvar.v, def: tvar.defVal, index: count});
-				count += 1;
-			}
-		}
-		return map;
-	}
-
+	public var connections : Array<ShaderGraph.Connection> = [];
 
 
 	public function setId(id : Int) {
 	public function setId(id : Int) {
 		this.id = id;
 		this.id = id;
 	}
 	}
 
 
-
-	function addOutput(key : String, t : Type) {
-		/*outputs.set(key, { parent: null,
-			id: 0,
-			kind: Local,
-			name: "output_" + id + "_" + key,
-			type: t
-		});*/
-	}
-
-	function removeOutput(key : String) {
-		/*outputs.remove(key);*/
-	}
-
-	function addOutputTvar(tVar : TVar) {
-		/*outputs.set(tVar.name, tVar);*/
-	}
-
-	public function computeOutputs() : Void {}
-
-	// public function getOutput(key : String) : TVar {
-	// 	return outputs.get(key);
-	// }
-
-	// public function getOutputType(key : String) : Type {
-	// 	var output = getOutput(key);
-	// 	if (output == null)
-	// 		return null;
-	// 	return output.type;
-	// }
-
-	// public function getOutputTExpr(key : String) : TExpr {
-	// 	var o = getOutput(key);
-	// 	if (o == null)
-	// 		return null;
-	// 	return {
-	// 		e: TVar(o),
-	// 		p: null,
-	// 		t: o.type
-	// 	};
-	// }
-
-	public function build(key : String) : TExpr {
-		throw "Build function not implemented";
-	}
-
-	public function checkTypeAndCompatibilyInput(key : String, type : hxsl.Ast.Type) : Bool {
-		/*var infoKey = getInputs2()[key].type;
-		if (infoKey != null && !(ShaderType.checkConversion(type, infoKey))) {
-			return false;
-		}
-		return checkValidityInput(key, type);*/
-		return true;
-	}
-
-	public function checkValidityInput(key : String, type : hxsl.Ast.Type) : Bool {
-		return true;
-	}
-
-
-
-	// public function getOutputInfoKeys() : Array<String> {
-	// 	return [];
-	// }
-
-	// public function getOutputInfo(key : String) : OutputInfo {
-	// 	return null;
-	// }
-
 	public function loadProperties(props : Dynamic) {
 	public function loadProperties(props : Dynamic) {
 		var fields = Reflect.fields(props);
 		var fields = Reflect.fields(props);
 		showPreview = props.showPreview ?? true;
 		showPreview = props.showPreview ?? true;

+ 224 - 104
hrt/shgraph/ShaderNodeHxsl.hx

@@ -2,133 +2,253 @@ package hrt.shgraph;
 
 
 import hxsl.Ast.TExpr;
 import hxsl.Ast.TExpr;
 using hxsl.Ast;
 using hxsl.Ast;
+using hrt.shgraph.Utils;
 using Lambda;
 using Lambda;
 
 
+typedef CacheEntry = {expr: TExpr, inputs: Array<ShaderNode.InputInfo>, outputs: Array<ShaderNode.OutputInfo>, idInputOrder: Map<Int, Int>, idOutputOrder: Map<Int,Int>};
+
+class CustomSerializer extends hxsl.Serializer {
+
+	// we override readvar to remove the allocation of new variable id
+	// because we rely on the variable ID to get our custom info about
+	// input and outputs
+	override function readVar() : TVar {
+		var id = readID();
+		if( id == 0 )
+			return null;
+		var v = varMap.get(id);
+		if( v != null ) return v;
+		v = {
+			id : id,
+			name : readString(),
+			type : null,
+			kind : null,
+		}
+		varMap.set(id, v);
+		v.type = readType();
+		v.kind = hxsl.Serializer.VKINDS[input.readByte()];
+		v.parent = readVar();
+		var nq = input.readByte();
+		if( nq > 0 ) {
+			v.qualifiers = [];
+			for( i in 0...nq ) {
+				var qid = input.readByte();
+				var q = switch( qid ) {
+				case 0: var n = input.readInt32(); Const(n == 0 ? null : n);
+				case 1: Private;
+				case 2: Nullable;
+				case 3: PerObject;
+				case 4: Name(readString());
+				case 5: Shared;
+				case 6: Precision(hxsl.Serializer.PRECS[input.readByte()]);
+				case 7: Range(input.readDouble(), input.readDouble());
+				case 8: Ignore;
+				case 9: PerInstance(input.readInt32());
+				case 10: Doc(readString());
+				case 11: Borrow(readString());
+				case 12: Sampler(readString());
+				case 13: Final;
+				default: throw "assert";
+				}
+				v.qualifiers.push(q);
+			}
+		}
+		return v;
+	}
+}
 
 
 @:autoBuild(hrt.shgraph.Macros.buildNode())
 @:autoBuild(hrt.shgraph.Macros.buildNode())
 class ShaderNodeHxsl extends ShaderNode {
 class ShaderNodeHxsl extends ShaderNode {
 
 
-	static var nodeCache : Map<String, ShaderGraph.ShaderNodeDef> = [];
+	static var cache : Map<{}, CacheEntry> = [];
 
 
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
+	override public function getInputs() : Array<ShaderNode.InputInfo> {
 		var cl = std.Type.getClass(this);
 		var cl = std.Type.getClass(this);
-		var className = std.Type.getClassName(cl);
-		var def = null;//nodeCache.get(className);
-		if (def == null) {
-			var unser = new hxsl.Serializer();
-			var toUnser = (cl:Dynamic).SRC;
-			if (toUnser == null) throw "Node " + className + " has no SRC";
-			var data = @:privateAccess unser.unserialize(toUnser);
-			var expr = null;
-			var funs = null;
-			for (fun in data.funs) {
-				if (fun.ref.name == "fragment")
-					expr = fun.expr;
-				else {
-					if (funs == null)
-						funs = new Array<TFunction>();
-					funs.push(fun);
-				}
+		return cache.getOrPut(cast cl, genCache(cl)).inputs;
+	}
+
+	override public function getOutputs() : Array<ShaderNode.OutputInfo> {
+		var cl = std.Type.getClass(this);
+		return cache.getOrPut(cast cl, genCache(cl)).outputs;
+	}
+
+	function genCache(cl: Class<ShaderNodeHxsl>) : CacheEntry {
+		var toUnser = (cl:Dynamic).SRC;
+		if (toUnser == null) throw "Node " + std.Type.getClassName(cl) + " has no SRC";
+
+		var unser = new CustomSerializer();
+		var data = @:privateAccess unser.unserialize(toUnser);
+
+		var expr : TExpr = null;
+		for (fn in data.funs) {
+			if (fn.ref.name == "fragment") {
+				expr = fn.expr;
+				break;
 			}
 			}
+		}
 
 
-			var idToNewId : Map<Int, Int> = [];
-
-			function patchExprId(expr: TExpr) : TExpr {
-				switch (expr.e) {
-					case TVar(v):
-						var newId = idToNewId.get(v.id);
-						if (newId == null) {
-							newId = getNewIdFn();
-							idToNewId.set(v.id, newId);
-						}
-						v.id = newId;
-						return expr;
-					default:
-						return expr.map(patchExprId);
-				}
+		var inputs : Array<ShaderNode.InputInfo> = [];
+		var outputs : Array<ShaderNode.OutputInfo> = [];
+		var idInputOrder : Map<Int,Int> = [];
+		var idOutputOrder : Map<Int,Int> = [];
+		var inputCount = 0;
+		var outputCount = 0;
+
+		var infos : Map<Int, SgHxslVar> = cast (cl:Dynamic)._variablesInfos;
+		for (v in data.vars) {
+			var info = infos.get(v.id);
+			switch (info) {
+				case SgInput(isDynamic, defaultValue):
+					inputs.push({name: v.name, type: isDynamic ? SgGeneric(0, ShaderGraph.ConstraintFloat) : typeToSgType(v.type), def: defaultValue});
+					idInputOrder.set(v.id, inputCount++);
+				case SgOutput(isDynamic):
+					outputs.push({name: v.name, type: isDynamic ? SgGeneric(0, ShaderGraph.ConstraintFloat) : typeToSgType(v.type)});
+					idOutputOrder.set(v.id, outputCount++);
+				case SgConst:
+				case null:
 			}
 			}
+		}
+
+		return {expr: expr, inputs: inputs, outputs: outputs, idInputOrder: idInputOrder, idOutputOrder: idOutputOrder};
+	}
+
+	override public function generate(ctx: NodeGenContext) : Void {
+		var cl = std.Type.getClass(this);
+		var cache = cache.getOrPut(cast cl, genCache(cl));
+
+		var infos : Map<Int, SgHxslVar> = cast (cl:Dynamic)._variablesInfos;
+		var varsOverride : Map<Int, TExpr> = [];
+		var varsRemap : Map<Int, TVar> = [];
+		var outputs : Array<TVar> = [];
+		var genFailure : Bool = false;
+
+		function patch(e: TExpr) : TExpr {
+			switch (e.e) {
+				case TVar(v):
+					var replacement = varsOverride.get(v.id);
+					if (replacement != null)
+						return replacement;
 
 
-			patchExprId(expr);
-
-			var inVars : Array<hrt.shgraph.ShaderGraph.ShaderNodeDefInVar>= [];
-			var outVars : Array<hrt.shgraph.ShaderGraph.ShaderNodeDefOutVar> = [];
-			var externVars = [];
-
-			var classDynamicVal : Array<String> = cast (cl:Dynamic)._dynamicValues;
-
-			for (tvar in data.vars) {
-					var input = false;
-					var output = false;
-					var classInVars : Array<String> = cast (cl:Dynamic)._inVars;
-					var classDefVal : Array<String> = cast (cl:Dynamic)._defValues;
-
-					var indexOf = classInVars.indexOf(tvar.name);
-					if (indexOf > -1) {
-						var defStr = classDefVal[indexOf];
-						var def : hrt.shgraph.ShaderGraph.ShaderDefInput = null;
-						if (defStr != null) {
-							var float = Std.parseFloat(defStr);
-							if (!Math.isNaN(float)) {
-								def = Const(float);
-							} else {
-								def = Var(defStr);
+					var type = e.t;
+
+					var info = infos.get(v.id);
+
+					switch(info) {
+						case SgInput(isDynamic, defaultValue):
+							if (isDynamic) {
+								type = ctx.getType(SgGeneric(0, ShaderGraph.ConstraintFloat));
 							}
 							}
-						}
-						inVars.push({v:tvar, internal: false, defVal: def, isDynamic: classDynamicVal.contains(tvar.name)});
-						// TODO : handle default values
-						input = true;
-					}
-					var classOutVars : Array<String> = cast (cl:Dynamic)._outVars;
-					if (classOutVars.contains(tvar.name)) {
-						outVars.push({v: tvar, internal: false, isDynamic: false});
-						output = true;
-					}
-					if (input && output) {
-						throw "Variable is both sginput and sgoutput";
+							var inputId = cache.idInputOrder.get(v.id);
+							replacement = ctx.getInput(inputId);
+
+							// default value handling if we have no input connected
+							if (replacement == null) {
+								switch (defaultValue) {
+									case Const(init):
+										replacement = NodeGenContext.convertToType(type, makeFloat(getDef(v.name, init)));
+									case Var(name):
+										var globalId = Variables.getGlobalNameMap().get(name);
+										if (globalId != null) {
+											replacement = ctx.getGlobalInput(globalId);
+										}
+									case null, _:
+										genFailure = true;
+								}
+							}
+						case SgConst:
+							replacement = makeInt(getConstValue(v.name) ?? 0);
+						case SgOutput(_):
+							var outputId = cache.idOutputOrder.get(v.id);
+							var t = ctx.getType(cache.outputs[outputId].type);
+
+							var outputVar : TVar= {
+								name: v.name,
+								id: hxsl.Tools.allocVarId(),
+								type: t,
+								kind: v.kind,
+								parent: v.parent,
+								qualifiers: v.qualifiers,
+							};
+							replacement = makeVar(outputVar);
+							outputs[outputId] = outputVar;
+						case null:
+							var tvar = varsRemap.getOrPut(v.id,
+							{
+								name: v.name,
+								id: hxsl.Tools.allocVarId(),
+								type: v.type,
+								kind: v.kind,
+								parent: v.parent,
+								qualifiers: v.qualifiers,
+							});
+							replacement = {e: TVar(tvar), p: e.p, t: e.t};
 					}
 					}
-					if (!input && !output) {
-						switch (tvar.kind) {
-							case Function:
-								// do nothing
-							default:
-								externVars.push(tvar);
-						}
+					if (replacement == null) {
+						genFailure = true;
+						replacement = e;
 					}
 					}
-			}
+					varsOverride.set(v.id, replacement);
 
 
-			for (v in outVars) {
-				if (classDynamicVal.contains(v.v.name)) {
-					v.isDynamic = true;
-				}
+					return replacement;
+				case TVarDecl(v, init):
+					var tvar = varsRemap.getOrPut(v.id,
+						{
+							name: v.name,
+							id: hxsl.Tools.allocVarId(),
+							type: v.type,
+							kind: v.kind,
+							parent: v.parent,
+							qualifiers: v.qualifiers,
+						});
+					return makeExpr(TVarDecl(tvar, if( init != null ) patch(init) else null), e.t);
+				case TFor(v, it, loop):
+					var tvar = varsRemap.getOrPut(v.id,
+						{
+							name: v.name,
+							id: hxsl.Tools.allocVarId(),
+							type: v.type,
+							kind: v.kind,
+							parent: v.parent,
+							qualifiers: v.qualifiers,
+						});
+					return makeExpr(TFor(tvar, patch(it), patch(loop)), e.t);
+				default:
+					return e.map(patch);
 			}
 			}
+		}
 
 
-			var classConstVars : Array<String> = cast (cl:Dynamic)._constVars;
-
-			// Const replacement
-			for (const in classConstVars) {
-				var value = getConstValue(const);
-				if (value == null)
-					throw "unhandled const value " + const;
+		var expr = patch(cache.expr);
 
 
-				function patchExprConst(expr : TExpr) {
-					switch (expr.e) {
-						case TVar(v):
-							if (v.name == const) {
-								expr.e = TConst(CInt(value));
-							}
-						default:
-							expr.map(patchExprConst);
-					}
-					return expr;
+		if (genFailure) {
+			for (outputId => o in cache.outputs) {
+				var t = ctx.getType(o.type);
+				var expr = NodeGenContext.convertToType(t, makeVec([0.0,0.0,0.0,0.0]));
+				ctx.setOutput(outputId, NodeGenContext.convertToType(t, makeVec([0.0,0.0,0.0,0.0])));
+				if (outputId == 0) {
+					ctx.addPreview(expr);
 				}
 				}
-				expr.map(patchExprConst);
 			}
 			}
-
-			def = {expr: expr, inVars: inVars, outVars: outVars, externVars: externVars, inits: [], functions: funs};
-			nodeCache.set(className, def);
 		}
 		}
+		else {
+			for (outputId => o in cache.outputs) {
+				var tvar = outputs[outputId];
+				ctx.addExpr(makeExpr(TVarDecl(tvar), tvar.type));
+				var expr = makeVar(tvar);
+				ctx.setOutput(outputId, expr);
+				if (outputId == 0) {
+					ctx.addPreview(expr);
+				}
+			}
 
 
-		return def;
+			switch(expr.e){
+				case TBlock(exprs):
+					for (e in exprs) {
+						ctx.addExpr(e);
+					}
+				default:
+					throw "function expr is not a block";
+			}
+		}
 	}
 	}
 
 
 	function getConstValue(name: String) : Null<Int> {
 	function getConstValue(name: String) : Null<Int> {

+ 30 - 133
hrt/shgraph/ShaderOutput.hx

@@ -19,6 +19,22 @@ class ShaderOutput extends ShaderNode {
 		this.variable = variable;
 		this.variable = variable;
 	}
 	}
 
 
+	var inputs : Array<ShaderNode.InputInfo>;
+	override public function getInputs() : Array<ShaderNode.InputInfo> {
+		if (inputs == null) {
+			var global = availableOutputs[variable].g;
+			var info = Variables.Globals[global];
+			inputs = [{name: "input", type: ShaderGraph.typeToSgType(info.type)}];
+		}
+		return inputs;
+	}
+
+	override public function generate(ctx: NodeGenContext) {
+		var out = ctx.getInput(0, SgHxslVar.ShaderDefInput.Const(getDef("input", 0.0)));
+		ctx.setGlobalOutput(availableOutputs[variable].g, out);
+		ctx.addPreview(out);
+	}
+
 	override public function getAliases(name: String, group: String, description: String) {
 	override public function getAliases(name: String, group: String, description: String) {
 		var aliases = super.getAliases(name, group, description);
 		var aliases = super.getAliases(name, group, description);
 		for (key => output in hrt.shgraph.ShaderOutput.availableOutputs) {
 		for (key => output in hrt.shgraph.ShaderOutput.availableOutputs) {
@@ -32,139 +48,19 @@ class ShaderOutput extends ShaderNode {
 		return aliases;
 		return aliases;
 	}
 	}
 
 
-	public function getVariable() {
-		return availableOutputs.get(variable).v;
-	}
-
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var variable = availableOutputs.get(variable).v;
-
-		var inVar : TVar = {name: "input", id: getNewIdFn(), type: variable.type, kind: Param, qualifiers: []};
-		var output : TVar = {name: variable.name, id: getNewIdFn(), type: variable.type, kind: variable.kind, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TVar(inVar), p: pos, t: output.type}), p: pos, t: output.type};
-
-		//var param = getParameter(inputNode.parameterId);
-		//inits.push({variable: inVar, value: param.defaultValue});
-		var inVars = [{v: inVar, internal: false, isDynamic: false}];
-
-		// if (variable.name == "pixelColor") {
-		// 	var vec3 = TVec(3, VFloat);
-		// 	inVar.type = vec3;
-		// 	finalExpr =
-		// 		{
-		// 			e: TBinop(
-		// 			OpAssign,
-		// 				{
-		// 					e: TSwiz(
-		// 							{
-		// 								e: TVar(output),
-		// 								p: pos,
-		// 								t: vec3,
-		// 							},
-		// 							[X,Y,Z]
-		// 						),
-		// 					p:pos,
-		// 					t:vec3
-		// 				},
-		// 				{
-		// 					e: TVar(inVar),
-		// 					p: pos,
-		// 					t: vec3
-		// 				}
-		// 			),
-		// 			p: pos,
-		// 			t: vec3
-		// 		};
-		// } else if (variable.name == "alpha") {
-		// 	var flt = TFloat;
-		// 	inVar.type = flt;
-		// 	output.name = "pixelColor";
-		// 	output.type = TVec(4, VFloat);
-		// 	finalExpr =
-		// 		{
-		// 			e: TBinop(
-		// 			OpAssign,
-		// 				{
-		// 					e: TSwiz(
-		// 							{
-		// 								e: TVar(output),
-		// 								p: pos,
-		// 								t: flt,
-		// 							},
-		// 							[W]
-		// 						),
-		// 					p:pos,
-		// 					t:flt
-		// 				},
-		// 				{
-		// 					e: TVar(inVar),
-		// 					p: pos,
-		// 					t: flt
-		// 				}
-		// 			),
-		// 			p: pos,
-		// 			t: flt
-		// 		};
-		// }
-		// if (generatePreview && variable.name == "pixelColor") {
-		// 	var outputSelect : TVar = {name: "__sg_PREVIEW_output_select", id: getNewIdFn(), type: TInt, kind: Param, qualifiers: []};
-
-		// 	finalExpr = {
-		// 		e: TIf(
-		// 				{
-		// 					e: TBinop(
-		// 						OpEq,
-		// 						{e:TVar(outputSelect),p:pos, t:TInt},
-		// 						{e:TConst(CInt(0)), p:pos, t:TInt}
-		// 					),
-		// 					p:pos,
-		// 					t:TInt
-		// 				},
-		// 				finalExpr,
-		// 				null
-		// 			),
-		// 		p: pos,
-		// 		t:null
-		// 	};
-
-		// 	inVars.push( {v: outputSelect, internal: true, isDynamic: false});
-		// }
-
-		return {expr: finalExpr, inVars: inVars, outVars:[{v: output, internal: true, isDynamic: false}], externVars: [], inits: []};
-	}
-
-	/*override public function checkValidityInput(key : String, type : hxsl.Ast.Type) : Bool {
-		return ShaderType.checkConversion(type, variable.type);
-	}*/
-
-	// override public function build(key : String) : TExpr {
-	// 	return {
-	// 			p : null,
-	// 			t : TVoid,
-	// 			e : TBinop(OpAssign, {
-	// 				e: TVar(variable),
-	// 				p: null,
-	// 				t: variable.type
-	// 			}, input.getVar(variable.type))
-	// 		};
-
-	// }
-
-	public static var availableOutputs : Map<String, ShaderNode.VariableDecl> = [
-		"_sg_out_color" => {display:"Pixel Color", v:{parent: null,id: 0,kind: Local,name: "_sg_out_color",type: TVec(3, VFloat)}},
-		"_sg_out_alpha" => {display:"Alpha", v:{parent: null,id: 0,kind: Local,name: "_sg_out_alpha",type: TFloat}},
-		"relativePosition" => {display:"Position (Object Space)", vertexOnly: true, v:{parent: null,id: 0,kind: Local,name: "relativePosition",type: TVec(3, VFloat)}},
-		"transformedPosition" => {display:"Position (World Space)", vertexOnly: true, v:{parent: null,id: 0,kind: Local,name: "transformedPosition",type: TVec(3, VFloat)}},
-		"projectedPosition" => {display: "Position (View Space)", vertexOnly: true, v: { parent: null, id: 0, kind: Local, name: "projectedPosition", type: TVec(4, VFloat) }},
-		// Disabled because calculated UV need to be initialized in vertexShader for some reason
-		"calculatedUV" => { display: "UV", v: { parent: null, id: 0, kind: Var, name: "calculatedUV", type: TVec(2, VFloat)}},
-		"transformedNormal" => { display: "Normal (World Space)", vertexOnly: true, v: {parent: null, id: 0, kind: Local, name: "transformedNormal", type: TVec(3, VFloat)}},
-		"metalness" => {display: "Metalness", v: {parent: null,id: 0,kind: Local,name: "metalness",type: TFloat}},
-		"roughness" => {display: "Roughness", v: {parent: null, id: 0, kind: Local, name: "roughness", type: TFloat}},
-		"emissive" => {display: "Emissive", v: {parent: null, id: 0, kind: Local, name: "emissive", type: TFloat}},
-		"occlusion" => {display: "Occlusion", v: {parent: null, id: 0, kind: Local, name: "occlusion", type: TFloat}},
+	public static var availableOutputs : Map<String, {display: String, g: Variables.Global}> = [
+		"_sg_out_color" => {display: "Pixel Color", g:SGPixelColor},
+		"_sg_out_alpha" => {display: "Alpha", g:SGPixelAlpha},
+		"relativePosition" => {display: "Position (Object Space)", g:RelativePosition},
+		"transformedPosition" => {display: "Position (World Space)", g:TransformedPosition},
+		"projectedPosition" => {display: "Position (View Space)", g:ProjectedPosition},
+		"calculatedUV" => { display: "UV", g:CalculatedUV},
+		"transformedNormal" => { display: "Normal (World Space)", g:TransformedNormal},
+
+		"metalness" => {display: "Metalness", g: Metalness},
+		"roughness" => {display: "Roughness", g: Roughness},
+		"emissive" => {display: "Emissive", g: Emissive},
+		"occlusion" => {display: "Occlusion", g: Occlusion},
 	];
 	];
 
 
 	override function loadProperties(props:Dynamic) {
 	override function loadProperties(props:Dynamic) {
@@ -233,6 +129,7 @@ class ShaderOutput extends ShaderNode {
 
 
 		input.on("change", function(e) {
 		input.on("change", function(e) {
 			variable = input.val();
 			variable = input.val();
+			inputs = null;
 			/*var value = input.val();
 			/*var value = input.val();
 			if (value < ShaderNode.availableVariables.length) {
 			if (value < ShaderNode.availableVariables.length) {
 				this.variable = ShaderNode.availableVariables[value];
 				this.variable = ShaderNode.availableVariables[value];

+ 23 - 46
hrt/shgraph/ShaderParam.hx

@@ -11,40 +11,36 @@ class ShaderParam extends ShaderNode {
 	@prop() public var parameterId : Int;
 	@prop() public var parameterId : Int;
 	@prop() public var perInstance : Bool;
 	@prop() public var perInstance : Bool;
 
 
+	override function getOutputs() : Array<ShaderNode.OutputInfo> {
+		var t = switch(variable.type) {
+			case TFloat:
+				SgFloat(1);
+			case TVec(n, _):
+				SgFloat(n);
+			case TSampler(_,_):
+				SgSampler;
+			default:
+				throw "Unhandled var type " + variable.type;
+		}
+		return [{name: "output", type: t}];
+	}
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
+	override function generate(ctx: NodeGenContext) {
+		var v = ctx.getGlobalParam(variable.name, variable.type);
 
 
-		var qual = [];
-		if (this.variable.type.isTexture()) {
-			qual.push(Sampler(this.variable.name));
+		ctx.setOutput(0, v);
+		if (v.t.match(TSampler(_,_))) {
+			var uv = ctx.getGlobalInput(CalculatedUV);
+			var sample = AstTools.makeGlobalCall(Texture, [v, uv], TVec(4, VFloat));
+			ctx.addPreview(sample);
+		}
+		else {
+			ctx.addPreview(v);
 		}
 		}
-
-		//if (this.variable.type != TSampler2D) {
-			var inVar : TVar = {name: this.variable.name, id: getNewIdFn(), type: this.variable.type, kind: Param, qualifiers: qual};
-			var output : TVar = {name: "output", id: getNewIdFn(), type: this.variable.type, kind: Local, qualifiers: []};
-			var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TVar(inVar), p: pos, t: output.type}), p: pos, t: output.type};
-
-			return {expr: finalExpr, inVars: [{v:inVar, internal: true, isDynamic: false}], outVars:[{v:output, internal: false, isDynamic: false}], externVars: [], inits: []};
-		//}
-		//else {
-			//var samplerVar : TVar = {name: this.variable.name, id: getNewIdFn(), type: this.variable.type, kind: Param, qualifiers: qual};
-			//var cuv = ShaderInput.availableInputs.get("calculatedUV");
-			//var uv : TVar = {name: cuv.v.name, id: getNewIdFn(), type: cuv.v.type, kind: cuv.v.kind, qualifiers: []};
-			//var output : TVar = {name: "output", id: getNewIdFn(), type: this.variable.type, kind: Local, qualifiers: []};
-		//	return
-		//}
 	}
 	}
 
 
 	public var variable : TVar;
 	public var variable : TVar;
 
 
-	override public function computeOutputs() {
-		if (variable != null)
-			addOutput("output", variable.type);
-		else
-			removeOutput("output");
-	}
-
 
 
 	override public function loadProperties(props : Dynamic) {
 	override public function loadProperties(props : Dynamic) {
 		parameterId = Reflect.field(props, "parameterId");
 		parameterId = Reflect.field(props, "parameterId");
@@ -60,25 +56,6 @@ class ShaderParam extends ShaderNode {
 		return parameters;
 		return parameters;
 	}
 	}
 
 
-
-	// override public function canHavePreview() {
-	// 	return this.variable.type != TSampler2D;
-	// }
-
-	override public function build(key : String) : TExpr {
-		if (variable != null){
-			if (variable.qualifiers == null)
-				variable.qualifiers = [];
-			if (perInstance)
-				if (!variable.qualifiers.contains(PerInstance(1)))
-					variable.qualifiers.push(PerInstance(1));
-			else
-				if (variable.qualifiers.contains(PerInstance(1)))
-					variable.qualifiers.remove(PerInstance(1));
-		}
-		return null;
-	}
-
 	#if editor
 	#if editor
 	private var parameterName : String;
 	private var parameterName : String;
 	private var eltName : hide.Element;
 	private var eltName : hide.Element;

+ 14 - 18
hrt/shgraph/ShaderParticleInput.hx

@@ -34,25 +34,21 @@ class ShaderParticleInputs extends ShaderNode {
 		return aliases;
 		return aliases;
 	}
 	}
 
 
+	override function getOutputs() {
+		static var outputs : Array<ShaderNode.OutputInfo> = [{name: "output", type: SgFloat(1)}];
+		return outputs;
+	}
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var variable : ShaderNode.VariableDecl = availableInputs.get(this.variable);
-		if (variable == null)
-			throw "Unknown input variable " + this.variable;
-
-		var inVar : TVar = Reflect.copy(variable.v);
-		inVar.id = getNewIdFn();
-		var output : TVar = {name: "output", id: getNewIdFn(), type: variable.v.type, kind: Local, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TVar(inVar), p: pos, t: output.type}), p: pos, t: output.type};
-
-		return {expr: finalExpr, inVars: [{v:inVar, internal: true, isDynamic: false}], outVars:[{v:output, internal: false, isDynamic: false}], externVars: [], inits: []};
+	override function generate(ctx:NodeGenContext) {
+		var global = availableInputs[variable].g;
+		var expr = ctx.getGlobalInput(global);
+		ctx.setOutput(0, expr);
+		ctx.addPreview(expr);
 	}
 	}
 
 
 	override function loadProperties(props:Dynamic) {
 	override function loadProperties(props:Dynamic) {
 		super.loadProperties(props);
 		super.loadProperties(props);
-		var ivar : ShaderNode.VariableDecl = availableInputs.get(this.variable);
+		var ivar = availableInputs.get(this.variable);
 		if (ivar == null) {
 		if (ivar == null) {
 			for (k => v in availableInputs) {
 			for (k => v in availableInputs) {
 				variable = k;
 				variable = k;
@@ -61,10 +57,10 @@ class ShaderParticleInputs extends ShaderNode {
 		}
 		}
 	}
 	}
 
 
-	public static var availableInputs : Map<String, ShaderNode.VariableDecl> = [
-		"life" => {display: "Particle Life", v: { parent: null, id: 0, kind: Local, name: "particleLife", type: TFloat }},
-		"lifetime" => {display: "Particle Life Time", v: { parent: null, id: 0, kind: Local, name: "particleLifeTime", type: TFloat }},
-		"random" => {display: "Particle Random", v: { parent: null, id: 0, kind: Local, name: "particleRandom", type: TFloat }},
+	public static var availableInputs : Map<String, {display: String, g: Variables.Global}> = [
+		"life" => {display: "Particle Life", g: ParticleLife},
+		"lifetime" => {display: "Particle Life Time", g: ParticleLifeTime},
+		"random" => {display: "Particle Random", g: ParticleRandom},
 	];
 	];
 
 
 	#if editor
 	#if editor

+ 23 - 0
hrt/shgraph/Utils.hx

@@ -0,0 +1,23 @@
+package hrt.shgraph;
+
+class MapUtils {
+	public static inline function getOrPut<K,V>(map: Map<K,V>, key: K, def: V) : V {
+		var v = map.get(key);
+		if (v == null) {
+			v = def;
+			map.set(key,v);
+		}
+		return v;
+	}
+}
+
+class ArrayUtils {
+	public static inline function getOrPut<V>(array: Array<V>, pos: Int, def: V) : V {
+		var v = array[pos];
+		if (v == null) {
+			v = def;
+			array[pos] = v;
+		}
+		return v;
+	}
+}

+ 104 - 0
hrt/shgraph/Variables.hx

@@ -0,0 +1,104 @@
+package hrt.shgraph;
+
+enum abstract Global(Int) to Int {
+	var PixelColor;
+	var PixelColorColor;
+	var PixelColorAlpha;
+	var Time;
+	var PixelSize;
+	var Global;
+
+	var CalculatedUV;
+
+	var Input;
+	var UV;
+
+	var RelativePosition;
+	var TransformedPosition;
+	var ProjectedPosition;
+
+	var Normal;
+	var FakeNormal;	// used as replacement for normal in previews
+	var TransformedNormal;
+
+	var Depth;
+	var Metalness;
+	var Roughness;
+	var Emissive;
+	var Occlusion;
+
+	var PreviewSelect;
+	var PreviewColor;
+
+	// Particles
+	var ParticleLife;
+	var ParticleLifeTime;
+	var ParticleRandom;
+
+	// Internal Shadergraph vars
+	var SGPixelColor;
+	var SGPixelAlpha;
+}
+
+enum VariableKind {
+	KVar(kind: hxsl.Ast.VarKind, ?parent: Global, ?def: Dynamic);
+	KSwizzle(global: Global, swiz: Array<hxsl.Ast.Component>);
+}
+
+typedef GlobalInfo = {type: hxsl.Ast.Type, name: String, varkind: VariableKind};
+class Variables {
+	public static var previewSelectName = "previewSelect_SG";
+
+	public static var Globals : Array<GlobalInfo> = {
+		var g : Array<GlobalInfo> = [];
+
+		g[PixelColor] 			= {type: TVec(4, VFloat), 	name: "pixelColor", 	varkind: KVar(Local)};
+
+		g[CalculatedUV] 		= {type: TVec(2, VFloat), 	name: "calculatedUV", varkind: KVar(Var)};
+
+		g[Time] 				= {type: TFloat, 	name: "time", 			varkind: KVar(Local, Global)};
+		g[PixelSize]			= {type: TVec(2, VFloat), 	name: "pixelSize", 		varkind: KVar(Local, Global)};
+		g[Global] 				= {type: TVoid, 	name: "global", 		varkind: KVar(Global)};
+
+		g[Input]			= {type: TVoid, name: "input", varkind: KVar(Input)};
+		g[UV] 					= {type: TVec(2, VFloat), 	name: "uv", varkind: KVar(Input, Input)};
+		g[RelativePosition]			= {type: TVec(3, VFloat), name: "relativePosition", varkind: KVar(Local)};
+		g[TransformedPosition]		= {type: TVec(3, VFloat), name: "transformedPosition", varkind: KVar(Local)};
+		g[ProjectedPosition]		= {type: TVec(4, VFloat), name: "projectedPosition", varkind: KVar(Local)};
+
+		g[Normal] 				= {type: TVec(3, VFloat), name: "normal", varkind: KVar(Input, Input)};
+		g[FakeNormal] 			= {type: TVec(3, VFloat), name: "fakeNormal", varkind: KVar(Local)};
+		g[TransformedNormal] 	= {type: TVec(3, VFloat), name: "transformedNormal", varkind: KVar(Local)};
+
+		g[Depth] 				= {type: TFloat, name: "depth", varkind: KVar(Local)};
+		g[Metalness] 				= {type: TFloat, name: "metalness", varkind: KVar(Local)};
+		g[Roughness] 				= {type: TFloat, name: "depth", varkind: KVar(Local)};
+		g[Emissive] 				= {type: TFloat, name: "depth", varkind: KVar(Local)};
+		g[Occlusion]			= {type: TFloat, name: "occlusion", varkind: KVar(Local)};
+
+		g[PreviewSelect]		= {type: TInt, 		name: previewSelectName, varkind: KVar(Param,null, -1)};
+		g[PreviewColor] 		= {type: TVec(4,VFloat), name: "_previewColor", varkind: KVar(Local)};
+
+
+		g[SGPixelColor] 		= {type: TVec(3, VFloat), 	name: "_sg_out_color", varkind: KSwizzle(PixelColor, [X,Y,Z])};
+		g[SGPixelAlpha] 		= {type: TFloat, 	name: "_sg_out_alpha", 		varkind: KSwizzle(PixelColor, [W])};
+
+		g[ParticleLife]			= {type: TFloat, name: "particleLife", varkind: KVar(Local)};
+		g[ParticleLifeTime]		= {type: TFloat, name: "particleLifeTime", varkind: KVar(Local)};
+		g[ParticleRandom] 		= {type: TFloat, name: "particleRandom", varkind: KVar(Local)};
+
+
+
+		g;
+	};
+
+	public static function getGlobalNameMap() {
+		static var GlobalNameMap : Map<String, Global>;
+		if (GlobalNameMap == null)
+			GlobalNameMap = [
+				for (id => g in Globals) if (g != null) g.name => (cast id:Global)
+			];
+		return GlobalNameMap;
+	}
+
+}

+ 7 - 0
hrt/shgraph/import.hx

@@ -0,0 +1,7 @@
+package hrt.shgraph;
+
+#if !macro
+using hxsl.Ast;
+import hrt.shgraph.AstTools.*;
+import hrt.shgraph.ShaderGraph;
+#end

+ 8 - 6
hrt/shgraph/nodes/BoolConst.hx

@@ -12,13 +12,15 @@ class BoolConst extends ShaderConst {
 
 
 	@prop() var value : Bool = true;
 	@prop() var value : Bool = true;
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var output : TVar = {name: "output", id: getNewIdFn(), type: TBool, kind: Local, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TConst(CBool(value)), p: pos, t: output.type}), p: pos, t: output.type};
+	override function getOutputs() {
+		static var output : Array<ShaderNode.OutputInfo> = [{name: "output", type: SgBool}];
+		return output;
+	}
 
 
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false,  isDynamic: false}], externVars: [], inits: []};
+	override function generate(ctx: NodeGenContext) : Void {
+		var output = makeExpr(TConst(CBool(value)), TBool);
+		ctx.setOutput(0, output);
+		ctx.addPreview(output);
 	}
 	}
 
 
 	#if editor
 	#if editor

+ 8 - 107
hrt/shgraph/nodes/Color.hx

@@ -14,115 +14,16 @@ class Color extends ShaderConst {
 	@prop() var b : Float = 0;
 	@prop() var b : Float = 0;
 	@prop() var a : Float = 1;
 	@prop() var a : Float = 1;
 
 
-	// override public function computeOutputs() {
-	// 	addOutput("output", TVec(4, VFloat));
-	// }
-
-	// override public function getOutputTExpr(key : String) : TExpr {
-	// 	return {
-	// 		e: TVar(output),
-	// 		p: null,
-	// 		t: TVec(4, VFloat)
-	// 	};
-	// }
-
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var output : TVar = {name: "output", id:getNewIdFn(), type: TVec(4, VFloat), kind: Local, qualifiers: []};
-		var finalExpr : TExpr =
-		{ e: TBinop(OpAssign, {
-				e: TVar(output),
-				p: null,
-				t: output.type
-			}, {
-				e: TCall({
-					e: TGlobal(Vec4),
-					p: null,
-					t: TFun([
-						{
-							ret: output.type,
-							args: [
-							{ name: "r", type : TFloat },
-							{ name: "g", type : TFloat },
-							{ name: "b", type : TFloat },
-							{ name: "a", type : TFloat }]
-						}
-					])
-				}, [{
-						e: TConst(CFloat(r)),
-						p: null,
-						t: TFloat
-					},
-					{
-						e: TConst(CFloat(g)),
-						p: null,
-						t: TFloat
-					},
-					{
-						e: TConst(CFloat(b)),
-						p: null,
-						t: TFloat
-					},{
-						e: TConst(CFloat(a)),
-						p: null,
-						t: TFloat
-					}]),
-				p: null,
-				t: output.type
-			}),
-			p: null,
-			t: output.type
-		};
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false, isDynamic: false}], externVars: [], inits: []};
+	override function getOutputs() {
+		static var output : Array<ShaderNode.OutputInfo> = [{name: "output", type: SgFloat(4)}];
+		return output;
 	}
 	}
 
 
-	// override public function build(key : String) : TExpr {
-
-	// 	return { e: TBinop(OpAssign, {
-	// 					e: TVar(output),
-	// 					p: null,
-	// 					t: output.type
-	// 				}, {
-	// 					e: TCall({
-	// 						e: TGlobal(Vec4),
-	// 						p: null,
-	// 						t: TFun([
-	// 							{
-	// 								ret: output.type,
-	// 								args: [
-	// 								{ name: "r", type : TFloat },
-	// 								{ name: "g", type : TFloat },
-	// 								{ name: "b", type : TFloat },
-	// 								{ name: "a", type : TFloat }]
-	// 							}
-	// 						])
-	// 					}, [{
-	// 							e: TConst(CFloat(r)),
-	// 							p: null,
-	// 							t: TFloat
-	// 						},
-	// 						{
-	// 							e: TConst(CFloat(g)),
-	// 							p: null,
-	// 							t: TFloat
-	// 						},
-	// 						{
-	// 							e: TConst(CFloat(b)),
-	// 							p: null,
-	// 							t: TFloat
-	// 						},{
-	// 							e: TConst(CFloat(a)),
-	// 							p: null,
-	// 							t: TFloat
-	// 						}]),
-	// 					p: null,
-	// 					t: output.type
-	// 				}),
-	// 				p: null,
-	// 				t: output.type
-	// 			};
-	// }
+	override function generate(ctx: NodeGenContext) {
+		var expr = makeVec([r,g,b,a]);
+		ctx.setOutput(0, expr);
+		ctx.addPreview(expr);
+	}
 
 
 	#if editor
 	#if editor
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {

+ 1 - 9
hrt/shgraph/nodes/Comment.hx

@@ -10,15 +10,7 @@ class Comment extends ShaderNode {
 	@prop() public var width : Int = 200;
 	@prop() public var width : Int = 200;
 	@prop() public var height : Int = 200;
 	@prop() public var height : Int = 200;
 
 
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
-		return {
-			expr: {e:TBlock([]), t:TVoid, p: null},
-			inVars: [],
-			outVars: [],
-			inits: [],
-			externVars: [],
-		};
-	}
+	override function generate(ctx: NodeGenContext) {}
 
 
 	override function canHavePreview():Bool {
 	override function canHavePreview():Bool {
 		return false;
 		return false;

+ 18 - 17
hrt/shgraph/nodes/Cond.hx

@@ -9,26 +9,27 @@ import hrt.shgraph.AstTools.*;
 @group("Condition")
 @group("Condition")
 class Cond extends ShaderNode {
 class Cond extends ShaderNode {
 
 
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
-
-		var a : TVar = {name : "a", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
-		var b : TVar = {name : "b", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
+	override function getOutputs() {
+		static var output : Array<ShaderNode.OutputInfo> = [{name: "output", type: SgBool}];
+		return output;
+	}
 
 
-		var out : TVar = {name: "out", id: getNewIdFn(), type: TBool, kind: Local, qualifiers: []};
+	override function getInputs() {
+		static var inputs : Array<ShaderNode.InputInfo> =
+			[
+				{name: "a", type: SgFloat(1)},
+				{name: "b", type: SgFloat(1)},
+			];
+		return inputs;
+	}
 
 
-		var cond = makeExpr(TBinop(condition, makeVar(a), makeVar(b)), TBool);
-		var expr = makeAssign(makeVar(out), cond);
-		return {
-			expr: expr,
-			inVars: [{v:a, internal: false, defVal: Const(0.0), isDynamic: false}, {v:b, internal: false, defVal: Const(0.0), isDynamic: false}],
-			outVars:[{v:out, internal: false, isDynamic: false}],
-			inits: [],
-			externVars: []
-		};
-	};
+	override function generate(ctx: NodeGenContext) {
+		var a = ctx.getInput(0, Const(0.0));
+		var b = ctx.getInput(1, Const(0.0));
 
 
-	override function canHavePreview():Bool {
-		return false;
+		var expr = makeExpr(TBinop(condition, a, b), TBool);
+		ctx.setOutput(0, expr);
+		ctx.addPreview(expr);
 	}
 	}
 
 
 	// @input("Left") var leftVar = SType.Number;
 	// @input("Left") var leftVar = SType.Number;

+ 8 - 23
hrt/shgraph/nodes/FloatConst.hx

@@ -9,34 +9,19 @@ using hxsl.Ast;
 @noheader()
 @noheader()
 class FloatConst extends ShaderConst {
 class FloatConst extends ShaderConst {
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var output : TVar = {name: "output", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TConst(CFloat(value)), p: pos, t: output.type}), p: pos, t: output.type};
+	override function getOutputs() {
+		static var output : Array<ShaderNode.OutputInfo> = [{name: "output", type: SgFloat(1)}];
+		return output;
+	}
 
 
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false, isDynamic: false}], externVars: [], inits: []};
+	override function generate(ctx: NodeGenContext) : Void {
+		var output = makeExpr(TConst(CFloat(value)), TFloat);
+		ctx.setOutput(0, output);
+		ctx.addPreview(output);
 	}
 	}
 
 
 	@prop() var value : Float = 0.;
 	@prop() var value : Float = 0.;
 
 
-	// public function new(?value : Float) {
-	// 	if (value != null)
-	// 		this.value = value;
-	// }
-
-	// override public function getOutputTExpr(key : String) : TExpr {
-	// 	return {
-	// 				e: TConst(CFloat(value)),
-	// 				p: null,
-	// 				t: TFloat
-	// 			};
-	// }
-
-	// override public function build(key : String) : TExpr {
-	// 	return null;
-	// }
-
 	#if editor
 	#if editor
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
 	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
 		var elements = super.getPropertiesHTML(width);
 		var elements = super.getPropertiesHTML(width);

+ 24 - 62
hrt/shgraph/nodes/IfCondition.hx

@@ -8,72 +8,34 @@ import hrt.shgraph.AstTools.*;
 @group("Condition")
 @group("Condition")
 class IfCondition extends ShaderNode {
 class IfCondition extends ShaderNode {
 
 
-	// @input("Condition") var condition = SType.Bool;
-	// @input("True") var trueVar = SType.Variant;
-	// @input("False") var falseVar = SType.Variant;
-
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
-
-		var condition : TVar = {name : "condition", id: getNewIdFn(), type: TBool, kind: Local, qualifiers: []};
-
-		var vTrue : TVar = {name : "true", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
-		var vFalse : TVar = {name : "false", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
-
-		var out : TVar = {name : "out", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
-
-		var expr = makeIf(makeVar(condition), makeAssign(makeVar(out), makeVar(vTrue)), makeAssign(makeVar(out), makeVar(vFalse)));
-
-		return {
-			expr: expr,
-			inVars: [
-				{v:condition, internal: false, isDynamic: false, defVal: ConstBool(false)},
-				{v:vTrue, internal: false, defVal: Const(0.0), isDynamic: true}, {v:vFalse, internal: false, defVal: Const(0.0), isDynamic: true}],
-			outVars:[{v:out, internal: false, isDynamic: true}],
-			inits: [],
-			externVars: []
-		};
+	override function getOutputs() {
+		static var output : Array<ShaderNode.OutputInfo> = [{name: "output", type: SgGeneric(0, ConstraintFloat)}];
+		return output;
 	}
 	}
 
 
+	override function getInputs() {
+		static var inputs : Array<ShaderNode.InputInfo> =
+		[
+			{name: "condition", type: SgBool},
+			{name: "true", type: SgGeneric(0, ConstraintFloat)},
+			{name: "false", type: SgGeneric(0, ConstraintFloat)},
+		];
+		return inputs;
+	}
 
 
+	override function generate(ctx: NodeGenContext) {
+		var cond = ctx.getInput(0, ConstBool(true));
+		var vTrue = ctx.getInput(1, Const(1.0));
+		var vFalse = ctx.getInput(2, Const(0.0));
 
 
-	// override public function checkValidityInput(key : String, type : ShaderType.SType) : Bool {
-
-	// 	if (key == "trueVar" && falseVar != null && !falseVar.isEmpty())
-	// 		return ShaderType.checkCompatibilities(type, ShaderType.getSType(falseVar.getType()));
-
-	// 	if (key == "falseVar" && trueVar != null && !trueVar.isEmpty())
-	// 		return ShaderType.checkCompatibilities(type, ShaderType.getSType(trueVar.getType()));
-
-	// 	return true;
-	// }
-
-	// override public function computeOutputs() {
-	// 	if (trueVar != null && !trueVar.isEmpty() && falseVar != null && !falseVar.isEmpty())
-	// 		addOutput("output", trueVar.getVar(falseVar.getType()).t);
-	// 	else if (trueVar != null && !trueVar.isEmpty())
-	// 		addOutput("output", trueVar.getType());
-	// 	else if (falseVar != null && !falseVar.isEmpty())
-	// 		addOutput("output", falseVar.getType());
-	// 	else
-	// 		removeOutput("output");
-	// }
+		var outType = ctx.getType(SgGeneric(0, ConstraintFloat));
 
 
-	// // override public function build(key : String) : TExpr {
-	// // 	return {
-	// // 		p : null,
-	// // 		t: output.type,
-	// // 		e : TBinop(OpAssign, {
-	// // 				e: TVar(output),
-	// // 				p: null,
-	// // 				t: output.type
-	// // 			}, {
-	// // 			e: TIf( condition.getVar(),
-	// // 					trueVar.getVar(falseVar.getType()),
-	// // 					falseVar.getVar(trueVar.getType())),
-	// // 			p: null,
-	// // 			t: output.type
-	// // 		})
-	// // 	};
-	// // }
+		var test = makeIf(cond, vTrue, vFalse, null, outType);
 
 
+		var v : TVar = {name: "output", id: Tools.allocVarId(), type: outType, kind: Local};
+		var tmpvar = makeVarDecl(v, test);
+		ctx.addExpr(tmpvar);
+		ctx.setOutput(0, makeVar(v));
+		ctx.addPreview(makeVar(v));
+	}
 }
 }

+ 7 - 156
hrt/shgraph/nodes/Preview.hx

@@ -35,162 +35,13 @@ class Preview extends ShaderNode {
 
 
 	public var previewID : Int = 1;
 	public var previewID : Int = 1;
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var inVar : TVar = {name: "input", id: getNewIdFn(), type: TVec(4, VFloat), kind: Param, qualifiers: []};
-		var output : TVar = {name: "_sg_tmp", id: getNewIdFn(), type: TVec(4, VFloat), kind: Local, qualifiers: []};
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p:pos, t:output.type}, {e: TVar(inVar), p: pos, t: output.type}), p: pos, t: output.type};
-
-		return {expr: finalExpr, inVars: [{v: inVar, internal: false, isDynamic: false}], outVars:[{v: output, internal: true, isDynamic: false}], externVars: [], inits: []};
+	override function getInputs() {
+		static var inputs : Array<ShaderNode.InputInfo> = [{name: "input", type: SgFloat(4)}];
+		return inputs;
 	}
 	}
 
 
-	// @input("Input") var input = SType.Vec4;
-
-	// public var variable : TVar;
-
-	// override public function build(key : String) : TExpr {
-
-	// 	return {
-	// 			p : null,
-	// 			t : TVoid,
-	// 			e : TBinop(OpAssign, {
-	// 				e: TVar(variable),
-	// 				p: null,
-	// 				t: variable.type
-	// 			}, input.getVar(variable.type))
-	// 		};
-
-	// }
-
-	// #if editor
-	// var nodePreview : js.jquery.JQuery;
-	// var element : js.jquery.JQuery;
-	// public var shaderGraph: ShaderGraph;
-	// var cube : Mesh;
-	// var scene : hide.comp.Scene;
-	// var shader : hxsl.DynamicShader;
-	// public var shaderGraphDef : hrt.prefab.ContextShared.ShaderDef = null;
-
-	// var alphaPreview : AlphaPreview = null;
-	// var inited = false;
-	// public var config : hide.Config;
-
-	// override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
-	// 	var elements = super.getPropertiesHTML(width);
-
-	// 	if (element == null) {
-	// 		element = new hide.Element('<div style="width: 100px; height: 100px"><div class="preview-parent" top="-10px" ><div class="node-preview" style="height: 100px" ></div></div></div>');
-	// 		nodePreview = element.find(".node-preview");
-
-	// 		scene = new hide.comp.Scene(config, null, nodePreview);
-
-	// 		scene.onReady = function() {
-	// 			var prim = new h3d.prim.Cube();
-	// 			prim.addUVs();
-	// 			prim.addNormals();
-	// 			cube = new Mesh(prim, scene.s3d);
-	// 			scene.s3d.camera.pos = new h3d.Vector(0.5, 3.4, 0.5);
-	// 			scene.s3d.camera.target = new h3d.Vector(0.5, 0.5, 0.5);
-	// 			var light = new h3d.scene.pbr.DirLight(scene.s3d.camera.target.sub(scene.s3d.camera.pos), scene.s3d);
-	// 			light.setPosition(scene.s3d.camera.pos.x, scene.s3d.camera.pos.y, scene.s3d.camera.pos.z);
-	// 			scene.s3d.camera.zoom = 1;
-	// 			scene.init();
-	// 			onMove();
-	// 			inited = true;
-
-	// 			update();
-	// 		};
-	// 	}
-
-	// 	elements.push(element);
-	// 	return elements;
-	// }
-
-	// public function onMove(?x : Float, ?y : Float, zoom : Float = 1.) {
-	// 	// var top : Float;
-	// 	// var left : Float;
-	// 	// var parent = nodePreview.parent();
-	// 	// if (x != null && y != null) {
-	// 	// 	left = x;
-	// 	// 	top = y;
-	// 	// } else {
-	// 	// 	var offsetWindow = nodePreview.closest(".heaps-scene").offset();
-	// 	// 	var offset = nodePreview.closest("foreignObject").offset();
-	// 	// 	if (offsetWindow == null || offset == null) return;
-	// 	// 	top = offset.top - offsetWindow.top - 32;
-	// 	// 	left = offset.left - offsetWindow.left;
-	// 	// }
-	// 	// nodePreview.closest(".prop-group").attr("transform", 'translate(0, -5)');
-	// 	nodePreview.closest(".properties-group").children().first().css("fill", "#000");
-	// 	// parent.css("top", top/zoom + 17);
-	// 	// parent.css("left", left/zoom);
-	// 	// parent.css("zoom", zoom);
-	// }
-
-	// function onResize() {
-	// 	if( cube == null ) return;
-	// }
-
-
-	// public function update() {
-	// 	if (!inited)
-	// 		return;
-	// 	if (shader != null) {
-	// 		for (m in cube.getMaterials()) {
-	// 			m.mainPass.removeShader(shader);
-	// 		}
-	// 		shader = null;
-	// 	}
-
-	// 	if (@:privateAccess scene.window == null)
-	// 		return;
-	// 	scene.setCurrent();
-
-	// 	if (alphaPreview == null)
-	// 		alphaPreview = new AlphaPreview();
-
-	// 	if (shaderGraphDef != null) {
-	// 		shader = new hxsl.DynamicShader(shaderGraphDef.shader);
-	// 		for (init in shaderGraphDef.inits) {
-	// 			setParamValue(init.variable, init.value, shader);
-	// 		}
-	// 		var select = shaderGraphDef.inits.find((v) -> v.variable.name == "__sg_output_select");
-	// 		if (select != null) {
-	// 			setParamValue(select.variable, previewID, shader);
-	// 		}
-	// 		for (m in cube.getMaterials()) {
-	// 			m.mainPass.addShader(shader);
-	// 			//m.mainPass.addShader(alphaPreview);
-	// 		}
-	// 	}
-	// }
-
-	// public function setParamValueByName(varName : String, value : Dynamic) {
-	// 	if (shaderGraphDef == null) return;
-	// 	for (init in shaderGraphDef.inits) {
-	// 		if (init.variable.name == varName) {
-	// 			setParamValue(init.variable, value, shader);
-	// 			return;
-	// 		}
-	// 	}
-	// }
-
-	// public function setParamValue(variable : TVar, value : Dynamic, shader : hxsl.DynamicShader) {
-	// 	scene.setCurrent();
-	// 	try {
-	// 		switch (variable.type) {
-	// 			case TSampler2D:
-	// 				shader.setParamValue(variable, scene.loadTexture("", value));
-	// 			case TVec(size, _):
-	// 				shader.setParamValue(variable, h3d.Vector.fromArray(value));
-	// 			default:
-	// 				shader.setParamValue(variable, value);
-	// 		}
-	// 	} catch (e : Dynamic) {
-	// 		// variable not used
-	// 	}
-	// }
-
-	// #end
+	override function generate(ctx:NodeGenContext) {
+		var input = ctx.getInput(0, Const(0.0));
+		ctx.addPreview(input);
+	}
 }
 }

+ 38 - 208
hrt/shgraph/nodes/SubGraph.hx

@@ -6,6 +6,8 @@ using hxsl.Ast;
 @description("Include a subgraph")
 @description("Include a subgraph")
 @width(200)
 @width(200)
 @alwaysshowinputs()
 @alwaysshowinputs()
+@:access(hrt.shgraph.NodeGenContext)
+@:access(hrt.shgraph.NodeGenContextSubGraph)
 class SubGraph extends ShaderNode {
 class SubGraph extends ShaderNode {
 
 
 	@prop() public var pathShaderGraph : String;
 	@prop() public var pathShaderGraph : String;
@@ -14,230 +16,58 @@ class SubGraph extends ShaderNode {
 		pathShaderGraph = path;
 		pathShaderGraph = path;
 	}
 	}
 
 
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override public function generate(ctx: NodeGenContext) {
 		#if !editor
 		#if !editor
 		var shader = cast hxd.res.Loader.currentInstance.load(pathShaderGraph).toPrefab().load();
 		var shader = cast hxd.res.Loader.currentInstance.load(pathShaderGraph).toPrefab().load();
 		#else
 		#else
 		var shader = cast hide.Ide.inst.loadPrefab(pathShaderGraph);
 		var shader = cast hide.Ide.inst.loadPrefab(pathShaderGraph);
 		#end
 		#end
-		var gen = shader.getGraph(domain).generate2(false, getNewIdFn);
+		var graph = shader.getGraph(ctx.domain);
 
 
-		// for (tvar in gen.externVars) {
-		// 	if (tvar.qualifiers != null) {
-		// 		if (tvar.qualifiers.contains(SgInput)) {
-		// 			gen.inVars.push(tvar);
-		// 		}
-		// 		if (tvar.qualifiers.contains(SgOutput)) {
-		// 			gen.outVars.push(tvar);
-		// 		}
-		// 	}
-		// }
-
-		return gen;
+		var genCtx = new ShaderGraphGenContext(graph, false);
+		genCtx.generate(new NodeGenContext.NodeGenContextSubGraph(ctx));
 	}
 	}
 
 
-	var inputInfoKeys : Array<String> = [];
-	var outputInfoKeys : Array<String> = [];
-	var parameters : Array<ShaderGraph.Parameter> = [];
-	var propertiesSubGraph : Map<Int, Dynamic>;
-
-	public var subShaderGraph : ShaderGraph;
-
-	public var varsSubGraph : Array<TVar> = [];
-
-	// public function loadGraphShader() {
-	// 	if (this.pathShaderGraph != null) {
-	// 		subShaderGraph = new ShaderGraph(pathShaderGraph);
-	// 		inputsInfo = new Map<String, ShaderNode.InputInfo>();
-	// 		inputInfoKeys = [];
-	// 		var paramInfoKeys = [];
-	// 		outputsInfo = new Map<String, ShaderNode.OutputInfo>();
-	// 		outputInfoKeys = [];
-	// 		parameters = [];
-	// 		propertiesSubGraph = new Map<Int, Dynamic>();
-	// 		var prefixSubGraph = "shgraph_" + id + "_";
-
-	// 		for (node in subShaderGraph.getNodes()) {
-	// 			switch (node.type.split(".").pop()) {
-	// 				case "ShaderParam": // params become inputs
-	// 					var shaderParam = Std.downcast(node.instance, ShaderParam);
-	// 					var paramName = subShaderGraph.getParameter(shaderParam.parameterId).name;
-	// 					var paramId = "param_" + shaderParam.parameterId;
-	// 					var paramInfo = inputsInfo.get(prefixSubGraph+paramId);
-	// 					var ids = [];
-	// 					if (paramInfo != null && paramInfo.ids != null)
-	// 						ids = paramInfo.ids;
-	// 					ids.push(node.id);
-	// 					if (!inputsInfo.exists(prefixSubGraph + paramId)) {
-	// 						paramInfoKeys.push(prefixSubGraph+paramId);
-	// 					}
-	// 					inputsInfo.set(prefixSubGraph+paramId, { name : paramName , type: ShaderType.getSType(shaderParam.variable.type), hasProperty: false, isRequired : false, ids : ids, index :  subShaderGraph.getParameter(shaderParam.parameterId).index});
-	// 				case "ShaderInput":
-	// 					var shaderInput = Std.downcast(node.instance, ShaderInput);
-	// 					var inputId = "input_" + shaderInput.variable.name;
-	// 					var inputInfo = inputsInfo.get(prefixSubGraph+inputId);
-	// 					var ids = [];
-	// 					if (inputInfo != null && inputInfo.ids != null)
-	// 						ids = inputInfo.ids;
-	// 					ids.push(node.id);
-	// 					if (!inputsInfo.exists(prefixSubGraph+inputId)) {
-	// 						inputInfoKeys.push(prefixSubGraph+inputId);
-	// 					}
-	// 					inputsInfo.set(prefixSubGraph+inputId, { name : "*" + shaderInput.variable.name , type: ShaderType.getSType(shaderInput.variable.type), hasProperty: false, isRequired : false, ids : ids });
-	// 				case "ShaderGlobalInput":
-	// 					var shaderInput = Std.downcast(node.instance, ShaderGlobalInput);
-	// 					var inputId = "globalInput_" + shaderInput.variable.name;
-	// 					var inputInfo = inputsInfo.get(prefixSubGraph+inputId);
-	// 					var ids = [];
-	// 					if (inputInfo != null && inputInfo.ids != null)
-	// 						ids = inputInfo.ids;
-	// 					ids.push(node.id);
-	// 					if (!inputsInfo.exists(prefixSubGraph+inputId)) {
-	// 						inputInfoKeys.push(prefixSubGraph+inputId);
-	// 					}
-	// 					inputsInfo.set(prefixSubGraph+inputId, { name : "*" + shaderInput.variable.name , type: ShaderType.getSType(shaderInput.variable.type), hasProperty: false, isRequired : false, ids : ids });
-	// 				case "ShaderCameraInput":
-	// 					var shaderInput = Std.downcast(node.instance, ShaderCameraInput);
-	// 					var inputId = "cameraInput_" + shaderInput.variable.name;
-	// 					var inputInfo = inputsInfo.get(prefixSubGraph+inputId);
-	// 					var ids = [];
-	// 					if (inputInfo != null && inputInfo.ids != null)
-	// 						ids = inputInfo.ids;
-	// 					ids.push(node.id);
-	// 					if (!inputsInfo.exists(prefixSubGraph+inputId)) {
-	// 						inputInfoKeys.push(prefixSubGraph+inputId);
-	// 					}
-	// 					inputsInfo.set(prefixSubGraph+inputId, { name : "*" + shaderInput.variable.name , type: ShaderType.getSType(shaderInput.variable.type), hasProperty: false, isRequired : false, ids : ids });
-	// 				case "ShaderOutput":
-	// 					var shaderOutput = Std.downcast(node.instance, ShaderOutput);
-	// 					var prefix = shaderOutput.variable.kind == Local ? "" : "*";
-
-	// 					outputsInfo.set(prefixSubGraph+node.id, { name : prefix + shaderOutput.variable.name , type: ShaderType.getSType(shaderOutput.variable.type), id : node.id });
-	// 					outputInfoKeys.push(prefixSubGraph+node.id);
-
-	// 					addOutput(prefixSubGraph+node.id, shaderOutput.variable.type);
-	// 				default:
-	// 					var shaderConst = Std.downcast(node.instance, ShaderConst);
-	// 					if (shaderConst != null) { // input static become properties
-	// 						if (shaderConst.name.length == 0) continue;
-	// 						if (Std.isOfType(shaderConst, BoolConst)) {
-	// 							parameters.push({ name : shaderConst.name, type : TBool, defaultValue : null, id : shaderConst.id, index : parameters.length });
-	// 						} else if (Std.isOfType(shaderConst, FloatConst)) {
-	// 							parameters.push({ name : shaderConst.name, type : TFloat, defaultValue : null, id : shaderConst.id, index : parameters.length });
-	// 						} else if (Std.isOfType(shaderConst, Color)) {
-	// 							parameters.push({ name : shaderConst.name, type : TVec(4, VFloat), defaultValue : null, id : shaderConst.id, index : parameters.length });
-	// 						}
-	// 					}
-	// 			}
-	// 		}
-	// 		paramInfoKeys.sort((x,y) -> Reflect.compare(inputsInfo[x].index, inputsInfo[y].index));
-	// 		inputInfoKeys = paramInfoKeys.concat(inputInfoKeys);
-	// 	}
-	// }
-
-	// override public function getOutputInfo(key : String) : ShaderNode.OutputInfo {
-	// 	return outputsInfo.get(key);
-	// }
-
-	// override public function getOutputInfoKeys() : Array<String> {
-	// 	return outputInfoKeys;
-	// }
-
-	// override public function loadProperties(props : Dynamic) {
-	// 	this.pathShaderGraph = Reflect.field(props, "pathShaderGraph");
-	// 	loadGraphShader();
-
-	// 	var parametersValues : Array<hrt.shgraph.ShaderGraph.Parameter> = Reflect.field(props, "parametersValues");
-	// 	if (parametersValues == null) return;
-	// 	var index = 0;
-	// 	for (p in this.parameters) {
-	// 		if (parametersValues.length <= index) break;
-	// 		if (p.id != parametersValues[index].id) {
-	// 			continue;
-	// 		}
-	// 		p.defaultValue = parametersValues[index].defaultValue;
-	// 		index++;
-	// 	}
-	// }
-
-	// override public function saveProperties() : Dynamic {
-
-	// 	var parametersValues = [];
-	// 	for (p in this.parameters) {
-	// 		if (p.defaultValue != null) {
-	// 			parametersValues.push({id: p.id, defaultValue : p.defaultValue});
-	// 		}
-	// 	}
+	override public function getInputs() : Array<ShaderNode.InputInfo> {
+		#if !editor
+		var shader = cast hxd.res.Loader.currentInstance.load(pathShaderGraph).toPrefab().load();
+		#else
+		var shader = cast hide.Ide.inst.loadPrefab(pathShaderGraph);
+		#end
+		var graph = shader.getGraph(hrt.shgraph.Domain.Fragment);
 
 
-	// 	var properties = {
-	// 		pathShaderGraph: this.pathShaderGraph,
-	// 		parametersValues : parametersValues
-	// 	};
+		var genCtx = new ShaderGraphGenContext(graph, false);
+		var nodeGenCtx = new NodeGenContext.NodeGenContextSubGraph(null);
+		genCtx.generate(nodeGenCtx);
+		var inputs: Array<ShaderNode.InputInfo> = [];
 
 
-	// 	return properties;
-	// }
+		for (name => info in nodeGenCtx.globalInVars) {
+			var global = nodeGenCtx.globalVars.get(name);
+			var t = typeToSgType(global.v.type);
+			inputs[info.id] = {name: name, type: hrt.shgraph.ShaderGraph.typeToSgType(info.type)};
+		}
 
 
-	#if editor
-	override public function getPropertiesHTML(width : Float) : Array<hide.Element> {
-		var elements = super.getPropertiesHTML(width);
-		for (p in parameters) {
-			var element = new hide.Element('<div class="propertySubShader" style="width: 200px;"></div>');
-			element.on("mousedown", function(e) {
-				e.stopPropagation();
-			});
-			switch (p.type) {
-				case TBool:
-					new hide.Element('<span>${p.name}</span>').appendTo(element);
-					var inputBool = new hide.Element('<input type="checkbox" id="value" />').appendTo(element);
-					inputBool.on("change", function(e) {
-						p.defaultValue = (inputBool.is(":checked")) ? true : false;
-					});
-					if (p.defaultValue) {
-						inputBool.prop("checked", true);
-					}
-					element.css("height", 20);
-				case TFloat:
-					new hide.Element('<span>${p.name}</span>').appendTo(element);
-					var parentRange = new hide.Element('<input type="range" min="-1" max="1" value="" />').appendTo(element);
-					var range = new hide.comp.Range(null, parentRange);
-					var rangeInput = @:privateAccess range.f;
-					rangeInput.on("mousedown", function(e) {
-						e.stopPropagation();
-					});
-					rangeInput.on("mouseup", function(e) {
-						e.stopPropagation();
-					});
-					parentRange.parent().css("width", 50);
-					if (p.defaultValue != null) {
-						range.value = p.defaultValue;
-					}
-					range.onChange = function(moving) {
-						p.defaultValue = range.value;
-					};
-					element.css("height", 40);
-				case TVec(4, VFloat):
-					new hide.Element('<span>${p.name}</span>').appendTo(element);
-					var inputColor = new hide.comp.ColorPicker.ColorBox(null, element, true, true);
+		return inputs;
+	}
 
 
-					if (p.defaultValue != null) {
-						var start = h3d.Vector.fromArray([p.defaultValue.x, p.defaultValue.y, p.defaultValue.z, p.defaultValue.w]);
-						inputColor.value = start.toColor();
-					}
+	override public function getOutputs() : Array<ShaderNode.InputInfo> {
+		#if !editor
+		var shader = cast hxd.res.Loader.currentInstance.load(pathShaderGraph).toPrefab().load();
+		#else
+		var shader = cast hide.Ide.inst.loadPrefab(pathShaderGraph);
+		#end
+		var graph = shader.getGraph(hrt.shgraph.Domain.Fragment);
 
 
-					inputColor.onChange = function(move) {
-						var vec = h3d.Vector.fromColor(inputColor.value);
-						p.defaultValue = vec;
-					};
-					element.css("height", 25);
-				default:
+		var genCtx = new ShaderGraphGenContext(graph, false);
+		var nodeGenCtx = new NodeGenContext.NodeGenContextSubGraph(null);
+		genCtx.generate(nodeGenCtx);
+		var outputs: Array<ShaderNode.InputInfo> = [];
 
 
-			}
-			elements.push(element);
+		for (name => info in nodeGenCtx.globalOutVars) {
+			outputs[info.id] = {name: name, type: hrt.shgraph.ShaderGraph.typeToSgType(info.type)};
 		}
 		}
 
 
-
-		return elements;
+		return outputs;
 	}
 	}
-	#end
 
 
 }
 }

+ 1 - 2
hrt/shgraph/nodes/Text.hx

@@ -12,8 +12,7 @@ class Text extends ShaderNode {
 
 
 	@prop() var text : String = "";
 	@prop() var text : String = "";
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		return {expr: null, inVars: [], outVars: [], inits: [], externVars: []};
+	override function generate(ctx:NodeGenContext) {
 	}
 	}
 
 
 	#if editor
 	#if editor

+ 1 - 1
hrt/shgraph/nodes/UVScroll.hx

@@ -11,7 +11,7 @@ class UVScroll extends  ShaderNodeHxsl {
 		@sginput("calculatedUV") var uv : Vec2;
 		@sginput("calculatedUV") var uv : Vec2;
 		@sginput(1.0) var uSpeed : Float;
 		@sginput(1.0) var uSpeed : Float;
 		@sginput(1.0) var vSpeed : Float;
 		@sginput(1.0) var vSpeed : Float;
-		@sginput("global.time") var time : Float;
+		@sginput("time") var time : Float;
 
 
 		@sgoutput var output : Vec2;
 		@sgoutput var output : Vec2;
 
 

+ 0 - 16
hrt/shgraph/nodes/test/CalculatedUVNode.hx

@@ -1,16 +0,0 @@
-package hrt.shgraph.nodes.test;
-
-@name("Calculated UV")
-@description("Testing only")
-@width(80)
-@group("Misc")
-class CalculatedUVNode extends ShaderNodeHxsl {
-
-	static var SRC = {
-		@input var uv : Vec2;
-		@sgoutput var output : Vec4;
-		function fragment() {
-			output = vec4(uv, 0.0,0.0);
-		}
-	}
-}

+ 0 - 1
hrt/shgraph/nodes/test/DynamicAdd.hx

@@ -1 +0,0 @@
-package hrt.shgraph.nodes.test;

+ 0 - 26
hrt/shgraph/nodes/test/DynamicIdentity.hx

@@ -1,26 +0,0 @@
-package hrt.shgraph.nodes.test;
-
-
-using hxsl.Ast;
-
-// Just pass the node around
-
-@name("DynamicIdentity")
-@description("Just for testing")
-@width(80)
-@group("Test")
-class DynamicPass extends ShaderNode {
-
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
-		var pos : Position = {file: "", min: 0, max: 0};
-
-		var t = inputTypes != null ? inputTypes[0] : null;
-
-		var input : TVar = {name: "in", id: getNewIdFn(), type: t, kind: Local, qualifiers: []};
-		var output : TVar = {name: "out", id: getNewIdFn(), type: t, kind: Local, qualifiers: []};
-
-		var finalExpr : TExpr = {e: TBinop(OpAssign, {e:TVar(output), p: pos, t:output.type}, {e:TVar(input), p: pos, t:output.type}), p: pos, t: output.type};
-
-		return {expr: finalExpr, inVars: [{v: input, internal: false, isDynamic: t == null}], outVars: [{v: output, internal: false, isDynamic: t == null}], externVars: [], inits: []};
-	}
-}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio