Browse Source

ShaderGraph: refactoring create graph view and shadereditor view

Tom SPIRA 6 years ago
parent
commit
0389439d8f
6 changed files with 719 additions and 655 deletions
  1. 74 74
      bin/style.css
  2. 1 1
      bin/style.less
  3. 30 577
      hide/view/Graph.hx
  4. 2 2
      hide/view/shadereditor/Box.hx
  5. 611 0
      hide/view/shadereditor/ShaderEditor.hx
  6. 1 1
      hrt/shgraph/ShaderGraph.hx

+ 74 - 74
bin/style.css

@@ -1030,89 +1030,89 @@ input[type=checkbox]:checked:after {
   visibility: visible;
   visibility: visible;
 }
 }
 /* Shader Editor */
 /* Shader Editor */
-.shader-view .tabs {
+.graph-view .tabs {
   z-index: 1;
   z-index: 1;
   background: #222222;
   background: #222222;
   height: 100%;
   height: 100%;
   user-select: none;
   user-select: none;
 }
 }
-.shader-view .tabs .tab {
+.graph-view .tabs .tab {
   height: 100%;
   height: 100%;
 }
 }
-.shader-view .tabs .tab .hide-block {
+.graph-view .tabs .tab .hide-block {
   height: 70%;
   height: 70%;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList {
+.graph-view .tabs .tab .hide-block #parametersList {
   height: 100%;
   height: 100%;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter {
+.graph-view .tabs .tab .hide-block #parametersList .parameter {
   border: 1px solid;
   border: 1px solid;
   margin: 5px;
   margin: 5px;
   background-color: #222;
   background-color: #222;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .header {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .header {
   background-color: #d6d6d6;
   background-color: #d6d6d6;
   height: 20px;
   height: 20px;
   padding: 5px;
   padding: 5px;
   color: black;
   color: black;
   cursor: pointer;
   cursor: pointer;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .header .title {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .header .title {
   float: left;
   float: left;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .header .title input {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .header .title input {
   width: 130px;
   width: 130px;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .header .type {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .header .type {
   float: right;
   float: right;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .header .type span {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .header .type span {
   color: black;
   color: black;
   font-style: italic;
   font-style: italic;
   font-size: 13px;
   font-size: 13px;
   padding-right: 5px;
   padding-right: 5px;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .content {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .content {
   background-color: #b3b3b3;
   background-color: #b3b3b3;
   padding: 5px;
   padding: 5px;
   box-shadow: inset 0px 13px 6px -15px black;
   box-shadow: inset 0px 13px 6px -15px black;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .content div {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .content div {
   width: 100%;
   width: 100%;
   height: 20px;
   height: 20px;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .content div span {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .content div span {
   float: left;
   float: left;
   color: black;
   color: black;
   font-size: 13px;
   font-size: 13px;
   padding-right: 5px;
   padding-right: 5px;
   font-weight: normal;
   font-weight: normal;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter .content .action-btns input {
+.graph-view .tabs .tab .hide-block #parametersList .parameter .content .action-btns input {
   float: right;
   float: right;
 }
 }
-.shader-view .tabs .tab .hide-block #parametersList .parameter > span {
+.graph-view .tabs .tab .hide-block #parametersList .parameter > span {
   font-size: 12px;
   font-size: 12px;
 }
 }
-.shader-view .tabs .tab .options-block {
+.graph-view .tabs .tab .options-block {
   height: 25%;
   height: 25%;
 }
 }
-.shader-view .tabs .tab .options-block > * {
+.graph-view .tabs .tab .options-block > * {
   display: block;
   display: block;
   margin-bottom: 10px;
   margin-bottom: 10px;
   width: 100%;
   width: 100%;
 }
 }
-.shader-view #add-menu {
+.graph-view #add-menu {
   position: absolute;
   position: absolute;
   width: 400px;
   width: 400px;
   box-shadow: 0px 0px 25px -4px black;
   box-shadow: 0px 0px 25px -4px black;
   background-color: #222222;
   background-color: #222222;
   border: 1px solid #444;
   border: 1px solid #444;
 }
 }
-.shader-view #add-menu .search-container {
+.graph-view #add-menu .search-container {
   display: flex;
   display: flex;
 }
 }
-.shader-view #add-menu .search-container .icon {
+.graph-view #add-menu .search-container .icon {
   width: 16px;
   width: 16px;
   font-size: 15px;
   font-size: 15px;
   text-align: center;
   text-align: center;
@@ -1121,61 +1121,61 @@ input[type=checkbox]:checked:after {
   background: #2d2d2d;
   background: #2d2d2d;
   border-width: 0 1px 1px 0;
   border-width: 0 1px 1px 0;
 }
 }
-.shader-view #add-menu .search-container .search-bar {
+.graph-view #add-menu .search-container .search-bar {
   flex: 1;
   flex: 1;
   margin: -1px 11px 0px -1px;
   margin: -1px 11px 0px -1px;
 }
 }
-.shader-view #add-menu .search-container .search-bar input {
+.graph-view #add-menu .search-container .search-bar input {
   width: 100%;
   width: 100%;
   height: 30px;
   height: 30px;
   padding-left: 10px;
   padding-left: 10px;
   outline: none;
   outline: none;
 }
 }
-.shader-view #add-menu .search-container .search-bar input:focus {
+.graph-view #add-menu .search-container .search-bar input:focus {
   border: 1px solid #888888;
   border: 1px solid #888888;
 }
 }
-.shader-view #add-menu #results {
+.graph-view #add-menu #results {
   max-height: 300px;
   max-height: 300px;
   overflow-y: auto;
   overflow-y: auto;
 }
 }
-.shader-view #add-menu #results div {
+.graph-view #add-menu #results div {
   padding: 5px;
   padding: 5px;
   border-top: 1px solid #444444;
   border-top: 1px solid #444444;
   cursor: pointer;
   cursor: pointer;
   color: #d4d4d4;
   color: #d4d4d4;
   position: relative;
   position: relative;
 }
 }
-.shader-view #add-menu #results div.group {
+.graph-view #add-menu #results div.group {
   background: #474747;
   background: #474747;
   color: #c0c0c0;
   color: #c0c0c0;
   cursor: default;
   cursor: default;
   z-index: 1;
   z-index: 1;
   box-shadow: 0px 0px 34px -9px black;
   box-shadow: 0px 0px 34px -9px black;
 }
 }
-.shader-view #add-menu #results div.group:hover {
+.graph-view #add-menu #results div.group:hover {
   background: #474747!important;
   background: #474747!important;
   color: #dadada!important;
   color: #dadada!important;
   box-shadow: none;
   box-shadow: none;
 }
 }
-.shader-view #add-menu #results div > span:first-child {
+.graph-view #add-menu #results div > span:first-child {
   font-weight: bold;
   font-weight: bold;
   padding-left: 10px;
   padding-left: 10px;
 }
 }
-.shader-view #add-menu #results div > span:last-child {
+.graph-view #add-menu #results div > span:last-child {
   font-style: oblique;
   font-style: oblique;
   padding-left: 7px;
   padding-left: 7px;
 }
 }
-.shader-view #add-menu #results div:first-child {
+.graph-view #add-menu #results div:first-child {
   border-top: 0;
   border-top: 0;
 }
 }
-.shader-view #add-menu #results div:hover,
-.shader-view #add-menu #results div.selected {
+.graph-view #add-menu #results div:hover,
+.graph-view #add-menu #results div.selected {
   background: #5eb7bf;
   background: #5eb7bf;
   color: #000000;
   color: #000000;
   box-shadow: inset 0px 0px 12px -1px black;
   box-shadow: inset 0px 0px 12px -1px black;
   z-index: 3;
   z-index: 3;
 }
 }
-.shader-view #context-menu {
+.graph-view #context-menu {
   position: absolute;
   position: absolute;
   width: 100px;
   width: 100px;
   box-shadow: 0px 0px 25px -4px black;
   box-shadow: 0px 0px 25px -4px black;
@@ -1183,51 +1183,51 @@ input[type=checkbox]:checked:after {
   border: 1px solid #444;
   border: 1px solid #444;
   z-index: 2;
   z-index: 2;
 }
 }
-.shader-view #context-menu #options {
+.graph-view #context-menu #options {
   max-height: 300px;
   max-height: 300px;
   overflow-y: auto;
   overflow-y: auto;
 }
 }
-.shader-view #context-menu #options div {
+.graph-view #context-menu #options div {
   padding: 5px;
   padding: 5px;
   border-top: 1px solid #444444;
   border-top: 1px solid #444444;
   cursor: pointer;
   cursor: pointer;
   color: #d4d4d4;
   color: #d4d4d4;
   position: relative;
   position: relative;
 }
 }
-.shader-view #context-menu #options div.group {
+.graph-view #context-menu #options div.group {
   background: #474747;
   background: #474747;
   color: #c0c0c0;
   color: #c0c0c0;
   cursor: default;
   cursor: default;
   z-index: 3;
   z-index: 3;
   box-shadow: 0px 0px 34px -9px black;
   box-shadow: 0px 0px 34px -9px black;
 }
 }
-.shader-view #context-menu #options div.group:hover {
+.graph-view #context-menu #options div.group:hover {
   background: #474747!important;
   background: #474747!important;
   color: #dadada!important;
   color: #dadada!important;
   box-shadow: none;
   box-shadow: none;
 }
 }
-.shader-view #context-menu #options div > span:first-child {
+.graph-view #context-menu #options div > span:first-child {
   font-weight: bold;
   font-weight: bold;
   padding-left: 10px;
   padding-left: 10px;
 }
 }
-.shader-view #context-menu #options div > span:last-child {
+.graph-view #context-menu #options div > span:last-child {
   font-style: oblique;
   font-style: oblique;
   padding-left: 7px;
   padding-left: 7px;
 }
 }
-.shader-view #context-menu #options div:first-child {
+.graph-view #context-menu #options div:first-child {
   border-top: 0;
   border-top: 0;
 }
 }
-.shader-view #context-menu #options div:hover,
-.shader-view #context-menu #options div.selected {
+.graph-view #context-menu #options div:hover,
+.graph-view #context-menu #options div.selected {
   background: #5eb7bf;
   background: #5eb7bf;
   color: #000000;
   color: #000000;
   box-shadow: inset 0px 0px 12px -1px black;
   box-shadow: inset 0px 0px 12px -1px black;
   z-index: 3;
   z-index: 3;
 }
 }
-.shader-view .heaps-scene {
+.graph-view .heaps-scene {
   outline: none !important;
   outline: none !important;
 }
 }
-.shader-view .heaps-scene #preview {
+.graph-view .heaps-scene #preview {
   position: absolute;
   position: absolute;
   width: 300px;
   width: 300px;
   height: 300px;
   height: 300px;
@@ -1236,7 +1236,7 @@ input[type=checkbox]:checked:after {
   right: 290px;
   right: 290px;
   bottom: 35px;
   bottom: 35px;
 }
 }
-.shader-view .heaps-scene #status-bar {
+.graph-view .heaps-scene #status-bar {
   position: absolute;
   position: absolute;
   width: 84%;
   width: 84%;
   min-height: 20px;
   min-height: 20px;
@@ -1245,96 +1245,96 @@ input[type=checkbox]:checked:after {
   left: -1px;
   left: -1px;
   bottom: -1px;
   bottom: -1px;
 }
 }
-.shader-view .heaps-scene #status-bar span {
+.graph-view .heaps-scene #status-bar span {
   padding: 10px;
   padding: 10px;
   vertical-align: sub;
   vertical-align: sub;
 }
 }
-.shader-view .heaps-scene #status-bar span.error {
+.graph-view .heaps-scene #status-bar span.error {
   color: #c74848;
   color: #c74848;
 }
 }
-.shader-view .heaps-scene svg g {
+.graph-view .heaps-scene svg g {
   stroke: #202020;
   stroke: #202020;
 }
 }
-.shader-view .heaps-scene svg g.selected .outline {
+.graph-view .heaps-scene svg g.selected .outline {
   stroke: #668fff;
   stroke: #668fff;
 }
 }
-.shader-view .heaps-scene svg g.error .outline {
+.graph-view .heaps-scene svg g.error .outline {
   stroke: #ff6666;
   stroke: #ff6666;
 }
 }
-.shader-view .heaps-scene svg .outline {
+.graph-view .heaps-scene svg .outline {
   fill: none;
   fill: none;
 }
 }
-.shader-view .heaps-scene svg .head-box {
+.graph-view .heaps-scene svg .head-box {
   fill: #3e3e3e;
   fill: #3e3e3e;
 }
 }
-.shader-view .heaps-scene svg .title-box {
+.graph-view .heaps-scene svg .title-box {
   fill: #ffffff;
   fill: #ffffff;
   stroke: none;
   stroke: none;
   user-select: none;
   user-select: none;
 }
 }
-.shader-view .heaps-scene svg .title-box::selection {
+.graph-view .heaps-scene svg .title-box::selection {
   background: none;
   background: none;
 }
 }
-.shader-view .heaps-scene svg .nodes,
-.shader-view .heaps-scene svg .properties {
+.graph-view .heaps-scene svg .nodes,
+.graph-view .heaps-scene svg .properties {
   fill: #737373;
   fill: #737373;
   opacity: 0.75;
   opacity: 0.75;
 }
 }
-.shader-view .heaps-scene svg .hasLink .input-field {
+.graph-view .heaps-scene svg .hasLink .input-field {
   display: none;
   display: none;
 }
 }
-.shader-view .heaps-scene svg .input-node,
-.shader-view .heaps-scene svg .output-node {
+.graph-view .heaps-scene svg .input-node,
+.graph-view .heaps-scene svg .output-node {
   fill: #c8c8c8;
   fill: #c8c8c8;
   stroke-width: 13px;
   stroke-width: 13px;
   stroke-opacity: 0;
   stroke-opacity: 0;
 }
 }
-.shader-view .heaps-scene svg .input-node.nodeMatch,
-.shader-view .heaps-scene svg .output-node.nodeMatch {
+.graph-view .heaps-scene svg .input-node.nodeMatch,
+.graph-view .heaps-scene svg .output-node.nodeMatch {
   fill: #5ca4ab;
   fill: #5ca4ab;
 }
 }
-.shader-view .heaps-scene svg .title-node {
+.graph-view .heaps-scene svg .title-node {
   fill: #ffffff;
   fill: #ffffff;
   stroke: none;
   stroke: none;
   user-select: none;
   user-select: none;
 }
 }
-.shader-view .heaps-scene svg .title-node::selection {
+.graph-view .heaps-scene svg .title-node::selection {
   background: none;
   background: none;
 }
 }
-.shader-view .heaps-scene svg .properties-group span {
+.graph-view .heaps-scene svg .properties-group span {
   color: #ffffff;
   color: #ffffff;
   user-select: none;
   user-select: none;
 }
 }
-.shader-view .heaps-scene svg .properties-group span::selection {
+.graph-view .heaps-scene svg .properties-group span::selection {
   background: none;
   background: none;
 }
 }
-.shader-view .heaps-scene svg .properties-group input[type=text],
-.shader-view .heaps-scene svg .properties-group select {
+.graph-view .heaps-scene svg .properties-group input[type=text],
+.graph-view .heaps-scene svg .properties-group select {
   width: 100%;
   width: 100%;
 }
 }
-.shader-view .heaps-scene svg .properties-group input[type=text].error {
+.graph-view .heaps-scene svg .properties-group input[type=text].error {
   border: red 1px solid;
   border: red 1px solid;
 }
 }
-.shader-view .heaps-scene svg .edge {
+.graph-view .heaps-scene svg .edge {
   stroke-width: 2;
   stroke-width: 2;
   stroke: #c8c8c8;
   stroke: #c8c8c8;
   fill: transparent;
   fill: transparent;
 }
 }
-.shader-view .heaps-scene svg .edge:not(.draft):hover,
-.shader-view .heaps-scene svg .edge:not(.draft).selected {
+.graph-view .heaps-scene svg .edge:not(.draft):hover,
+.graph-view .heaps-scene svg .edge:not(.draft).selected {
   stroke-width: 5;
   stroke-width: 5;
   stroke: #72b4ff;
   stroke: #72b4ff;
 }
 }
-.shader-view .heaps-scene svg .rect-selection {
+.graph-view .heaps-scene svg .rect-selection {
   stroke: rgba(0, 0, 255, 0.7);
   stroke: rgba(0, 0, 255, 0.7);
   fill: rgba(70, 70, 116, 0.08);
   fill: rgba(70, 70, 116, 0.08);
 }
 }
-.shader-view .tabs {
+.graph-view .tabs {
   width: 280px;
   width: 280px;
   padding-top: 10px;
   padding-top: 10px;
   border-left: 1px solid #444444;
   border-left: 1px solid #444444;
 }
 }
-.shader-view .tabs span {
+.graph-view .tabs span {
   margin-left: 5px;
   margin-left: 5px;
   font-size: 15px;
   font-size: 15px;
   font-weight: bold;
   font-weight: bold;

+ 1 - 1
bin/style.less

@@ -1145,7 +1145,7 @@ input[type=checkbox] {
 }
 }
 
 
 /* Shader Editor */
 /* Shader Editor */
-.shader-view {
+.graph-view {
 	.tabs {
 	.tabs {
 		z-index: 1;
 		z-index: 1;
     	background: #222222;
     	background: #222222;

+ 30 - 577
hide/view/ShaderEditor.hx → hide/view/Graph.hx

@@ -1,22 +1,13 @@
 package hide.view;
 package hide.view;
 
 
-import hrt.shgraph.ShaderException;
 import haxe.Timer;
 import haxe.Timer;
-import h3d.shader.LineShader;
-import h3d.shader.ColorAdd;
-using hxsl.Ast.Type;
 
 
-import haxe.rtti.Rtti;
 import haxe.rtti.Meta;
 import haxe.rtti.Meta;
-import hxsl.Shader;
-import hxsl.SharedShader;
-import hide.comp.SceneEditor;
 import js.jquery.JQuery;
 import js.jquery.JQuery;
 import h2d.col.Point;
 import h2d.col.Point;
 import h2d.col.IPoint;
 import h2d.col.IPoint;
 import hide.comp.SVG;
 import hide.comp.SVG;
 import hide.view.shadereditor.Box;
 import hide.view.shadereditor.Box;
-import hrt.shgraph.ShaderGraph;
 import hrt.shgraph.ShaderNode;
 import hrt.shgraph.ShaderNode;
 import hrt.shgraph.ShaderType;
 import hrt.shgraph.ShaderType;
 import hrt.shgraph.ShaderType.SType;
 import hrt.shgraph.ShaderType.SType;
@@ -26,13 +17,12 @@ typedef NodeInfo = { name : String, description : String, key : String };
 
 
 typedef Edge = { from : Box, nodeFrom : JQuery, to : Box, nodeTo : JQuery, elt : JQuery };
 typedef Edge = { from : Box, nodeFrom : JQuery, to : Box, nodeTo : JQuery, elt : JQuery };
 
 
-class ShaderEditor extends FileView {
+class Graph extends FileView {
 
 
 	var parent : JQuery;
 	var parent : JQuery;
 	var editor : SVG;
 	var editor : SVG;
 	var editorMatrix : JQuery;
 	var editorMatrix : JQuery;
 	var statusBar : JQuery;
 	var statusBar : JQuery;
-	var parametersList : JQuery;
 
 
 	var listOfClasses : Map<String, Array<NodeInfo>>;
 	var listOfClasses : Map<String, Array<NodeInfo>>;
 	var addMenu : JQuery;
 	var addMenu : JQuery;
@@ -45,6 +35,11 @@ class ShaderEditor extends FileView {
 
 
 	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;
+
+
+	// used for moving when mouse is close to borders
+	static var BORDER_SIZE = 75;
+	static var SPEED_BORDER_MOVE = 0.05;
 	var timerUpdateView : Timer;
 	var timerUpdateView : Timer;
 
 
 	// used for selection
 	// used for selection
@@ -66,30 +61,13 @@ class ShaderEditor extends FileView {
 	// used for deleting
 	// used for deleting
 	var currentEdge : Edge;
 	var currentEdge : Edge;
 
 
-	// used to preview
-	var sceneEditor : SceneEditor;
-
-	var root : hrt.prefab.Prefab;
-	var obj : h3d.scene.Object;
-	var plight : hrt.prefab.Prefab;
-	var light : h3d.scene.Object;
-	var lightDirection : h3d.Vector;
-
-	var shaderGraph : ShaderGraph;
-
-	var timerCompileShader : Timer;
-	var COMPILE_SHADER_DEBOUNCE : Int = 100;
-	var shaderGenerated : Shader;
-
 	override function onDisplay() {
 	override function onDisplay() {
-		shaderGraph = new ShaderGraph(getPath());
-		addMenu = null;
 		element.html('
 		element.html('
 			<div class="flex vertical">
 			<div class="flex vertical">
-				<div class="flex-elt shader-view">
+				<div class="flex-elt graph-view">
 					<div class="heaps-scene" tabindex="0" >
 					<div class="heaps-scene" tabindex="0" >
 					</div>
 					</div>
-					<div class="tabs">
+					<div id="rightPanel" class="tabs">
 						<span>Parameters</span>
 						<span>Parameters</span>
 						<div class="tab expand" name="Scene" icon="sitemap">
 						<div class="tab expand" name="Scene" icon="sitemap">
 							<div class="hide-block" >
 							<div class="hide-block" >
@@ -107,46 +85,13 @@ class ShaderEditor extends FileView {
 			</div>');
 			</div>');
 		parent = element.find(".heaps-scene");
 		parent = element.find(".heaps-scene");
 		editor = new SVG(parent);
 		editor = new SVG(parent);
-		var preview = new Element('<div id="preview" ></div>');
-		preview.on("mousedown", function(e) { e.stopPropagation(); });
-		preview.on("wheel", function(e) { e.stopPropagation(); });
-		parent.append(preview);
 		statusBar = new Element('<div id="status-bar" ><span> </span></div>').appendTo(parent).find("span");
 		statusBar = new Element('<div id="status-bar" ><span> </span></div>').appendTo(parent).find("span");
 
 
-		var def = new hrt.prefab.Library();
-		new hrt.prefab.RenderProps(def).name = "renderer";
-		var l = new hrt.prefab.Light(def);
-		l.name = "sunLight";
-		l.kind = Directional;
-		l.power = 1.5;
-		var q = new h3d.Quat();
-		q.initDirection(new h3d.Vector(-1,-1.5,-3));
-		var a = q.toEuler();
-		l.rotationX = Math.round(a.x * 180 / Math.PI);
-		l.rotationY = Math.round(a.y * 180 / Math.PI);
-		l.rotationZ = Math.round(a.z * 180 / Math.PI);
-		l.shadows.mode = Dynamic;
-		l.shadows.size = 1024;
-		root = def;
-
-		sceneEditor = new hide.comp.SceneEditor(this, root);
-		sceneEditor.editorDisplay = false;
-		sceneEditor.onRefresh = onRefresh;
-		sceneEditor.onUpdate = update;
-		sceneEditor.view.keys = new hide.ui.Keys(null); // Remove SceneEditor Shortcuts
-		sceneEditor.view.keys.register("save", function() {
-			save();
-			skipNextChange = true;
-			modified = false;
-		});
-
 		editorMatrix = editor.group(editor.element);
 		editorMatrix = editor.group(editor.element);
 
 
 		// rectangle Selection
 		// rectangle Selection
 		parent.on("mousedown", function(e) {
 		parent.on("mousedown", function(e) {
 
 
-			closeAddMenu();
-
 			if (e.button == 0) {
 			if (e.button == 0) {
 				startRecSelection = new Point(lX(e.clientX), lY(e.clientY));
 				startRecSelection = new Point(lX(e.clientX), lY(e.clientY));
 				if (currentEdge != null) {
 				if (currentEdge != null) {
@@ -176,20 +121,6 @@ class ShaderEditor extends FileView {
 			if(timerUpdateView != null)
 			if(timerUpdateView != null)
 				stopUpdateViewPosition();
 				stopUpdateViewPosition();
 			if (e.button == 0) {
 			if (e.button == 0) {
-				// Stop link creation
-				if (isCreatingLink != None) {
-					if (startLinkBox != null && endLinkBox != null && createEdgeInShaderGraph()) {
-
-					} else {
-						if (currentLink != null) currentLink.remove();
-						currentLink = null;
-					}
-					startLinkBox = endLinkBox = null;
-					startLinkGrNode = endLinkNode = null;
-					isCreatingLink = None;
-					clearAvailableNodes();
-					return;
-				}
 
 
 				// Stop rectangle selection
 				// Stop rectangle selection
 				lastClickDrag = null;
 				lastClickDrag = null;
@@ -224,12 +155,6 @@ class ShaderEditor extends FileView {
 
 
 		parent.on("keydown", function(e) {
 		parent.on("keydown", function(e) {
 
 
-			if (e.shiftKey && e.keyCode != 16) {
-				openAddMenu();
-
-				return;
-			}
-
 			if (e.keyCode == 46) {
 			if (e.keyCode == 46) {
 				if (currentEdge != null) {
 				if (currentEdge != null) {
 					removeEdge(currentEdge);
 					removeEdge(currentEdge);
@@ -243,269 +168,13 @@ class ShaderEditor extends FileView {
 				return;
 				return;
 			} else if (e.keyCode == 32) {
 			} else if (e.keyCode == 32) {
 
 
-			} else if (e.keyCode == 83 && e.ctrlKey) { // CTRL+S : save
-				shaderGraph.save();
-			} else if (e.keyCode == 74 && e.ctrlKey) { // CTRL+J : test
-				trace(shaderGraph.hasCycle());
 			}
 			}
 		});
 		});
 
 
-		element.find("#addParameter").on("click", function() {
-			function createElement(name : String, type : Type) : Element {
-				var elt = new Element('
-					<div>
-						<span> ${name} </span>
-					</div>');
-				elt.on("click", function() {
-					addParameter(type);
-				});
-				return elt;
-			}
-
-			customContextMenu([
-				createElement("Boolean", TBool),
-				createElement("Color", TVec(4, VFloat)),
-				createElement("Texture", TSampler2D)
-				]);
-		});
-
-		element.find("#launchCompileShader").on("click", function() {
-			launchCompileShader();
-		});
-
-		element.find("#saveShader").on("click", function() {
-			save();
-		});
-
-		parametersList = element.find("#parametersList");
-
-		editorMatrix.on("change", "input, select", function(ev) {
-			try {
-				shaderGraph.nodeUpdated(ev.target.closest(".box").id);
-				launchCompileShader();
-			} catch (e : Dynamic) {
-				if (Std.is(e, ShaderException)) {
-					error(e.msg, e.idBox);
-				}
-			}
-		});
-
-		addMenu = null;
-		listOfClasses = new Map<String, Array<NodeInfo>>();
-		var mapOfNodes = ShaderNode.registeredNodes;
-		for (key in mapOfNodes.keys()) {
-			var metas = haxe.rtti.Meta.getType(mapOfNodes[key]);
-			if (metas.group == null) {
-				continue;
-			}
-			var group = metas.group[0];
-
-			if (listOfClasses[group] == null)
-				listOfClasses[group] = new Array<NodeInfo>();
-
-			listOfClasses[group].push({ name : (metas.name != null) ? metas.name[0] : key , description : (metas.description != null) ? metas.description[0] : "" , key : key });
-		}
-
-		for (key in listOfClasses.keys()) {
-			listOfClasses[key].sort(function (a, b): Int {
-				if (a.name < b.name) return -1;
-				else if (a.name > b.name) return 1;
-				return 0;
-			});
-		}
-
 		listOfBoxes = [];
 		listOfBoxes = [];
 		listOfEdges = [];
 		listOfEdges = [];
 
 
 		updateMatrix();
 		updateMatrix();
-
-		new Element("svg").ready(function(e) {
-
-			for (node in shaderGraph.getNodes()) {
-				addBox(new Point(node.x, node.y), std.Type.getClass(node.instance), node.instance);
-			}
-
-			new Element(".nodes").ready(function(e) {
-
-				for (box in listOfBoxes) {
-					for (key in box.getShaderNode().getInputsKey()) {
-						var input = box.getShaderNode().getInput(key);
-						if (input != null) {
-							var fromBox : Box = null;
-							for (boxFrom in listOfBoxes) {
-								if (boxFrom.getId() == input.node.id) {
-									fromBox = boxFrom;
-									break;
-								}
-							}
-							var nodeFrom = fromBox.getElement().find('[field=${input.getKey()}]');
-							var nodeTo = box.getElement().find('[field=${key}]');
-							createEdgeInEditorGraph({from: fromBox, nodeFrom: nodeFrom, to : box, nodeTo: nodeTo, elt : createCurve(nodeFrom, nodeTo) });
-						}
-					}
-				}
-
-			});
-		});
-
-	}
-
-	override function save() {
-		var content = shaderGraph.save();
-		currentSign = haxe.crypto.Md5.encode(content);
-		sys.io.File.saveContent(getPath(), content);
-		super.save();
-		info("Shader saved");
-	}
-
-	function update(dt : Float) {
-
-	}
-
-	function onRefresh() {
-
-		plight = root.getAll(hrt.prefab.Light)[0];
-		if( plight != null ) {
-			this.light = sceneEditor.context.shared.contexts.get(plight).local3d;
-			lightDirection = this.light.getDirection();
-		}
-
-		obj = sceneEditor.scene.loadModel("fx/Common/PrimitiveShapes/Sphere.fbx", true);
-		sceneEditor.scene.s3d.addChild(obj);
-
-		element.find("#preview").first().append(sceneEditor.scene.element);
-
-		launchCompileShader();
-	}
-
-	function addParameter(type : Type, ?title : String, ?value : Dynamic) {
-
-		//TODO: link with shadergraph
-		//TODO: drag to graph
-		//TODO: edit => edit everywhere
-		//TODO: type sampler => file choosen
-
-		var exist = (title != null);
-
-		var elt = new Element('<div class="parameter"></div>').appendTo(parametersList);
-		var content = new Element('<div class="content" ${(!exist) ? "style='display: none'" : "" } ></div>');
-		var defaultValue = new Element("<div><span>Default: </span></div>").appendTo(content);
-
-		var typeName = "";
-
-		switch(type) {
-			case TBool:
-				var checkbox = new Element('<input type="checkbox" />');
-				checkbox.prop("checked", ${(value != null && value == "true") ? true : false});
-				defaultValue.append(checkbox);
-				typeName = "Boolean";
-			default:
-				defaultValue.append(new Element('<input type="text" value="${(value != null) ? value : ""}" />'));
-				typeName = "String";
-		}
-
-		var header = new Element('<div class="header">
-									<div class="title">
-										<i class="fa fa-chevron-right" ></i>
-										<input class="input-title" type="input" value="${(title != null) ? title : ""}" />
-									</div>
-									<div class="type">
-										<span>${typeName}</span>
-									</div>
-								</div>');
-
-		header.appendTo(elt);
-		content.appendTo(elt);
-		var actionBtns = new Element('<div class="action-btns" ></div>').appendTo(content);
-		var deleteBtn = new Element('<input type="button" value="Delete" />');
-		deleteBtn.on("click", function() {
-			elt.remove();
-		});
-		deleteBtn.appendTo(actionBtns);
-
-
-		var inputTitle = elt.find(".input-title");
-		inputTitle.on("click", function(e) {
-			e.stopPropagation();
-		});
-		inputTitle.on("change", function(e) {
-		});
-		elt.find(".header").on("click", function(e) {
-			content.toggle();
-			var icon = elt.find(".fa");
-			if (icon.hasClass("fa-chevron-right")) {
-				icon.removeClass("fa-chevron-right");
-				icon.addClass("fa-chevron-down");
-			} else {
-				icon.addClass("fa-chevron-right");
-				icon.removeClass("fa-chevron-down");
-			}
-		});
-	}
-
-	function launchCompileShader() {
-		if (timerCompileShader != null) {
-			timerCompileShader.stop();
-		}
-		timerCompileShader = new Timer(COMPILE_SHADER_DEBOUNCE);
-		timerCompileShader.run = function() {
-			compileShader();
-			timerCompileShader.stop();
-		};
-	}
-
-	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) {
@@ -565,7 +234,7 @@ class ShaderEditor extends FileView {
 
 
 			for (b in listOfBoxesSelected) {
 			for (b in listOfBoxesSelected) {
 				b.setPosition(b.getX() + dx, b.getY() + dy);
 				b.setPosition(b.getX() + dx, b.getY() + dy);
-				shaderGraph.setPosition(b.getId(), b.getX(), b.getY());
+				updatePosition(b.getId(), b.getX(), b.getY());
 				// move edges from and to this box
 				// move edges from and to this box
 				for (edge in listOfEdges) {
 				for (edge in listOfEdges) {
 					if (edge.from == b || edge.to == b) {
 					if (edge.from == b || edge.to == b) {
@@ -587,11 +256,7 @@ class ShaderEditor extends FileView {
 		}
 		}
 	}
 	}
 
 
-	function addNode(p : Point, nodeClass : Class<ShaderNode>) {
-		var node = shaderGraph.addNode(p.x, p.y, nodeClass);
-
-		addBox(p, nodeClass, node);
-	}
+	dynamic function updatePosition(id : Int, x : Float, y : Float) { }
 
 
 	function addBox(p : Point, nodeClass : Class<ShaderNode>, node : ShaderNode) {
 	function addBox(p : Point, nodeClass : Class<ShaderNode>, node : ShaderNode) {
 
 
@@ -643,7 +308,7 @@ class ShaderEditor extends FileView {
 
 
 				var defaultValue = null;
 				var defaultValue = null;
 				if (m.input.length >= 2 && inputMeta[1]) {
 				if (m.input.length >= 2 && inputMeta[1]) {
-					defaultValue = Reflect.field(box.getShaderNode(), 'prop_${f}');
+					defaultValue = Reflect.field(box.getInstance(), 'prop_${f}');
 					if (defaultValue == null) {
 					if (defaultValue == null) {
 						defaultValue = "0";
 						defaultValue = "0";
 					}
 					}
@@ -656,7 +321,7 @@ class ShaderEditor extends FileView {
 						if (Math.isNaN(tmpValue) ) {
 						if (Math.isNaN(tmpValue) ) {
 							fieldEditInput.addClass("error");
 							fieldEditInput.addClass("error");
 						} else {
 						} else {
-							Reflect.setField(box.getShaderNode(), 'prop_${f}', tmpValue);
+							Reflect.setField(box.getInstance(), 'prop_${f}', tmpValue);
 							fieldEditInput.val(tmpValue);
 							fieldEditInput.val(tmpValue);
 							fieldEditInput.removeClass("error");
 							fieldEditInput.removeClass("error");
 						}
 						}
@@ -710,30 +375,27 @@ class ShaderEditor extends FileView {
 		}
 		}
 		box.dispose();
 		box.dispose();
 		listOfBoxes.remove(box);
 		listOfBoxes.remove(box);
-		shaderGraph.removeNode(box.getId());
 	}
 	}
 
 
 	function removeEdge(edge : Edge) {
 	function removeEdge(edge : Edge) {
 		edge.elt.remove();
 		edge.elt.remove();
 		edge.nodeTo.removeAttr("hasLink");
 		edge.nodeTo.removeAttr("hasLink");
 		edge.nodeTo.parent().removeClass("hasLink");
 		edge.nodeTo.parent().removeClass("hasLink");
-		shaderGraph.removeEdge(edge.to.getId(), edge.nodeTo.attr("field"));
 		listOfEdges.remove(edge);
 		listOfEdges.remove(edge);
-		launchCompileShader();
 	}
 	}
 
 
 	function setAvailableInputNodes(boxOutput : Box, field : String) {
 	function setAvailableInputNodes(boxOutput : Box, field : String) {
-		var type = boxOutput.getShaderNode().getOutputType(field);
+		var type = boxOutput.getInstance().getOutputType(field);
 		var sType : SType;
 		var sType : SType;
 		if (type == null) {
 		if (type == null) {
-			sType = boxOutput.getShaderNode().getOutputInfo(field);
+			sType = boxOutput.getInstance().getOutputInfo(field);
 		} else {
 		} else {
 			sType = ShaderType.getType(type);
 			sType = ShaderType.getType(type);
 		}
 		}
 
 
 		for (box in listOfBoxes) {
 		for (box in listOfBoxes) {
 			for (input in box.inputs) {
 			for (input in box.inputs) {
-				if (box.getShaderNode().checkTypeAndCompatibilyInput(input.attr("field"), sType)) {
+				if (box.getInstance().checkTypeAndCompatibilyInput(input.attr("field"), sType)) {
 					input.addClass("nodeMatch");
 					input.addClass("nodeMatch");
 				}
 				}
 			}
 			}
@@ -744,14 +406,14 @@ class ShaderEditor extends FileView {
 		for (box in listOfBoxes) {
 		for (box in listOfBoxes) {
 			for (output in box.outputs) {
 			for (output in box.outputs) {
 				var outputField = output.attr("field");
 				var outputField = output.attr("field");
-				var type = box.getShaderNode().getOutputType(outputField);
+				var type = box.getInstance().getOutputType(outputField);
 				var sType : SType;
 				var sType : SType;
 				if (type == null) {
 				if (type == null) {
-					sType = box.getShaderNode().getOutputInfo(outputField);
+					sType = box.getInstance().getOutputInfo(outputField);
 				} else {
 				} else {
 					sType = ShaderType.getType(type);
 					sType = ShaderType.getType(type);
 				}
 				}
-				if (boxInput.getShaderNode().checkTypeAndCompatibilyInput(field, sType)) {
+				if (boxInput.getInstance().checkTypeAndCompatibilyInput(field, sType)) {
 					output.addClass("nodeMatch");
 					output.addClass("nodeMatch");
 				}
 				}
 			}
 			}
@@ -779,46 +441,6 @@ class ShaderEditor extends FileView {
 		new Element(".box").removeClass("error");
 		new Element(".box").removeClass("error");
 	}
 	}
 
 
-	function createEdgeInShaderGraph() : Bool {
-		var startLinkNode = startLinkGrNode.find(".node");
-		if (isCreatingLink == FromInput) {
-			var tmpBox = startLinkBox;
-			startLinkBox = endLinkBox;
-			endLinkBox = tmpBox;
-
-			var tmpNode = startLinkNode;
-			startLinkNode = endLinkNode;
-			endLinkNode = tmpNode;
-		}
-
-		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)) {
-					removeEdge(edge);
-					break;
-				}
-			}
-		}
-		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;
-				launchCompileShader();
-				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;
-		}
-	}
-
 	function createEdgeInEditorGraph(edge) {
 	function createEdgeInEditorGraph(edge) {
 		listOfEdges.push(edge);
 		listOfEdges.push(edge);
 		edge.nodeTo.attr("hasLink", "true");
 		edge.nodeTo.attr("hasLink", "true");
@@ -940,7 +562,7 @@ class ShaderEditor extends FileView {
 	}
 	}
 
 
 	function customContextMenu( elts : Array<Element> ) {
 	function customContextMenu( elts : Array<Element> ) {
-			closeCustomContextMenu();
+		closeCustomContextMenu();
 
 
 		if (elts.length == 0) return;
 		if (elts.length == 0) return;
 
 
@@ -953,9 +575,6 @@ class ShaderEditor extends FileView {
 
 
 		var posCursor = new IPoint(Std.int(ide.mouseX - parent.offset().left), Std.int(ide.mouseY - parent.offset().top));
 		var posCursor = new IPoint(Std.int(ide.mouseX - parent.offset().left), Std.int(ide.mouseY - parent.offset().top));
 
 
-		contextMenu.css("left", posCursor.x);
-		contextMenu.css("top", posCursor.y);
-
 		contextMenu.on("mousedown", function(e) {
 		contextMenu.on("mousedown", function(e) {
 			e.stopPropagation();
 			e.stopPropagation();
 		});
 		});
@@ -968,6 +587,9 @@ class ShaderEditor extends FileView {
 		for (elt in elts) {
 		for (elt in elts) {
 			elt.appendTo(options);
 			elt.appendTo(options);
 		}
 		}
+
+		contextMenu.css("left", Math.min(posCursor.x, element.width() - contextMenu.width() - 5));
+		contextMenu.css("top", Math.min(posCursor.y, element.height() - contextMenu.height() - 5));
 	}
 	}
 
 
 	function closeCustomContextMenu() {
 	function closeCustomContextMenu() {
@@ -977,171 +599,6 @@ class ShaderEditor extends FileView {
 		}
 		}
 	}
 	}
 
 
-	function openAddMenu() {
-		if (addMenu != null) {
-			var input = addMenu.find("#search-input");
-			input.val("");
-			addMenu.show();
-			input.focus();
-			var posCursor = new IPoint(Std.int(ide.mouseX - parent.offset().left), Std.int(ide.mouseY - parent.offset().top));
-
-			addMenu.css("left", posCursor.x);
-			addMenu.css("top", posCursor.y);
-			return;
-		}
-
-		addMenu = new Element('
-		<div id="add-menu">
-			<div class="search-container">
-				<div class="icon" >
-					<i class="fa fa-search"></i>
-				</div>
-				<div class="search-bar" >
-					<input type="text" id="search-input" >
-				</div>
-			</div>
-			<div id="results">
-			</div>
-		</div>').appendTo(parent);
-
-		var posCursor = new IPoint(Std.int(ide.mouseX - parent.offset().left), Std.int(ide.mouseY - parent.offset().top));
-
-		addMenu.css("left", posCursor.x);
-		addMenu.css("top", posCursor.y);
-
-		addMenu.on("mousedown", function(e) {
-			e.stopPropagation();
-		});
-
-		var results = addMenu.find("#results");
-		results.on("wheel", function(e) {
-			e.stopPropagation();
-		});
-
-		var keys = listOfClasses.keys();
-		var sortedKeys = [];
-		for (k in keys) {
-			sortedKeys.push(k);
-		}
-		sortedKeys.sort(function (a, b) {
-			if (a < b) return -1;
-			if (a > b) return 1;
-			return 0;
-		});
-
-		for (key in sortedKeys) {
-			new Element('
-				<div class="group" >
-					<span> ${key} </span>
-				</div>').appendTo(results);
-			for (node in listOfClasses[key]) {
-				new Element('
-					<div node="${node.key}" >
-						<span> ${node.name} </span> <span> ${node.description} </span>
-					</div>').appendTo(results);
-			}
-		}
-
-		var input = addMenu.find("#search-input");
-		input.focus();
-		var divs = new Element("#results > div");
-		input.on("keydown", function(ev) {
-			if (ev.keyCode == 38 || ev.keyCode == 40) {
-				ev.stopPropagation();
-				ev.preventDefault();
-
-				if (selectedNode != null)
-					this.selectedNode.removeClass("selected");
-
-				var selector = "div[node]:not([style*='display: none'])";
-				var elt = this.selectedNode;
-
-				if (ev.keyCode == 38) {
-					do {
-						elt = elt.prev();
-					} while (elt.length > 0 && !elt.is(selector));
-				} else if (ev.keyCode == 40) {
-					do {
-						elt = elt.next();
-					} while (elt.length > 0 && !elt.is(selector));
-				}
-				if (elt.length == 1) {
-					this.selectedNode = elt;
-				}
-				if (this.selectedNode != null)
-					this.selectedNode.addClass("selected");
-
-				var offsetDiff = this.selectedNode.offset().top - results.offset().top;
-				if (offsetDiff > 225) {
-					results.scrollTop((offsetDiff-225)+results.scrollTop());
-				} else if (offsetDiff < 35) {
-					results.scrollTop(results.scrollTop()-(35-offsetDiff));
-				}
-			}
-		});
-		input.on("keyup", function(ev) {
-			if (ev.keyCode == 38 || ev.keyCode == 40) {
-				return;
-			}
-
-			if (ev.keyCode == 13) {
-				var key = this.selectedNode.attr("node");
-				var posCursor = new Point(lX(ide.mouseX - 25), lY(ide.mouseY - 10));
-				addNode(posCursor, ShaderNode.registeredNodes[key]);
-				closeAddMenu();
-			} else {
-				if (this.selectedNode != null)
-					this.selectedNode.removeClass("selected");
-				var value = input.val();
-				var children = divs.elements();
-				var isFirst = true;
-				var lastGroup = null;
-				for (elt in children) {
-					if (elt.hasClass("group")) {
-						lastGroup = elt;
-						elt.hide();
-						continue;
-					}
-					if (elt.children().first().html().toLowerCase().indexOf(value.toLowerCase()) != -1) {
-						if (isFirst) {
-							this.selectedNode = elt;
-							isFirst = false;
-						}
-						elt.show();
-						if (lastGroup != null)
-							lastGroup.show();
-					} else {
-						elt.hide();
-					}
-				}
-				if (this.selectedNode != null)
-					this.selectedNode.addClass("selected");
-			}
-		});
-		divs.mouseover(function(ev) {
-			if (ev.getThis().hasClass("group")) {
-				return;
-			}
-			this.selectedNode.removeClass("selected");
-			this.selectedNode = ev.getThis();
-			this.selectedNode.addClass("selected");
-		});
-		divs.mouseup(function(ev) {
-			if (ev.getThis().hasClass("group")) {
-				return;
-			}
-			var key = ev.getThis().attr("node");
-			var posCursor = new Point(lX(ide.mouseX - 25), lY(ide.mouseY - 10));
-			addNode(posCursor, ShaderNode.registeredNodes[key]);
-			closeAddMenu();
-		});
-	}
-
-	function closeAddMenu() {
-		if (addMenu != null)
-			addMenu.hide();
-	}
-
 	function clearSelectionBoxes() {
 	function clearSelectionBoxes() {
 		for(b in listOfBoxesSelected) b.setSelected(false);
 		for(b in listOfBoxesSelected) b.setSelected(false);
 		listOfBoxesSelected = [];
 		listOfBoxesSelected = [];
@@ -1153,28 +610,26 @@ class ShaderEditor extends FileView {
 	function startUpdateViewPosition() {
 	function startUpdateViewPosition() {
 		if (timerUpdateView != null)
 		if (timerUpdateView != null)
 			return;
 			return;
-		var PADDING_BOUNDS = 75;
-		var SPEED_BOUNDS = 0.1;
 		timerUpdateView = new Timer(0);
 		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;
-			if (posCursor.x < PADDING_BOUNDS) {
-				pan(new Point((PADDING_BOUNDS - posCursor.x)*SPEED_BOUNDS, 0));
+			if (posCursor.x < BORDER_SIZE) {
+				pan(new Point((BORDER_SIZE - posCursor.x)*SPEED_BORDER_MOVE, 0));
 				wasUpdated = true;
 				wasUpdated = true;
 			}
 			}
-			if (posCursor.y < PADDING_BOUNDS) {
-				pan(new Point(0, (PADDING_BOUNDS - posCursor.y)*SPEED_BOUNDS));
+			if (posCursor.y < BORDER_SIZE) {
+				pan(new Point(0, (BORDER_SIZE - posCursor.y)*SPEED_BORDER_MOVE));
 				wasUpdated = true;
 				wasUpdated = true;
 			}
 			}
-			var rightBorder = parent.width() - PADDING_BOUNDS;
+			var rightBorder = parent.width() - BORDER_SIZE;
 			if (posCursor.x > rightBorder) {
 			if (posCursor.x > rightBorder) {
-				pan(new Point((rightBorder - posCursor.x)*SPEED_BOUNDS, 0));
+				pan(new Point((rightBorder - posCursor.x)*SPEED_BORDER_MOVE, 0));
 				wasUpdated = true;
 				wasUpdated = true;
 			}
 			}
-			var botBorder = parent.height() - PADDING_BOUNDS;
+			var botBorder = parent.height() - BORDER_SIZE;
 			if (posCursor.y > botBorder) {
 			if (posCursor.y > botBorder) {
-				pan(new Point(0, (botBorder - posCursor.y)*SPEED_BOUNDS));
+				pan(new Point(0, (botBorder - posCursor.y)*SPEED_BORDER_MOVE));
 				wasUpdated = true;
 				wasUpdated = true;
 			}
 			}
 			mouseMoveFunction(ide.mouseX, ide.mouseY);
 			mouseMoveFunction(ide.mouseX, ide.mouseY);
@@ -1260,6 +715,4 @@ class ShaderEditor extends FileView {
 		return new Point(lX(x), lY(y));
 		return new Point(lX(x), lY(y));
 	}
 	}
 
 
-	static var _ = FileTree.registerExtension(ShaderEditor,["hlshader"],{ icon : "scribd" });
-
 }
 }

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

@@ -103,7 +103,7 @@ class Box {
 			element.find(".output-node-group > .title-node").html("");
 			element.find(".output-node-group > .title-node").html("");
 		}
 		}
 
 
-			// create properties box
+		// create properties box
 		editor.rect(propertiesGroup, 0, 0, this.width, 0).addClass("properties");
 		editor.rect(propertiesGroup, 0, 0, this.width, 0).addClass("properties");
 		propsHeight = 5;
 		propsHeight = 5;
 
 
@@ -160,7 +160,7 @@ class Box {
 	public function getId() {
 	public function getId() {
 		return this.nodeInstance.id;
 		return this.nodeInstance.id;
 	}
 	}
-	public function getShaderNode() {
+	public function getInstance() {
 		return this.nodeInstance;
 		return this.nodeInstance;
 	}
 	}
 	public function getX() {
 	public function getX() {

+ 611 - 0
hide/view/shadereditor/ShaderEditor.hx

@@ -0,0 +1,611 @@
+package hide.view.shadereditor;
+
+import hrt.shgraph.ShaderException;
+import haxe.Timer;
+using hxsl.Ast.Type;
+
+import haxe.rtti.Meta;
+import hxsl.Shader;
+import hxsl.SharedShader;
+import hide.comp.SceneEditor;
+import js.jquery.JQuery;
+import h2d.col.Point;
+import h2d.col.IPoint;
+import hide.view.shadereditor.Box;
+import hrt.shgraph.ShaderGraph;
+import hrt.shgraph.ShaderNode;
+
+class ShaderEditor extends hide.view.Graph {
+
+	var parametersList : JQuery;
+
+	// used to preview
+	var sceneEditor : SceneEditor;
+
+	var root : hrt.prefab.Prefab;
+	var obj : h3d.scene.Object;
+	var plight : hrt.prefab.Prefab;
+	var light : h3d.scene.Object;
+	var lightDirection : h3d.Vector;
+
+	var shaderGraph : ShaderGraph;
+
+	var timerCompileShader : Timer;
+	var COMPILE_SHADER_DEBOUNCE : Int = 100;
+	var shaderGenerated : Shader;
+
+	override function onDisplay() {
+		super.onDisplay();
+		shaderGraph = new ShaderGraph(getPath());
+		addMenu = null;
+
+		element.find("#rightPanel").append(new Element('
+						<span>Parameters</span>
+						<div class="tab expand" name="Scene" icon="sitemap">
+							<div class="hide-block" >
+								<div id="parametersList" class="hide-scene-tree hide-list">
+								</div>
+							</div>
+							<div class="options-block hide-block">
+								<input id="addParameter" type="button" value="Add parameter" />
+								<input id="launchCompileShader" type="button" value="Compile shader" />
+								<input id="saveShader" type="button" value="Save" />
+							</div>
+						</div>)'));
+
+
+		var preview = new Element('<div id="preview" ></div>');
+		preview.on("mousedown", function(e) { e.stopPropagation(); });
+		preview.on("wheel", function(e) { e.stopPropagation(); });
+		parent.append(preview);
+
+		var def = new hrt.prefab.Library();
+		new hrt.prefab.RenderProps(def).name = "renderer";
+		var l = new hrt.prefab.Light(def);
+		l.name = "sunLight";
+		l.kind = Directional;
+		l.power = 1.5;
+		var q = new h3d.Quat();
+		q.initDirection(new h3d.Vector(-1,-1.5,-3));
+		var a = q.toEuler();
+		l.rotationX = Math.round(a.x * 180 / Math.PI);
+		l.rotationY = Math.round(a.y * 180 / Math.PI);
+		l.rotationZ = Math.round(a.z * 180 / Math.PI);
+		l.shadows.mode = Dynamic;
+		l.shadows.size = 1024;
+		root = def;
+
+		sceneEditor = new hide.comp.SceneEditor(this, root);
+		sceneEditor.editorDisplay = false;
+		sceneEditor.onRefresh = onRefresh;
+		sceneEditor.onUpdate = function(dt : Float) {};
+		sceneEditor.view.keys = new hide.ui.Keys(null); // Remove SceneEditor Shortcuts
+		sceneEditor.view.keys.register("save", function() {
+			save();
+			skipNextChange = true;
+			modified = false;
+		});
+
+		editorMatrix = editor.group(editor.element);
+
+		element.on("mousedown", function(e) {
+			closeAddMenu();
+			closeCustomContextMenu();
+		});
+
+		parent.on("mouseup", function(e) {
+			if (e.button == 0) {
+				// Stop link creation
+				if (isCreatingLink != None) {
+					if (startLinkBox != null && endLinkBox != null && createEdgeInShaderGraph()) {
+
+					} else {
+						if (currentLink != null) currentLink.remove();
+						currentLink = null;
+					}
+					startLinkBox = endLinkBox = null;
+					startLinkGrNode = endLinkNode = null;
+					isCreatingLink = None;
+					clearAvailableNodes();
+					return;
+				}
+				return;
+			}
+		});
+
+		parent.on("keydown", function(e) {
+
+			if (e.shiftKey && e.keyCode != 16) {
+				openAddMenu();
+
+				return;
+			}
+			if (e.keyCode == 83 && e.ctrlKey) { // CTRL+S : save
+				shaderGraph.save();
+			}
+		});
+
+		element.find("#addParameter").on("click", function() {
+			function createElement(name : String, type : Type) : Element {
+				var elt = new Element('
+					<div>
+						<span> ${name} </span>
+					</div>');
+				elt.on("click", function() {
+					addParameter(type);
+				});
+				return elt;
+			}
+
+			customContextMenu([
+				createElement("Boolean", TBool),
+				createElement("Color", TVec(4, VFloat)),
+				createElement("Texture", TSampler2D)
+				]);
+		});
+
+		element.find("#launchCompileShader").on("click", function() {
+			launchCompileShader();
+		});
+
+		element.find("#saveShader").on("click", function() {
+			save();
+		});
+
+		parametersList = element.find("#parametersList");
+
+		editorMatrix.on("change", "input, select", function(ev) {
+			try {
+				shaderGraph.nodeUpdated(ev.target.closest(".box").id);
+				launchCompileShader();
+			} catch (e : Dynamic) {
+				if (Std.is(e, ShaderException)) {
+					error(e.msg, e.idBox);
+				}
+			}
+		});
+
+		addMenu = null;
+		listOfClasses = new Map<String, Array<Graph.NodeInfo>>();
+		var mapOfNodes = ShaderNode.registeredNodes;
+		for (key in mapOfNodes.keys()) {
+			var metas = haxe.rtti.Meta.getType(mapOfNodes[key]);
+			if (metas.group == null) {
+				continue;
+			}
+			var group = metas.group[0];
+
+			if (listOfClasses[group] == null)
+				listOfClasses[group] = new Array<Graph.NodeInfo>();
+
+			listOfClasses[group].push({ name : (metas.name != null) ? metas.name[0] : key , description : (metas.description != null) ? metas.description[0] : "" , key : key });
+		}
+
+		for (key in listOfClasses.keys()) {
+			listOfClasses[key].sort(function (a, b): Int {
+				if (a.name < b.name) return -1;
+				else if (a.name > b.name) return 1;
+				return 0;
+			});
+		}
+
+		listOfBoxes = [];
+		listOfEdges = [];
+
+		updateMatrix();
+
+		new Element("svg").ready(function(e) {
+
+			for (node in shaderGraph.getNodes()) {
+				addBox(new Point(node.x, node.y), std.Type.getClass(node.instance), node.instance);
+			}
+
+			new Element(".nodes").ready(function(e) {
+
+				for (box in listOfBoxes) {
+					for (key in box.getInstance().getInputsKey()) {
+						var input = box.getInstance().getInput(key);
+						if (input != null) {
+							var fromBox : Box = null;
+							for (boxFrom in listOfBoxes) {
+								if (boxFrom.getId() == input.node.id) {
+									fromBox = boxFrom;
+									break;
+								}
+							}
+							var nodeFrom = fromBox.getElement().find('[field=${input.getKey()}]');
+							var nodeTo = box.getElement().find('[field=${key}]');
+							createEdgeInEditorGraph({from: fromBox, nodeFrom: nodeFrom, to : box, nodeTo: nodeTo, elt : createCurve(nodeFrom, nodeTo) });
+						}
+					}
+				}
+
+			});
+		});
+
+	}
+
+	override function save() {
+		var content = shaderGraph.save();
+		currentSign = haxe.crypto.Md5.encode(content);
+		sys.io.File.saveContent(getPath(), content);
+		super.save();
+		info("Shader saved");
+	}
+
+	function onRefresh() {
+
+		plight = root.getAll(hrt.prefab.Light)[0];
+		if( plight != null ) {
+			this.light = sceneEditor.context.shared.contexts.get(plight).local3d;
+			lightDirection = this.light.getDirection();
+		}
+
+		obj = sceneEditor.scene.loadModel("fx/Common/PrimitiveShapes/Sphere.fbx", true);
+		sceneEditor.scene.s3d.addChild(obj);
+
+		element.find("#preview").first().append(sceneEditor.scene.element);
+
+		launchCompileShader();
+	}
+
+	function addParameter(type : Type, ?title : String, ?value : Dynamic) {
+
+		//TODO: link with shadergraph
+		//TODO: drag to graph
+		//TODO: edit => edit everywhere
+		//TODO: type sampler => file choosen
+
+		var exist = (title != null);
+
+		var elt = new Element('<div class="parameter"></div>').appendTo(parametersList);
+		var content = new Element('<div class="content" ></div>');
+		var defaultValue = new Element("<div><span>Default: </span></div>").appendTo(content);
+		if (!exist) content.hide();
+
+		var typeName = "";
+
+		switch(type) {
+			case TBool:
+				var checkbox = new Element('<input type="checkbox" />');
+				checkbox.prop("checked", ${(value != null && value == "true") ? true : false});
+				defaultValue.append(checkbox);
+				typeName = "Boolean";
+			default:
+				defaultValue.append(new Element('<input type="text" value="${(value != null) ? value : ""}" />'));
+				typeName = "String";
+		}
+
+		var header = new Element('<div class="header">
+									<div class="title">
+										<i class="fa fa-chevron-right" ></i>
+										<input class="input-title" type="input" value="${(title != null) ? title : ""}" />
+									</div>
+									<div class="type">
+										<span>${typeName}</span>
+									</div>
+								</div>');
+
+		header.appendTo(elt);
+		content.appendTo(elt);
+		var actionBtns = new Element('<div class="action-btns" ></div>').appendTo(content);
+		var deleteBtn = new Element('<input type="button" value="Delete" />');
+		deleteBtn.on("click", function() {
+			elt.remove();
+		});
+		deleteBtn.appendTo(actionBtns);
+
+
+		var inputTitle = elt.find(".input-title");
+		inputTitle.on("click", function(e) {
+			e.stopPropagation();
+		});
+		inputTitle.on("change", function(e) {
+		});
+		elt.find(".header").on("click", function(ev) {
+			elt.find(".content").toggle();
+			var icon = elt.find(".fa");
+			if (icon.hasClass("fa-chevron-right")) {
+				icon.removeClass("fa-chevron-right");
+				icon.addClass("fa-chevron-down");
+			} else {
+				icon.addClass("fa-chevron-right");
+				icon.removeClass("fa-chevron-down");
+			}
+		});
+	}
+
+	function launchCompileShader() {
+		if (timerCompileShader != null) {
+			timerCompileShader.stop();
+		}
+		timerCompileShader = new Timer(COMPILE_SHADER_DEBOUNCE);
+		timerCompileShader.run = function() {
+			compileShader();
+			timerCompileShader.stop();
+		};
+	}
+
+	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 addNode(p : Point, nodeClass : Class<ShaderNode>) {
+		var node = shaderGraph.addNode(p.x, p.y, nodeClass);
+
+		addBox(p, nodeClass, node);
+	}
+
+	function createEdgeInShaderGraph() : Bool {
+		var startLinkNode = startLinkGrNode.find(".node");
+		if (isCreatingLink == FromInput) {
+			var tmpBox = startLinkBox;
+			startLinkBox = endLinkBox;
+			endLinkBox = tmpBox;
+
+			var tmpNode = startLinkNode;
+			startLinkNode = endLinkNode;
+			endLinkNode = tmpNode;
+		}
+
+		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)) {
+					removeEdge(edge);
+					break;
+				}
+			}
+		}
+		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;
+				launchCompileShader();
+				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;
+		}
+	}
+
+	function openAddMenu() {
+		if (addMenu != null) {
+			var input = addMenu.find("#search-input");
+			input.val("");
+			addMenu.show();
+			input.focus();
+			var posCursor = new IPoint(Std.int(ide.mouseX - parent.offset().left), Std.int(ide.mouseY - parent.offset().top));
+
+			addMenu.css("left", posCursor.x);
+			addMenu.css("top", posCursor.y);
+			return;
+		}
+
+		addMenu = new Element('
+		<div id="add-menu">
+			<div class="search-container">
+				<div class="icon" >
+					<i class="fa fa-search"></i>
+				</div>
+				<div class="search-bar" >
+					<input type="text" id="search-input" >
+				</div>
+			</div>
+			<div id="results">
+			</div>
+		</div>').appendTo(parent);
+
+		var posCursor = new IPoint(Std.int(ide.mouseX - parent.offset().left), Std.int(ide.mouseY - parent.offset().top));
+
+		addMenu.css("left", posCursor.x);
+		addMenu.css("top", posCursor.y);
+
+		addMenu.on("mousedown", function(e) {
+			e.stopPropagation();
+		});
+
+		var results = addMenu.find("#results");
+		results.on("wheel", function(e) {
+			e.stopPropagation();
+		});
+
+		var keys = listOfClasses.keys();
+		var sortedKeys = [];
+		for (k in keys) {
+			sortedKeys.push(k);
+		}
+		sortedKeys.sort(function (a, b) {
+			if (a < b) return -1;
+			if (a > b) return 1;
+			return 0;
+		});
+
+		for (key in sortedKeys) {
+			new Element('
+				<div class="group" >
+					<span> ${key} </span>
+				</div>').appendTo(results);
+			for (node in listOfClasses[key]) {
+				new Element('
+					<div node="${node.key}" >
+						<span> ${node.name} </span> <span> ${node.description} </span>
+					</div>').appendTo(results);
+			}
+		}
+
+		var input = addMenu.find("#search-input");
+		input.focus();
+		var divs = new Element("#results > div");
+		input.on("keydown", function(ev) {
+			if (ev.keyCode == 38 || ev.keyCode == 40) {
+				ev.stopPropagation();
+				ev.preventDefault();
+
+				if (selectedNode != null)
+					this.selectedNode.removeClass("selected");
+
+				var selector = "div[node]:not([style*='display: none'])";
+				var elt = this.selectedNode;
+
+				if (ev.keyCode == 38) {
+					do {
+						elt = elt.prev();
+					} while (elt.length > 0 && !elt.is(selector));
+				} else if (ev.keyCode == 40) {
+					do {
+						elt = elt.next();
+					} while (elt.length > 0 && !elt.is(selector));
+				}
+				if (elt.length == 1) {
+					this.selectedNode = elt;
+				}
+				if (this.selectedNode != null)
+					this.selectedNode.addClass("selected");
+
+				var offsetDiff = this.selectedNode.offset().top - results.offset().top;
+				if (offsetDiff > 225) {
+					results.scrollTop((offsetDiff-225)+results.scrollTop());
+				} else if (offsetDiff < 35) {
+					results.scrollTop(results.scrollTop()-(35-offsetDiff));
+				}
+			}
+		});
+		input.on("keyup", function(ev) {
+			if (ev.keyCode == 38 || ev.keyCode == 40) {
+				return;
+			}
+
+			if (ev.keyCode == 13) {
+				var key = this.selectedNode.attr("node");
+				var posCursor = new Point(lX(ide.mouseX - 25), lY(ide.mouseY - 10));
+				addNode(posCursor, ShaderNode.registeredNodes[key]);
+				closeAddMenu();
+			} else {
+				if (this.selectedNode != null)
+					this.selectedNode.removeClass("selected");
+				var value = input.val();
+				var children = divs.elements();
+				var isFirst = true;
+				var lastGroup = null;
+				for (elt in children) {
+					if (elt.hasClass("group")) {
+						lastGroup = elt;
+						elt.hide();
+						continue;
+					}
+					if (elt.children().first().html().toLowerCase().indexOf(value.toLowerCase()) != -1) {
+						if (isFirst) {
+							this.selectedNode = elt;
+							isFirst = false;
+						}
+						elt.show();
+						if (lastGroup != null)
+							lastGroup.show();
+					} else {
+						elt.hide();
+					}
+				}
+				if (this.selectedNode != null)
+					this.selectedNode.addClass("selected");
+			}
+		});
+		divs.mouseover(function(ev) {
+			if (ev.getThis().hasClass("group")) {
+				return;
+			}
+			this.selectedNode.removeClass("selected");
+			this.selectedNode = ev.getThis();
+			this.selectedNode.addClass("selected");
+		});
+		divs.mouseup(function(ev) {
+			if (ev.getThis().hasClass("group")) {
+				return;
+			}
+			var key = ev.getThis().attr("node");
+			var posCursor = new Point(lX(ide.mouseX - 25), lY(ide.mouseY - 10));
+			addNode(posCursor, ShaderNode.registeredNodes[key]);
+			closeAddMenu();
+		});
+	}
+
+	function closeAddMenu() {
+		if (addMenu != null)
+			addMenu.hide();
+	}
+
+	override function removeBox(box : Box) {
+		super.removeBox(box);
+		shaderGraph.removeNode(box.getId());
+	}
+
+	override function removeEdge(edge : Graph.Edge) {
+		super.removeEdge(edge);
+		shaderGraph.removeEdge(edge.to.getId(), edge.nodeTo.attr("field"));
+		launchCompileShader();
+	}
+
+	override function updatePosition(id : Int, x : Float, y : Float) {
+		shaderGraph.setPosition(id, x, y);
+	}
+
+	static var _ = FileTree.registerExtension(ShaderEditor,["hlshader"],{ icon : "scribd" });
+
+}

+ 1 - 1
hrt/shgraph/ShaderGraph.hx

@@ -2,7 +2,7 @@ package hrt.shgraph;
 
 
 using hxsl.Ast;
 using hxsl.Ast;
 
 
-private typedef Node = {
+typedef Node = {
 	x : Float,
 	x : Float,
 	y : Float,
 	y : Float,
 	comment : String,
 	comment : String,