Browse Source

Dynamic shader nodes

Clement Espeute 2 years ago
parent
commit
3f824f2ba9

+ 43 - 36
hide/view/shadereditor/Box.hx

@@ -93,25 +93,29 @@ class Box {
 		var node = editor.group(element).addClass("input-node-group");
 		var node = 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 : ""}
-		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 TSampler2D:
-				style.fill = samplerColor;
-			default:
-				style.fill = defaultColor;
+		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 TSampler2D:
+					style.fill = samplerColor;
+				default:
+			}
 		}
 		}
+
 		var nodeCircle = editor.circle(node, 0, nodeHeight, NODE_RADIUS, style).addClass("node input-node");
 		var nodeCircle = editor.circle(node, 0, nodeHeight, NODE_RADIUS, style).addClass("node input-node");
 
 
 		var nameWidth = 0.0;
 		var nameWidth = 0.0;
@@ -144,26 +148,29 @@ class Box {
 		var node = editor.group(element).addClass("output-node-group");
 		var node = 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 : ""}
-		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 TSampler2D:
-				style.fill = samplerColor;
-			default:
-				style.fill = defaultColor;
 
 
+		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 TSampler2D:
+					style.fill = samplerColor;
+				default:
+			}
 		}
 		}
+
 		var nodeCircle = editor.circle(node, width, nodeHeight, NODE_RADIUS, style).addClass("node output-node");
 		var nodeCircle = editor.circle(node, width, nodeHeight, NODE_RADIUS, style).addClass("node output-node");
 
 
 		if (name.length > 0 && name != "output")
 		if (name.length > 0 && name != "output")

+ 29 - 12
hrt/shgraph/Macros.hx

@@ -32,6 +32,7 @@ class Macros {
 							var inVars : Array<String> = [];
 							var inVars : Array<String> = [];
 							var outVars : Array<String> = [];
 							var outVars : Array<String> = [];
 							var defValues : Array<String> = [];
 							var defValues : Array<String> = [];
+							var dynamicValues : Array<String> = [];
 
 
 							function iter(e: haxe.macro.Expr) : Void {
 							function iter(e: haxe.macro.Expr) : Void {
 								switch(e.expr) {
 								switch(e.expr) {
@@ -61,10 +62,20 @@ 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);
-														}
-														e.expr = subexpr.expr;
+																inVars.push(v.name);
+																defValues.push(defValue);
+
+																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
+																		}
+																	}
+																	default:
+																}
+															}
+															e.expr = subexpr.expr;
 													default:
 													default:
 														throw "sginput must be used with variables only";
 														throw "sginput must be used with variables only";
 												}
 												}
@@ -73,6 +84,16 @@ class Macros {
 													case EVars(vars):
 													case EVars(vars):
 														for (v in vars) {
 														for (v in vars) {
 															outVars.push(v.name);
 															outVars.push(v.name);
+
+															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
+																	}
+																}
+																default:
+															}
 														}
 														}
 														e.expr = subexpr.expr;
 														e.expr = subexpr.expr;
 													default:
 													default:
@@ -123,14 +144,10 @@ class Macros {
 								};
 								};
 							}
 							}
 
 
-							var inVarField : Field = makeField("_inVars", inVars);
-							var outVarField : Field = makeField("_outVars", outVars);
-							var defValuesField : Field = makeField("_defValues", defValues);
-
-							fields.push(inVarField);
-							fields.push(outVarField);
-							fields.push(defValuesField);
-
+							fields.push(makeField("_inVars", inVars));
+							fields.push(makeField("_outVars", outVars));
+							fields.push(makeField("_defValues", defValues));
+							fields.push(makeField("_dynamicValues", dynamicValues));
 
 
 						} catch( e : hxsl.Ast.Error ) {
 						} catch( e : hxsl.Ast.Error ) {
 							fields.remove(f);
 							fields.remove(f);

+ 2 - 2
hrt/shgraph/ShaderGlobalInput.hx

@@ -16,7 +16,7 @@ class ShaderGlobalInput extends ShaderNode {
 										{ parent: null, id: 0, kind: Global, name: "global.modelView", type: TMat4 },
 										{ parent: null, id: 0, kind: Global, name: "global.modelView", type: TMat4 },
 										{ parent: null, id: 0, kind: Global, name: "global.modelViewInverse", type: TMat4 } ];
 										{ parent: null, id: 0, kind: Global, name: "global.modelViewInverse", type: TMat4 } ];
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var inVar : TVar = Reflect.copy(globalInputs[variableIdx]);
 		var inVar : TVar = Reflect.copy(globalInputs[variableIdx]);
@@ -24,7 +24,7 @@ class ShaderGlobalInput extends ShaderNode {
 		var output : TVar = {name: "output", id: getNewIdFn(), type: inVar.type, kind: Local, qualifiers: []};
 		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};
 		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}], externVars: [inVar], inits: []};
+		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false, isDynamic: false}], externVars: [inVar], inits: []};
 	}
 	}
 
 
 	#if editor
 	#if editor

+ 151 - 31
hrt/shgraph/ShaderGraph.hx

@@ -11,8 +11,8 @@ enum ShaderDefInput {
 	Const(intialValue: Float);
 	Const(intialValue: Float);
 }
 }
 
 
-typedef ShaderNodeDefInVar = {v: TVar, internal: Bool, ?defVal: ShaderDefInput};
-typedef ShaderNodeDefOutVar = {v: TVar, internal: Bool};
+typedef ShaderNodeDefInVar = {v: TVar, internal: Bool, ?defVal: ShaderDefInput, isDynamic: Bool};
+typedef ShaderNodeDefOutVar = {v: TVar, internal: Bool, isDynamic: Bool};
 typedef ShaderNodeDef = {
 typedef ShaderNodeDef = {
 	expr: TExpr,
 	expr: TExpr,
 	inVars: Array<ShaderNodeDefInVar>, // If internal = true, don't show input in ui
 	inVars: Array<ShaderNodeDefInVar>, // If internal = true, don't show input in ui
@@ -30,7 +30,8 @@ typedef Node = {
 	?properties : Dynamic,
 	?properties : Dynamic,
 	?instance : ShaderNode,
 	?instance : ShaderNode,
 	?outputs: Array<Node>,
 	?outputs: Array<Node>,
-	?indegree : Int
+	?indegree : Int,
+	?generateId : Int, // Id used to index the node in the generate function
 };
 };
 
 
 private typedef Edge = {
 private typedef Edge = {
@@ -514,9 +515,9 @@ class Graph {
 
 
 	public function areTypesCompatible(input: hxsl.Ast.Type, output: hxsl.Ast.Type) : Bool {
 	public function areTypesCompatible(input: hxsl.Ast.Type, output: hxsl.Ast.Type) : Bool {
 		return switch (input) {
 		return switch (input) {
-			case TFloat, TVec(_, VFloat):
+			case TFloat, TVec(_, VFloat), null:
 				switch (output) {
 				switch (output) {
-					case TFloat, TVec(_, VFloat): true;
+					case TFloat, TVec(_, VFloat), null: true;
 					default: false;
 					default: false;
 				}
 				}
 			default: haxe.EnumTools.EnumValueTools.equals(input, output);
 			default: haxe.EnumTools.EnumValueTools.equals(input, output);
@@ -559,36 +560,68 @@ class Graph {
 			return '_sg_${(node.type).split(".").pop()}_var_$id';
 			return '_sg_${(node.type).split(".").pop()}_var_$id';
 		}
 		}
 
 
-		var nodeOutputs : Map<Node, Map<String, TVar>> = [];
-		var nodeDef : Map<Node, ShaderGraph.ShaderNodeDef> = [];
+		var nodeData : Array<
+		{
+			outputToInputMap: Map<String, {node: Node, inputName: String}>,
+			inputTypes: Array<Type>,
+			?outputs: Map<String, TVar>,
+			?def: ShaderGraph.ShaderNodeDef,
+		}> = [];
+
+		{
+			var currIndex = 0;
+			for (node in nodes) {
+				node.generateId = currIndex;
+				nodeData[node.generateId] = {
+					outputToInputMap: [],
+					inputTypes: []
+				};
+				currIndex++;
+			}
+		}
+
 
 
 		function getDef(node: Node) : ShaderGraph.ShaderNodeDef {
 		function getDef(node: Node) : ShaderGraph.ShaderNodeDef {
-			var def = nodeDef.get(node);
-			if (def != null)
-				return def;
-			def = node.instance.getShaderDef(domain, getNewVarId);
-			nodeDef.set(node, def);
+			var data = nodeData[node.generateId];
+			if (data.def != null)
+				return data.def;
+
+			var def = node.instance.getShaderDef(domain, getNewVarId, data.inputTypes);
+
+			// Don't cache vars while there still are dynamics inputs as getShaderDef
+			// is responsible for dynamic typing resolution
+			for (input in def.inVars) {
+				if (input.isDynamic)
+					return def;
+			}
+
+			for (output in def.outVars) {
+				if (output.isDynamic)
+					throw "Output is dynamic while there is no remaining dynamic inputs.";
+			}
+
+			data.def = def;
+
 			return def;
 			return def;
 		}
 		}
 
 
 		function getOutputs(node: Node) : Map<String, TVar> {
 		function getOutputs(node: Node) : Map<String, TVar> {
-			if (!nodeOutputs.exists(node)) {
-				var outputs : Map<String, TVar> = [];
-
-				var def = getDef(node);
-				for (output in def.outVars) {
-					if (output.internal)
-						continue;
-					var type = output.v.type;
-					if (type == null) throw "no type";
-					var id = getNewVarId();
-					var outVar = {id: id, name: getNewVarName(node, id), type: type, kind : Local};
-					outputs.set(output.v.name, outVar);
-				}
-
-				nodeOutputs.set(node, outputs);
+			var data = nodeData[node.generateId];
+			if (data.outputs != null)
+				return data.outputs;
+			data.outputs = [];
+
+			var def = getDef(node);
+			for (output in def.outVars) {
+				if (output.internal)
+					continue;
+				var type = output.v.type;
+				if (type == null) throw "no type";
+				var id = getNewVarId();
+				var outVar = {id: id, name: getNewVarName(node, id), type: type, kind : Local};
+				data.outputs.set(output.v.name, outVar);
 			}
 			}
-			return nodeOutputs.get(node);
+			return data.outputs;
 		}
 		}
 
 
 
 
@@ -642,12 +675,12 @@ class Graph {
 			}
 			}
 			if (isInput) {
 			if (isInput) {
 				if (graphInputVars.find((o) -> o.v == v) == null) {
 				if (graphInputVars.find((o) -> o.v == v) == null) {
-					graphInputVars.pushUnique({v: v, internal: false, defVal: null});
+					graphInputVars.pushUnique({v: v, internal: false, defVal: null, isDynamic: false});
 				}
 				}
 			}
 			}
 			else {
 			else {
 				if (graphOutputVars.find((o) -> o.v == v) == null) {
 				if (graphOutputVars.find((o) -> o.v == v) == null) {
-					graphOutputVars.push({v: v, internal: false});
+					graphOutputVars.push({v: v, internal: false, isDynamic: false});
 				}
 				}
 			}
 			}
 
 
@@ -734,6 +767,93 @@ class Graph {
 			throw "unreachable";
 			throw "unreachable";
 		}
 		}
 
 
+
+
+
+
+		// Build output to input map
+		for (node in sortedNodes) {
+			for (inputName => co in node.instance.connections) {
+				var targetNodeMap = nodeData[co.from.generateId].outputToInputMap;
+				targetNodeMap.set(co.fromName, {node: node, inputName: inputName});
+			}
+		}
+
+		// Interate from input to output to resolve dynamic types
+		for (i => _ in sortedNodes) {
+			var node = sortedNodes[sortedNodes.length - i - 1];
+
+			var def = getDef(node);
+			var data = nodeData[node.generateId];
+
+			for (i => inputVar in def.inVars) {
+				var from = node.instance.connections.get(inputVar.v.name);
+				if (from == null) {
+					var init = def.inits.find((v) -> v.variable == inputVar.v);
+					if (init != null) {
+						data.inputTypes[i] = init.variable.type;
+					}
+				}
+			}
+
+			for (outputVar in def.outVars) {
+				if (outputVar.internal)
+					continue;
+				var input = data.outputToInputMap.get(outputVar.v.name);
+
+				// if there is no connection, skip
+				if (input == null)
+					continue;
+
+				var def = getDef(input.node);
+
+				var inputVarId = def.inVars.findIndex((v) -> v.v.name == input.inputName);
+				if (inputVarId < 0)
+					throw "Missing var " + input.inputName;
+
+				nodeData[input.node.generateId].inputTypes[inputVarId] = outputVar.v.type;
+			}
+		}
+
+		trace("------ Typing done : ------");
+		for (i => _ in sortedNodes) {
+			var node = sortedNodes[sortedNodes.length - i - 1];
+			var data = nodeData[node.generateId];
+
+			var className = Type.getClassName(Type.getClass(node.instance));
+			trace("node " +  className + ":" + node.generateId);
+			var def = getDef(node);
+			trace("inVars:");
+			for (i => v in def.inVars) {
+				if (v.internal)
+					continue;
+				var from = node.instance.connections.get(v.v.name);
+				var className = if (from != null) {
+					Type.getClassName(Type.getClass(from.from.instance)) + ':${from.from.generateId}:${from.fromName}';
+				} else {
+					"not connected";
+				}
+				trace('\t$className ----> ${v.v.name}, type:${v.v.type}, genType:${data.inputTypes[i]}');
+			}
+
+			trace("outVars:");
+
+			for (i => v in def.outVars) {
+				if (v.internal)
+					continue;
+				var to = data.outputToInputMap.get(v.v.name);
+
+				var className = if (to != null) {
+					Type.getClassName(Type.getClass(to.node.instance)) + ':${to.node.generateId}:${to.inputName}';
+				} else {
+					"not connected";
+				}
+
+				trace('\t${v.v.name}, type:${v.v.type} ------> $className');
+			}
+
+		}
+
 		// Actually build the final shader expression
 		// Actually build the final shader expression
 		var exprsReverse : Array<TExpr> = [];
 		var exprsReverse : Array<TExpr> = [];
 		for (currentNode in sortedNodes) {
 		for (currentNode in sortedNodes) {
@@ -783,7 +903,7 @@ class Graph {
 
 
 								expr = null;
 								expr = null;
 								if (graphInputVars.find((v) -> v.v == outVar) == null) {
 								if (graphInputVars.find((v) -> v.v == outVar) == null) {
-									graphInputVars.push({v: outVar, internal: false, defVal: null});
+									graphInputVars.push({v: outVar, internal: false, defVal: null, isDynamic: false});
 									var shParam = Std.downcast(currentNode.instance, ShaderParam);
 									var shParam = Std.downcast(currentNode.instance, ShaderParam);
 									var param = getParameter(shParam.parameterId);
 									var param = getParameter(shParam.parameterId);
 									inits.push({variable: outVar, value: param.defaultValue});
 									inits.push({variable: outVar, value: param.defaultValue});

+ 2 - 2
hrt/shgraph/ShaderInput.hx

@@ -21,7 +21,7 @@ class ShaderInput extends ShaderNode {
 	// 	return null;
 	// 	return null;
 	// }
 	// }
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var variable : InputVariable = availableInputs.get(this.variable);
 		var variable : InputVariable = availableInputs.get(this.variable);
@@ -34,7 +34,7 @@ class ShaderInput extends ShaderNode {
 		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 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}], outVars:[{v:output, internal: false}], externVars: [], inits: []};
+		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) {

+ 2 - 5
hrt/shgraph/ShaderNode.hx

@@ -2,9 +2,6 @@ package hrt.shgraph;
 
 
 using hxsl.Ast;
 using hxsl.Ast;
 
 
-typedef InputInfo = { name : String, type : ShaderType.SType, hasProperty : Bool, isRequired : Bool, ?ids : Array<Int>, ?index : Int };
-typedef OutputInfo = { name : String, type : ShaderType.SType, ?id : Int };
-
 @:autoBuild(hrt.shgraph.Macros.autoRegisterNode())
 @:autoBuild(hrt.shgraph.Macros.autoRegisterNode())
 @:keepSub
 @:keepSub
 class ShaderNode {
 class ShaderNode {
@@ -23,7 +20,7 @@ class ShaderNode {
 					}];
 					}];
 
 
 
 
-	public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ) : ShaderGraph.ShaderNodeDef {
+	public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
 		throw "getShaderDef is not defined for class " + Type.getClassName(Type.getClass(this));
 		throw "getShaderDef is not defined for class " + Type.getClassName(Type.getClass(this));
 		return {expr: null, inVars: [], outVars: [], inits: [], externVars: []};
 		return {expr: null, inVars: [], outVars: [], inits: [], externVars: []};
 	}
 	}
@@ -33,7 +30,7 @@ class ShaderNode {
 	public var outputCompiled : Map<String, Bool> = []; // todo: put with outputs variable
 	public var outputCompiled : Map<String, Bool> = []; // todo: put with outputs variable
 
 
 	// TODO(ces) : caching
 	// TODO(ces) : caching
-	public function getOutputs2(domain: ShaderGraph.Domain) : Map<String, TVar> {
+	public function getOutputs2(domain: ShaderGraph.Domain, ?inputTypes: Array<Type>) : Map<String, TVar> {
 		var def = getShaderDef(domain, () -> 0);
 		var def = getShaderDef(domain, () -> 0);
 		var map : Map<String, TVar> = [];
 		var map : Map<String, TVar> = [];
 		for (tvar in def.outVars) {
 		for (tvar in def.outVars) {

+ 48 - 5
hrt/shgraph/ShaderNodeHxsl.hx

@@ -2,13 +2,15 @@ package hrt.shgraph;
 
 
 import hxsl.Ast.TExpr;
 import hxsl.Ast.TExpr;
 using hxsl.Ast;
 using hxsl.Ast;
+using Lambda;
+
 
 
 @: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 nodeCache : Map<String, ShaderGraph.ShaderNodeDef> = [];
 
 
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ) : ShaderGraph.ShaderNodeDef {
+	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>) : ShaderGraph.ShaderNodeDef {
 		var cl = Type.getClass(this);
 		var cl = Type.getClass(this);
 		var className = Type.getClassName(cl);
 		var className = Type.getClassName(cl);
 		var def = null;//nodeCache.get(className);
 		var def = null;//nodeCache.get(className);
@@ -38,8 +40,8 @@ class ShaderNodeHxsl extends ShaderNode {
 
 
 			patchExprId(expr);
 			patchExprId(expr);
 
 
-			var inVars = [];
-			var outVars = [];
+			var inVars : Array<hrt.shgraph.ShaderGraph.ShaderNodeDefInVar>= [];
+			var outVars : Array<hrt.shgraph.ShaderGraph.ShaderNodeDefOutVar> = [];
 			var externVars = [];
 			var externVars = [];
 
 
 			for (tvar in data.vars) {
 			for (tvar in data.vars) {
@@ -61,13 +63,13 @@ class ShaderNodeHxsl extends ShaderNode {
 							}
 							}
 							trace(def);
 							trace(def);
 						}
 						}
-						inVars.push({v:tvar, internal: false, defVal: def});
+						inVars.push({v:tvar, internal: false, defVal: def, isDynamic: false});
 						// TODO : handle default values
 						// TODO : handle default values
 						input = true;
 						input = true;
 					}
 					}
 					var classOutVars : Array<String> = cast (cl:Dynamic)._outVars;
 					var classOutVars : Array<String> = cast (cl:Dynamic)._outVars;
 					if (classOutVars.contains(tvar.name)) {
 					if (classOutVars.contains(tvar.name)) {
-						outVars.push({v: tvar, internal: false});
+						outVars.push({v: tvar, internal: false, isDynamic: false});
 						output = true;
 						output = true;
 					}
 					}
 					if (input && output) {
 					if (input && output) {
@@ -83,6 +85,47 @@ class ShaderNodeHxsl extends ShaderNode {
 					}
 					}
 			}
 			}
 
 
+			// DynamicType is the smallest vector type or float if all inputTypes are floats
+			var dynamicType : Type = null;
+			if (inputTypes != null) {
+				for (t in inputTypes) {
+					switch (t) {
+						case null:
+						case TFloat: // TFloat doesn't change output because vec * scalar is always correct
+						case TVec(size, t1): // Vec2 always convert to it because it's the smallest vec type
+							switch(dynamicType) {
+								case TFloat, null:
+									dynamicType = t;
+								case TVec(size2, t2):
+									if (t1 != t2)
+										throw "Incompatible vectors types";
+									dynamicType = TVec(size < size2 ? size : size2, t1);
+								default:
+							}
+						default:
+							throw "Type " + t + " is incompatible with Dynamic";
+					}
+				}
+			}
+
+
+			var classDynamicVal : Array<String> = cast (cl:Dynamic)._dynamicValues;
+			for (v in inVars) {
+				if (classDynamicVal.contains(v.v.name)) {
+					v.v.type = dynamicType;
+					if (dynamicType == null)
+						v.isDynamic = true;
+				}
+			}
+
+			for (v in outVars) {
+				if (classDynamicVal.contains(v.v.name)) {
+					v.v.type = dynamicType;
+					if (dynamicType == null)
+						v.isDynamic = true;
+				}
+			}
+
 			def = {expr: expr, inVars: inVars, outVars: outVars, externVars: externVars, inits: []};
 			def = {expr: expr, inVars: inVars, outVars: outVars, externVars: externVars, inits: []};
 			nodeCache.set(className, def);
 			nodeCache.set(className, def);
 		}
 		}

+ 2 - 2
hrt/shgraph/ShaderOutput.hx

@@ -14,7 +14,7 @@ class ShaderOutput extends ShaderNode {
 	var components = [X, Y, Z, W];
 	var components = [X, Y, Z, W];
 
 
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var inVar : TVar = {name: "input", id: getNewIdFn(), type: this.variable.type, kind: Param, qualifiers: []};
 		var inVar : TVar = {name: "input", id: getNewIdFn(), type: this.variable.type, kind: Param, qualifiers: []};
@@ -24,7 +24,7 @@ class ShaderOutput extends ShaderNode {
 		//var param = getParameter(inputNode.parameterId);
 		//var param = getParameter(inputNode.parameterId);
 		//inits.push({variable: inVar, value: param.defaultValue});
 		//inits.push({variable: inVar, value: param.defaultValue});
 
 
-		return {expr: finalExpr, inVars: [{v: inVar, internal: false}], outVars:[{v: output, internal: true}], externVars: [], inits: []};
+		return {expr: finalExpr, inVars: [{v: inVar, internal: false, isDynamic: false}], outVars:[{v: output, internal: true, isDynamic: false}], externVars: [], inits: []};
 	}
 	}
 
 
 	/*override public function checkValidityInput(key : String, type : hxsl.Ast.Type) : Bool {
 	/*override public function checkValidityInput(key : String, type : hxsl.Ast.Type) : Bool {

+ 2 - 2
hrt/shgraph/ShaderParam.hx

@@ -12,7 +12,7 @@ class ShaderParam extends ShaderNode {
 	@prop() public var perInstance : Bool;
 	@prop() public var perInstance : Bool;
 
 
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var qual = [];
 		var qual = [];
@@ -23,7 +23,7 @@ class ShaderParam extends ShaderNode {
 		var output : TVar = {name: "output", id: getNewIdFn(), type: this.variable.type, kind: Local, qualifiers: []};
 		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};
 		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}], outVars:[{v:output, internal: false}], externVars: [], inits: []};
+		return {expr: finalExpr, inVars: [{v:inVar, internal: true, isDynamic: false}], outVars:[{v:output, internal: false, isDynamic: false}], externVars: [], inits: []};
 	}
 	}
 
 
 	public var variable : TVar;
 	public var variable : TVar;

+ 3 - 3
hrt/shgraph/nodes/Add.hx

@@ -9,9 +9,9 @@ using hxsl.Ast;
 class Add extends Operation {
 class Add extends Operation {
 
 
 	static var SRC = {
 	static var SRC = {
-		@sginput(0.0) var a : Vec4;
-		@sginput(0.0) var b : Vec4;
-		@sgoutput var output : Vec4;
+		@sginput(0.0) var a : Dynamic;
+		@sginput(0.0) var b : Dynamic;
+		@sgoutput var output : Dynamic;
 		function fragment() {
 		function fragment() {
 			output = a + b;
 			output = a + b;
 		}
 		}

+ 2 - 2
hrt/shgraph/nodes/BoolConst.hx

@@ -12,13 +12,13 @@ class BoolConst extends ShaderConst {
 
 
 	@prop() var value : Bool = true;
 	@prop() var value : Bool = true;
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var output : TVar = {name: "output", id: getNewIdFn(), type: TBool, kind: Local, qualifiers: []};
 		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};
 		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};
 
 
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false}], externVars: [], inits: []};
+		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false,  isDynamic: false}], externVars: [], inits: []};
 	}
 	}
 
 
 	#if editor
 	#if editor

+ 2 - 2
hrt/shgraph/nodes/Color.hx

@@ -26,7 +26,7 @@ class Color extends ShaderConst {
 	// 	};
 	// 	};
 	// }
 	// }
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var output : TVar = {name: "output", id:getNewIdFn(), type: TVec(4, VFloat), kind: Local, qualifiers: []};
 		var output : TVar = {name: "output", id:getNewIdFn(), type: TVec(4, VFloat), kind: Local, qualifiers: []};
@@ -74,7 +74,7 @@ class Color extends ShaderConst {
 			p: null,
 			p: null,
 			t: output.type
 			t: output.type
 		};
 		};
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false}], externVars: [], inits: []};
+		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false, isDynamic: false}], externVars: [], inits: []};
 	}
 	}
 
 
 	// override public function build(key : String) : TExpr {
 	// override public function build(key : String) : TExpr {

+ 2 - 2
hrt/shgraph/nodes/FloatConst.hx

@@ -9,13 +9,13 @@ using hxsl.Ast;
 @noheader()
 @noheader()
 class FloatConst extends ShaderConst {
 class FloatConst extends ShaderConst {
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var output : TVar = {name: "output", id: getNewIdFn(), type: TFloat, kind: Local, qualifiers: []};
 		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};
 		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};
 
 
-		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false}], externVars: [], inits: []};
+		return {expr: finalExpr, inVars: [], outVars:[{v: output, internal: false, isDynamic: false}], externVars: [], inits: []};
 	}
 	}
 
 
 	@prop() var value : Float = 0.;
 	@prop() var value : Float = 0.;

+ 2 - 2
hrt/shgraph/nodes/Preview.hx

@@ -32,7 +32,7 @@ class AlphaPreview extends hxsl.Shader {
 @noheader()
 @noheader()
 class Preview extends ShaderNode {
 class Preview extends ShaderNode {
 
 
-	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var pos : Position = {file: "", min: 0, max: 0};
 		var pos : Position = {file: "", min: 0, max: 0};
 
 
 		var inVar : TVar = {name: "input", id: getNewIdFn(), type: TVec(4, VFloat), kind: Param, qualifiers: []};
 		var inVar : TVar = {name: "input", id: getNewIdFn(), type: TVec(4, VFloat), kind: Param, qualifiers: []};
@@ -42,7 +42,7 @@ class Preview extends ShaderNode {
 		//var param = getParameter(inputNode.parameterId);
 		//var param = getParameter(inputNode.parameterId);
 		//inits.push({variable: inVar, value: param.defaultValue});
 		//inits.push({variable: inVar, value: param.defaultValue});
 
 
-		return {expr: finalExpr, inVars: [{v: inVar, internal: false}], outVars:[], externVars: [output], inits: []};
+		return {expr: finalExpr, inVars: [{v: inVar, internal: false, isDynamic: false}], outVars:[], externVars: [output], inits: []};
 	}
 	}
 
 
 	// @input("Input") var input = SType.Vec4;
 	// @input("Input") var input = SType.Vec4;

+ 1 - 3
hrt/shgraph/nodes/SubGraph.hx

@@ -10,7 +10,7 @@ class SubGraph extends ShaderNode {
 
 
 	@prop() public var pathShaderGraph : String;
 	@prop() public var pathShaderGraph : String;
 
 
-	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int ):hrt.shgraph.ShaderGraph.ShaderNodeDef {
+	override public function getShaderDef(domain: ShaderGraph.Domain, getNewIdFn : () -> Int, ?inputTypes: Array<Type>):hrt.shgraph.ShaderGraph.ShaderNodeDef {
 		var shader = new ShaderGraph(pathShaderGraph);
 		var shader = new ShaderGraph(pathShaderGraph);
 		var gen = shader.getGraph(domain).generate2(getNewIdFn);
 		var gen = shader.getGraph(domain).generate2(getNewIdFn);
 
 
@@ -28,9 +28,7 @@ class SubGraph extends ShaderNode {
 		return gen;
 		return gen;
 	}
 	}
 
 
-	var inputsInfo : Map<String, ShaderNode.InputInfo>;
 	var inputInfoKeys : Array<String> = [];
 	var inputInfoKeys : Array<String> = [];
-	var outputsInfo : Map<String, ShaderNode.OutputInfo>;
 	var outputInfoKeys : Array<String> = [];
 	var outputInfoKeys : Array<String> = [];
 	var parameters : Array<ShaderGraph.Parameter> = [];
 	var parameters : Array<ShaderGraph.Parameter> = [];
 	var propertiesSubGraph : Map<Int, Dynamic>;
 	var propertiesSubGraph : Map<Int, Dynamic>;

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

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

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

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

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

@@ -0,0 +1,26 @@
+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: []};
+	}
+}