Browse Source

Save without reloading moved, shadergraph : handling error message + autocompile

Tom SPIRA 6 years ago
parent
commit
3ea3b42ff3

+ 4 - 1
bin/style.css

@@ -1115,7 +1115,7 @@ input[type=checkbox]:checked:after {
 .shader-view .heaps-scene #status-bar {
 .shader-view .heaps-scene #status-bar {
   position: absolute;
   position: absolute;
   width: 100%;
   width: 100%;
-  height: 25px;
+  min-height: 20px;
   background-color: #111;
   background-color: #111;
   border: 1px solid #444;
   border: 1px solid #444;
   right: -1px;
   right: -1px;
@@ -1134,6 +1134,9 @@ input[type=checkbox]:checked:after {
 .shader-view .heaps-scene svg g.selected .outline {
 .shader-view .heaps-scene svg g.selected .outline {
   stroke: #668fff;
   stroke: #668fff;
 }
 }
+.shader-view .heaps-scene svg g.error .outline {
+  stroke: #ff6666;
+}
 .shader-view .heaps-scene svg .outline {
 .shader-view .heaps-scene svg .outline {
   fill: none;
   fill: none;
 }
 }

+ 6 - 1
bin/style.less

@@ -1249,7 +1249,7 @@ input[type=checkbox] {
 			position: absolute;
 			position: absolute;
 
 
 			width: 100%;
 			width: 100%;
-			height: 25px;
+			min-height: 20px;
 
 
 			background-color: #111;
 			background-color: #111;
 			border: 1px solid #444;
 			border: 1px solid #444;
@@ -1276,6 +1276,11 @@ input[type=checkbox] {
 						stroke: #668fff;
 						stroke: #668fff;
 					}
 					}
 				}
 				}
+				&.error {
+					.outline {
+						stroke: #ff6666;
+					}
+				}
 			}
 			}
 			.outline {
 			.outline {
 				fill: none;
 				fill: none;

+ 0 - 13
hide/view/FXEditor.hx

@@ -133,8 +133,6 @@ class FXEditor extends FileView {
 	var autoSync : Bool;
 	var autoSync : Bool;
 	var currentVersion : Int = 0;
 	var currentVersion : Int = 0;
 	var lastSyncChange : Float = 0.;
 	var lastSyncChange : Float = 0.;
-	var currentSign : String;
-
 	var showGrid = true;
 	var showGrid = true;
 	var grid : h3d.scene.Graphics;
 	var grid : h3d.scene.Graphics;
 
 
@@ -160,17 +158,6 @@ class FXEditor extends FileView {
 		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.fx.FX().saveData()));
 		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.fx.FX().saveData()));
 	}
 	}
 
 
-	override function onFileChanged(wasDeleted:Bool) {
-		if( !wasDeleted ) {
-			// double check if content has changed
-			var content = sys.io.File.getContent(getPath());
-			var sign = haxe.crypto.Md5.encode(content);
-			if( sign == currentSign )
-				return;
-		}
-		super.onFileChanged(wasDeleted);
-	}
-
 	override function save() {
 	override function save() {
 		var content = ide.toJSON(data.saveData());
 		var content = ide.toJSON(data.saveData());
 		var newSign = haxe.crypto.Md5.encode(content);
 		var newSign = haxe.crypto.Md5.encode(content);

+ 9 - 0
hide/view/FileView.hx

@@ -7,6 +7,8 @@ class FileView extends hide.ui.View<{ path : String }> {
 	var skipNextChange : Bool;
 	var skipNextChange : Bool;
 	var lastSaveTag : Int;
 	var lastSaveTag : Int;
 
 
+	var currentSign : String;
+
 	public function new(state) {
 	public function new(state) {
 		super(state);
 		super(state);
 		if( state.path != null )
 		if( state.path != null )
@@ -20,6 +22,13 @@ class FileView extends hide.ui.View<{ path : String }> {
 	}
 	}
 
 
 	function onFileChanged( wasDeleted : Bool ) {
 	function onFileChanged( wasDeleted : Bool ) {
+		if( !wasDeleted ) {
+			// double check if content has changed
+			var content = sys.io.File.getContent(getPath());
+			var sign = haxe.crypto.Md5.encode(content);
+			if( sign == currentSign )
+				return;
+		}
 		if( wasDeleted ) {
 		if( wasDeleted ) {
 			if( modified ) return;
 			if( modified ) return;
 			element.html('${state.path} no longer exists');
 			element.html('${state.path} no longer exists');

+ 0 - 13
hide/view/Prefab.hx

@@ -43,23 +43,10 @@ class Prefab extends FileView {
 	var autoSync : Bool;
 	var autoSync : Bool;
 	var currentVersion : Int = 0;
 	var currentVersion : Int = 0;
 	var lastSyncChange : Float = 0.;
 	var lastSyncChange : Float = 0.;
-	var currentSign : String;
 
 
 	override function getDefaultContent() {
 	override function getDefaultContent() {
 		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.Library().saveData()));
 		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.Library().saveData()));
 	}
 	}
-
-	override function onFileChanged(wasDeleted:Bool) {
-		if( !wasDeleted ) {
-			// double check if content has changed
-			var content = sys.io.File.getContent(getPath());
-			var sign = haxe.crypto.Md5.encode(content);
-			if( sign == currentSign )
-				return;
-		}
-		super.onFileChanged(wasDeleted);
-	}
-
 	override function save() {
 	override function save() {
 		var content = ide.toJSON(data.saveData());
 		var content = ide.toJSON(data.saveData());
 		currentSign = haxe.crypto.Md5.encode(content);
 		currentSign = haxe.crypto.Md5.encode(content);

+ 131 - 48
hide/view/ShaderEditor.hx

@@ -1,5 +1,6 @@
 package hide.view;
 package hide.view;
 
 
+import hrt.shgraph.ShaderException;
 import haxe.Timer;
 import haxe.Timer;
 import h3d.shader.LineShader;
 import h3d.shader.LineShader;
 import h3d.shader.ColorAdd;
 import h3d.shader.ColorAdd;
@@ -7,7 +8,7 @@ using hxsl.Ast.Type;
 
 
 import haxe.rtti.Rtti;
 import haxe.rtti.Rtti;
 import haxe.rtti.Meta;
 import haxe.rtti.Meta;
-import hxsl.DynamicShader;
+import hxsl.Shader;
 import hxsl.SharedShader;
 import hxsl.SharedShader;
 import hide.comp.SceneEditor;
 import hide.comp.SceneEditor;
 import js.jquery.JQuery;
 import js.jquery.JQuery;
@@ -28,7 +29,7 @@ typedef Edge = { from : Box, nodeFrom : JQuery, to : Box, nodeTo : JQuery, elt :
 class ShaderEditor extends FileView {
 class ShaderEditor extends FileView {
 
 
 	var parent : JQuery;
 	var parent : JQuery;
-	static var editor : SVG;
+	var editor : SVG;
 	var editorMatrix : JQuery;
 	var editorMatrix : JQuery;
 	var statusBar : JQuery;
 	var statusBar : JQuery;
 
 
@@ -40,7 +41,7 @@ class ShaderEditor extends FileView {
 	var listOfBoxes : Array<Box> = [];
 	var listOfBoxes : Array<Box> = [];
 	var listOfEdges : Array<Edge> = [];
 	var listOfEdges : Array<Edge> = [];
 
 
-	static var transformMatrix : Array<Float> = [1, 0, 0, 1, 0, 0];
+	var transformMatrix : Array<Float> = [1, 0, 0, 1, 0, 0];
 	var isPanning : Bool = false;
 	var isPanning : Bool = false;
 	var timerUpdateView : Timer;
 	var timerUpdateView : Timer;
 
 
@@ -49,6 +50,7 @@ class ShaderEditor extends FileView {
 	var recSelection : JQuery;
 	var recSelection : JQuery;
 	var startRecSelection : h2d.col.Point;
 	var startRecSelection : h2d.col.Point;
 	var lastClickDrag : h2d.col.Point;
 	var lastClickDrag : h2d.col.Point;
+	var lastClickPan : h2d.col.Point;
 
 
 	// used to build edge
 	// used to build edge
 	static var NODE_TRIGGER_NEAR = 2000.0;
 	static var NODE_TRIGGER_NEAR = 2000.0;
@@ -71,9 +73,9 @@ class ShaderEditor extends FileView {
 	var light : h3d.scene.Object;
 	var light : h3d.scene.Object;
 	var lightDirection : h3d.Vector;
 	var lightDirection : h3d.Vector;
 
 
-	static var shaderGraph : ShaderGraph;
+	var shaderGraph : ShaderGraph;
 
 
-	var shaderGenerated : DynamicShader;
+	var shaderGenerated : Shader;
 
 
 	override function onDisplay() {
 	override function onDisplay() {
 		shaderGraph = new ShaderGraph(getPath());
 		shaderGraph = new ShaderGraph(getPath());
@@ -145,10 +147,12 @@ class ShaderEditor extends FileView {
 				}
 				}
 
 
 				clearSelectionBoxes();
 				clearSelectionBoxes();
+				return;
 			}
 			}
 			if (e.button == 1) {
 			if (e.button == 1) {
-				lastClickDrag = new Point(lX(e.clientX), lY(e.clientY));
+				lastClickPan = new Point(e.clientX, e.clientY);
 				isPanning = true;
 				isPanning = true;
+				return;
 			}
 			}
 		});
 		});
 
 
@@ -176,6 +180,7 @@ class ShaderEditor extends FileView {
 					startLinkGrNode = endLinkNode = null;
 					startLinkGrNode = endLinkNode = null;
 					isCreatingLink = None;
 					isCreatingLink = None;
 					clearAvailableNodes();
 					clearAvailableNodes();
+					return;
 				}
 				}
 
 
 				// Stop rectangle selection
 				// Stop rectangle selection
@@ -187,13 +192,16 @@ class ShaderEditor extends FileView {
 					for (b in listOfBoxes)
 					for (b in listOfBoxes)
 						if (b.selected)
 						if (b.selected)
 							listOfBoxesSelected.push(b);
 							listOfBoxesSelected.push(b);
+					return;
 				}
 				}
+				return;
 			}
 			}
 
 
 			// Stop panning
 			// Stop panning
 			if (e.button == 1) {
 			if (e.button == 1) {
 				lastClickDrag = null;
 				lastClickDrag = null;
 				isPanning = false;
 				isPanning = false;
+				return;
 			}
 			}
 		});
 		});
 
 
@@ -224,29 +232,27 @@ class ShaderEditor extends FileView {
 					}
 					}
 					clearSelectionBoxes();
 					clearSelectionBoxes();
 				}
 				}
+				return;
 			} else if (e.keyCode == 32) {
 			} else if (e.keyCode == 32) {
-				var test = new LineShader();
-				trace(test);
-				var s = new SharedShader("");
-				s.data = shaderGraph.buildFragment();
-				info("Shader compiled");
-				s.initialize();
-
-				if (shaderGenerated != null)
-					for (m in obj.getMaterials())
-						m.mainPass.removeShader(shaderGenerated);
-
-				shaderGenerated = new DynamicShader(s);
-				for (m in obj.getMaterials()) {
-					m.mainPass.addShader(shaderGenerated);
-				}
+
 			} else if (e.keyCode == 83 && e.ctrlKey) { // CTRL+S : save
 			} else if (e.keyCode == 83 && e.ctrlKey) { // CTRL+S : save
 				shaderGraph.save();
 				shaderGraph.save();
-			} else if (e.keyCode == 74 && e.ctrlKey) { // CTRL+S : save
+			} else if (e.keyCode == 74 && e.ctrlKey) { // CTRL+J : test
 				trace(shaderGraph.hasCycle());
 				trace(shaderGraph.hasCycle());
 			}
 			}
 		});
 		});
 
 
+		editorMatrix.on("change", "input, select", function(ev) {
+			try {
+				shaderGraph.nodeUpdated(ev.target.closest(".box").id);
+				compileShader();
+			} catch (e : Dynamic) {
+				if (Std.is(e, ShaderException)) {
+					error(e.msg, e.idBox);
+				}
+			}
+		});
+
 		addMenu = null;
 		addMenu = null;
 		listOfClasses = new Map<String, Array<NodeInfo>>();
 		listOfClasses = new Map<String, Array<NodeInfo>>();
 		var mapOfNodes = ShaderNode.registeredNodes;
 		var mapOfNodes = ShaderNode.registeredNodes;
@@ -276,7 +282,7 @@ class ShaderEditor extends FileView {
 
 
 		updateMatrix();
 		updateMatrix();
 
 
-		new Element("body").ready(function(e) {
+		new Element("svg").ready(function(e) {
 
 
 			for (node in shaderGraph.getNodes()) {
 			for (node in shaderGraph.getNodes()) {
 				addBox(new Point(node.x, node.y), std.Type.getClass(node.instance), node.instance);
 				addBox(new Point(node.x, node.y), std.Type.getClass(node.instance), node.instance);
@@ -301,11 +307,19 @@ class ShaderEditor extends FileView {
 						}
 						}
 					}
 					}
 				}
 				}
+
 			});
 			});
 		});
 		});
 
 
 	}
 	}
 
 
+	override function save() {
+		var content = shaderGraph.save();
+		currentSign = haxe.crypto.Md5.encode(content);
+		sys.io.File.saveContent(getPath(), content);
+		super.save();
+	}
+
 	function update(dt : Float) {
 	function update(dt : Float) {
 
 
 	}
 	}
@@ -318,15 +332,68 @@ class ShaderEditor extends FileView {
 			lightDirection = this.light.getDirection();
 			lightDirection = this.light.getDirection();
 		}
 		}
 
 
-		var sphere = new h3d.prim.Sphere(1, 32, 32);
-		sphere.addNormals();
-		var mesh = new h3d.scene.Mesh(sphere, sceneEditor.scene.s3d);
-		mesh.material.mainPass.enableLights = true;
-		mesh.material.shadows = true;
-		mesh.material.color.load(new h3d.Vector(1, 1, 1));
-		obj = mesh;
+
+		var cache = new h3d.prim.ModelCache();
+		obj = cache.loadModel(hxd.Res.load("fx/Common/PrimitiveShapes/Sphere.fbx").toModel());
+		sceneEditor.scene.s3d.addChild(obj);
 
 
 		element.find("#preview").first().append(sceneEditor.scene.element);
 		element.find("#preview").first().append(sceneEditor.scene.element);
+
+		compileShader();
+	}
+
+	function compileShader() {
+		var saveShader : Shader = null;
+		if (shaderGenerated != null)
+			saveShader = shaderGenerated.clone();
+		try {
+			var timeStart = Date.now().getTime();
+			var s = new SharedShader("");
+			s.data = shaderGraph.buildFragment();
+			@:privateAccess s.initialize();
+
+			if (shaderGenerated != null)
+				for (m in obj.getMaterials())
+					m.mainPass.removeShader(shaderGenerated);
+
+			shaderGenerated = new hxsl.DynamicShader(s);
+			for (m in obj.getMaterials()) {
+				m.mainPass.addShader(shaderGenerated);
+			}
+			@:privateAccess sceneEditor.scene.render(sceneEditor.scene.engine);
+			info('Shader compiled in  ${Date.now().getTime() - timeStart}ms');
+
+		} catch (e : Dynamic) {
+			if (Std.is(e, String)) {
+				var str : String = e;
+				if (str.split(":")[0] == "An error occurred compiling the shaders") { // aie
+					error("Compilation of shader failed > " + str);
+					if (shaderGenerated != null)
+						for (m in obj.getMaterials())
+							m.mainPass.removeShader(shaderGenerated);
+					if (saveShader != null) {
+						shaderGenerated = saveShader;
+						for (m in obj.getMaterials()) {
+							m.mainPass.addShader(shaderGenerated);
+						}
+					}
+					return;
+				}
+			} else if (Std.is(e, ShaderException)) {
+				error(e.msg, e.idBox);
+				return;
+			}
+			error("Compilation of shader failed > " + e);
+			if (shaderGenerated != null)
+				for (m in obj.getMaterials())
+					m.mainPass.removeShader(shaderGenerated);
+			if (saveShader != null) {
+				shaderGenerated = saveShader;
+				for (m in obj.getMaterials()) {
+					m.mainPass.addShader(shaderGenerated);
+				}
+			}
+		}
 	}
 	}
 
 
 	function mouseMoveFunction(clientX : Int, clientY : Int) {
 	function mouseMoveFunction(clientX : Int, clientY : Int) {
@@ -342,9 +409,9 @@ class ShaderEditor extends FileView {
 			// try to use the same code when user clicks on input node already connected and here
 			// try to use the same code when user clicks on input node already connected and here
 		}
 		}
 		if (isPanning) {
 		if (isPanning) {
-			pan(new Point(lX(clientX) - lastClickDrag.x, lY(clientY) - lastClickDrag.y));
-			lastClickDrag.x = lX(clientX);
-			lastClickDrag.y = lY(clientY);
+			pan(new Point(clientX - lastClickPan.x, clientY - lastClickPan.y));
+			lastClickPan.x = clientX;
+			lastClickPan.y = clientY;
 			return;
 			return;
 		}
 		}
 		// Edit rectangle selection
 		// Edit rectangle selection
@@ -540,6 +607,7 @@ class ShaderEditor extends FileView {
 		edge.nodeTo.parent().removeClass("hasLink");
 		edge.nodeTo.parent().removeClass("hasLink");
 		shaderGraph.removeEdge(edge.to.getId(), edge.nodeTo.attr("field"));
 		shaderGraph.removeEdge(edge.to.getId(), edge.nodeTo.attr("field"));
 		listOfEdges.remove(edge);
 		listOfEdges.remove(edge);
+		compileShader();
 	}
 	}
 
 
 	function setAvailableInputNodes(boxOutput : Box, field : String) {
 	function setAvailableInputNodes(boxOutput : Box, field : String) {
@@ -582,14 +650,21 @@ class ShaderEditor extends FileView {
 		editor.element.find(".nodeMatch").removeClass("nodeMatch");
 		editor.element.find(".nodeMatch").removeClass("nodeMatch");
 	}
 	}
 
 
-	function error(str : String) {
+	function error(str : String, ?idBox : Int) {
 		statusBar.html(str);
 		statusBar.html(str);
 		statusBar.addClass("error");
 		statusBar.addClass("error");
+
+		new Element(".box").removeClass("error");
+		if (idBox != null) {
+			var elt = new Element('#${idBox}');
+			elt.addClass("error");
+		}
 	}
 	}
 
 
 	function info(str : String) {
 	function info(str : String) {
 		statusBar.html(str);
 		statusBar.html(str);
 		statusBar.removeClass("error");
 		statusBar.removeClass("error");
+		new Element(".box").removeClass("error");
 	}
 	}
 
 
 	function createEdgeInShaderGraph() : Bool {
 	function createEdgeInShaderGraph() : Bool {
@@ -613,13 +688,21 @@ class ShaderEditor extends FileView {
 				}
 				}
 			}
 			}
 		}
 		}
-		if (shaderGraph.addEdge({ idOutput: startLinkBox.getId(), nameOutput: startLinkNode.attr("field"), idInput: endLinkBox.getId(), nameInput: endLinkNode.attr("field") })) {
-			createEdgeInEditorGraph(newEdge);
-			currentLink.removeClass("draft");
-			currentLink = null;
-			return true;
-		} else {
-			error("This edge creates a cycle.");
+		try {
+			if (shaderGraph.addEdge({ idOutput: startLinkBox.getId(), nameOutput: startLinkNode.attr("field"), idInput: endLinkBox.getId(), nameInput: endLinkNode.attr("field") })) {
+				createEdgeInEditorGraph(newEdge);
+				currentLink.removeClass("draft");
+				currentLink = null;
+				compileShader();
+				return true;
+			} else {
+				error("This edge creates a cycle.");
+				return false;
+			}
+		} catch (e : Dynamic) {
+			if (Std.is(e, ShaderException)) {
+				error(e.msg, e.idBox);
+			}
 			return false;
 			return false;
 		}
 		}
 	}
 	}
@@ -922,7 +1005,7 @@ class ShaderEditor extends FileView {
 			return;
 			return;
 		var PADDING_BOUNDS = 75;
 		var PADDING_BOUNDS = 75;
 		var SPEED_BOUNDS = 0.1;
 		var SPEED_BOUNDS = 0.1;
-		timerUpdateView = new Timer(10);
+		timerUpdateView = new Timer(0);
 		timerUpdateView.run = function() {
 		timerUpdateView.run = function() {
 			var posCursor = new Point(ide.mouseX - parent.offset().left, ide.mouseY - parent.offset().top);
 			var posCursor = new Point(ide.mouseX - parent.offset().left, ide.mouseY - parent.offset().top);
 			var wasUpdated = false;
 			var wasUpdated = false;
@@ -1004,26 +1087,26 @@ class ShaderEditor extends FileView {
 		var dy = Math.max(Math.abs(y - (element.offset().top + element.height() / 2)) - element.height() / 2, 0);
 		var dy = Math.max(Math.abs(y - (element.offset().top + element.height() / 2)) - element.height() / 2, 0);
 		return dx * dx + dy * dy;
 		return dx * dx + dy * dy;
 	}
 	}
-	static function gX(x : Float) : Float {
+	function gX(x : Float) : Float {
 		return x*transformMatrix[0] + transformMatrix[4];
 		return x*transformMatrix[0] + transformMatrix[4];
 	}
 	}
-	static function gY(y : Float) : Float {
+	function gY(y : Float) : Float {
 		return y*transformMatrix[3] + transformMatrix[5];
 		return y*transformMatrix[3] + transformMatrix[5];
 	}
 	}
-	static function gPos(x : Float, y : Float) : Point {
+	function gPos(x : Float, y : Float) : Point {
 		return new Point(gX(x), gY(y));
 		return new Point(gX(x), gY(y));
 	}
 	}
-	static function lX(x : Float) : Float {
+	function lX(x : Float) : Float {
 		var screenOffset = editor.element.offset();
 		var screenOffset = editor.element.offset();
 		x -= screenOffset.left;
 		x -= screenOffset.left;
 		return (x - transformMatrix[4])/transformMatrix[0];
 		return (x - transformMatrix[4])/transformMatrix[0];
 	}
 	}
-	static function lY(y : Float) : Float {
+	function lY(y : Float) : Float {
 		var screenOffset = editor.element.offset();
 		var screenOffset = editor.element.offset();
 		y -= screenOffset.top;
 		y -= screenOffset.top;
 		return (y - transformMatrix[5])/transformMatrix[3];
 		return (y - transformMatrix[5])/transformMatrix[3];
 	}
 	}
-	static function lPos(x : Float, y : Float) : Point {
+	function lPos(x : Float, y : Float) : Point {
 		return new Point(lX(x), lY(y));
 		return new Point(lX(x), lY(y));
 	}
 	}
 
 

+ 0 - 12
hide/view/l3d/Level3D.hx

@@ -300,7 +300,6 @@ class Level3D extends FileView {
 	var showGrid = true;
 	var showGrid = true;
 	var currentVersion : Int = 0;
 	var currentVersion : Int = 0;
 	var lastSyncChange : Float = 0.;
 	var lastSyncChange : Float = 0.;
-	var currentSign : String;
 	var sceneFilters : Map<String, Bool>;
 	var sceneFilters : Map<String, Bool>;
 	var statusText : h2d.Text;
 	var statusText : h2d.Text;
 	var lastRenderProps : hrt.prefab.RenderProps = null;
 	var lastRenderProps : hrt.prefab.RenderProps = null;
@@ -503,17 +502,6 @@ class Level3D extends FileView {
 		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.l3d.Level3D().saveData()));
 		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.l3d.Level3D().saveData()));
 	}
 	}
 
 
-	override function onFileChanged(wasDeleted:Bool) {
-		if( !wasDeleted ) {
-			// double check if content has changed
-			var content = sys.io.File.getContent(getPath());
-			var sign = haxe.crypto.Md5.encode(content);
-			if( sign == currentSign )
-				return;
-		}
-		super.onFileChanged(wasDeleted);
-	}
-
 	override function save() {
 	override function save() {
 		var content = ide.toJSON(data.saveData());
 		var content = ide.toJSON(data.saveData());
 		var newSign = haxe.crypto.Md5.encode(content);
 		var newSign = haxe.crypto.Md5.encode(content);

+ 4 - 2
hide/view/shadereditor/Box.hx

@@ -36,7 +36,8 @@ class Box {
 		}
 		}
 		var className = (metas.name != null) ? metas.name[0] : "Undefined";
 		var className = (metas.name != null) ? metas.name[0] : "Undefined";
 
 
-		element = editor.group(parent).addClass("not-selected");
+		element = editor.group(parent).addClass("box").addClass("not-selected");
+		element.attr("id", node.id);
 		setPosition(x, y);
 		setPosition(x, y);
 
 
 		// outline of box
 		// outline of box
@@ -148,10 +149,11 @@ class Box {
 
 
 	public function setSelected(b : Bool) {
 	public function setSelected(b : Bool) {
 		selected = b;
 		selected = b;
-		element.removeClass();
 		if (b) {
 		if (b) {
+			element.removeClass("not-selected");
 			element.addClass("selected");
 			element.addClass("selected");
 		} else {
 		} else {
+			element.removeClass("selected");
 			element.addClass("not-selected");
 			element.addClass("not-selected");
 		}
 		}
 	}
 	}

+ 4 - 0
hrt/shgraph/NodeVar.hx

@@ -24,6 +24,10 @@ class NodeVar {
 		return node.getOutputType(keyOutput);
 		return node.getOutputType(keyOutput);
 	}
 	}
 
 
+	public function isEmpty() {
+		return node.getOutputTExpr(keyOutput) == null;
+	}
+
 	public function getVar(?type: Type) : TExpr {
 	public function getVar(?type: Type) : TExpr {
 		var currentType = getType();
 		var currentType = getType();
 		if (type == null || currentType == type) {
 		if (type == null || currentType == type) {

+ 4 - 4
hrt/shgraph/Operation.hx

@@ -17,12 +17,12 @@ class Operation extends ShaderNode {
 		this.operation = operation;
 		this.operation = operation;
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (a != null && b != null)
+	override public function computeOutputs() {
+		if (a != null && !a.isEmpty() && b != null && !b.isEmpty())
 			addOutput("output", a.getVar(b.getType()).t);
 			addOutput("output", a.getVar(b.getType()).t);
-		else if (a != null)
+		else if (a != null && !a.isEmpty() )
 			addOutput("output", a.getType());
 			addOutput("output", a.getType());
-		else if (b != null)
+		else if (b != null && !b.isEmpty())
 			addOutput("output", b.getType());
 			addOutput("output", b.getType());
 		else
 		else
 			removeOutput("output");
 			removeOutput("output");

+ 7 - 5
hrt/shgraph/ParseFieldsMacro.hx

@@ -5,8 +5,6 @@ import haxe.macro.Context;
 import haxe.macro.Expr;
 import haxe.macro.Expr;
 using haxe.macro.Tools;
 using haxe.macro.Tools;
 
 
-typedef SField = { name : String, value : ShaderType.SType };
-
 class ParseFieldsMacro {
 class ParseFieldsMacro {
 
 
 #if macro
 #if macro
@@ -15,6 +13,7 @@ class ParseFieldsMacro {
 		var fields = Context.getBuildFields();
 		var fields = Context.getBuildFields();
 
 
 		var mapInputs = new Array<Expr>();
 		var mapInputs = new Array<Expr>();
+		var inputsList = new Array<String>();
 		var hasInputs = false;
 		var hasInputs = false;
 		var mapOutputs = new Array<Expr>();
 		var mapOutputs = new Array<Expr>();
 		var hasOutputs = false;
 		var hasOutputs = false;
@@ -68,10 +67,12 @@ class ParseFieldsMacro {
 							}
 							}
 							if (e == null)
 							if (e == null)
 								Context.error('Input ${sel} has not affectation', f.pos);
 								Context.error('Input ${sel} has not affectation', f.pos);
+
 							var enumValue = ["ShaderType", "SType", e.toString().split(".").pop()];
 							var enumValue = ["ShaderType", "SType", e.toString().split(".").pop()];
-							mapInputs.push(macro $v{sel} => ${enumValue.toFieldExpr()});
+							mapInputs.push(macro $v{sel} => { type : ${enumValue.toFieldExpr()}, hasProperty: $v{hasProperty} });
 							f.kind = FProp("get", "null", TPath({ pack: ["hrt", "shgraph"], name: "NodeVar" }));
 							f.kind = FProp("get", "null", TPath({ pack: ["hrt", "shgraph"], name: "NodeVar" }));
 							f.meta = saveMeta;
 							f.meta = saveMeta;
+							inputsList.push(f.name);
 
 
 							break;
 							break;
 						}
 						}
@@ -100,11 +101,12 @@ class ParseFieldsMacro {
 			fields.push({
 			fields.push({
 				name: "inputsInfo",
 				name: "inputsInfo",
 				access: [Access.APrivate],
 				access: [Access.APrivate],
-				kind: FieldType.FVar(macro:Map<String, ShaderType.SType>, macro $a{mapInputs}),
+				kind: FieldType.FVar(macro:Map<String, ShaderNode.InputInfo>, macro $a{mapInputs}),
 				pos: Context.currentPos(),
 				pos: Context.currentPos(),
 			});
 			});
 			var sfields = macro class {
 			var sfields = macro class {
-				override public function getInputInfo(key : String) : ShaderType.SType return inputsInfo.get(key);
+				override public function getInputInfo(key : String) : ShaderNode.InputInfo return inputsInfo.get(key);
+				override public function getInputInfoKeys() : Array<String> return $v{inputsList};
 			};
 			};
 			for( field in sfields.fields )
 			for( field in sfields.fields )
 				fields.push(field);
 				fields.push(field);

+ 21 - 0
hrt/shgraph/ShaderException.hx

@@ -0,0 +1,21 @@
+package hrt.shgraph;
+
+class ShaderException {
+
+	public var msg : String;
+	public var idBox : Int;
+
+	public function new( msg, idBox) {
+		this.msg = msg;
+		this.idBox = idBox;
+	}
+
+	public function toString() {
+		return 'ShaderException : ${msg} @${idBox}';
+	}
+
+	public static function t( msg : String, idBox : Int) : Dynamic {
+		throw new ShaderException(msg, idBox);
+		return null;
+	}
+}

+ 26 - 8
hrt/shgraph/ShaderGraph.hx

@@ -65,7 +65,7 @@ class ShaderGraph {
 
 
 		node.instance = std.Type.createInstance(nameClass, []);
 		node.instance = std.Type.createInstance(nameClass, []);
 		node.instance.setId(current_node_id);
 		node.instance.setId(current_node_id);
-		node.instance.createOutputs();
+		node.instance.computeOutputs();
 		node.outputs = [];
 		node.outputs = [];
 
 
 		this.nodes.set(node.id, node);
 		this.nodes.set(node.id, node);
@@ -87,12 +87,24 @@ class ShaderGraph {
 			removeEdge(edge.idInput, edge.nameInput, false);
 			removeEdge(edge.idInput, edge.nameInput, false);
 			return false;
 			return false;
 		}
 		}
-		updateOutputs(output);
+		try {
+			updateOutputs(output);
+		} catch (e : Dynamic) {
+			removeEdge(edge.idInput, edge.nameInput);
+			throw e;
+		}
 		return true;
 		return true;
 	}
 	}
 
 
+	public function nodeUpdated(idNode : Int) {
+		var node = this.nodes.get(idNode);
+		if (node != null) {
+			updateOutputs(node);
+		}
+	}
+
 	function updateOutputs(node : Node) {
 	function updateOutputs(node : Node) {
-		node.instance.createOutputs();
+		node.instance.computeOutputs();
 		for (o in node.outputs) {
 		for (o in node.outputs) {
 			updateOutputs(o);
 			updateOutputs(o);
 		}
 		}
@@ -144,7 +156,7 @@ class ShaderGraph {
 			}
 			}
 			counter++;
 			counter++;
 		}
 		}
-		trace(counter, nbNodes);
+
 		return counter != nbNodes;
 		return counter != nbNodes;
 	}
 	}
 
 
@@ -153,9 +165,15 @@ class ShaderGraph {
 		if (node == null)
 		if (node == null)
 			return [];
 			return [];
 		var res = [];
 		var res = [];
-		var inputs = node.getInputs();
-		for (k in inputs) {
-			res = res.concat(buildNodeVar(k));
+		var keys = node.getInputInfoKeys();
+		for (key in keys) {
+			var input = node.getInput(key);
+			if (input != null) {
+				res = res.concat(buildNodeVar(input));
+			} else if (node.getInputInfo(key).hasProperty) {
+			} else {
+				throw ShaderException.t("This box has inputs not connected", node.id);
+			}
 		}
 		}
 		var build = nodeVar.getExpr();
 		var build = nodeVar.getExpr();
 		res = res.concat(build);
 		res = res.concat(build);
@@ -234,6 +252,6 @@ class ShaderGraph {
 			edges: edgesJson
 			edges: edgesJson
 		});
 		});
 
 
-		sys.io.File.saveContent(this.filepath, json);
+		return json;
 	}
 	}
 }
 }

+ 1 - 1
hrt/shgraph/ShaderInput.hx

@@ -6,7 +6,7 @@ import hxsl.*;
 using hxsl.Ast;
 using hxsl.Ast;
 
 
 @name("Inputs")
 @name("Inputs")
-@description("Parameters inputs, it's dynamic")
+@description("Shader inputs of Heaps, it's dynamic")
 @group("Input")
 @group("Input")
 @noheader()
 @noheader()
 class ShaderInput extends ShaderNode {
 class ShaderInput extends ShaderNode {

+ 11 - 3
hrt/shgraph/ShaderNode.hx

@@ -3,6 +3,8 @@ package hrt.shgraph;
 import hide.Element;
 import hide.Element;
 using hxsl.Ast;
 using hxsl.Ast;
 
 
+typedef InputInfo = { type : ShaderType.SType, hasProperty : Bool };
+
 @:autoBuild(hrt.shgraph.ParseFieldsMacro.build())
 @:autoBuild(hrt.shgraph.ParseFieldsMacro.build())
 @:keepSub
 @:keepSub
 class ShaderNode {
 class ShaderNode {
@@ -61,7 +63,7 @@ class ShaderNode {
 		outputs.set(tVar.name, tVar);
 		outputs.set(tVar.name, tVar);
 	}
 	}
 
 
-	public function createOutputs() : Void {}
+	public function computeOutputs() : Void {}
 
 
 	public function getOutput(key : String) : TVar {
 	public function getOutput(key : String) : TVar {
 		return outputs.get(key);
 		return outputs.get(key);
@@ -76,6 +78,8 @@ class ShaderNode {
 
 
 	public function getOutputTExpr(key : String) : TExpr {
 	public function getOutputTExpr(key : String) : TExpr {
 		var o = getOutput(key);
 		var o = getOutput(key);
+		if (o == null)
+			return null;
 		return {
 		return {
 			e: TVar(o),
 			e: TVar(o),
 			p: null,
 			p: null,
@@ -88,7 +92,7 @@ class ShaderNode {
 	}
 	}
 
 
 	public function checkTypeAndCompatibilyInput(key : String, type : ShaderType.SType) : Bool {
 	public function checkTypeAndCompatibilyInput(key : String, type : ShaderType.SType) : Bool {
-		var infoKey = getInputInfo(key);
+		var infoKey = getInputInfo(key).type;
 		if (infoKey != null && !(ShaderType.checkConversion(type, infoKey))) {
 		if (infoKey != null && !(ShaderType.checkConversion(type, infoKey))) {
 			return false;
 			return false;
 		}
 		}
@@ -99,7 +103,11 @@ class ShaderNode {
 		return true;
 		return true;
 	}
 	}
 
 
-	public function getInputInfo(key : String) : ShaderType.SType {
+	public function getInputInfoKeys() : Array<String> {
+		return [];
+	}
+
+	public function getInputInfo(key : String) : InputInfo {
 		return null;
 		return null;
 	}
 	}
 
 

+ 75 - 0
hrt/shgraph/ShaderParam.hx

@@ -0,0 +1,75 @@
+package hrt.shgraph;
+
+import hide.Element;
+import hxsl.*;
+
+using hxsl.Ast;
+
+@name("Param")
+@description("Parameters inputs, it's dynamic")
+@group("Input")
+@noheader()
+class ShaderParam extends ShaderNode {
+
+	@output() var output = SType.Variant;
+
+	@param("Variable") public var variable : TVar;
+
+	override public function getOutput(key : String) : TVar {
+		return variable;
+	}
+
+	override public function build(key : String) : TExpr {
+		return null;
+	}
+
+	override public function loadProperties(props : Dynamic) {
+		var paramVariable : String = Reflect.field(props, "variable");
+
+		for (c in ShaderNode.availableVariables) {
+			if (c.name == paramVariable) {
+				this.variable = c;
+				return;
+			}
+		}
+	}
+
+	override public function saveProperties() : Dynamic {
+		var parameters = {
+			variable: variable.name
+		};
+
+		return parameters;
+	}
+
+	#if editor
+	override public function getPropertiesHTML(width : Float) : Array<Element> {
+		var elements = super.getPropertiesHTML(width);
+		var element = new Element('<div style="width: 110px; height: 30px"></div>');
+		element.append(new Element('<select id="variable"></select>'));
+
+		if (this.variable == null) {
+			this.variable = ShaderNode.availableVariables[0];
+		}
+		var input = element.children("select");
+		var indexOption = 0;
+		for (c in ShaderNode.availableVariables) {
+			input.append(new Element('<option value="${indexOption}">${c.name}</option>'));
+			if (this.variable.name == c.name) {
+				input.val(indexOption);
+			}
+			indexOption++;
+		}
+		input.on("change", function(e) {
+			var value = input.val();
+			this.variable = ShaderNode.availableVariables[value];
+		});
+
+
+		elements.push(element);
+
+		return elements;
+	}
+	#end
+
+}

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

@@ -15,8 +15,8 @@ class Abs extends ShaderFunction {
 		super(Abs);
 		super(Abs);
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (a != null)
+	override public function computeOutputs() {
+		if (a != null && !a.isEmpty())
 			addOutput("output", a.getType());
 			addOutput("output", a.getType());
 		else
 		else
 			removeOutput("output");
 			removeOutput("output");

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

@@ -16,7 +16,7 @@ class Color extends ShaderNode {
 	@prop() var b : Float = 0;
 	@prop() var b : Float = 0;
 	@prop() var a : Float = 1;
 	@prop() var a : Float = 1;
 
 
-	override public function createOutputs() {
+	override public function computeOutputs() {
 		addOutput("output", TVec(4, VFloat));
 		addOutput("output", TVec(4, VFloat));
 	}
 	}
 
 

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

@@ -39,7 +39,7 @@ class Combine extends ShaderNode {
 				};
 				};
 	}
 	}
 
 
-	override public function createOutputs() {
+	override public function computeOutputs() {
 		addOutput("output", TVec(4, VFloat));
 		addOutput("output", TVec(4, VFloat));
 	}
 	}
 
 

+ 6 - 5
hrt/shgraph/nodes/Cond.hx

@@ -17,21 +17,22 @@ class Cond extends ShaderNode {
 
 
 	override public function checkValidityInput(key : String, type : ShaderType.SType) : Bool {
 	override public function checkValidityInput(key : String, type : ShaderType.SType) : Bool {
 
 
-		if (key == "leftVar" && rightVar != null)
+		if (key == "leftVar" && rightVar != null && !rightVar.isEmpty())
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(rightVar.getType()));
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(rightVar.getType()));
 
 
-		if (key == "rightVar" && leftVar != null)
+		if (key == "rightVar" && leftVar != null && !leftVar.isEmpty())
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(leftVar.getType()));
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(leftVar.getType()));
 
 
 		return true;
 		return true;
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (leftVar != null && leftVar.getType() != null && rightVar != null && rightVar.getType() != null) {
+	override public function computeOutputs() {
+		if (leftVar != null && !leftVar.isEmpty() && rightVar != null && !rightVar.isEmpty()) {
 			var type = leftVar.getVar(rightVar.getType()).t;
 			var type = leftVar.getVar(rightVar.getType()).t;
 			switch(type) {
 			switch(type) {
 				case TVec(s, t):
 				case TVec(s, t):
-					throw "Vector of bools not supported";//addOutput("output", TVec(s, VBool));
+					removeOutput("output");
+					throw ShaderException.t("Vector of bools is not supported", this.id); //addOutput("output", TVec(s, VBool));
 				case TFloat:
 				case TFloat:
 					addOutput("output", TBool);
 					addOutput("output", TBool);
 				default:
 				default:

+ 6 - 6
hrt/shgraph/nodes/IfCondition.hx

@@ -15,21 +15,21 @@ class IfCondition extends ShaderNode {
 
 
 	override public function checkValidityInput(key : String, type : ShaderType.SType) : Bool {
 	override public function checkValidityInput(key : String, type : ShaderType.SType) : Bool {
 
 
-		if (key == "trueVar" && falseVar != null)
+		if (key == "trueVar" && falseVar != null && !falseVar.isEmpty())
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(falseVar.getType()));
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(falseVar.getType()));
 
 
-		if (key == "falseVar" && trueVar != null)
+		if (key == "falseVar" && trueVar != null && !trueVar.isEmpty())
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(trueVar.getType()));
 			return ShaderType.checkCompatibilities(type, ShaderType.getType(trueVar.getType()));
 
 
 		return true;
 		return true;
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (trueVar != null && falseVar != null)
+	override public function computeOutputs() {
+		if (trueVar != null && !trueVar.isEmpty() && falseVar != null && !falseVar.isEmpty())
 			addOutput("output", trueVar.getVar(falseVar.getType()).t);
 			addOutput("output", trueVar.getVar(falseVar.getType()).t);
-		else if (trueVar != null)
+		else if (trueVar != null && !trueVar.isEmpty())
 			addOutput("output", trueVar.getType());
 			addOutput("output", trueVar.getType());
-		else if (falseVar != null)
+		else if (falseVar != null && !falseVar.isEmpty())
 			addOutput("output", falseVar.getType());
 			addOutput("output", falseVar.getType());
 		else
 		else
 			removeOutput("output");
 			removeOutput("output");

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

@@ -16,10 +16,10 @@ class Maximum extends ShaderFunction {
 		super(Max);
 		super(Max);
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (a != null)
+	override public function computeOutputs() {
+		if (a != null && !a.isEmpty())
 			addOutput("output", a.getType());
 			addOutput("output", a.getType());
-		else if (b != null)
+		else if (b != null && !b.isEmpty())
 			addOutput("output", b.getType());
 			addOutput("output", b.getType());
 		else
 		else
 			removeOutput("output");
 			removeOutput("output");

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

@@ -16,10 +16,10 @@ class Minimum extends ShaderFunction {
 		super(Min);
 		super(Min);
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (a != null)
+	override public function computeOutputs() {
+		if (a != null && !a.isEmpty())
 			addOutput("output", a.getType());
 			addOutput("output", a.getType());
-		else if (b != null)
+		else if (b != null && !b.isEmpty())
 			addOutput("output", b.getType());
 			addOutput("output", b.getType());
 		else
 		else
 			removeOutput("output");
 			removeOutput("output");

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

@@ -16,7 +16,7 @@ class Sampler extends ShaderFunction {
 		super(Texture);
 		super(Texture);
 	}
 	}
 
 
-	override public function createOutputs() {
+	override public function computeOutputs() {
 		addOutput("rgba", TVec(4, VFloat));
 		addOutput("rgba", TVec(4, VFloat));
 	}
 	}
 
 

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

@@ -15,8 +15,8 @@ class Sin extends ShaderFunction {
 		super(Sin);
 		super(Sin);
 	}
 	}
 
 
-	override public function createOutputs() {
-		if (a != null)
+	override public function computeOutputs() {
+		if (a != null && !a.isEmpty())
 			addOutput("output", a.getType());
 			addOutput("output", a.getType());
 		else
 		else
 			removeOutput("output");
 			removeOutput("output");

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

@@ -19,7 +19,7 @@ class Split extends ShaderNode {
 	var components = [X, Y, Z, W];
 	var components = [X, Y, Z, W];
 	var componentsString = ["r", "g", "b", "a"];
 	var componentsString = ["r", "g", "b", "a"];
 
 
-	override public function createOutputs() {
+	override public function computeOutputs() {
 		addOutput("r", TFloat);
 		addOutput("r", TFloat);
 		addOutput("g", TFloat);
 		addOutput("g", TFloat);
 		addOutput("b", TFloat);
 		addOutput("b", TFloat);
@@ -32,7 +32,7 @@ class Split extends ShaderNode {
 					e: TVar(getOutput(key)),
 					e: TVar(getOutput(key)),
 					p: null,
 					p: null,
 					t: getOutput(key).type
 					t: getOutput(key).type
-				}, {e: TSwiz(input.getVar(), [components[compIdx]]), p: null, t: getOutput(key).type }),
+				}, {e: TSwiz(input.getVar(TVec(4, VFloat)), [components[compIdx]]), p: null, t: getOutput(key).type }),
 				p: null,
 				p: null,
 				t: getOutput(key).type
 				t: getOutput(key).type
 			};
 			};

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

@@ -13,7 +13,7 @@ class Texture extends ShaderNode {
 
 
 	@prop() var fileTexture : String;
 	@prop() var fileTexture : String;
 
 
-	override public function createOutputs() {
+	override public function computeOutputs() {
 		addOutput("rgba", TSampler2D);
 		addOutput("rgba", TSampler2D);
 	}
 	}