2
0
Эх сурвалжийг харах

SDK :New Material Definition editor based on shader nodes

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10441 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
rem..om 12 жил өмнө
parent
commit
152411cb17
100 өөрчлөгдсөн 9431 нэмэгдсэн , 19 устгасан
  1. 4 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/Bundle.properties
  2. 264 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/EditableMatDefFile.java
  3. 34 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatDef.j3md
  4. 309 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatDefDataObject.java
  5. 398 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatDefMetaData.java
  6. 135 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddAttributeDialog.form
  7. 196 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddAttributeDialog.java
  8. 135 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddMaterialParameterDialog.form
  9. 161 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddMaterialParameterDialog.java
  10. 167 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddNodeDialog.form
  11. 254 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddNodeDialog.java
  12. 107 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddWorldParameterDialog.form
  13. 122 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddWorldParameterDialog.java
  14. 17 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/Bundle.properties
  15. 539 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Connection.java
  16. 531 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Diagram.java
  17. 262 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Dot.java
  18. 82 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/DraggablePanel.java
  19. 25 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/InOut.java
  20. 56 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatDefEditorlElement.form
  21. 566 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatDefEditorlElement.java
  22. 277 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatPanel.form
  23. 379 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatPanel.java
  24. 460 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/NodePanel.java
  25. 283 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/OutBusPanel.java
  26. 16 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Selectable.java
  27. 22 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/FragmentShaderNodesBlock.java
  28. 42 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/InputMappingsBlock.java
  29. 94 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/MatDefBlock.java
  30. 40 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/MaterialParametersBlock.java
  31. 39 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/OutputMappingsBlock.java
  32. 250 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/ShaderNodeBlock.java
  33. 59 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/ShaderNodesBlock.java
  34. 239 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/TechniqueBlock.java
  35. 61 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/UberStatement.java
  36. 23 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/VertexShaderNodesBlock.java
  37. 39 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/WorldParametersBlock.java
  38. 57 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/ConditionBlock.java
  39. 72 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/DefinitionBlock.java
  40. 32 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/FragmentShaderFileBlock.java
  41. 69 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/InputmappingBlock.java
  42. 42 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/LeafStatement.java
  43. 62 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/LightModeBlock.java
  44. 112 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/MappingBlock.java
  45. 128 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/MatParamBlock.java
  46. 73 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/OutputMappingBlock.java
  47. 53 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/ShaderFileBlock.java
  48. 22 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/UnsupportedStatement.java
  49. 32 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/VertexShaderFileBlock.java
  50. 64 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/WorldParamBlock.java
  51. 30 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/Icons.java
  52. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/Quad.png
  53. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/Sphere.png
  54. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/attrib.png
  55. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/busOutput.png
  56. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/cube.png
  57. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/cube.psd
  58. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dot.png
  59. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dotGreen.png
  60. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dotOrange.png
  61. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dotRed.png
  62. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/earth.png
  63. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/earth_1.png
  64. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/expend.png
  65. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/flip.png
  66. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/fragment.png
  67. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/in.png
  68. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/inputOutput.psd
  69. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/mat.png
  70. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/matdef.png
  71. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/node.png
  72. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/node.psd
  73. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/ouptut.psd
  74. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/out.png
  75. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/output.png
  76. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/pirates.wmv
  77. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/previewIcons.psd
  78. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/reload.png
  79. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/repeat.png
  80. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/tech.png
  81. BIN
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/vert.png
  82. 37 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/MatDefNavigatorPanel.form
  83. 182 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/MatDefNavigatorPanel.java
  84. 66 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/AbstractMatDefNode.java
  85. 112 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/MappingNode.java
  86. 103 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/MatDefNode.java
  87. 177 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/ShaderNodeNode.java
  88. 118 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/TechniqueNode.java
  89. 32 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/DefaultProperty.java
  90. 208 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/MatParamProperty.java
  91. 94 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/VectorInplaceEditor.java
  92. 47 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/VectorPropertyEditor.java
  93. 170 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/VectorTextField.java
  94. 3 19
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/package-info.java
  95. 4 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/Bundle.properties
  96. 44 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/MatDefShaderElement.form
  97. 169 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/MatDefShaderElement.java
  98. 105 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/ShaderVisualToolBar.form
  99. 128 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/ShaderVisualToolBar.java
  100. 97 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/utils/DocFormatter.java

+ 4 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/Bundle.properties

@@ -0,0 +1,4 @@
+MatParamTopComponent.jScrollPane2.TabConstraints.tabTitle=Parameters
+MatParamTopComponent.jScrollPane3.TabConstraints.tabTitle=Render states
+MatParamTopComponent.jScrollPane3.TabConstraints.tabToolTip=Material parameters
+MatParamTopComponent.jScrollPane2.TabConstraints.tabToolTip=Additional render states

+ 264 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/EditableMatDefFile.java

@@ -0,0 +1,264 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.MaterialKey;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.scene.SceneApplication;
+import com.jme3.gde.materialdefinition.fileStructure.MatDefBlock;
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.fileStructure.TechniqueBlock;
+import com.jme3.gde.materialdefinition.fileStructure.UberStatement;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.LeafStatement;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.gde.materialdefinition.navigator.node.MatDefNode;
+import com.jme3.material.MatParam;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.plugins.J3MLoader;
+import com.jme3.shader.Glsl100ShaderGenerator;
+import com.jme3.shader.Glsl150ShaderGenerator;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderGenerator;
+import com.jme3.util.blockparser.BlockLanguageParser;
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyledDocument;
+import org.openide.cookies.EditorCookie;
+import org.openide.explorer.ExplorerManager;
+import org.openide.filesystems.FileLock;
+import org.openide.filesystems.FileObject;
+import org.openide.nodes.Node;
+import org.openide.text.NbDocument;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class EditableMatDefFile {
+
+    private FileObject matDefFile;
+    private MatDefDataObject obj;
+    private Material material;
+    private MatDefBlock matDefStructure;
+    private TechniqueBlock currentTechnique;
+    private MaterialDef materialDef;
+    private ProjectAssetManager assetManager;
+//    MatParamTopComponent matParamComponent;
+    private ShaderGenerator glsl100;
+    private ShaderGenerator glsl150;
+    private String selectedTechnique = "Default";
+    private final static String GLSL100 = "GLSL100";
+    private final static String GLSL150 = "GLSL150";
+    private Lookup lookup;
+
+    public EditableMatDefFile(Lookup lookup) {
+        obj = lookup.lookup(MatDefDataObject.class);
+
+        this.matDefFile = obj.getPrimaryFile();
+        this.assetManager = lookup.lookup(ProjectAssetManager.class);
+        this.glsl100 = new Glsl100ShaderGenerator(assetManager);
+        this.glsl150 = new Glsl150ShaderGenerator(assetManager);
+        this.lookup = lookup;
+
+        materialDef = null;
+        matDefStructure = null;
+        FileLock lock = null;
+
+        try {
+            lock = matDefFile.lock();
+            List<Statement> sta = BlockLanguageParser.parse(obj.getPrimaryFile().getInputStream());
+            matDefStructure = new MatDefBlock(sta.get(0));
+            //  System.err.println(block.toString());
+            materialDef = (MaterialDef) assetManager.loadAsset(assetManager.getRelativeAssetPath(matDefFile.getPath()));
+            lock.releaseLock();
+        } catch (Exception ex) {
+            Exceptions.printStackTrace(ex);
+        } finally {
+            if (lock != null) {
+                lock.releaseLock();
+            }
+        }
+        if (materialDef != null) {
+            currentTechnique = matDefStructure.getTechniques().get(0);
+            registerListener(matDefStructure);
+            obj.getLookupContents().add(matDefStructure);
+            updateLookupWithMaterialData(obj);
+        }
+
+    }
+
+    private void registerListener(Statement sta) {
+        if (sta instanceof UberStatement) {
+            ((UberStatement) sta).addPropertyChangeListener(WeakListeners.propertyChange(changeListener, ((UberStatement) sta)));
+        } else if (sta instanceof LeafStatement) {
+            ((LeafStatement) sta).addPropertyChangeListener(WeakListeners.propertyChange(changeListener, ((LeafStatement) sta)));
+        }
+        if (sta.getContents() != null) {
+            for (Statement statement : sta.getContents()) {
+                registerListener(statement);
+            }
+        }
+    }
+
+    public void buildOverview(ExplorerManager mgr) {
+        if (materialDef != null) {
+            mgr.setRootContext(new MatDefNode(lookup));
+
+        } else {
+            mgr.setRootContext(Node.EMPTY);
+        }
+    }
+
+    public String getShaderCode(String version, Shader.ShaderType type) {
+        try {
+            material.selectTechnique("Default", SceneApplication.getApplication().getRenderManager());
+            Shader s = null;
+            if (version.equals(GLSL100)) {
+                s = glsl100.generateShader(material.getActiveTechnique());
+            } else {
+                s = glsl150.generateShader(material.getActiveTechnique());
+            }
+            for (Iterator<Shader.ShaderSource> it = s.getSources().iterator(); it.hasNext();) {
+                Shader.ShaderSource source = it.next();
+                if (source.getType() == type) {
+                    return source.getSource();
+                }
+            }
+            return "";
+        } catch (Exception e) {
+            Exceptions.printStackTrace(e);
+            return "error generating shader " + e.getMessage();
+        }
+    }
+
+//    public MatParamTopComponent getMatParamComponent() {
+//        return matParamComponent;
+//    }
+//    
+//    public void setMatParamComponent(MatParamTopComponent matParamComponent) {
+//        this.matParamComponent = matParamComponent;
+//    }
+    public TechniqueBlock getCurrentTechnique() {
+        return currentTechnique;
+    }
+
+    public MatDefBlock getMatDefStructure() {
+        return matDefStructure;
+    }
+    private MatStructChangeListener changeListener = new MatStructChangeListener();
+    J3MLoader loader = new J3MLoader();
+
+    private void updateLookupWithMaterialData(MatDefDataObject obj) {
+        obj.getLookupContents().add(materialDef);
+        material = new Material(materialDef);
+
+        try {
+            material.selectTechnique("Default", SceneApplication.getApplication().getRenderManager());
+            if (matToRemove != null) {
+                for (MatParam matParam : matToRemove.getParams()) {
+                    material.setParam(matParam.getName(), matParam.getVarType(), matParam.getValue());
+                }
+                obj.getLookupContents().remove(matToRemove);
+                matToRemove = null;
+            }
+            obj.getLookupContents().add(material);
+        } catch (Exception e) {
+            Logger.getLogger(EditableMatDefFile.class.getName()).log(Level.WARNING, "Error making material {0}", e.getMessage());
+            material = matToRemove;
+        }
+    }
+
+    private class MatStructChangeListener implements PropertyChangeListener {
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (evt.getSource() instanceof ShaderNodeBlock && evt.getPropertyName().equals("name")) {
+                String oldValue = (String) evt.getOldValue();
+                String newValue = (String) evt.getNewValue();
+                for (ShaderNodeBlock shaderNodeBlock : currentTechnique.getShaderNodes()) {
+                    List<InputMappingBlock> lin = shaderNodeBlock.getInputs();
+                    if (lin != null) {
+                        for (InputMappingBlock inputMappingBlock : shaderNodeBlock.getInputs()) {
+                            if (inputMappingBlock.getLeftNameSpace().equals(oldValue)) {
+                                inputMappingBlock.setLeftNameSpace(newValue);
+                            }
+                            if (inputMappingBlock.getRightNameSpace().equals(oldValue)) {
+                                inputMappingBlock.setRightNameSpace(newValue);
+                            }
+                        }
+                    }
+                    List<OutputMappingBlock> l = shaderNodeBlock.getOutputs();
+                    if (l != null) {
+                        for (OutputMappingBlock outputMappingBlock : l) {
+                            if (outputMappingBlock.getRightNameSpace().equals(oldValue)) {
+                                outputMappingBlock.setRightNameSpace(newValue);
+                            }
+                        }
+                    }
+                }
+            }
+            if (evt.getPropertyName().equals(MatDefBlock.ADD_MAT_PARAM)
+                    || evt.getPropertyName().equals(TechniqueBlock.ADD_SHADER_NODE)
+                    || evt.getPropertyName().equals(ShaderNodeBlock.ADD_MAPPING)) {
+                registerListener((Statement) evt.getNewValue());
+            }
+            applyChange();
+        }
+    }
+    Material matToRemove;
+
+    private void applyChange() {
+
+        try {
+            EditorCookie ec = (EditorCookie) lookup.lookup(EditorCookie.class);
+            final StyledDocument doc = ec.getDocument();
+            final BadLocationException[] exc = new BadLocationException[]{null};
+            NbDocument.runAtomicAsUser(ec.getDocument(), new Runnable() {
+                public void run() {
+                    try {
+                        doc.remove(0, doc.getLength());
+                        doc.insertString(doc.getLength(),
+                                matDefStructure.toString(),
+                                SimpleAttributeSet.EMPTY);
+                    } catch (BadLocationException e) {
+                        exc[0] = e;
+                    }
+                }
+            });
+        } catch (BadLocationException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        AssetKey<MaterialDef> key = new AssetKey<MaterialDef>(assetManager.getRelativeAssetPath(matDefFile.getPath()));
+        obj.getLookupContents().remove(materialDef);
+        matToRemove = material;
+
+        List<Statement> l = new ArrayList<Statement>();
+        l.add(matDefStructure);
+        try {
+            materialDef = loader.loadMaterialDef(l, assetManager, key);
+        } catch (IOException ex) {
+            Logger.getLogger(EditableMatDefFile.class.getName()).log(Level.SEVERE, ex.getMessage());
+        }
+        updateLookupWithMaterialData(obj);
+        AssetKey matDefKey = new AssetKey(assetManager.getRelativeAssetPath(assetManager.getRelativeAssetPath(matDefFile.getPath())));
+        assetManager.deleteFromCache(matDefKey);
+
+    }
+}

+ 34 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatDef.j3md

@@ -0,0 +1,34 @@
+MaterialDef Simple {
+    MaterialParameters {
+        Color Color
+    }
+    Technique {
+        WorldParameters {
+            WorldViewProjectionMatrix
+        }
+        VertexShaderNodes {
+            ShaderNode CommonVert {
+                Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn
+                InputMappings {
+                    worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix
+                    modelPosition = Global.position.xyz
+                }
+                OutputMappings {
+                    Global.position = projPosition
+                }
+            }
+        }
+        FragmentShaderNodes {
+            ShaderNode ColorMult {
+                Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
+                InputMappings {
+                    color1 = MatParam.Color
+                    color2 = Global.color
+                }
+                OutputMappings {
+                    Global.color = outColor
+                }
+            }
+        }
+    }
+}

+ 309 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatDefDataObject.java

@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.gde.materialdefinition;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.materialdefinition.navigator.MatDefNavigatorPanel;
+import java.io.IOException;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.core.spi.multiview.MultiViewElement;
+import org.netbeans.core.spi.multiview.text.MultiViewEditorElement;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.cookies.SaveCookie;
+import org.openide.filesystems.FileChangeAdapter;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.MIMEResolver;
+import org.openide.loaders.DataFolder;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiDataObject;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle.Messages;
+import org.openide.util.lookup.AbstractLookup;
+import org.openide.util.lookup.InstanceContent;
+import org.openide.util.lookup.ProxyLookup;
+import org.openide.windows.TopComponent;
+
+@Messages({
+    "LBL_MatDef_LOADER=JME Material definition"
+})
[email protected](
+    displayName = "#LBL_MatDef_LOADER",
+mimeType = "text/jme-materialdefinition",
+extension = {"j3md", "J3MD"})
[email protected](
+    mimeType = "text/jme-materialdefinition",
+iconBase = "com/jme3/gde/materialdefinition/icons/matdef.png",
+displayName = "#LBL_MatDef_LOADER",
+position = 300)
+@ActionReferences({
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.OpenAction"),
+    position = 100,
+    separatorAfter = 200),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.CutAction"),
+    position = 300),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"),
+    position = 400,
+    separatorAfter = 500),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
+    position = 600),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.RenameAction"),
+    position = 700,
+    separatorAfter = 800),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"),
+    position = 900,
+    separatorAfter = 1000),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"),
+    position = 1100,
+    separatorAfter = 1200),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.ToolsAction"),
+    position = 1300),
+    @ActionReference(
+        path = "Loaders/text/jme-materialdefinition/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
+    position = 1400)
+})
+public class MatDefDataObject extends MultiDataObject {
+
+    protected final Lookup lookup;
+    protected final InstanceContent lookupContents = new InstanceContent();
+    protected AbstractLookup contentLookup;
+    private EditableMatDefFile file = null;
+    private boolean loaded = false;
+
+    public MatDefDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+        registerEditor("text/jme-materialdefinition", true);
+        contentLookup = new AbstractLookup(lookupContents);
+        lookupContents.add(this);
+        lookup = new ProxyLookup(getCookieSet().getLookup(), contentLookup);
+        findAssetManager();
+        final MatDefMetaData metaData = new MatDefMetaData(this);
+        lookupContents.add(metaData);
+        pf.addFileChangeListener(new FileChangeAdapter(){
+
+            @Override
+            public void fileChanged(FileEvent fe) {
+                super.fileChanged(fe);
+                metaData.save();
+            }
+            
+        });
+
+    }
+
+    private void findAssetManager() {
+        FileObject primaryFile = getPrimaryFile();
+        ProjectManager pm = ProjectManager.getDefault();
+        while (primaryFile != null) {
+            if (primaryFile.isFolder() && pm.isProject(primaryFile)) {
+                try {
+                    Project project = ProjectManager.getDefault().findProject(primaryFile);
+                    if (project != null) {
+                        getLookupContents().add(project);
+                        ProjectAssetManager mgr = project.getLookup().lookup(ProjectAssetManager.class);
+                        if (mgr != null) {
+                            getLookupContents().add(mgr);
+                            return;
+                        }
+                    }
+                } catch (IOException ex) {
+                } catch (IllegalArgumentException ex) {
+                }
+            }
+            primaryFile = primaryFile.getParent();
+        }
+//        getLookupContents().add(new ProjectAssetManager(file.getParent()));
+    }
+
+    @Override
+    protected int associateLookup() {
+        return 1;
+    }
+
+    @Override
+    public Lookup getLookup() {
+        return lookup;
+    }
+
+    @MultiViewElement.Registration(
+        displayName = "#LBL_MatDef_EDITOR",
+    iconBase = "com/jme3/gde/materialdefinition/icons/matdef.png",
+    mimeType = "text/jme-materialdefinition",
+    persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED,
+    preferredID = "MatDef",
+    position = 1000)
+    @Messages("LBL_MatDef_EDITOR=Text")
+    public static MultiViewEditorElement createEditor(Lookup lkp) {
+        final MatDefDataObject obj = lkp.lookup(MatDefDataObject.class);
+        obj.loaded = true;
+        MatDefNavigatorPanel nav = obj.getLookup().lookup(MatDefNavigatorPanel.class);
+        if (nav != null) {
+            nav.updateData(obj);
+        }
+        MultiViewEditorElement ed = new MultiViewEditorElement(lkp) {
+            @Override
+            public void componentClosed() {
+                super.componentClosed();
+                obj.unload();
+            }
+        };
+
+
+        return ed;
+    }
+
+    @Override
+    protected void handleDelete() throws IOException {
+        MatDefMetaData metaData = lookup.lookup(MatDefMetaData.class);
+        metaData.cleanup();
+        super.handleDelete();
+    }
+
+    @Override
+    protected FileObject handleRename(String name) throws IOException {
+        MatDefMetaData metaData = lookup.lookup(MatDefMetaData.class);
+        metaData.rename(null, name);
+        return super.handleRename(name);
+    }
+
+    @Override
+    protected FileObject handleMove(DataFolder df) throws IOException {
+        MatDefMetaData metaData = lookup.lookup(MatDefMetaData.class);
+        metaData.rename(df, null);
+        return super.handleMove(df);
+    }
+
+    @Override
+    protected DataObject handleCopy(DataFolder df) throws IOException {
+        MatDefMetaData metaData = lookup.lookup(MatDefMetaData.class);
+        metaData.duplicate(df, null);
+        return super.handleCopy(df);
+    }
+
+    @Override
+    protected DataObject handleCopyRename(DataFolder df, String name, String ext) throws IOException {
+        MatDefMetaData metaData = lookup.lookup(MatDefMetaData.class);
+        metaData.duplicate(df, name);
+        return super.handleCopyRename(df, name, ext);
+    }
+    
+    
+
+    
+    
+    public EditableMatDefFile getEditableFile() {
+        if (file == null) {
+            file = new EditableMatDefFile(getLookup());
+        }
+
+        return file;
+    }
+
+    public boolean isLoaded() {
+        return loaded;
+    }
+
+    public void unload() {
+        if (loaded) {
+            loaded = false;
+            getLookup().lookup(MatDefNavigatorPanel.class).updateData(null);
+        }
+    }
+
+    public InstanceContent getLookupContents() {
+        return lookupContents;
+    }
+//    @Override
+//    public synchronized void saveAsset() throws IOException {
+//        
+////        ProgressHandle progressHandle = ProgressHandleFactory.createHandle("Saving File..");
+////        progressHandle.start();
+// //      BinaryExporter exp = BinaryExporter.getInstance();
+//        FileLock lock = null;
+//        OutputStream out = null;
+//        try {
+//             PrintWriter to = new PrintWriter(getPrimaryFile().getOutputStream(lock));
+//            try {
+//                to.print(getEditableFile().getMatDefStructure().toString());
+//              
+//            } finally {
+//                to.close();
+//            }
+//        } finally {
+//            if (lock != null) {
+//                lock.releaseLock();
+//            }
+//            if (out != null) {
+//                out.close();
+//            }
+//        }
+//     //   progressHandle.finish();
+//        StatusDisplayer.getDefault().setStatusText(getPrimaryFile().getNameExt() + " saved.");
+//        setModified(false);
+//        
+////        getPrimaryFile().
+////                getOutputStream().write(getEditableFile().getMatDefStructure().toString().getBytes());        
+//       
+//    }
+}

+ 398 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatDefMetaData.java

@@ -0,0 +1,398 @@
+/*
+ *  Copyright (c) 2009-2010 jMonkeyEngine
+ *  All rights reserved.
+ * 
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ * 
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ *  * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.gde.materialdefinition;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.BufferedOutputStream;
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.project.Project;
+import org.openide.filesystems.FileLock;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataFolder;
+import org.openide.util.Exceptions;
+import org.openide.util.Mutex;
+import org.openide.util.Mutex.Action;
+
+/**
+ * Global object to access actual jME3 data within an AssetDataObject, available
+ * through the Lookup of any AssetDataObject. AssetDataObjects that wish to use
+ *
+ * @author normenhansen
+ */
+@SuppressWarnings("unchecked")
+public class MatDefMetaData {
+
+    private static final Logger logger = Logger.getLogger(MatDefMetaData.class.getName());
+    private final List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
+    private final Mutex propsMutex = new Mutex();
+    private final Properties props = new Properties();
+    private static final Properties defaultProps = new Properties();
+
+    static {
+        defaultProps.put("Default/position", "0,120");
+        defaultProps.put("Default/MatParam.Color", "438,351");
+        defaultProps.put("Default/Default/ColorMult", "605,372");
+        defaultProps.put("Default/WorldParam.WorldViewProjectionMatrix", "33,241");
+        defaultProps.put("Default/color", "0,478");
+        defaultProps.put("Default/Default/CommonVert", "211,212");
+    }
+    private MatDefDataObject file;
+    private String extension = "jmpdata";
+    private Date lastLoaded;
+    private FileObject folder;
+    private FileObject root;
+
+//    private XMLFileSystem fs;
+    public MatDefMetaData(MatDefDataObject file) {
+        try {
+            this.file = file;
+            getFolder();
+            FileObject primaryFile = file.getPrimaryFile();
+
+            if (primaryFile != null) {
+                extension = primaryFile.getExt() + "data";
+            }
+
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+    }
+
+    public MatDefMetaData(MatDefDataObject file, String extension) {
+        this.file = file;
+        this.extension = extension;
+    }
+
+    public synchronized String getProperty(final String key) {
+        readProperties();
+        return propsMutex.readAccess(new Action<String>() {
+            public String run() {
+                String prop = props.getProperty(key);
+                if (prop == null) {
+                    return defaultProps.getProperty(key);
+                }
+                return prop;
+            }
+        });
+    }
+
+    public synchronized String getProperty(final String key, final String defaultValue) {
+        readProperties();
+        return propsMutex.readAccess(new Action<String>() {
+            public String run() {
+                String prop = props.getProperty(key);
+                if (prop == null) {
+                    return defaultProps.getProperty(key);
+                }
+                return prop;                
+            }
+        });
+    }
+
+    public synchronized String setProperty(final String key, final String value) {
+        readProperties();
+        String ret = propsMutex.writeAccess(new Action<String>() {
+            public String run() {
+                String ret = (String) props.setProperty(key, value);
+                return ret;
+            }
+        });
+       // writeProperties();
+        notifyListeners(key, ret, value);
+        return ret;
+    }
+
+    private void readProperties() {
+        propsMutex.writeAccess(new Runnable() {
+            public void run() {
+                try {
+                    FileObject pFile = file.getPrimaryFile();
+                    FileObject storageFolder = getFolder();
+                    if (storageFolder == null) {
+                        return;
+                    }
+                    final FileObject myFile = storageFolder.getFileObject(getFileFullName(pFile)); //fs.findResource(fs.getRoot().getPath()+"/"+ file.getPrimaryFile().getName() + "." + extension);//
+
+                    if (myFile == null) {
+                        return;
+                    }
+                    final Date lastMod = myFile.lastModified();
+                    if (!lastMod.equals(lastLoaded)) {
+                        props.clear();
+                        lastLoaded = lastMod;
+                        InputStream in = null;
+                        try {
+                            in = new BufferedInputStream(myFile.getInputStream());
+                            try {
+                                props.load(in);
+                            } catch (IOException ex) {
+                                Exceptions.printStackTrace(ex);
+                            }
+                        } catch (FileNotFoundException ex) {
+                            Exceptions.printStackTrace(ex);
+                        } finally {
+                            try {
+                                in.close();
+                            } catch (IOException ex) {
+                                Exceptions.printStackTrace(ex);
+                            }
+                        }
+                        logger.log(Level.FINE, "Read AssetData properties for {0}", file);
+                    }
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        });
+    }
+
+    public void cleanup() {
+        propsMutex.writeAccess(new Runnable() {
+            public void run() {
+                OutputStream out = null;
+                FileLock lock = null;
+                try {
+                    FileObject pFile = file.getPrimaryFile();
+                    FileObject storageFolder = getFolder();
+                    if (storageFolder == null) {
+                        return;
+                    }
+
+                    FileObject myFile = storageFolder.getFileObject(getFileFullName(pFile));//FileUtil.findBrother(pFile, extension);//fs.findResource(fs.getRoot().getPath()+"/"+ pFile.getName() + "." + extension);//
+                    if (myFile == null) {
+                        return;
+                    }
+                    lock = myFile.lock();
+                    myFile.delete(lock);
+                } catch (IOException e) {
+                    Exceptions.printStackTrace(e);
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (IOException ex) {
+                            Exceptions.printStackTrace(ex);
+                        }
+                    }
+                    if (lock != null) {
+                        lock.releaseLock();
+                    }
+                }
+            }
+        });
+
+    }
+
+    public void rename(final DataFolder df, final String name) {
+        propsMutex.writeAccess(new Runnable() {
+            public void run() {
+                OutputStream out = null;
+                FileLock lock = null;
+                try {
+                    FileObject pFile = file.getPrimaryFile();
+                    FileObject storageFolder = getFolder();
+                    if (storageFolder == null) {
+                        return;
+                    }
+
+                    FileObject myFile = storageFolder.getFileObject(getFileFullName(pFile));//FileUtil.findBrother(pFile, extension);//fs.findResource(fs.getRoot().getPath()+"/"+ pFile.getName() + "." + extension);//
+                    if (myFile == null) {
+                        return;
+                    }
+                    lock = myFile.lock();
+                    if (df == null) {
+                        myFile.rename(lock, getFileFullName(pFile).replaceAll(file.getName() + "." + extension, name + "." + extension), "");
+                    } else {
+                        myFile.rename(lock, FileUtil.getRelativePath(root, df.getPrimaryFile()).replaceAll("/", ".") + "." + pFile.getName(), extension);
+                    }
+                } catch (IOException e) {
+                    Exceptions.printStackTrace(e);
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (IOException ex) {
+                            Exceptions.printStackTrace(ex);
+                        }
+                    }
+                    if (lock != null) {
+                        lock.releaseLock();
+                    }
+                }
+            }
+        });
+
+    }
+
+    public void duplicate(final DataFolder df, final String name) {
+        propsMutex.writeAccess(new Runnable() {
+            public void run() {
+                OutputStream out = null;
+                FileLock lock = null;
+                try {
+                    FileObject pFile = file.getPrimaryFile();
+                    FileObject storageFolder = getFolder();
+                    if (storageFolder == null) {
+                        return;
+                    }
+                    String newName = name;
+                    if (newName == null) {
+                        newName = file.getName();
+                    }
+                    String path = FileUtil.getRelativePath(root, df.getPrimaryFile()).replaceAll("/", ".") + "." + newName;
+                    FileObject myFile = storageFolder.getFileObject(getFileFullName(pFile));
+                    if (myFile == null) {
+                        return;
+                    }
+
+                    myFile.copy(storageFolder, path, extension);
+
+                } catch (IOException e) {
+                    Exceptions.printStackTrace(e);
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (IOException ex) {
+                            Exceptions.printStackTrace(ex);
+                        }
+                    }
+                    if (lock != null) {
+                        lock.releaseLock();
+                    }
+                }
+            }
+        });
+
+    }
+
+    public void save(){
+        writeProperties();
+    }
+    
+    private void writeProperties() {
+        //writeAccess because we write lastMod date, not because we write to the file
+        //the mutex protects the properties object, not the file
+        propsMutex.writeAccess(new Runnable() {
+            public void run() {
+                OutputStream out = null;
+                FileLock lock = null;
+                try {
+                    FileObject pFile = file.getPrimaryFile();
+                    FileObject storageFolder = getFolder();
+                    if (storageFolder == null) {
+                        return;
+                    }
+
+                    FileObject myFile = storageFolder.getFileObject(getFileFullName(pFile));//FileUtil.findBrother(pFile, extension);//fs.findResource(fs.getRoot().getPath()+"/"+ pFile.getName() + "." + extension);//
+                    if (myFile == null) {
+                        myFile = FileUtil.createData(storageFolder, getFileFullName(pFile));
+                    }
+                    lock = myFile.lock();
+                    out = new BufferedOutputStream(myFile.getOutputStream(lock));
+                    props.store(out, "");
+                    out.flush();
+                    lastLoaded = myFile.lastModified();
+                    logger.log(Level.FINE, "Written AssetData properties for {0}", file);
+                } catch (IOException e) {
+                    Exceptions.printStackTrace(e);
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (IOException ex) {
+                            Exceptions.printStackTrace(ex);
+                        }
+                    }
+                    if (lock != null) {
+                        lock.releaseLock();
+                    }
+                }
+            }
+        });
+    }
+
+    private String getFileFullName(FileObject pFile) {
+        return FileUtil.getRelativePath(root, pFile).replaceAll("/", ".").replaceAll("j3md", extension);
+    }
+
+    protected void notifyListeners(String property, String before, String after) {
+        synchronized (listeners) {
+            for (Iterator<PropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
+                PropertyChangeListener propertyChangeListener = it.next();
+                propertyChangeListener.propertyChange(new PropertyChangeEvent(this, property, before, after));
+            }
+        }
+    }
+
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+    }
+
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+    }
+
+    private FileObject getFolder() throws IOException {
+        if (folder == null) {
+            Project p = file.getLookup().lookup(Project.class);
+            if (p != null) {
+                root = p.getProjectDirectory();
+
+                FileObject jmedataFolder = root.getFileObject("/nbproject/jme3Data");
+                if (jmedataFolder == null) {
+                    jmedataFolder = root.getFileObject("/nbproject");
+                    jmedataFolder = jmedataFolder.createFolder("jme3Data");
+                }
+                return jmedataFolder;
+            }
+        }
+        return folder;
+    }
+}

+ 135 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddAttributeDialog.form

@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+    <Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+      <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddAttributeDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+    </Property>
+    <Property name="modal" type="boolean" value="true"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="1" attributes="0">
+                      <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="32767" attributes="0"/>
+                      <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Group type="103" groupAlignment="1" attributes="0">
+                          <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
+                          <Component id="jLabel1" min="-2" pref="123" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="nameField" pref="253" max="32767" attributes="0"/>
+                          <Component id="typeField" max="32767" attributes="0"/>
+                      </Group>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="nameField" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="typeField" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JButton" name="jButton1">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddAttributeDialog.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JButton" name="jButton2">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddAttributeDialog.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel1">
+      <Properties>
+        <Property name="horizontalAlignment" type="int" value="11"/>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddAttributeDialog.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="nameField">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="4">
+            <StringItem index="0" value="Item 1"/>
+            <StringItem index="1" value="Item 2"/>
+            <StringItem index="2" value="Item 3"/>
+            <StringItem index="3" value="Item 4"/>
+          </StringArray>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="nameFieldActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel2">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddAttributeDialog.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="typeField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddAttributeDialog.typeField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>

+ 196 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddAttributeDialog.java

@@ -0,0 +1,196 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.dialog;
+
+import com.jme3.gde.materialdefinition.editor.Diagram;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.UniformBinding;
+import java.awt.Point;
+import javax.swing.DefaultComboBoxModel;
+
+/**
+ *
+ * @author Nehon
+ */
+public class AddAttributeDialog extends javax.swing.JDialog {
+
+    private Diagram diagram;
+    private Point clickPosition;
+
+    /**
+     * Creates new form AddMaterialParameter
+     */
+    public AddAttributeDialog(java.awt.Frame parent, boolean modal, Diagram diagram, Point clickPosition) {
+        super(parent, modal);
+        initComponents();
+        DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>();
+
+        for (VertexBuffer.Type attr : VertexBuffer.Type.values()) {
+            model.addElement("in" + attr.name());
+        }
+        this.diagram = diagram;
+        this.clickPosition = clickPosition;
+        nameField.setModel(model);
+        updateType();
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jButton1 = new javax.swing.JButton();
+        jButton2 = new javax.swing.JButton();
+        jLabel1 = new javax.swing.JLabel();
+        nameField = new javax.swing.JComboBox();
+        jLabel2 = new javax.swing.JLabel();
+        typeField = new javax.swing.JTextField();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle(org.openide.util.NbBundle.getMessage(AddAttributeDialog.class, "AddAttributeDialog.title")); // NOI18N
+        setModal(true);
+
+        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(AddAttributeDialog.class, "AddAttributeDialog.jButton1.text")); // NOI18N
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton1ActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(AddAttributeDialog.class, "AddAttributeDialog.jButton2.text")); // NOI18N
+        jButton2.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton2ActionPerformed(evt);
+            }
+        });
+
+        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AddAttributeDialog.class, "AddAttributeDialog.jLabel1.text")); // NOI18N
+
+        nameField.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
+        nameField.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                nameFieldActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddAttributeDialog.class, "AddAttributeDialog.jLabel2.text")); // NOI18N
+
+        typeField.setText(org.openide.util.NbBundle.getMessage(AddAttributeDialog.class, "AddAttributeDialog.typeField.text")); // NOI18N
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                        .addComponent(jButton2)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(jButton1))
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                            .addComponent(jLabel2)
+                            .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 123, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(nameField, 0, 253, Short.MAX_VALUE)
+                            .addComponent(typeField))))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel1)
+                    .addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel2)
+                    .addComponent(typeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jButton1)
+                    .addComponent(jButton2))
+                .addContainerGap())
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
+        setVisible(false);
+    }//GEN-LAST:event_jButton2ActionPerformed
+
+    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+        diagram.addAttribute((String) nameField.getSelectedItem(), typeField.getText(), clickPosition);
+        setVisible(false);
+    }//GEN-LAST:event_jButton1ActionPerformed
+
+    private void nameFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameFieldActionPerformed
+        updateType();
+    }
+
+    private void updateType() {
+        VertexBuffer.Type attr = VertexBuffer.Type.valueOf(((String) nameField.getSelectedItem()).replaceFirst("in", ""));
+        switch (attr) {
+
+            case BoneWeight:
+
+            case BindPoseNormal:
+            case Binormal:
+
+            case Normal:
+                typeField.setText("vec3");
+                break;
+
+            case Size:
+                typeField.setText("float");
+                break;
+
+            case Position:
+            case BindPosePosition:
+            case BindPoseTangent:
+            case Tangent:
+            case Color:
+                typeField.setText("vec4");
+                break;
+            case InterleavedData:
+                typeField.setText("int");
+                break;
+            case Index:
+                typeField.setText("uint");
+                break;
+            case BoneIndex:
+                typeField.setText("uvec4");
+                break;
+
+            case TexCoord:
+            case TexCoord2:
+            case TexCoord3:
+            case TexCoord4:
+            case TexCoord5:
+            case TexCoord6:
+            case TexCoord7:
+            case TexCoord8:
+                typeField.setText("vec2");
+                break;
+        }
+    }//GEN-LAST:event_nameFieldActionPerformed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JButton jButton2;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JLabel jLabel2;
+    private javax.swing.JComboBox nameField;
+    private javax.swing.JTextField typeField;
+    // End of variables declaration//GEN-END:variables
+}

+ 135 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddMaterialParameterDialog.form

@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+    <Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+      <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddMaterialParameterDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+    </Property>
+    <Property name="modal" type="boolean" value="true"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="1" attributes="0">
+                      <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="32767" attributes="0"/>
+                      <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Group type="103" groupAlignment="1" attributes="0">
+                          <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+                          <Component id="jLabel1" min="-2" pref="123" max="-2" attributes="0"/>
+                      </Group>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="typeField" pref="253" max="32767" attributes="0"/>
+                          <Component id="nameField" max="32767" attributes="0"/>
+                      </Group>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="typeField" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace type="unrelated" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="nameField" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace pref="23" max="32767" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JButton" name="jButton1">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddMaterialParameterDialog.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JButton" name="jButton2">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddMaterialParameterDialog.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel1">
+      <Properties>
+        <Property name="horizontalAlignment" type="int" value="11"/>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddMaterialParameterDialog.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="typeField">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="4">
+            <StringItem index="0" value="Item 1"/>
+            <StringItem index="1" value="Item 2"/>
+            <StringItem index="2" value="Item 3"/>
+            <StringItem index="3" value="Item 4"/>
+          </StringArray>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel3">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddMaterialParameterDialog.jLabel3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="nameField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddMaterialParameterDialog.nameField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="keyPressed" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="nameFieldKeyPressed"/>
+      </Events>
+    </Component>
+  </SubComponents>
+</Form>

+ 161 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddMaterialParameterDialog.java

@@ -0,0 +1,161 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.dialog;
+
+import com.jme3.gde.materialdefinition.editor.Diagram;
+import com.jme3.shader.VarType;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import javax.swing.DefaultComboBoxModel;
+
+/**
+ *
+ * @author Nehon
+ */
+public class AddMaterialParameterDialog extends javax.swing.JDialog {
+
+    private Diagram diagram;
+    private Point clickPosition;
+
+    /**
+     * Creates new form AddMaterialParameter
+     */
+    public AddMaterialParameterDialog(java.awt.Frame parent, boolean modal, Diagram diagram, Point clickPosition) {
+        super(parent, modal);
+        initComponents();
+        DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>();
+        model.addElement("Color");
+        for (VarType varType : VarType.values()) {
+
+            model.addElement(varType.name());
+        }
+        this.diagram = diagram;
+        this.clickPosition = clickPosition;
+        typeField.setModel(model);
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jButton1 = new javax.swing.JButton();
+        jButton2 = new javax.swing.JButton();
+        jLabel1 = new javax.swing.JLabel();
+        typeField = new javax.swing.JComboBox();
+        jLabel3 = new javax.swing.JLabel();
+        nameField = new javax.swing.JTextField();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle(org.openide.util.NbBundle.getMessage(AddMaterialParameterDialog.class, "AddMaterialParameterDialog.title")); // NOI18N
+        setModal(true);
+
+        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(AddMaterialParameterDialog.class, "AddMaterialParameterDialog.jButton1.text")); // NOI18N
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton1ActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(AddMaterialParameterDialog.class, "AddMaterialParameterDialog.jButton2.text")); // NOI18N
+        jButton2.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton2ActionPerformed(evt);
+            }
+        });
+
+        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AddMaterialParameterDialog.class, "AddMaterialParameterDialog.jLabel1.text")); // NOI18N
+
+        typeField.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
+
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(AddMaterialParameterDialog.class, "AddMaterialParameterDialog.jLabel3.text")); // NOI18N
+
+        nameField.setText(org.openide.util.NbBundle.getMessage(AddMaterialParameterDialog.class, "AddMaterialParameterDialog.nameField.text")); // NOI18N
+        nameField.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyPressed(java.awt.event.KeyEvent evt) {
+                nameFieldKeyPressed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                        .addComponent(jButton2)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(jButton1))
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                            .addComponent(jLabel3)
+                            .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 123, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(typeField, 0, 253, Short.MAX_VALUE)
+                            .addComponent(nameField))))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel1)
+                    .addComponent(typeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel3)
+                    .addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 23, Short.MAX_VALUE)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jButton1)
+                    .addComponent(jButton2))
+                .addContainerGap())
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
+        setVisible(false);
+    }//GEN-LAST:event_jButton2ActionPerformed
+
+    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+        okPressed();
+    }//GEN-LAST:event_jButton1ActionPerformed
+
+    private void nameFieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_nameFieldKeyPressed
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
+            okPressed();
+        }
+    }//GEN-LAST:event_nameFieldKeyPressed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JButton jButton2;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JLabel jLabel3;
+    private javax.swing.JTextField nameField;
+    private javax.swing.JComboBox typeField;
+    // End of variables declaration//GEN-END:variables
+
+    private void okPressed() {
+        String name = nameField.getText();
+        if (name.length() > 1) {
+            name = name.substring(0, 1).toUpperCase() + name.substring(1);
+        } else {
+            name = name.toUpperCase();
+        }
+        diagram.addMatParam((String) typeField.getSelectedItem(), name, clickPosition);
+        setVisible(false);
+    }
+}

+ 167 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddNodeDialog.form

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+    <Property name="title" type="java.lang.String" value="Add a Shader Node"/>
+    <Property name="modal" type="boolean" value="true"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="1" attributes="0">
+          <Component id="jPanel2" max="32767" attributes="0"/>
+          <Component id="jSplitPane1" alignment="0" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <Component id="jSplitPane1" max="32767" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jPanel2" min="-2" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="jPanel2">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="1" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="32767" attributes="0"/>
+                  <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JButton" name="jButton1">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="ok"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="jButton2">
+          <Properties>
+            <Property name="mnemonic" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="KeyEvent.VK_ESCAPE" type="code"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="cancel"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JSplitPane" name="jSplitPane1">
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
+      <SubComponents>
+        <Container class="javax.swing.JPanel" name="jPanel1">
+          <Properties>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
+                <TitledBorder title="Shader node definitions"/>
+              </Border>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="left"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout>
+            <DimensionLayout dim="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jScrollPane3" alignment="0" pref="236" max="32767" attributes="0"/>
+              </Group>
+            </DimensionLayout>
+            <DimensionLayout dim="1">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jScrollPane3" alignment="0" pref="468" max="32767" attributes="0"/>
+              </Group>
+            </DimensionLayout>
+          </Layout>
+          <SubComponents>
+            <Container class="javax.swing.JScrollPane" name="jScrollPane3">
+              <AuxValues>
+                <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+              </AuxValues>
+
+              <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+              <SubComponents>
+                <Component class="javax.swing.JTree" name="jTree1">
+                </Component>
+              </SubComponents>
+            </Container>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JPanel" name="shaderNodesList">
+          <Properties>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
+                <TitledBorder title="Shader node definition"/>
+              </Border>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="right"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout>
+            <DimensionLayout dim="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jTabbedPane1" alignment="1" pref="339" max="32767" attributes="0"/>
+              </Group>
+            </DimensionLayout>
+            <DimensionLayout dim="1">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jTabbedPane1" alignment="1" pref="468" max="32767" attributes="0"/>
+              </Group>
+            </DimensionLayout>
+          </Layout>
+          <SubComponents>
+            <Container class="javax.swing.JTabbedPane" name="jTabbedPane1">
+
+              <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
+            </Container>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 254 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddNodeDialog.java

@@ -0,0 +1,254 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.dialog;
+
+import com.jme3.asset.ShaderNodeDefinitionKey;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.util.TreeUtil;
+import com.jme3.gde.materialdefinition.editor.Diagram;
+import com.jme3.gde.materialdefinition.editor.NodePanel;
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.gde.materialdefinition.utils.DocFormatter;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderNodeDefinition;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreeSelectionModel;
+
+/**
+ *
+ * @author m327836
+ */
+public class AddNodeDialog extends javax.swing.JDialog {
+
+    private List<ShaderNodeDefinition> defList = new ArrayList<ShaderNodeDefinition>();
+    private Diagram diagram;
+    private Point clickPosition;
+    private String path;
+
+    /**
+     * Creates new form NewJDialog
+     */
+    public AddNodeDialog(java.awt.Frame parent, boolean modal, ProjectAssetManager mgr, Diagram diagram, Point clickPosition) {
+        super(parent, modal);
+        this.diagram = diagram;
+        initComponents();
+        loadDefs(mgr);
+        fillList(mgr);
+        this.clickPosition = clickPosition;
+
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jPanel2 = new javax.swing.JPanel();
+        jButton1 = new javax.swing.JButton();
+        jButton2 = new javax.swing.JButton();
+        jSplitPane1 = new javax.swing.JSplitPane();
+        jPanel1 = new javax.swing.JPanel();
+        jScrollPane3 = new javax.swing.JScrollPane();
+        jTree1 = new javax.swing.JTree();
+        shaderNodesList = new javax.swing.JPanel();
+        jTabbedPane1 = new javax.swing.JTabbedPane();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle("Add a Shader Node");
+        setModal(true);
+
+        jButton1.setText("ok");
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton1ActionPerformed(evt);
+            }
+        });
+
+        jButton2.setMnemonic(KeyEvent.VK_ESCAPE);
+        jButton2.setText("cancel");
+        jButton2.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton2ActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+        jPanel2.setLayout(jPanel2Layout);
+        jPanel2Layout.setHorizontalGroup(
+            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(jButton2)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(jButton1)
+                .addContainerGap())
+        );
+        jPanel2Layout.setVerticalGroup(
+            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                .addComponent(jButton1)
+                .addComponent(jButton2))
+        );
+
+        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Shader node definitions"));
+
+        jScrollPane3.setViewportView(jTree1);
+
+        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+        jPanel1.setLayout(jPanel1Layout);
+        jPanel1Layout.setHorizontalGroup(
+            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 236, Short.MAX_VALUE)
+        );
+        jPanel1Layout.setVerticalGroup(
+            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 468, Short.MAX_VALUE)
+        );
+
+        jSplitPane1.setLeftComponent(jPanel1);
+
+        shaderNodesList.setBorder(javax.swing.BorderFactory.createTitledBorder("Shader node definition"));
+
+        javax.swing.GroupLayout shaderNodesListLayout = new javax.swing.GroupLayout(shaderNodesList);
+        shaderNodesList.setLayout(shaderNodesListLayout);
+        shaderNodesListLayout.setHorizontalGroup(
+            shaderNodesListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jTabbedPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 339, Short.MAX_VALUE)
+        );
+        shaderNodesListLayout.setVerticalGroup(
+            shaderNodesListLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jTabbedPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 468, Short.MAX_VALUE)
+        );
+
+        jSplitPane1.setRightComponent(shaderNodesList);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+            .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+            .addComponent(jSplitPane1, javax.swing.GroupLayout.Alignment.LEADING)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addComponent(jSplitPane1)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
+        setVisible(false);
+    }//GEN-LAST:event_jButton2ActionPerformed
+
+    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+        setVisible(false);
+        diagram.addNodesFromDefs(defList, path, clickPosition);
+    }//GEN-LAST:event_jButton1ActionPerformed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JButton jButton2;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JPanel jPanel2;
+    private javax.swing.JScrollPane jScrollPane3;
+    private javax.swing.JSplitPane jSplitPane1;
+    private javax.swing.JTabbedPane jTabbedPane1;
+    private javax.swing.JTree jTree1;
+    private javax.swing.JPanel shaderNodesList;
+    // End of variables declaration//GEN-END:variables
+
+    private void loadDefs(ProjectAssetManager mgr) {
+//          defList.addAll();
+//          defList.addAll(mgr.getDependenciesShaderNodeDefs());
+//        List<ShaderNodeVariable> inputs = new ArrayList<ShaderNodeVariable>();
+//        inputs.add(new ShaderNodeVariable("vec2", "texCoord1"));
+//        inputs.add(new ShaderNodeVariable("vec2", "texCoord2"));
+//        List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
+//        outputs.add(new ShaderNodeVariable("vec2", "texCoord1"));
+//        outputs.add(new ShaderNodeVariable("vec2", "texCoord2"));
+//        ShaderNodeDefinition fogdef = new ShaderNodeDefinition("FogFactor", Shader.ShaderType.Vertex, "", "");
+//        fogdef.setInputs(inputs);
+//        fogdef.setOutputs(outputs);
+//        fogdef.setDocumentation(
+//                "                   This Node is responsible for adding a fog contribution to a color according to a fogColor and a fogFactor.\n"
+//                + "                    This node should be used with a FogFactor node that will be responsible to compute the fogFactor in the vertex shader.\n"
+//                + "                    Inputs :    \n"
+//                + "                    color : the color on which the fog contribution will be added.\n"
+//                + "                    fogFactor : the previously computed fog factor                     \n"
+//                + "                    fogColor : the fog color\n"
+//                + "                    Outputs : \n"
+//                + "                    outColor : the color with fog contribution (usually assigned to Global.color) ");
+//        
+//        defList.add(fogdef);
+    }
+
+    private void createDoc(ShaderNodeDefinition def) {
+        JTextPane doc = new JTextPane();
+        doc.setEditable(false);
+        doc.setBackground(new java.awt.Color(240, 240, 240));
+        doc.setMaximumSize(new java.awt.Dimension(300, 300));
+        doc.setMinimumSize(new java.awt.Dimension(300, 300));
+        doc.setPreferredSize(new java.awt.Dimension(300, 300));
+        JScrollPane defPanel = new JScrollPane();
+        defPanel.setViewportView(doc);
+
+        jTabbedPane1.addTab(def.getName(), def.getType() == Shader.ShaderType.Vertex ? Icons.vert : Icons.frag, defPanel);
+        doc.setText("");
+        DocFormatter.addDoc(def, doc.getStyledDocument());
+        doc.setCaretPosition(0);
+    }
+
+    private void fillList(final ProjectAssetManager mgr) {
+        List<String> l = new ArrayList<String>();
+        l.addAll(mgr.getProjectShaderNodeDefs());
+        l.addAll(mgr.getDependenciesShaderNodeDefs());
+        String[] leaves = l.toArray(new String[l.size()]);
+        TreeUtil.createTree(jTree1, leaves);
+        TreeUtil.expandTree(jTree1, (TreeNode) jTree1.getModel().getRoot(), 10);
+        jTree1.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+        jTree1.addTreeSelectionListener(new TreeSelectionListener() {
+            public void valueChanged(TreeSelectionEvent e) {
+
+                DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree1.getLastSelectedPathComponent();
+
+                if (node == null) {
+                    return;
+                }
+
+
+                if (node.isLeaf()) {
+                    jTabbedPane1.removeAll();
+                    path = TreeUtil.getPath(node.getUserObjectPath());
+                    path = path.substring(0, path.lastIndexOf("/"));
+                    ShaderNodeDefinitionKey k = new ShaderNodeDefinitionKey(path);
+                    k.setLoadDocumentation(true);
+                    defList = (List<ShaderNodeDefinition>) mgr.loadAsset(k);
+
+                    for (ShaderNodeDefinition def : defList) {
+                        createDoc(def);
+                    }
+
+                }
+            }
+        });
+    }
+}

+ 107 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddWorldParameterDialog.form

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+    <Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+      <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddWorldParameterDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+    </Property>
+    <Property name="modal" type="boolean" value="true"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="1" attributes="0">
+                      <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="32767" attributes="0"/>
+                      <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="jLabel1" min="-2" pref="123" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="nameField" pref="253" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="nameField" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JButton" name="jButton1">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddWorldParameterDialog.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JButton" name="jButton2">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddWorldParameterDialog.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel1">
+      <Properties>
+        <Property name="horizontalAlignment" type="int" value="11"/>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/dialog/Bundle.properties" key="AddWorldParameterDialog.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="nameField">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="4">
+            <StringItem index="0" value="Item 1"/>
+            <StringItem index="1" value="Item 2"/>
+            <StringItem index="2" value="Item 3"/>
+            <StringItem index="3" value="Item 4"/>
+          </StringArray>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>

+ 122 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/AddWorldParameterDialog.java

@@ -0,0 +1,122 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.dialog;
+
+import com.jme3.gde.materialdefinition.editor.Diagram;
+import com.jme3.shader.UniformBinding;
+import java.awt.Point;
+import javax.swing.DefaultComboBoxModel;
+
+/**
+ *
+ * @author Nehon
+ */
+public class AddWorldParameterDialog extends javax.swing.JDialog {
+
+    private Diagram diagram;
+    private Point clickPosition;
+
+    /**
+     * Creates new form AddMaterialParameter
+     */
+    public AddWorldParameterDialog(java.awt.Frame parent, boolean modal, Diagram diagram, Point clickPosition) {
+        super(parent, modal);
+        initComponents();
+        DefaultComboBoxModel<UniformBinding> model = new DefaultComboBoxModel<UniformBinding>();
+
+        for (UniformBinding binding : UniformBinding.values()) {
+            model.addElement(binding);
+        }
+        this.diagram = diagram;
+        this.clickPosition = clickPosition;
+        nameField.setModel(model);
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jButton1 = new javax.swing.JButton();
+        jButton2 = new javax.swing.JButton();
+        jLabel1 = new javax.swing.JLabel();
+        nameField = new javax.swing.JComboBox();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle(org.openide.util.NbBundle.getMessage(AddWorldParameterDialog.class, "AddWorldParameterDialog.title")); // NOI18N
+        setModal(true);
+
+        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(AddWorldParameterDialog.class, "AddWorldParameterDialog.jButton1.text")); // NOI18N
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton1ActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(AddWorldParameterDialog.class, "AddWorldParameterDialog.jButton2.text")); // NOI18N
+        jButton2.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton2ActionPerformed(evt);
+            }
+        });
+
+        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AddWorldParameterDialog.class, "AddWorldParameterDialog.jLabel1.text")); // NOI18N
+
+        nameField.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                        .addComponent(jButton2)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(jButton1))
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 123, javax.swing.GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(nameField, 0, 253, Short.MAX_VALUE)))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel1)
+                    .addComponent(nameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jButton1)
+                    .addComponent(jButton2))
+                .addContainerGap())
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
+        setVisible(false);
+    }//GEN-LAST:event_jButton2ActionPerformed
+
+    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+        diagram.addWorldParam((UniformBinding) nameField.getSelectedItem(), clickPosition);
+        setVisible(false);
+    }//GEN-LAST:event_jButton1ActionPerformed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JButton jButton2;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JComboBox nameField;
+    // End of variables declaration//GEN-END:variables
+}

+ 17 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/dialog/Bundle.properties

@@ -0,0 +1,17 @@
+
+AddMaterialParameterDialog.nameField.text=
+AddMaterialParameterDialog.jLabel3.text=Parameter Name
+AddMaterialParameterDialog.jLabel1.text=Parameter Type
+AddMaterialParameterDialog.title=Add Material Parameter
+AddMaterialParameterDialog.jButton1.text=Ok
+AddMaterialParameterDialog.jButton2.text=Cancel
+AddWorldParameterDialog.title=Add Material Parameter
+AddWorldParameterDialog.jButton1.text=Ok
+AddWorldParameterDialog.jButton2.text=Cancel
+AddWorldParameterDialog.jLabel1.text=Parameter Name
+AddAttributeDialog.jButton2.text=Cancel
+AddAttributeDialog.jLabel1.text=Attribute
+AddAttributeDialog.title=Add Material Parameter
+AddAttributeDialog.jButton1.text=Ok
+AddAttributeDialog.jLabel2.text=GLSL Type
+AddAttributeDialog.typeField.text=

+ 539 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Connection.java

@@ -0,0 +1,539 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MappingBlock;
+import com.jme3.gde.materialdefinition.utils.MaterialUtils;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MouseInputListener;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Connection extends JPanel implements ComponentListener, MouseInputListener, KeyListener, Selectable, PropertyChangeListener {
+
+    protected Dot start;
+    protected Dot end;
+    private Point[] points = new Point[6];
+    private int pointsSize = 6;
+    private Corner[] corners = new Corner[6];
+    private String key = "";
+    protected MappingBlock mapping;
+
+    private MouseEvent convertEvent(MouseEvent e) {
+        MouseEvent me = null;
+        //workaround for swing utilities removing mouse button when converting events.
+        if (e instanceof MouseWheelEvent || e instanceof MenuDragMouseEvent) {
+            SwingUtilities.convertMouseEvent(this, e, getDiagram());
+        } else {
+            Point p = SwingUtilities.convertPoint(this, new Point(e.getX(),
+                    e.getY()),
+                    getDiagram());
+
+            me = new MouseEvent(getDiagram(),
+                    e.getID(),
+                    e.getWhen(),
+                    e.getModifiers()
+                    | e.getModifiersEx(),
+                    p.x, p.y,
+                    e.getXOnScreen(),
+                    e.getYOnScreen(),
+                    e.getClickCount(),
+                    e.isPopupTrigger(),
+                    e.getButton());
+        }
+        return me;
+    }
+
+    private enum Corner {
+
+        RightBottom,
+        BottomRight,
+        BottomLeft,
+        LeftBottom,
+        RightTop,
+        TopRight,
+        LeftTop,
+        TopLeft,
+        Top,
+        Bottom,
+        None,}
+
+    public Connection(Dot start, Dot end) {
+
+
+        if (start.getParamType() == Dot.ParamType.Output
+                || (start.getParamType() == Dot.ParamType.Both && end.getParamType() != Dot.ParamType.Output)
+                || (end.getParamType() == Dot.ParamType.Both && start.getParamType() != Dot.ParamType.Input)) {
+            this.start = start;
+            this.end = end;
+        } else {
+            this.start = end;
+            this.end = start;
+        }
+
+        for (int i = 0; i < 6; i++) {
+            points[i] = new Point();
+        }
+        resize(this.start, this.end);
+        addMouseMotionListener(this);
+        addMouseListener(this);
+        addKeyListener(this);
+        setFocusable(true);
+        setOpaque(false);
+
+    }
+
+    private void translate(Point p, Point store) {
+        store.x = p.x - getLocation().x - 1;
+        store.y = p.y - getLocation().y - 1;
+    }
+    private Point p1 = new Point();
+    private Point p2 = new Point();
+    private Point tp1 = new Point();
+    private Point bp1 = new Point();
+    private Point tp2 = new Point();
+    private Point bp2 = new Point();
+
+    @Override
+    protected void paintBorder(Graphics g) {
+//        super.paintBorder(g);
+//
+//        g.setColor(Color.GRAY);
+//        g.drawLine(0, 0, getWidth(), 0);
+//        g.drawLine(getWidth(), 0, getWidth(), getHeight() - 1);
+//        g.drawLine(getWidth(), getHeight() - 1, 0, getHeight() - 1);
+//        g.drawLine(0, getHeight() - 1, 0, 0);
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    protected void makeKey(MappingBlock mapping, String techName) {
+        this.mapping = mapping;
+        key = MaterialUtils.makeKey(mapping, techName);
+    }
+
+    private void adjustCorners(Corner corner, Point tp, Point bp) {
+        switch (corner) {
+            case LeftTop:
+            case TopLeft:
+                tp.x -= 1;
+                bp.x += 1;
+                tp.y += 1;
+                bp.y -= 1;
+                break;
+            case RightBottom:
+            case BottomRight:
+                tp.x += 1;
+                bp.x -= 1;
+                tp.y -= 1;
+                bp.y += 1;
+                break;
+            case RightTop:
+            case TopRight:
+                tp.x -= 1;
+                bp.x += 1;
+                tp.y -= 1;
+                bp.y += 1;
+                break;
+            case LeftBottom:
+            case BottomLeft:
+                tp.x += 1;
+                bp.x -= 1;
+                tp.y += 1;
+                bp.y -= 1;
+                break;
+            case None:
+                tp.y -= 1;
+                bp.y += 1;
+                break;
+            case Top:
+                tp.x -= 1;
+                bp.x += 1;
+                break;
+            case Bottom:
+                tp.x += 1;
+                bp.x -= 1;
+                break;
+        }
+    }
+
+    @Override
+    protected void paintComponent(Graphics g) {
+        if (paintDebug) {
+            for (int i = 0; i < pointsSize - 1; i++) {
+                translate(points[i], p1);
+                p1.x -= MARGIN;
+                p1.y -= MARGIN;
+                translate(points[i + 1], p2);
+                p2.x += MARGIN;
+                p2.y += MARGIN;
+                g.setColor(Color.GRAY);
+                g.drawLine(p1.x, p1.y, p2.x, p1.y);
+                g.drawLine(p2.x, p1.y, p2.x, p2.y);
+                g.drawLine(p2.x, p2.y, p1.x, p2.y);
+                g.drawLine(p1.x, p2.y, p1.x, p1.y);
+
+
+            }
+
+            paintDebug = false;
+        }
+
+        for (int i = 0; i < pointsSize - 1; i++) {
+
+            g.setColor(Color.YELLOW);
+            translate(points[i], p1);
+            translate(points[i + 1], p2);
+            g.drawLine(p1.x, p1.y, p2.x, p2.y);
+
+
+            if (getDiagram().selectedItem == this) {
+                g.setColor(Color.CYAN);
+            } else {
+                g.setColor(Color.GRAY);
+            }
+            tp1.setLocation(p1);
+            bp1.setLocation(p1);
+            tp2.setLocation(p2);
+            bp2.setLocation(p2);
+            adjustCorners(corners[i], tp1, bp1);
+            adjustCorners(corners[i + 1], tp2, bp2);
+            g.drawLine(tp1.x, tp1.y, tp2.x, tp2.y);
+            g.drawLine(bp1.x, bp1.y, bp2.x, bp2.y);
+
+        }
+
+    }
+    public final static int MARGIN = 10;
+
+    private int getOffset() {
+        return 5 * start.getIndex();
+    }
+
+    private int getHMiddle() {
+        int st = start.getNode().getLocation().y + start.getNode().getHeight();
+        int diff = end.getNode().getLocation().y - st;
+        return st + diff / 2 + getOffset();
+
+    }
+
+    private int getVMiddleStart() {
+        Point startLocation = start.getStartLocation();
+        Point endLocation = end.getEndLocation();
+        return startLocation.x + Math.max(MARGIN, (endLocation.x - startLocation.x) / 2) + getOffset();
+    }
+
+    private int getVMiddleStartClampedRight() {
+        Point startLocation = start.getStartLocation();
+        Point endLocation = end.getEndLocation();
+        int right = end.getNode().getLocation().x + end.getNode().getWidth() + MARGIN;
+        int loc = startLocation.x + Math.max(MARGIN, (endLocation.x - startLocation.x) / 2);
+        return Math.max(loc, right) + getOffset();
+    }
+
+    private int getVMiddleEnd() {
+        Point startLocation = start.getStartLocation();
+        Point endLocation = end.getEndLocation();
+        return endLocation.x - Math.max(0, Math.max(MARGIN, (endLocation.x - startLocation.x) / 2) + getOffset());
+
+    }
+
+    private int getVMiddleEndClampedLeft() {
+        Point startLocation = start.getStartLocation();
+        Point endLocation = end.getEndLocation();
+        int left = start.getNode().getLocation().x - MARGIN;//+ end.getNode().getWidth() + MARGIN;
+        int loc = endLocation.x - Math.max(0, Math.max(MARGIN, (endLocation.x - startLocation.x) / 2));
+        return Math.min(loc, left) + getOffset();
+
+    }
+
+    private int getHBottom() {
+        int endBottom = end.getNode().getLocation().y + end.getNode().getHeight() + MARGIN;
+        int startBottom = start.getNode().getLocation().y + start.getNode().getHeight() + MARGIN;
+        return Math.max(endBottom, startBottom) + getOffset();
+
+    }
+
+    public final void resize(Dot start, Dot end) {
+        Point startLocation = start.getStartLocation();
+        Point endLocation = end.getEndLocation();
+
+        if (start.getParamType() == Dot.ParamType.Both) {
+            startLocation.x = endLocation.x - MARGIN * 2;
+            pointsSize = 3;
+            points[0].setLocation(startLocation);
+            points[1].x = startLocation.x;
+            points[1].y = endLocation.y;
+            points[2].setLocation(endLocation);
+            if (startLocation.y <= endLocation.y) {
+                corners[0] = Corner.Bottom;
+                corners[1] = Corner.BottomRight;
+                corners[2] = Corner.None;
+            } else {
+                corners[0] = Corner.Top;
+                corners[1] = Corner.TopRight;
+                corners[2] = Corner.None;
+            }
+        } else if (end.getParamType() == Dot.ParamType.Both) {
+            endLocation.x = startLocation.x + MARGIN * 2;
+            pointsSize = 3;
+            points[0].setLocation(startLocation);
+            points[1].x = endLocation.x;
+            points[1].y = startLocation.y;
+            points[2].setLocation(endLocation);
+            if (startLocation.y <= endLocation.y) {
+                corners[0] = Corner.None;
+                corners[1] = Corner.RightBottom;
+                corners[2] = Corner.Bottom;
+            } else {
+                corners[0] = Corner.None;
+                corners[1] = Corner.RightTop;
+                corners[2] = Corner.Top;
+            }
+        } else if (startLocation.x + MARGIN <= endLocation.x - MARGIN) {
+            pointsSize = 4;
+            points[0].setLocation(startLocation);
+            points[1].x = getVMiddleStart();
+            points[1].y = startLocation.y;
+            points[2].x = getVMiddleStart();
+            points[2].y = endLocation.y;
+            corners[0] = Corner.None;
+            corners[3] = Corner.None;
+            points[3].setLocation(endLocation);
+            if (startLocation.y <= endLocation.y) {
+                corners[1] = Corner.RightBottom;
+                corners[2] = Corner.BottomRight;
+            } else {
+                corners[1] = Corner.RightTop;
+                corners[2] = Corner.TopRight;
+            }
+
+        } else {
+            pointsSize = 6;
+            points[0].setLocation(startLocation);
+            points[5].setLocation(endLocation);
+            points[1].x = getVMiddleStart();
+            points[1].y = startLocation.y;
+
+            points[4].x = getVMiddleEnd();
+            points[4].y = endLocation.y;
+            corners[0] = Corner.None;
+            corners[5] = Corner.None;
+            if ((start.getNode().getLocation().y + start.getNode().getHeight() + MARGIN
+                    > end.getNode().getLocation().y - MARGIN)
+                    && (end.getNode().getLocation().y + end.getNode().getHeight() + MARGIN
+                    > start.getNode().getLocation().y - MARGIN)) {
+
+                if (startLocation.y + MARGIN <= endLocation.y - MARGIN) {
+                    points[1].x = getVMiddleStartClampedRight();
+                    points[2].x = getVMiddleStartClampedRight();
+                } else {
+                    points[1].x = getVMiddleStart();
+                    points[2].x = getVMiddleStart();
+                }
+                points[2].y = getHBottom();
+
+                if (startLocation.y + MARGIN > endLocation.y - MARGIN) {
+                    points[3].x = getVMiddleEndClampedLeft();
+                    points[4].x = getVMiddleEndClampedLeft();
+
+                } else {
+                    points[3].x = getVMiddleEnd();
+                    points[4].x = getVMiddleEnd();
+                }
+
+                points[3].y = getHBottom();
+
+                corners[1] = Corner.RightBottom;
+                corners[2] = Corner.BottomLeft;
+                corners[3] = Corner.LeftTop;
+                corners[4] = Corner.TopRight;
+
+            } else {
+
+                points[2].x = getVMiddleStart();
+                points[2].y = getHMiddle();
+
+                points[3].x = getVMiddleEnd();
+                points[3].y = getHMiddle();
+
+
+                if (startLocation.y <= endLocation.y) {
+                    corners[1] = Corner.RightBottom;
+                    corners[2] = Corner.BottomLeft;
+                    corners[3] = Corner.LeftBottom;
+                    corners[4] = Corner.BottomRight;
+                } else {
+                    corners[1] = Corner.RightTop;
+                    corners[2] = Corner.TopLeft;
+                    corners[3] = Corner.LeftTop;
+                    corners[4] = Corner.TopRight;
+                }
+            }
+        }
+        updateBounds();
+    }
+
+    private void updateBounds() {
+        int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
+        for (int i = 0; i < pointsSize; i++) {
+            if (points[i].x < minX) {
+                minX = points[i].x;
+            }
+            if (points[i].y < minY) {
+                minY = points[i].y;
+            }
+
+            if (points[i].x > maxX) {
+                maxX = points[i].x;
+            }
+            if (points[i].y > maxY) {
+                maxY = points[i].y;
+            }
+        }
+        maxX += MARGIN;
+        maxY += MARGIN;
+        minX -= MARGIN;
+        minY -= MARGIN;
+
+        setLocation(minX, minY);
+        setSize(maxX - minX, maxY - minY);
+    }
+
+    private Diagram getDiagram() {
+        return (Diagram) start.getDiagram();
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        dispatchEventToDiagram(e);
+    }
+
+    private void dispatchEventToDiagram(MouseEvent e) {
+        MouseEvent me = null;
+        me = convertEvent(e);
+        getDiagram().dispatchEvent(me);
+    }
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+        dispatchEventToDiagram(e);
+    }
+    private boolean paintDebug = false;
+
+    private void debug() {
+        paintDebug = true;
+        repaint();
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        dispatchEventToDiagram(e);
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        dispatchEventToDiagram(e);
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        dispatchEventToDiagram(e);
+    }
+
+    public void select(MouseEvent e) {
+        boolean selected = false;
+        requestFocusInWindow(true);
+        for (int i = 0; i < pointsSize - 1; i++) {
+            translate(points[i], p1);
+            translate(points[i + 1], p2);
+            if (p1.x > p2.x || p1.y > p2.y) {
+                tp1.setLocation(p1);
+                p1.setLocation(p2);
+                p2.setLocation(tp1);
+            }
+
+            p1.x -= MARGIN / 2;
+            p1.y -= MARGIN / 2;
+
+            p2.x += MARGIN / 2;
+            p2.y += MARGIN / 2;
+
+
+            if (e.getX() >= p1.x && e.getX() <= p2.x
+                    && e.getY() >= p1.y && e.getY() <= p2.y) {
+                selected = true;
+            }
+        }
+
+        if (selected) {
+            getDiagram().select(this);
+            e.consume();
+        }
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+    }
+
+    @Override
+    public void keyTyped(KeyEvent e) {
+    }
+
+    @Override
+    public void keyPressed(KeyEvent e) {
+
+        if (e.getKeyCode() == KeyEvent.VK_DELETE) {
+            Diagram diag = getDiagram();
+            if (diag.selectedItem == this) {
+                diag.removeSelectedConnection();
+            }
+        }
+    }
+
+    @Override
+    public void keyReleased(KeyEvent e) {
+    }
+
+    public void componentResized(ComponentEvent e) {
+    }
+
+    public void componentMoved(ComponentEvent e) {
+        resize(start, end);
+    }
+
+    public void componentShown(ComponentEvent e) {
+    }
+
+    public void componentHidden(ComponentEvent e) {
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        MappingBlock mapping = (MappingBlock) evt.getSource();
+        key = MaterialUtils.makeKey(mapping, getDiagram().getCurrentTechniqueName());
+    }
+}

+ 531 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Diagram.java

@@ -0,0 +1,531 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.materialdefinition.dialog.AddAttributeDialog;
+import com.jme3.gde.materialdefinition.dialog.AddMaterialParameterDialog;
+import com.jme3.gde.materialdefinition.dialog.AddNodeDialog;
+import com.jme3.gde.materialdefinition.dialog.AddWorldParameterDialog;
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.material.Material;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderNodeDefinition;
+import com.jme3.shader.ShaderNodeVariable;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VarType;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.swing.BorderFactory;
+import javax.swing.Icon;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
+import javax.swing.border.Border;
+import javax.swing.border.TitledBorder;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Diagram extends JPanel implements MouseListener, MouseMotionListener, ComponentListener {
+    
+    protected Dot draggedFrom;
+    protected Dot draggedTo;
+    protected Selectable selectedItem;
+    protected List<Connection> connections = new ArrayList<Connection>();
+    protected List<NodePanel> nodes = new ArrayList<NodePanel>();
+    protected List<OutBusPanel> outBuses = new ArrayList<OutBusPanel>();
+    private MyMenu contextMenu = new MyMenu("Add");
+    private MatDefEditorlElement parent;
+    private String currentTechniqueName;
+    
+    public Diagram() {
+        
+        addMouseListener(this);
+        addMouseMotionListener(this);
+        createPopupMenu();
+    }
+    
+    @Override
+    public void mouseClicked(MouseEvent e) {
+    }
+    
+    @Override
+    public void mousePressed(MouseEvent e) {
+        
+        for (OutBusPanel outBusPanel : outBuses) {
+            Point p = SwingUtilities.convertPoint(this, e.getX(), e.getY(), outBusPanel);
+            if (outBusPanel.contains(p)) {
+                MouseEvent me = SwingUtilities.convertMouseEvent(this, e, outBusPanel);
+                outBusPanel.dispatchEvent(me);
+                if (me.isConsumed()) {
+                    return;
+                }
+            }
+        }
+        
+        for (Connection connection : connections) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(this, e, connection);
+            connection.select(me);
+            if (me.isConsumed()) {
+                return;
+            }
+        }
+        
+        selectedItem = null;
+        repaint();
+    }
+    
+    public void refreshPreviews(Material mat) {
+        for (OutBusPanel outBusPanel : outBuses) {
+            outBusPanel.updatePreview(mat);
+        }
+    }
+    Point clickLoc = new Point(0, 0);
+    
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        if (draggedFrom != null && draggedFrom.getNode() instanceof OutBusPanel) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(this, e, draggedFrom.getNode());
+            draggedFrom.getNode().dispatchEvent(me);
+            if (me.isConsumed()) {
+                return;
+            }
+        }
+        if (e.getButton() != MouseEvent.BUTTON3) {
+            dispatchToOutBuses(e);
+        } else {
+            contextMenu.show(this, e.getX(), e.getY());
+            clickLoc.setLocation(e.getX(), e.getY());
+        }
+        
+    }
+    
+    public MatDefEditorlElement getEditorParent() {
+        return parent;
+    }
+    
+    public void addConnection(Connection conn) {
+        connections.add(conn);
+        add(conn);
+        for (OutBusPanel bus : outBuses) {
+            setComponentZOrder(bus, getComponentCount() - 1);
+        }
+        repaint();
+    }
+    
+    public void notifyMappingCreation(Connection conn) {
+        parent.makeMapping(conn);
+    } 
+    
+    public void addNode(NodePanel node) {
+        add(node);
+        node.setTechName(currentTechniqueName);
+        node.setDiagram(this);
+        nodes.add(node);
+        setComponentZOrder(node, 0);
+        node.addComponentListener(this);
+    }
+    
+    public void addOutBus(OutBusPanel bus) {
+        outBuses.add(bus);
+        bus.setDiagram(this);
+        add(bus);
+        setComponentZOrder(bus, getComponentCount() - 1);
+        addComponentListener(bus);
+        bus.componentResized(new ComponentEvent(this, ActionEvent.ACTION_PERFORMED));
+        bus.revalidate();
+    }
+    
+    @Override
+    public void mouseEntered(MouseEvent e) {
+    }
+    
+    @Override
+    public void mouseExited(MouseEvent e) {
+    }
+    
+    protected void removeSelectedConnection() {
+        if (selectedItem instanceof Connection) {
+            Connection selectedConnection = (Connection) selectedItem;
+            removeConnection(selectedConnection);
+            selectedItem = null;
+            parent.notifyRemoveConnection(selectedConnection);
+        }
+    }
+    
+    public void addNodesFromDefs(List<ShaderNodeDefinition> defList, String path, Point clickPosition) {
+        int i = 0;
+        for (ShaderNodeDefinition def : defList) {
+            ShaderNodeBlock sn = new ShaderNodeBlock(def, path);
+            NodePanel np = new NodePanel(sn, def);
+            addNode(np);
+            np.setLocation(clickPosition.x + i * 150, clickPosition.y);
+            sn.setSpatialOrder(np.getLocation().x);
+            i++;
+            np.revalidate();
+            getEditorParent().notifyAddNode(sn, def);
+        }
+        repaint();
+    }
+    
+    public void addMatParam(String type, String name, Point point) {
+        String fixedType = type;
+        if (type.equals("Color")) {
+            fixedType = "Vector4";
+        }
+        ShaderNodeVariable param = new ShaderNodeVariable(VarType.valueOf(fixedType).getGlslType(), name);
+        NodePanel np = new NodePanel(param, NodePanel.NodeType.MatParam);
+        addNode(np);
+        np.setLocation(point.x, point.y);
+        np.revalidate();
+        repaint();
+        getEditorParent().notifyAddMapParam(type, name);
+    }
+    
+    public void addWorldParam(UniformBinding binding, Point point) {
+        
+        ShaderNodeVariable param = new ShaderNodeVariable(binding.getGlslType(), binding.name());
+        NodePanel np = new NodePanel(param, NodePanel.NodeType.WorldParam);
+        addNode(np);
+        np.setLocation(point.x, point.y);
+        np.revalidate();
+        repaint();
+        getEditorParent().notifyAddWorldParam(binding.name());
+    }
+    
+    public void addAttribute(String name, String type, Point point) {
+        ShaderNodeVariable param = new ShaderNodeVariable(type, "Attr", name);
+        NodePanel np = new NodePanel(param, NodePanel.NodeType.Attribute);
+        addNode(np);
+        np.setLocation(point.x, point.y);
+        np.revalidate();
+        repaint();        
+    }
+    
+    protected void removeSelectedNode() {
+        if (selectedItem instanceof NodePanel) {
+            int result = JOptionPane.showConfirmDialog(null, "Delete this node and all its mappings?", "Delete Shader Node", JOptionPane.OK_CANCEL_OPTION);
+            if (result == JOptionPane.OK_OPTION) {
+                NodePanel selectedNode = (NodePanel) selectedItem;
+                nodes.remove(selectedNode);
+                for (Iterator<Connection> it = connections.iterator(); it.hasNext();) {
+                    Connection conn = it.next();
+                    if (conn.start.getNode() == selectedNode || conn.end.getNode() == selectedNode) {
+                        it.remove();
+                        conn.end.disconnect();
+                        conn.start.disconnect();
+                        remove(conn);
+                    }
+                }
+                
+                remove(selectedNode);
+                selectedItem = null;
+                repaint();
+                parent.notifyRemoveNode(selectedNode);
+            }
+        }
+    }
+    
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        if (draggedFrom == null) {
+            if (selectedItem instanceof OutBusPanel) {
+                OutBusPanel bus = (OutBusPanel) selectedItem;
+                MouseEvent me = SwingUtilities.convertMouseEvent(this, e, bus);
+                bus.dispatchEvent(me);
+            }
+        }
+    }
+    
+    protected void draggingDot(MouseEvent e) {
+        for (OutBusPanel outBusPanel : outBuses) {
+            Point p = SwingUtilities.convertPoint(this, e.getX(), e.getY(), outBusPanel);
+            if (outBusPanel.contains(p)) {
+                MouseEvent me = SwingUtilities.convertMouseEvent(this, e, outBusPanel);
+                outBusPanel.draggingDot(me);
+                if (me.isConsumed()) {
+                    return;
+                }
+            }
+        }
+    }
+    
+    public Connection connect(Dot start, Dot end) {
+        Connection conn = new Connection(start, end);
+        start.connect(conn);
+        end.connect(conn);
+        
+        
+        addConnection(conn);
+        
+        return conn;
+    }
+    
+    public NodePanel getNodePanel(String key) {
+        for (NodePanel nodePanel : nodes) {
+            if (nodePanel.getKey().equals(key)) {
+                return nodePanel;
+            }
+        }
+        return null;
+    }
+    
+    public OutBusPanel getOutBusPanel(String key) {
+        for (OutBusPanel out : outBuses) {
+            if (out.getKey().equals(key)) {
+                return out;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * selection from the editor. Select the item and notify the topComponent
+     *
+     * @param selectable
+     */
+    public void select(Selectable selectable) {
+        parent.selectionChanged(doSelect(selectable));
+    }
+
+    /**
+     * do select the item and repaint the diagram
+     *
+     * @param selectable
+     * @return
+     */
+    private Selectable doSelect(Selectable selectable) {
+        this.selectedItem = selectable;
+        if (selectable instanceof Component) {
+            ((Component) selectable).requestFocusInWindow();
+        }
+        repaint();
+        return selectable;
+    }
+
+    /**
+     * find the item with the given key and select it without notifying the
+     * topComponent
+     *
+     * @param key
+     * @return
+     */
+    public Selectable select(String key) {
+        
+        for (NodePanel nodePanel : nodes) {
+            if (nodePanel.getKey().equals(key)) {
+                return doSelect(nodePanel);
+            }
+        }
+        
+        for (Connection connection : connections) {
+            if (connection.getKey().equals(key)) {
+                return doSelect(connection);
+            }
+        }
+        
+        for (OutBusPanel outBusPanel : outBuses) {
+            if (outBusPanel.getKey().equals(key)) {
+                return doSelect(outBusPanel);
+            }
+        }
+        return doSelect(null);
+    }
+    
+    @Override
+    public void mouseMoved(MouseEvent e) {
+        dispatchToOutBuses(e);
+    }
+    
+    private JMenuItem createMenuItem(String text, Icon icon) {
+        JMenuItem item = new JMenuItem(text, icon);
+        item.setFont(new Font("Tahoma", 1, 10)); // NOI18N
+        return item;
+    }
+    
+    private void createPopupMenu() {
+        contextMenu.setFont(new Font("Tahoma", 1, 10)); // NOI18N
+        contextMenu.setOpaque(true);
+        Border titleUnderline = BorderFactory.createMatteBorder(1, 0, 0, 0, Color.BLACK);
+        TitledBorder labelBorder = BorderFactory.createTitledBorder(
+                titleUnderline, contextMenu.getLabel(),
+                TitledBorder.LEADING, TitledBorder.ABOVE_TOP, contextMenu.getFont(), Color.BLACK);
+        
+        contextMenu.setBorder(BorderFactory.createLineBorder(Color.BLACK));
+        contextMenu.setBorder(BorderFactory.createCompoundBorder(contextMenu.getBorder(),
+                labelBorder));
+        
+        
+        JMenuItem nodeItem = createMenuItem("Node", Icons.node);
+        nodeItem.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                AddNodeDialog d = new AddNodeDialog(null, true, parent.obj.getLookup().lookup(ProjectAssetManager.class), Diagram.this, clickLoc);
+                d.setLocationRelativeTo(null);
+                d.setVisible(true);
+            }
+        });
+        
+        contextMenu.add(nodeItem);
+        contextMenu.add(createSeparator());
+        JMenuItem matParamItem = createMenuItem("Material Parameter", Icons.mat);
+        matParamItem.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                AddMaterialParameterDialog d = new AddMaterialParameterDialog(null, true, Diagram.this, clickLoc);
+                d.setLocationRelativeTo(null);
+                d.setVisible(true);
+            }
+        });
+        contextMenu.add(matParamItem);
+        JMenuItem worldParamItem = createMenuItem("World Parameter", Icons.world);        
+        worldParamItem.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                AddWorldParameterDialog d = new AddWorldParameterDialog(null, true, Diagram.this, clickLoc);
+                d.setLocationRelativeTo(null);
+                d.setVisible(true);
+            }
+        });
+        contextMenu.add(worldParamItem);
+        JMenuItem attributeItem = createMenuItem("Attribute", Icons.attrib);
+        attributeItem.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                AddAttributeDialog d = new AddAttributeDialog(null, true, Diagram.this, clickLoc);
+                d.setLocationRelativeTo(null);
+                d.setVisible(true);
+            }
+        });
+        contextMenu.add(attributeItem);
+        contextMenu.add(createSeparator());
+        JMenuItem outputItem = createMenuItem("Output color", Icons.output);
+        contextMenu.add(outputItem);
+        outputItem.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                OutBusPanel p2 = new OutBusPanel("color" + (outBuses.size() - 1), Shader.ShaderType.Fragment);
+                p2.setBounds(0, 350 + 50 * (outBuses.size() - 1), p2.getWidth(), p2.getHeight());
+                
+                addOutBus(p2);
+                
+            }
+        });
+    }
+    
+    private JSeparator createSeparator() {
+        JSeparator jsep = new JSeparator(JSeparator.HORIZONTAL);
+        jsep.setBackground(Color.BLACK);
+        return jsep;
+    }
+    
+    private void dispatchToOutBuses(MouseEvent e) {
+        for (OutBusPanel outBusPanel : outBuses) {
+            Point p = SwingUtilities.convertPoint(this, e.getX(), e.getY(), outBusPanel);
+            if (outBusPanel.contains(p)) {
+                MouseEvent me = SwingUtilities.convertMouseEvent(this, e, outBusPanel);
+                outBusPanel.dispatchEvent(me);
+                if (me.isConsumed()) {
+                    return;
+                }
+            }
+        }
+    }
+    
+    private void removeConnection(Connection selectedConnection) {
+        connections.remove(selectedConnection);
+        selectedConnection.end.disconnect();
+        selectedConnection.start.disconnect();
+        remove(selectedConnection);
+    }
+    
+    private class MyMenu extends JPopupMenu {
+        
+        public MyMenu(String label) {
+            super(label);
+        }
+        
+        @Override
+        protected void paintComponent(Graphics g) {
+            //Color c1 = new Color(100, 100, 100, 255);
+            Color bg = new Color(175, 175, 175);
+            g.setColor(bg);
+            g.fillRect(0, 0, getWidth(), getHeight());
+        }
+    }
+    
+    public void fixSize() {
+        int maxWidth = minWidth;
+        int maxHeight = minHeight;
+        
+        for (NodePanel nodePanel : nodes) {
+            int w = nodePanel.getLocation().x + nodePanel.getWidth() + 150;
+            if (w > maxWidth) {
+                maxWidth = w;
+            }
+            int h = nodePanel.getLocation().y + nodePanel.getHeight();
+            if (h > maxHeight) {
+                maxHeight = h;
+            }
+        }
+        for (OutBusPanel outBusPanel : outBuses) {
+            int h = outBusPanel.getLocation().y + outBusPanel.getHeight();
+            if (h > maxHeight) {
+                maxHeight = h;
+            }
+        }
+        setPreferredSize(new Dimension(maxWidth, maxHeight));
+        revalidate();
+    }
+    int minWidth = 0;
+    int minHeight = 0;
+    
+    public void componentResized(ComponentEvent e) {
+        minWidth = e.getComponent().getWidth() - 2;
+        minHeight = e.getComponent().getHeight() - 2;
+        fixSize();
+    }
+    
+    public void componentMoved(ComponentEvent e) {
+    }
+    
+    public void componentShown(ComponentEvent e) {
+    }
+    
+    public void componentHidden(ComponentEvent e) {
+    }
+    
+    public void setParent(MatDefEditorlElement parent) {
+        this.parent = parent;
+    }
+    
+    public void setCurrentTechniqueName(String currentTechniqueName) {
+        this.currentTechniqueName = currentTechniqueName;
+    }
+    
+    public String getCurrentTechniqueName() {
+        return currentTechniqueName;
+    }
+}

+ 262 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Dot.java

@@ -0,0 +1,262 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.shader.ShaderUtils;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.MouseEvent;
+import javax.swing.ImageIcon;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.MouseInputListener;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Dot extends JPanel implements MouseInputListener {
+
+    public static boolean pressed = false;
+    protected ImageIcon img;
+    protected ImageIcon prevImg;
+    private String type;
+    private ParamType paramType;
+    private String text = "";
+    private DraggablePanel node;
+    private int index = 1;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public enum ParamType {
+
+        Input,
+        Output,
+        Both
+    }
+
+    public Dot() {
+        super();
+        setMaximumSize(new Dimension(10, 10));
+        setMinimumSize(new Dimension(10, 10));
+        setPreferredSize(new Dimension(10, 10));
+        setSize(10, 10);
+        addMouseMotionListener(this);
+        addMouseListener(this);
+    }
+
+    @Override
+    protected void paintComponent(Graphics g) {
+        if (img == null) {
+
+            img = Icons.imgGrey;
+        }
+        g.drawImage(img.getImage(), 0, 0, this);
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        prevImg = img;
+        img = Icons.imgOrange;
+        Diagram diag = getDiagram();
+        diag.draggedFrom = this;
+        repaint();
+        e.consume();
+    }
+
+    @Override
+    public void repaint() {
+        if (getNode() != null) {
+            getDiagram().repaint();
+        } else {
+            super.repaint();
+        }
+    }
+
+    public Diagram getDiagram() {
+        return node.getDiagram();
+    }
+
+    public DraggablePanel getNode() {
+        return node;
+    }
+
+    public void setNode(DraggablePanel node) {
+        this.node = node;
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        Diagram diag = getDiagram();
+        if (diag.draggedFrom == this && diag.draggedTo != null) {
+            if (this.canConnect(diag.draggedTo)) {
+                diag.notifyMappingCreation(diag.connect(this, diag.draggedTo));
+
+            } else {
+                diag.draggedTo.reset();
+                this.reset();
+            }
+            diag.draggedFrom = null;
+            diag.draggedTo = null;
+        } else {
+            reset();
+            diag.draggedFrom = null;
+        }
+        e.consume();
+    }
+
+    public void reset() {
+        img = prevImg;
+        repaint();
+    }
+
+    public void disconnect() {
+        img = Icons.imgGrey;
+        repaint();
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+        Diagram diag = getDiagram();
+        if (diag.draggedFrom != null && diag.draggedFrom != this) {
+            prevImg = img;
+            canConnect(diag.draggedFrom);
+            diag.draggedTo = this;
+            diag.draggedFrom.canConnect(this);
+        }
+
+    }
+
+    public boolean canConnect(Dot pair) {
+        if (pair == null) {
+            img = Icons.imgOrange;
+            repaint();
+            return false;
+        }
+
+
+
+        if (matches(pair.getType(), type) && (pair.getParamType() != paramType
+                || pair.getParamType() == ParamType.Both
+                || paramType == ParamType.Both)
+                || ShaderUtils.isSwizzlable(pair.getType()) && ShaderUtils.isSwizzlable(type)) {
+            img = Icons.imgGreen;
+            repaint();
+            return true;
+        }
+
+
+        img = Icons.imgRed;
+        repaint();
+        return false;
+    }
+
+    private boolean matches(String type1, String type2) {
+        String[] s1 = type1.split("\\|");
+        String[] s2 = type2.split("\\|");
+        for (String string : s1) {
+            for (String string1 : s2) {
+                if (string.equals(string1)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+
+    }
+
+    protected void connect(Connection connection) {
+        img = Icons.imgGreen;
+        getNode().addComponentListener(connection);
+        repaint();
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+        Diagram diag = getDiagram();
+        if (diag.draggedFrom != null) {
+            diag.draggedFrom.canConnect(null);
+            if (diag.draggedFrom != this) {
+                reset();
+            }
+            if (diag.draggedTo == this) {
+                diag.draggedTo = null;
+            }
+        }
+    }
+
+    public Point getStartLocation() {
+        Point p = getLocation();
+        Component parent = getParent();
+        while (parent != getNode()) {
+            p.x += parent.getLocation().x;
+            p.y += parent.getLocation().y;
+            parent = parent.getParent();
+        }
+        p.x += 10 + getNode().getLocation().x;
+        p.y += 5 + getNode().getLocation().y;
+        return p;
+    }
+
+    public Point getEndLocation() {
+        Point p = getLocation();
+        Component parent = getParent();
+        while (parent != getNode()) {
+            p.x += parent.getLocation().x;
+            p.y += parent.getLocation().y;
+            parent = parent.getParent();
+        }
+        p.x += getNode().getLocation().x + 2;
+        p.y += 5 + getNode().getLocation().y;
+        return p;
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        MouseEvent me = SwingUtilities.convertMouseEvent(this, e, getDiagram());
+        getDiagram().draggingDot(me);
+    }
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public ParamType getParamType() {
+        return paramType;
+    }
+
+    public void setParamType(ParamType paramType) {
+        this.paramType = paramType;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+}

+ 82 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/DraggablePanel.java

@@ -0,0 +1,82 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import javax.swing.JPanel;
+
+/**
+ *
+ * @author m327836
+ */
+public class DraggablePanel extends JPanel implements MouseListener, MouseMotionListener {
+
+    protected int svdx, svdy, svdex, svdey;
+    private boolean vertical = false;
+    protected Diagram diagram;
+   
+    
+    public DraggablePanel(boolean vertical) {
+        this();
+        this.vertical = vertical;
+    }
+
+    public DraggablePanel() {
+        addMouseListener(this);
+        addMouseMotionListener(this);
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        svdx = getLocation().x;
+        if (!vertical) {
+            svdex = e.getXOnScreen();
+        }
+        svdy = getLocation().y;
+        svdey = e.getYOnScreen();
+        e.consume();
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        int xoffset = 0;
+        if (!vertical) {
+            xoffset = e.getLocationOnScreen().x - svdex;
+        }
+        int yoffset = e.getLocationOnScreen().y - svdey;
+        setLocation(Math.max(0, svdx + xoffset), Math.max(0,svdy + yoffset));
+        e.consume();
+    }
+
+    public Diagram getDiagram() {
+        return diagram;
+    }
+
+    public void setDiagram(Diagram diagram) {
+        this.diagram = diagram;
+    }
+}

+ 25 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/InOut.java

@@ -0,0 +1,25 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+
+/**
+ *
+ * @author Nehon
+ */
+public interface InOut {
+
+    public String getName();
+
+    public void addInputMapping(InputMappingBlock block);
+
+    public void removeInputMapping(InputMappingBlock block);
+
+    public void addOutputMapping(OutputMappingBlock block);
+
+    public void removeOutputMapping(OutputMappingBlock block);
+}

+ 56 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatDefEditorlElement.form

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Container class="com.jme3.gde.materialdefinition.editor.Diagram" name="diagram1">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="99" green="99" red="99" type="rgb"/>
+            </Property>
+          </Properties>
+
+          <Layout>
+            <DimensionLayout dim="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <EmptySpace min="0" pref="398" max="32767" attributes="0"/>
+              </Group>
+            </DimensionLayout>
+            <DimensionLayout dim="1">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <EmptySpace min="0" pref="298" max="32767" attributes="0"/>
+              </Group>
+            </DimensionLayout>
+          </Layout>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 566 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatDefEditorlElement.java

@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.materialdefinition.EditableMatDefFile;
+import com.jme3.gde.materialdefinition.MatDefDataObject;
+import com.jme3.gde.materialdefinition.MatDefMetaData;
+import com.jme3.gde.materialdefinition.fileStructure.MatDefBlock;
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.fileStructure.TechniqueBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.WorldParamBlock;
+import com.jme3.gde.materialdefinition.navigator.MatDefNavigatorPanel;
+import com.jme3.gde.materialdefinition.utils.MaterialUtils;
+import com.jme3.material.Material;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderNodeDefinition;
+import com.jme3.shader.ShaderNodeVariable;
+import com.jme3.shader.ShaderUtils;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.beans.PropertyVetoException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JToolBar;
+import org.netbeans.core.spi.multiview.CloseOperationState;
+import org.netbeans.core.spi.multiview.MultiViewElement;
+import org.netbeans.core.spi.multiview.MultiViewElementCallback;
+import org.openide.awt.UndoRedo;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+import org.openide.util.NbBundle.Messages;
+import org.openide.util.lookup.InstanceContent;
+import org.openide.windows.TopComponent;
+
[email protected](
+    displayName = "#LBL_MatDef_EDITOR",
+iconBase = "com/jme3/gde/materialdefinition/icons/matdef.png",
+mimeType = "text/jme-materialdefinition",
+persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED,
+preferredID = "MatDefVisual",
+position = 2000)
+@Messages("LBL_MatDef_EDITOR=Editor")
+public final class MatDefEditorlElement extends JPanel implements MultiViewElement {
+
+    protected MatDefDataObject obj;
+    private JToolBar toolbar = new JToolBar();
+    private transient MultiViewElementCallback callback;
+    InstanceContent content;
+    Selectable prevNode;
+    MatDefMetaData metaData;
+
+    public MatDefEditorlElement(Lookup lkp) {
+        obj = lkp.lookup(MatDefDataObject.class);
+        metaData = lkp.lookup(MatDefMetaData.class);
+        assert obj != null;
+        EditableMatDefFile file = obj.getEditableFile();
+        initComponents();
+        diagram1.setParent(this);
+
+        Material mat = lkp.lookup(Material.class);
+
+        ProjectAssetManager manager = obj.getLookup().lookup(ProjectAssetManager.class);
+        final MatDefBlock matDef = obj.getLookup().lookup(MatDefBlock.class);
+        TechniqueBlock technique = getTechnique(matDef);
+
+        diagram1.setCurrentTechniqueName(technique.getName());
+
+        List<ShaderNodeVariable> vertexGlobals = new ArrayList<ShaderNodeVariable>();
+        List<ShaderNodeVariable> fragmentGlobals = new ArrayList<ShaderNodeVariable>();
+        List<ShaderNodeVariable> attributes = new ArrayList<ShaderNodeVariable>();
+        List<ShaderNodeVariable> uniforms = new ArrayList<ShaderNodeVariable>();
+        initData(technique, manager, vertexGlobals, fragmentGlobals, attributes, matDef, uniforms);
+
+        int i = 0;
+        for (ShaderNodeBlock sn : technique.getShaderNodes()) {
+            ShaderNodeDefinition def = MaterialUtils.loadShaderNodeDefinition(sn, manager);
+            NodePanel np = new NodePanel(sn, def);
+            diagram1.addNode(np);
+            Point position = getPositionFromMetaData(np.getKey(), 150 * i + 20, 190);
+            np.setLocation(position);
+            sn.setSpatialOrder(np.getLocation().x);
+            i++;
+        }
+        //  TechniqueDef tech = def.getDefaultTechniques().get(0);
+        for (ShaderNodeVariable shaderNodeVariable : vertexGlobals) {
+            OutBusPanel out = new OutBusPanel(shaderNodeVariable.getName(), Shader.ShaderType.Vertex);
+            diagram1.addOutBus(out);
+            Point position = getPositionFromMetaData(out.getKey(), 0, 125);
+            out.setLocation(position);
+        }
+
+        i = 2;
+        for (ShaderNodeVariable var : fragmentGlobals) {
+            OutBusPanel out2 = new OutBusPanel(var.getName(), Shader.ShaderType.Fragment);
+            diagram1.addOutBus(out2);
+            Point position = getPositionFromMetaData(out2.getKey(), 0, 150 * i + 190);
+            out2.setLocation(position);
+            i++;
+        }
+        i = 0;
+        for (ShaderNodeVariable shaderNodeVariable : attributes) {
+            NodePanel np = diagram1.getNodePanel(shaderNodeVariable.getNameSpace() + "." + shaderNodeVariable.getName());
+            if (np == null) {
+                np = new NodePanel(shaderNodeVariable, NodePanel.NodeType.Attribute);
+                diagram1.addNode(np);
+                Point position = getPositionFromMetaData(np.getKey(), 150 * i + 20, 5);
+                np.setLocation(position);
+                i++;
+            }
+        }
+        i = 0;
+        for (ShaderNodeVariable shaderNodeVariable : uniforms) {
+            NodePanel np = diagram1.getNodePanel(shaderNodeVariable.getNameSpace() + "." + shaderNodeVariable.getName());
+            if (np == null) {
+                np = new NodePanel(shaderNodeVariable, shaderNodeVariable.getNameSpace().equals("MatParam") ? NodePanel.NodeType.MatParam : NodePanel.NodeType.WorldParam);
+                diagram1.addNode(np);
+                Point position = getPositionFromMetaData(np.getKey(), 150 * i + 20, 65);
+                np.setLocation(position);
+
+                i++;
+            }
+        }
+
+        for (ShaderNodeBlock sn : technique.getShaderNodes()) {
+            //NodePanel np = diagram1.getNodePanel(sn.getName());
+            List<InputMappingBlock> ins = sn.getInputs();
+            if (ins != null) {
+                for (InputMappingBlock mapping : ins) {
+                    makeConnection(mapping);
+                    if (!mapping.getRightNameSpace().equals("Global")
+                            && !mapping.getRightNameSpace().equals("MatParam")
+                            && !mapping.getRightNameSpace().equals("Attribute")
+                            && !mapping.getRightNameSpace().equals("WorldParam")) {
+                        sn.addInputNode(mapping.getRightNameSpace());
+                    } else if (mapping.getRightNameSpace().equals("Global")) {
+                        sn.setGlobalInput(true);
+                    }
+                }
+            }
+            List<OutputMappingBlock> outs = sn.getOutputs();
+            if (outs != null) {
+                for (OutputMappingBlock mapping : outs) {
+                    makeConnection(mapping);
+                    if (mapping.getLeftNameSpace().equals("Global")) {
+                        sn.setGlobalOutput(true);
+                    }
+                }
+            }
+
+        }
+
+        diagram1.setPreferredSize(new Dimension(jScrollPane1.getWidth() - 2, jScrollPane1.getHeight() - 2));
+        diagram1.revalidate();
+        jScrollPane1.addComponentListener(diagram1);
+
+
+        diagram1.refreshPreviews(mat);
+        final Lookup.Result<Material> resMat = obj.getLookup().lookupResult(Material.class);
+        resMat.addLookupListener(new LookupListener() {
+            public void resultChanged(LookupEvent ev) {
+                Collection<Material> col = (Collection<Material>) resMat.allInstances();
+                if (!col.isEmpty()) {
+                    Material material = col.iterator().next();
+                    diagram1.refreshPreviews(material);
+                }
+            }
+        });
+
+
+        final MatDefNavigatorPanel nav = obj.getLookup().lookup(MatDefNavigatorPanel.class);
+        if (nav != null) {
+
+            Lookup.Result<Selectable> res = nav.getLookup().lookupResult(Selectable.class);
+            res.addLookupListener(new LookupListener() {
+                public void resultChanged(LookupEvent ev) {
+                    Selectable selected = nav.getLookup().lookup(Selectable.class);
+                    if (selected != null && (prevNode == null || !(selected.getKey().equals(prevNode.getKey())))) {
+                        prevNode = diagram1.select(selected.getKey());
+                    }
+
+                }
+            });
+        }
+
+
+
+    }
+
+    @Override
+    public String getName() {
+        return "MatDefVisualElement";
+    }
+
+    protected void selectionChanged(Selectable selectable) {
+        MatDefNavigatorPanel nav = obj.getLookup().lookup(MatDefNavigatorPanel.class);
+        try {
+            Node n = findNode(nav.getExplorerManager().getRootContext(), selectable.getKey());
+            if (n == null) {
+                n = nav.getExplorerManager().getRootContext();
+            }
+            prevNode = selectable;
+            nav.getExplorerManager().setSelectedNodes(new Node[]{n});
+            //FIXME this is hackish, each time it's used it spits a warning in the log.
+            //without this line selecting a node in the editor select it in 
+            //the navigator explorer but does not displays its property sheet.
+            //the warning says to manipulate the MultiViewElement lookup, but it just voids the tree view             
+            callback.getTopComponent().setActivatedNodes(new Node[]{n});
+
+        } catch (PropertyVetoException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+    }
+
+    private Node findNode(Node root, String key) {
+        if (root instanceof Selectable) {
+            Selectable s = (Selectable) root;
+            if (s.getKey().equals(key)) {
+                return root;
+            } else if (root.getChildren() != Children.LEAF) {
+                Node n;
+                for (Node node : root.getChildren().getNodes()) {
+                    n = findNode(node, key);
+                    if (n != null) {
+                        return n;
+                    }
+                }
+            }
+        }
+
+        return null;
+
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jScrollPane1 = new javax.swing.JScrollPane();
+        diagram1 = new com.jme3.gde.materialdefinition.editor.Diagram();
+
+        diagram1.setBackground(new java.awt.Color(153, 153, 153));
+
+        javax.swing.GroupLayout diagram1Layout = new javax.swing.GroupLayout(diagram1);
+        diagram1.setLayout(diagram1Layout);
+        diagram1Layout.setHorizontalGroup(
+            diagram1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 398, Short.MAX_VALUE)
+        );
+        diagram1Layout.setVerticalGroup(
+            diagram1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 298, Short.MAX_VALUE)
+        );
+
+        jScrollPane1.setViewportView(diagram1);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane1)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane1)
+        );
+    }// </editor-fold>//GEN-END:initComponents
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private com.jme3.gde.materialdefinition.editor.Diagram diagram1;
+    private javax.swing.JScrollPane jScrollPane1;
+    // End of variables declaration//GEN-END:variables
+
+    @Override
+    public JComponent getVisualRepresentation() {
+        return this;
+    }
+
+    @Override
+    public JComponent getToolbarRepresentation() {
+        return toolbar;
+    }
+
+    @Override
+    public Action[] getActions() {
+        return new Action[0];
+    }
+
+    @Override
+    public Lookup getLookup() {
+        return obj.getLookup();
+    }
+
+    @Override
+    public void componentOpened() {
+    }
+
+    @Override
+    public void componentClosed() {
+    }
+
+    @Override
+    public void componentShowing() {
+    }
+
+    @Override
+    public void componentHidden() {
+    }
+
+    @Override
+    public void componentActivated() {
+    }
+
+    @Override
+    public void componentDeactivated() {
+    }
+
+    @Override
+    public UndoRedo getUndoRedo() {
+        return UndoRedo.NONE;
+    }
+
+    @Override
+    public void setMultiViewCallback(MultiViewElementCallback callback) {
+        this.callback = callback;
+    }
+
+    @Override
+    public CloseOperationState canCloseElement() {
+        return CloseOperationState.STATE_OK;
+    }
+
+    protected void makeMapping(Connection conn) {
+        InOut startNode = (InOut) conn.start.getNode();
+        InOut endNode = (InOut) conn.end.getNode();
+        String leftVarName = conn.end.getText();
+        String rightVarName = conn.start.getText();
+        String leftVarSwizzle = null;
+        String rightVarSwizzle = null;
+
+        int endCard = ShaderUtils.getCardinality(conn.end.getType(), "");
+        int startCard = ShaderUtils.getCardinality(conn.start.getType(), "");
+        String swizzle = "xyzw";
+        if (startCard > endCard) {
+            rightVarSwizzle = swizzle.substring(0, endCard);
+        } else if (endCard > startCard) {
+            leftVarSwizzle = swizzle.substring(0, startCard);
+        }
+
+        if (endNode instanceof OutBusPanel) {
+            OutputMappingBlock mapping = new OutputMappingBlock(leftVarName, rightVarName, leftVarSwizzle, rightVarSwizzle, endNode.getName(), startNode.getName(), null);
+            startNode.addOutputMapping(mapping);
+            conn.makeKey(mapping, diagram1.getCurrentTechniqueName());
+        } else {
+            InputMappingBlock mapping = new InputMappingBlock(leftVarName, rightVarName, leftVarSwizzle, rightVarSwizzle, endNode.getName(), startNode.getName(), null);
+            endNode.addInputMapping(mapping);
+            conn.makeKey(mapping, diagram1.getCurrentTechniqueName());
+        }
+    }
+
+    protected void notifyRemoveConnection(Connection conn) {
+        InOut startNode = (InOut) conn.start.getNode();
+        InOut endNode = (InOut) conn.end.getNode();
+        if (endNode instanceof OutBusPanel) {
+            startNode.removeOutputMapping((OutputMappingBlock) conn.mapping);
+        } else {
+            endNode.removeInputMapping((InputMappingBlock) conn.mapping);
+        }
+    }
+
+    public void notifyAddNode(ShaderNodeBlock node, ShaderNodeDefinition def) {
+        MatDefBlock matDef = obj.getLookup().lookup(MatDefBlock.class);
+        TechniqueBlock technique = getTechnique(matDef);
+        if (def.getType() == Shader.ShaderType.Vertex) {
+            technique.addVertexShaderNode(node);
+        } else if (def.getType() == Shader.ShaderType.Fragment) {
+            technique.addFragmentShaderNode(node);
+        }
+    }
+
+    public void notifyAddMapParam(String type, String name) {
+        MatDefBlock matDef = obj.getLookup().lookup(MatDefBlock.class);
+        //FIXME add a way to set fixed pipeline function and default value
+        MatParamBlock param = new MatParamBlock(type, name, null, null);
+        matDef.addMatParam(param);
+    }
+
+    public void notifyAddWorldParam(String name) {
+        MatDefBlock matDef = obj.getLookup().lookup(MatDefBlock.class);
+        //FIXME add a way to set fixed pipeline function and default value
+        WorldParamBlock param = new WorldParamBlock(name);
+        getTechnique(matDef).addWorldParam(param);
+    }
+
+    public void notifyRemoveNode(NodePanel node) {
+        MatDefBlock matDef = obj.getLookup().lookup(MatDefBlock.class);
+        if (node.getType() == NodePanel.NodeType.Fragment || node.getType() == NodePanel.NodeType.Vertex) {
+            TechniqueBlock technique = getTechnique(matDef);
+            for (ShaderNodeBlock shaderNodeBlock : technique.getShaderNodes()) {
+                if (shaderNodeBlock.getName().equals(node.getName())) {
+                    technique.removeShaderNode(shaderNodeBlock);
+                }
+            }
+        } else if (node.getType() == NodePanel.NodeType.MatParam) {
+            matDef.removeMatParam(new MatParamBlock("", node.getKey().replaceAll("MatParam.", ""), "", ""));
+        } else if (node.getType() == NodePanel.NodeType.WorldParam) {
+            getTechnique(matDef).removeWorldParam(new WorldParamBlock(node.getKey().replaceAll("WorldParam.", "")));
+        } else if (node.getType() == NodePanel.NodeType.Attribute) {
+            getTechnique(matDef).cleanupMappings("Attr", node.getKey().replaceAll("Attr.", ""));
+        }
+    }
+
+    private Dot findConnectPoint(String nameSpace, String name, boolean isInput) {
+
+        if (nameSpace.equals("MatParam")
+                || nameSpace.equals("WorldParam")
+                || nameSpace.equals("Attr")) {
+            NodePanel np = diagram1.getNodePanel(nameSpace + "." + name);
+            return isInput ? np.getInputConnectPoint(name) : np.getOutputConnectPoint(name);
+        } else if (nameSpace.equals("Global")) {
+            OutBusPanel outBus = diagram1.getOutBusPanel(name);
+            return outBus.getConnectPoint();
+        } else {
+            NodePanel np = diagram1.getNodePanel(diagram1.getCurrentTechniqueName() + "/" + nameSpace);
+            return isInput ? np.getInputConnectPoint(name) : np.getOutputConnectPoint(name);
+        }
+    }
+
+    private void makeConnection(MappingBlock mapping) {
+
+        Dot leftDot = findConnectPoint(mapping.getLeftNameSpace(), mapping.getLeftVar(), true);
+        Dot rightDot = findConnectPoint(mapping.getRightNameSpace(), mapping.getRightVar(), false);
+        Connection conn = diagram1.connect(leftDot, rightDot);
+        mapping.addPropertyChangeListener(conn);
+        conn.makeKey(mapping, diagram1.getCurrentTechniqueName());
+    }
+
+    private void initData(TechniqueBlock technique, ProjectAssetManager manager, List<ShaderNodeVariable> vertexGlobals, List<ShaderNodeVariable> fragmentGlobals, List<ShaderNodeVariable> attributes, MatDefBlock matDef, List<ShaderNodeVariable> uniforms) {
+        for (ShaderNodeBlock sn : technique.getShaderNodes()) {
+            ShaderNodeDefinition def = MaterialUtils.loadShaderNodeDefinition(sn, manager);
+            List<InputMappingBlock> in = sn.getInputs();
+            if (in != null) {
+                for (InputMappingBlock map : in) {
+                    ShaderNodeVariable var = new ShaderNodeVariable("", map.getRightNameSpace(), map.getRightVar());
+                    if (var.getNameSpace().equals("Global")) {
+                        var.setType("vec4");
+                        if (def.getType() == Shader.ShaderType.Vertex) {
+                            if (!MaterialUtils.contains(vertexGlobals, var)) {
+                                vertexGlobals.add(var);
+                            }
+                        } else {
+                            if (!MaterialUtils.contains(fragmentGlobals, var)) {
+                                fragmentGlobals.add(var);
+                            }
+                        }
+                    } else if (var.getNameSpace().equals("Attr")) {
+                        ShaderNodeVariable left = MaterialUtils.getVar(def.getInputs(), map.getLeftVar());
+                        var.setType(MaterialUtils.guessType(map, left));
+                        attributes.add(var);
+                    }
+                }
+            }
+            List<OutputMappingBlock> out = sn.getOutputs();
+            if (out != null) {
+                for (OutputMappingBlock map : out) {
+                    ShaderNodeVariable var = new ShaderNodeVariable("", map.getLeftNameSpace(), map.getLeftVar());
+                    if (var.getNameSpace().equals("Global")) {
+                        var.setType("vec4");
+                        if (def.getType() == Shader.ShaderType.Vertex) {
+                            if (!MaterialUtils.contains(vertexGlobals, var)) {
+                                vertexGlobals.add(var);
+                            }
+                        } else {
+                            if (!MaterialUtils.contains(fragmentGlobals, var)) {
+                                fragmentGlobals.add(var);
+                            }
+                        }
+                    }
+                }
+            }
+
+        }
+
+
+        for (WorldParamBlock worldParamBlock : technique.getWorldParams()) {
+            ShaderNodeVariable var = new ShaderNodeVariable("", "WorldParam", worldParamBlock.getName());
+            var.setType(MaterialUtils.getWorldParamType(var.getName()));
+            uniforms.add(var);
+        }
+
+        for (MatParamBlock matParamBlock : matDef.getMatParams()) {
+            ShaderNodeVariable var = new ShaderNodeVariable("", "MatParam", matParamBlock.getName());
+            var.setType(MaterialUtils.getMatParamType(matParamBlock));
+            uniforms.add(var);
+        }
+
+    }
+
+    private TechniqueBlock getTechnique(MatDefBlock matDef) {
+        TechniqueBlock technique = matDef.getTechniques().get(0);
+
+        return technique;
+    }
+
+    protected Point getPositionFromMetaData(String key, int defaultx, int defaulty) throws NumberFormatException {
+        Point position = new Point();
+        String pos = metaData.getProperty(diagram1.getCurrentTechniqueName() + "/" + key, defaultx + "," + defaulty);
+
+        if (pos != null) {
+            String[] s = pos.split(",");
+            position.x = Integer.parseInt(s[0]);
+            position.y = Integer.parseInt(s[1]);
+        }
+        return position;
+    }
+
+    protected void savePositionToMetaData(String key, int x, int y) throws NumberFormatException {
+
+        metaData.setProperty(diagram1.getCurrentTechniqueName() + "/" + key, x + "," + y);
+
+    }
+}

+ 277 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatPanel.form

@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <Properties>
+    <Property name="opaque" type="boolean" value="false"/>
+  </Properties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,120,0,0,0,120"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
+    <Property name="useNullLayout" type="boolean" value="true"/>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="toolBar">
+      <Properties>
+        <Property name="opaque" type="boolean" value="false"/>
+      </Properties>
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
+          <AbsoluteConstraints x="0" y="0" width="120" height="120"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="sphereButton" min="-2" pref="16" max="-2" attributes="0"/>
+                      <Component id="boxButton" min="-2" pref="16" max="-2" attributes="0"/>
+                      <Component id="quadButton" min="-2" pref="16" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace pref="104" max="32767" attributes="0"/>
+              </Group>
+              <Group type="102" attributes="0">
+                  <Component id="expandButton" min="-2" pref="16" max="-2" attributes="0"/>
+                  <EmptySpace max="32767" attributes="0"/>
+                  <Component id="reloadButton" min="-2" pref="16" max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="expandButton" min="-2" pref="16" max="-2" attributes="0"/>
+                      <Component id="reloadButton" min="-2" pref="16" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace pref="39" max="32767" attributes="0"/>
+                  <Component id="sphereButton" min="-2" pref="16" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="boxButton" min="-2" pref="16" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="quadButton" min="-2" pref="16" max="-2" attributes="0"/>
+                  <EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JButton" name="sphereButton">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="99" green="99" red="99" type="rgb"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/materialdefinition/icons/Sphere.png"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Sphere"/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="null"/>
+            </Property>
+            <Property name="borderPainted" type="boolean" value="false"/>
+            <Property name="contentAreaFilled" type="boolean" value="false"/>
+            <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+              <Color id="Curseur par d&#xe9;faut"/>
+            </Property>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="iconTextGap" type="int" value="0"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sphereButtonMouseEntered"/>
+            <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sphereButtonMouseExited"/>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sphereButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="boxButton">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="99" green="99" red="99" type="rgb"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/materialdefinition/icons/cube.png"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Cube"/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="null"/>
+            </Property>
+            <Property name="borderPainted" type="boolean" value="false"/>
+            <Property name="contentAreaFilled" type="boolean" value="false"/>
+            <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+              <Color id="Curseur par d&#xe9;faut"/>
+            </Property>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="iconTextGap" type="int" value="0"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="boxButtonMouseEntered"/>
+            <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="boxButtonMouseExited"/>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="boxButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="reloadButton">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="99" green="99" red="99" type="rgb"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/materialdefinition/icons/reload.png"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Refresh"/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="null"/>
+            </Property>
+            <Property name="borderPainted" type="boolean" value="false"/>
+            <Property name="contentAreaFilled" type="boolean" value="false"/>
+            <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+              <Color id="Curseur par d&#xe9;faut"/>
+            </Property>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="iconTextGap" type="int" value="0"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="reloadButtonMouseEntered"/>
+            <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="reloadButtonMouseExited"/>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="reloadButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="quadButton">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="99" green="99" red="99" type="rgb"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/materialdefinition/icons/Quad.png"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Quad"/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="null"/>
+            </Property>
+            <Property name="borderPainted" type="boolean" value="false"/>
+            <Property name="contentAreaFilled" type="boolean" value="false"/>
+            <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+              <Color id="Curseur par d&#xe9;faut"/>
+            </Property>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="iconTextGap" type="int" value="0"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="quadButtonMouseEntered"/>
+            <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="quadButtonMouseExited"/>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="quadButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="expandButton">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="99" green="99" red="99" type="rgb"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/materialdefinition/icons/expend.png"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" value="Expand"/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="null"/>
+            </Property>
+            <Property name="borderPainted" type="boolean" value="false"/>
+            <Property name="contentAreaFilled" type="boolean" value="false"/>
+            <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+              <Color id="Curseur par d&#xe9;faut"/>
+            </Property>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="iconTextGap" type="int" value="0"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[24, 24]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="expandButtonMouseEntered"/>
+            <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="expandButtonMouseExited"/>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="expandButtonActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JLabel" name="previewLabel">
+      <Properties>
+        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="64" green="64" red="64" type="rgb"/>
+        </Property>
+        <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="64" green="64" red="64" type="rgb"/>
+        </Property>
+        <Property name="horizontalAlignment" type="int" value="0"/>
+        <Property name="horizontalTextPosition" type="int" value="0"/>
+        <Property name="iconTextGap" type="int" value="0"/>
+        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[100, 100]"/>
+        </Property>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[100, 100]"/>
+        </Property>
+        <Property name="opaque" type="boolean" value="true"/>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[100, 100]"/>
+        </Property>
+      </Properties>
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
+          <AbsoluteConstraints x="20" y="20" width="-1" height="100"/>
+        </Constraint>
+      </Constraints>
+    </Component>
+  </SubComponents>
+</Form>

+ 379 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatPanel.java

@@ -0,0 +1,379 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.materials.MaterialPreviewRenderer;
+import com.jme3.material.Material;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MatPanel extends javax.swing.JPanel implements MouseListener,ComponentListener {
+
+    
+    private MaterialPreviewRenderer renderer;
+    private Material mat;
+    /**
+     * Creates new form PreviewPanel
+     */
+    public MatPanel() {
+        initComponents();
+        setBounds(0, 0, 120, 120);
+        toolBar.setVisible(false);
+        addMouseListener(this);
+        renderer = new MaterialPreviewRenderer(previewLabel);
+    }
+    
+    public void cleanup(){
+        renderer.cleanUp();
+        cleanup();
+    }
+    
+    public void showMaterial(Material mat) {
+        this.mat = mat;
+        renderer.showMaterial(mat);
+    }
+    
+    
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        toolBar = new javax.swing.JPanel();
+        sphereButton = new javax.swing.JButton();
+        boxButton = new javax.swing.JButton();
+        reloadButton = new javax.swing.JButton();
+        quadButton = new javax.swing.JButton();
+        expandButton = new javax.swing.JButton();
+        previewLabel = new javax.swing.JLabel();
+
+        setOpaque(false);
+        setLayout(null);
+
+        toolBar.setOpaque(false);
+
+        sphereButton.setBackground(new java.awt.Color(153, 153, 153));
+        sphereButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/materialdefinition/icons/Sphere.png"))); // NOI18N
+        sphereButton.setToolTipText("Sphere");
+        sphereButton.setBorder(null);
+        sphereButton.setBorderPainted(false);
+        sphereButton.setContentAreaFilled(false);
+        sphereButton.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
+        sphereButton.setFocusable(false);
+        sphereButton.setIconTextGap(0);
+        sphereButton.setMaximumSize(new java.awt.Dimension(24, 24));
+        sphereButton.setMinimumSize(new java.awt.Dimension(24, 24));
+        sphereButton.setPreferredSize(new java.awt.Dimension(24, 24));
+        sphereButton.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseEntered(java.awt.event.MouseEvent evt) {
+                sphereButtonMouseEntered(evt);
+            }
+            public void mouseExited(java.awt.event.MouseEvent evt) {
+                sphereButtonMouseExited(evt);
+            }
+        });
+        sphereButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                sphereButtonActionPerformed(evt);
+            }
+        });
+
+        boxButton.setBackground(new java.awt.Color(153, 153, 153));
+        boxButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/materialdefinition/icons/cube.png"))); // NOI18N
+        boxButton.setToolTipText("Cube");
+        boxButton.setBorder(null);
+        boxButton.setBorderPainted(false);
+        boxButton.setContentAreaFilled(false);
+        boxButton.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
+        boxButton.setFocusable(false);
+        boxButton.setIconTextGap(0);
+        boxButton.setMaximumSize(new java.awt.Dimension(24, 24));
+        boxButton.setMinimumSize(new java.awt.Dimension(24, 24));
+        boxButton.setPreferredSize(new java.awt.Dimension(24, 24));
+        boxButton.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseEntered(java.awt.event.MouseEvent evt) {
+                boxButtonMouseEntered(evt);
+            }
+            public void mouseExited(java.awt.event.MouseEvent evt) {
+                boxButtonMouseExited(evt);
+            }
+        });
+        boxButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                boxButtonActionPerformed(evt);
+            }
+        });
+
+        reloadButton.setBackground(new java.awt.Color(153, 153, 153));
+        reloadButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/materialdefinition/icons/reload.png"))); // NOI18N
+        reloadButton.setToolTipText("Refresh");
+        reloadButton.setBorder(null);
+        reloadButton.setBorderPainted(false);
+        reloadButton.setContentAreaFilled(false);
+        reloadButton.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
+        reloadButton.setFocusable(false);
+        reloadButton.setIconTextGap(0);
+        reloadButton.setMaximumSize(new java.awt.Dimension(24, 24));
+        reloadButton.setMinimumSize(new java.awt.Dimension(24, 24));
+        reloadButton.setPreferredSize(new java.awt.Dimension(24, 24));
+        reloadButton.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseEntered(java.awt.event.MouseEvent evt) {
+                reloadButtonMouseEntered(evt);
+            }
+            public void mouseExited(java.awt.event.MouseEvent evt) {
+                reloadButtonMouseExited(evt);
+            }
+        });
+        reloadButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                reloadButtonActionPerformed(evt);
+            }
+        });
+
+        quadButton.setBackground(new java.awt.Color(153, 153, 153));
+        quadButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/materialdefinition/icons/Quad.png"))); // NOI18N
+        quadButton.setToolTipText("Quad");
+        quadButton.setBorder(null);
+        quadButton.setBorderPainted(false);
+        quadButton.setContentAreaFilled(false);
+        quadButton.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
+        quadButton.setFocusable(false);
+        quadButton.setIconTextGap(0);
+        quadButton.setMaximumSize(new java.awt.Dimension(24, 24));
+        quadButton.setMinimumSize(new java.awt.Dimension(24, 24));
+        quadButton.setPreferredSize(new java.awt.Dimension(24, 24));
+        quadButton.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseEntered(java.awt.event.MouseEvent evt) {
+                quadButtonMouseEntered(evt);
+            }
+            public void mouseExited(java.awt.event.MouseEvent evt) {
+                quadButtonMouseExited(evt);
+            }
+        });
+        quadButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                quadButtonActionPerformed(evt);
+            }
+        });
+
+        expandButton.setBackground(new java.awt.Color(153, 153, 153));
+        expandButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/materialdefinition/icons/expend.png"))); // NOI18N
+        expandButton.setToolTipText("Expand");
+        expandButton.setBorder(null);
+        expandButton.setBorderPainted(false);
+        expandButton.setContentAreaFilled(false);
+        expandButton.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
+        expandButton.setFocusable(false);
+        expandButton.setIconTextGap(0);
+        expandButton.setMaximumSize(new java.awt.Dimension(24, 24));
+        expandButton.setMinimumSize(new java.awt.Dimension(24, 24));
+        expandButton.setPreferredSize(new java.awt.Dimension(24, 24));
+        expandButton.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseEntered(java.awt.event.MouseEvent evt) {
+                expandButtonMouseEntered(evt);
+            }
+            public void mouseExited(java.awt.event.MouseEvent evt) {
+                expandButtonMouseExited(evt);
+            }
+        });
+        expandButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                expandButtonActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout toolBarLayout = new javax.swing.GroupLayout(toolBar);
+        toolBar.setLayout(toolBarLayout);
+        toolBarLayout.setHorizontalGroup(
+            toolBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(toolBarLayout.createSequentialGroup()
+                .addGroup(toolBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(sphereButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(boxButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(quadButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addContainerGap(104, Short.MAX_VALUE))
+            .addGroup(toolBarLayout.createSequentialGroup()
+                .addComponent(expandButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(reloadButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
+        );
+        toolBarLayout.setVerticalGroup(
+            toolBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(toolBarLayout.createSequentialGroup()
+                .addGroup(toolBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(expandButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(reloadButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 39, Short.MAX_VALUE)
+                .addComponent(sphereButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(boxButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(quadButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(5, 5, 5))
+        );
+
+        add(toolBar);
+        toolBar.setBounds(0, 0, 120, 120);
+
+        previewLabel.setBackground(new java.awt.Color(100, 100, 100));
+        previewLabel.setForeground(new java.awt.Color(100, 100, 100));
+        previewLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+        previewLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        previewLabel.setIconTextGap(0);
+        previewLabel.setMaximumSize(new java.awt.Dimension(100, 100));
+        previewLabel.setMinimumSize(new java.awt.Dimension(100, 100));
+        previewLabel.setOpaque(true);
+        previewLabel.setPreferredSize(new java.awt.Dimension(100, 100));
+        add(previewLabel);
+        previewLabel.setBounds(20, 20, 100, 100);
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void expandButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_expandButtonActionPerformed
+        // TODO add your handling code here:
+    }//GEN-LAST:event_expandButtonActionPerformed
+
+    private void expandButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_expandButtonMouseEntered
+        mouseEntered(SwingUtilities.convertMouseEvent(expandButton, evt, this));
+    }//GEN-LAST:event_expandButtonMouseEntered
+
+    private void expandButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_expandButtonMouseExited
+        mouseExited(SwingUtilities.convertMouseEvent(expandButton, evt, this));
+    }//GEN-LAST:event_expandButtonMouseExited
+
+    private void sphereButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sphereButtonMouseEntered
+        mouseEntered(SwingUtilities.convertMouseEvent(sphereButton, evt, this));
+    }//GEN-LAST:event_sphereButtonMouseEntered
+
+    private void sphereButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sphereButtonMouseExited
+        mouseExited(SwingUtilities.convertMouseEvent(sphereButton, evt, this));
+    }//GEN-LAST:event_sphereButtonMouseExited
+
+    private void sphereButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sphereButtonActionPerformed
+        renderer.switchDisplay(MaterialPreviewRenderer.DisplayType.Sphere);
+        refresh();
+    }//GEN-LAST:event_sphereButtonActionPerformed
+
+    private void boxButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_boxButtonMouseEntered
+        mouseEntered(SwingUtilities.convertMouseEvent(boxButton, evt, this));
+    }//GEN-LAST:event_boxButtonMouseEntered
+
+    private void boxButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_boxButtonMouseExited
+        mouseExited(SwingUtilities.convertMouseEvent(boxButton, evt, this));
+    }//GEN-LAST:event_boxButtonMouseExited
+
+    private void boxButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_boxButtonActionPerformed
+        renderer.switchDisplay(MaterialPreviewRenderer.DisplayType.Box);
+        refresh();
+    }//GEN-LAST:event_boxButtonActionPerformed
+
+    private void reloadButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_reloadButtonMouseEntered
+        mouseEntered(SwingUtilities.convertMouseEvent(reloadButton, evt, this));
+    }//GEN-LAST:event_reloadButtonMouseEntered
+
+    private void reloadButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_reloadButtonMouseExited
+        mouseExited(SwingUtilities.convertMouseEvent(reloadButton, evt, this));
+    }//GEN-LAST:event_reloadButtonMouseExited
+
+    private void reloadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reloadButtonActionPerformed
+        refresh();
+    }//GEN-LAST:event_reloadButtonActionPerformed
+
+    private void quadButtonMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_quadButtonMouseEntered
+        mouseEntered(SwingUtilities.convertMouseEvent(quadButton, evt, this));
+    }//GEN-LAST:event_quadButtonMouseEntered
+
+    private void quadButtonMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_quadButtonMouseExited
+        mouseExited(SwingUtilities.convertMouseEvent(quadButton, evt, this));
+    }//GEN-LAST:event_quadButtonMouseExited
+
+    private void quadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_quadButtonActionPerformed
+        renderer.switchDisplay(MaterialPreviewRenderer.DisplayType.Quad);
+        refresh();
+    }//GEN-LAST:event_quadButtonActionPerformed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton boxButton;
+    private javax.swing.JButton expandButton;
+    private javax.swing.JLabel previewLabel;
+    private javax.swing.JButton quadButton;
+    private javax.swing.JButton reloadButton;
+    private javax.swing.JButton sphereButton;
+    private javax.swing.JPanel toolBar;
+    // End of variables declaration//GEN-END:variables
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+        toolBar.setVisible(true);
+        t.stop();
+    }
+    Timer t = new Timer(500, new ActionListener() {
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            toolBar.setVisible(false);
+        }
+    });
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+        t.restart();
+    }
+    
+     @Override
+    public void componentResized(ComponentEvent e) {
+        update(e.getComponent());
+    }
+
+    @Override
+    public void componentMoved(ComponentEvent e) {
+        update(e.getComponent());
+    }
+
+    @Override
+    public void componentShown(ComponentEvent e) {
+         update(e.getComponent());
+    }
+
+    @Override
+    public void componentHidden(ComponentEvent e) {
+    }
+
+    protected void update(Component c) {
+        setLocation(c.getLocation().x + c.getWidth() - 150, c.getLocation().y + 10 - 120);
+    }
+
+    private void refresh() {
+        if(mat!=null){
+            renderer.showMaterial(mat);
+        }
+    }
+}

+ 460 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/NodePanel.java

@@ -0,0 +1,460 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderNodeDefinition;
+import com.jme3.shader.ShaderNodeVariable;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RadialGradientPaint;
+import java.awt.RenderingHints;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.GroupLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.LayoutStyle;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class NodePanel extends DraggablePanel implements Selectable, PropertyChangeListener, InOut, KeyListener {
+
+    List<JLabel> inputLabels = new ArrayList<JLabel>();
+    List<JLabel> outputLabels = new ArrayList<JLabel>();
+    List<Dot> inputDots = new ArrayList<Dot>();
+    List<Dot> outputDots = new ArrayList<Dot>();
+    private NodeType type = NodeType.Vertex;
+    private JPanel content;
+    private JLabel header;
+    private Color color;
+    private String name;
+    private String techName;
+
+//    private List listeners = Collections.synchronizedList(new LinkedList());
+//
+//    public void addPropertyChangeListener(PropertyChangeListener pcl) {
+//        listeners.add(pcl);
+//    }
+//
+//    public void removePropertyChangeListener(PropertyChangeListener pcl) {
+//        listeners.remove(pcl);
+//    }
+//
+//    protected void fire(String propertyName, Object old, Object nue) {
+//        //Passing 0 below on purpose, so you only synchronize for one atomic call:
+//        PropertyChangeListener[] pcls = (PropertyChangeListener[]) listeners.toArray(new PropertyChangeListener[0]);
+//        for (int i = 0; i < pcls.length; i++) {
+//            pcls[i].propertyChange(new PropertyChangeEvent(this, propertyName, old, nue));
+//        }
+//    }
+    public enum NodeType {
+
+        Vertex(new Color(220, 220, 70)),//yellow
+        Fragment(new Color(114, 200, 255)),//bleue
+        Attribute(Color.WHITE),
+        MatParam(new Color(70, 220, 70)),//green
+        WorldParam(new Color(220, 70, 70)); //red
+        private Color color;
+
+        private NodeType() {
+        }
+
+        private NodeType(Color color) {
+            this.color = color;
+        }
+
+        public Color getColor() {
+            return color;
+        }
+    }
+
+    /**
+     * Creates new form NodePanel
+     */
+    public NodePanel(ShaderNodeBlock node, ShaderNodeDefinition def) {
+        super();
+        if (def.getType() == Shader.ShaderType.Vertex) {
+            type = NodePanel.NodeType.Vertex;
+        } else {
+            type = NodePanel.NodeType.Fragment;
+        }
+        init(def.getInputs(), def.getOutputs());
+
+        node.addPropertyChangeListener(WeakListeners.propertyChange(this, node));
+        this.addPropertyChangeListener(WeakListeners.propertyChange(node, this));
+        refresh(node);
+        addKeyListener(this);
+    }
+
+    /**
+     * Creates new form NodePanel
+     */
+    public NodePanel(ShaderNodeVariable singleOut, NodePanel.NodeType type) {
+        super();
+        List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
+        outputs.add(singleOut);
+        this.type = type;
+        init(new ArrayList<ShaderNodeVariable>(), outputs);
+        addKeyListener(this);
+    }
+
+    public final void refresh(ShaderNodeBlock node) {
+        name = node.getName();
+        header.setText(node.getName());
+        header.setToolTipText(node.getName());
+
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getPropertyName().equals("name")) {
+            refresh((ShaderNodeBlock) evt.getSource());
+        }
+    }
+
+    private void init(List<ShaderNodeVariable> inputs, List<ShaderNodeVariable> outputs) {
+
+        setBounds(0, 0, 120, 30 + inputs.size() * 20 + outputs.size() * 20);
+
+        for (ShaderNodeVariable input : inputs) {
+
+            JLabel label = createLabel(input.getType(), input.getName(), Dot.ParamType.Input);
+            Dot dot = createDot(input.getType(), Dot.ParamType.Input, input.getName());
+            inputLabels.add(label);
+            inputDots.add(dot);
+        }
+        int index = 0;
+        for (ShaderNodeVariable output : outputs) {
+            String outName = output.getName();
+            JLabel label = createLabel(output.getType(), outName, Dot.ParamType.Output);
+            Dot dot = createDot(output.getType(), Dot.ParamType.Output, outName);
+            dot.setIndex(index++);
+            outputLabels.add(label);
+            outputDots.add(dot);
+        }
+
+        initComponents();
+        updateType();
+        setOpaque(false);
+
+    }
+
+    public void setTitle(String s) {
+        header.setText(s);
+        header.setToolTipText(s);
+    }
+
+    public Dot getInputConnectPoint(String varName) {
+        return getConnectPoint(inputLabels, varName, inputDots);
+    }
+
+    public Dot getOutputConnectPoint(String varName) {
+        return getConnectPoint(outputLabels, varName, outputDots);
+    }
+
+    private Dot getConnectPoint(List<JLabel> list, String varName, List<Dot> listDot) {
+        if (varName.startsWith("m_") || varName.startsWith("g_")) {
+            varName = varName.substring(2);
+        }
+        for (int i = 0; i < list.size(); i++) {
+            if (list.get(i).getText().equals(varName)) {
+                return listDot.get(i);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void paintComponent(Graphics g1) {
+        Graphics2D g = (Graphics2D) g1;
+        Color boderColor = Color.BLACK;
+        if (diagram.selectedItem == this) {
+            boderColor = Color.WHITE;    
+        }
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
+                RenderingHints.VALUE_ANTIALIAS_ON);
+        Color[] colors = {new Color(0, 0, 0, 0.7f), new Color(0, 0, 0, 0.15f)};
+        if (diagram.selectedItem == this) {
+            colors = new Color[]{new Color(0.6f, 0.6f, 1.0f, 0.9f), new Color(0.6f, 0.6f, 1.0f, 0.5f)};
+        }
+        float[] factors = {0f, 1f};
+        g.setPaint(new RadialGradientPaint(getWidth() / 2, getHeight() / 2, getWidth() / 2, factors, colors));
+        g.fillRoundRect(8, 3, getWidth() - 10, getHeight() - 6, 15, 15);
+        g.setColor(new Color(170, 170, 170));
+        g.fillRoundRect(5, 1, getWidth() - 9, getHeight() - 6, 15, 15);
+        g.setColor(boderColor);
+
+        g.drawRoundRect(4, 0, getWidth() - 9, getHeight() - 6, 15, 15);
+        g.setColor(new Color(170, 170, 170));
+        g.fillRect(4, 1, 10, 10);
+        g.setColor(boderColor);
+        g.drawLine(4, 0, 14, 0);
+        g.drawLine(4, 0, 4, 10);
+        g.setColor(Color.BLACK);
+        g.drawLine(5, 15, getWidth() - 6, 15);
+        g.setColor(new Color(190, 190, 190));
+        g.drawLine(5, 16, getWidth() - 6, 16);
+
+        Color c1 = new Color(color.getRed(), color.getGreen(), color.getBlue(), 150);
+        Color c2 = new Color(color.getRed(), color.getGreen(), color.getBlue(), 0);
+        g.setPaint(new GradientPaint(0, 15, c1, getWidth(), 15, c2));
+        g.fillRect(5, 1, getWidth() - 10, 14);
+
+    }
+
+    public String getKey() {
+        switch (type) {
+            case Attribute:
+                return "Attr." + outputLabels.get(0).getText();
+            case WorldParam:
+                return "WorldParam." + outputLabels.get(0).getText();
+            case MatParam:
+                return "MatParam." + outputLabels.get(0).getText();
+            default:
+                return techName + "/" + name;
+        }
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        super.mousePressed(e);
+        diagram.select(this);
+    }
+
+    public NodeType getType() {
+        return type;
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        diagram.fixSize();
+        if (svdx != getLocation().x) {
+            firePropertyChange(ShaderNodeBlock.POSITION, svdx, getLocation().x);
+            getDiagram().getEditorParent().savePositionToMetaData(getKey(),  getLocation().x,  getLocation().y);
+        }
+    }
+
+    public final void updateType() {
+
+        switch (type) {
+            case Vertex:
+                header.setIcon(Icons.vert);
+                break;
+            case Fragment:
+                header.setIcon(Icons.frag);
+                break;
+            case Attribute:
+                header.setIcon(Icons.attrib);
+                header.setText("Attribute");
+                header.setToolTipText("Attribute");
+                name = "Attr";
+                break;
+            case WorldParam:
+                header.setIcon(Icons.world);
+                header.setText("WorldParam");
+                header.setToolTipText("WorldParam");
+                name = "WorldParam";
+                break;
+            case MatParam:
+                header.setIcon(Icons.mat);
+                header.setText("MatParam");
+                header.setToolTipText("MatParam");
+                name = "MatParam";
+                break;
+        }
+        color = type.getColor();
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
+    private void initComponents() {
+
+
+
+        ImageIcon icon = Icons.vert;
+        if (type == NodeType.Fragment) {
+            icon = Icons.frag;
+        }
+        header = new JLabel(icon);
+        header.addMouseListener(labelMouseMotionListener);
+        header.addMouseMotionListener(labelMouseMotionListener);
+        header.setHorizontalAlignment(SwingConstants.LEFT);
+        header.setFont(new Font("Tahoma", Font.BOLD, 11));
+        content = new JPanel();
+        content.setOpaque(false);
+        GroupLayout contentLayout = new GroupLayout(content);
+        content.setLayout(contentLayout);
+
+
+
+        int txtLength = 100;
+
+        GroupLayout.ParallelGroup grpHoriz = contentLayout.createParallelGroup(GroupLayout.Alignment.LEADING);
+
+        for (int i = 0; i < outputDots.size(); i++) {
+            grpHoriz.addGroup(GroupLayout.Alignment.TRAILING, contentLayout.createSequentialGroup()
+                    .addGap(0, 0, Short.MAX_VALUE)
+                    .addComponent(outputLabels.get(i), GroupLayout.PREFERRED_SIZE, txtLength, GroupLayout.PREFERRED_SIZE)
+                    .addGap(2, 2, 2)
+                    .addComponent(outputDots.get(i), GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE));
+        }
+        for (int i = 0; i < inputDots.size(); i++) {
+            grpHoriz.addGroup(GroupLayout.Alignment.LEADING, contentLayout.createSequentialGroup()
+                    .addComponent(inputDots.get(i), GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
+                    .addGap(2, 2, 2)
+                    .addComponent(inputLabels.get(i), GroupLayout.PREFERRED_SIZE, txtLength, GroupLayout.PREFERRED_SIZE));
+        }
+
+        contentLayout.setHorizontalGroup(grpHoriz);
+
+        GroupLayout.ParallelGroup grpVert = contentLayout.createParallelGroup(GroupLayout.Alignment.LEADING);
+
+        GroupLayout.SequentialGroup grp = contentLayout.createSequentialGroup();
+        for (int i = 0; i < inputDots.size(); i++) {
+            grp.addGroup(contentLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
+                    .addComponent(inputDots.get(i), GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(inputLabels.get(i))).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
+        }
+        for (int i = 0; i < outputDots.size(); i++) {
+            grp.addGroup(contentLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
+                    .addComponent(outputDots.get(i), GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(outputLabels.get(i))).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
+        }
+
+
+        grpVert.addGroup(GroupLayout.Alignment.TRAILING, grp);
+
+        contentLayout.setVerticalGroup(grpVert);
+
+        GroupLayout layout = new GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+                .addGap(6, 6, 6)
+                .addComponent(header, 100, 100, 100))
+                .addGroup(GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+                .addGap(6, 6, 6))
+                .addComponent(content, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
+        layout.setVerticalGroup(
+                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                .addGroup(layout.createSequentialGroup()
+                .addComponent(header, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                .addGap(10, 10, 10)
+                .addComponent(content, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addGap(10, 10, 10));
+    }
+
+    public JLabel createLabel(String glslType, String txt, Dot.ParamType type) {
+        JLabel label = new JLabel(txt);
+        label.setToolTipText(glslType + " " + txt);
+        label.setOpaque(false);
+        //label.setPreferredSize(new Dimension(50, 15));        
+        label.setHorizontalAlignment(type == Dot.ParamType.Output ? SwingConstants.RIGHT : SwingConstants.LEFT);
+        label.setFont(new Font("Tahoma", 0, 10));
+        label.addMouseListener(labelMouseMotionListener);
+        label.addMouseMotionListener(labelMouseMotionListener);
+        // label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
+        return label;
+    }
+
+    public Dot createDot(String type, Dot.ParamType paramType, String paramName) {
+        Dot dot1 = new Dot();
+        dot1.setNode(this);
+        dot1.setText(paramName);
+        dot1.setParamType(paramType);
+        dot1.setType(type);
+        return dot1;
+    }
+
+    public void keyTyped(KeyEvent e) {
+    }
+
+    @Override
+    public void keyPressed(KeyEvent e) {
+
+        if (e.getKeyCode() == KeyEvent.VK_DELETE) {
+            Diagram diag = getDiagram();
+            if (diag.selectedItem == this) {
+                diag.removeSelectedNode();
+            }
+        }
+    }
+
+    public void keyReleased(KeyEvent e) {
+    }
+    // used to pass press and drag events to the NodePanel when they occur on the label
+    private LabelMouseMotionListener labelMouseMotionListener = new LabelMouseMotionListener();
+
+    private class LabelMouseMotionListener extends MouseAdapter {
+
+        @Override
+        public void mousePressed(MouseEvent e) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(e.getComponent(), e, NodePanel.this);
+            NodePanel.this.dispatchEvent(me);
+        }
+
+        @Override
+        public void mouseDragged(MouseEvent e) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(e.getComponent(), e, NodePanel.this);
+            NodePanel.this.dispatchEvent(me);
+        }
+
+        @Override
+        public void mouseReleased(MouseEvent e) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(e.getComponent(), e, NodePanel.this);
+            NodePanel.this.dispatchEvent(me);
+        }
+    }
+
+    public void setTechName(String techName) {
+        this.techName = techName;
+    }
+
+    public void addInputMapping(InputMappingBlock block) {
+        firePropertyChange(ShaderNodeBlock.INPUT, null, block);
+    }
+
+    public void removeInputMapping(InputMappingBlock block) {
+        firePropertyChange(ShaderNodeBlock.INPUT, block, null);
+    }
+
+    public void addOutputMapping(OutputMappingBlock block) {
+        firePropertyChange(ShaderNodeBlock.OUTPUT, null, block);
+    }
+
+    public void removeOutputMapping(OutputMappingBlock block) {
+        firePropertyChange(ShaderNodeBlock.OUTPUT, block, null);
+    }
+}

+ 283 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/OutBusPanel.java

@@ -0,0 +1,283 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.material.Material;
+import com.jme3.shader.Shader;
+import java.awt.Color;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.RenderingHints;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import javax.swing.GroupLayout;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+
+/**
+ *
+ * @author m327836
+ */
+public class OutBusPanel extends DraggablePanel implements ComponentListener, Selectable, InOut {
+
+    private Color color = new Color(220, 220, 70);
+    private String name = "";
+    private InnerPanel panel;
+    private MatPanel preview;
+    private Shader.ShaderType type;
+
+    public OutBusPanel(String name, Shader.ShaderType type) {
+        this(name);
+        this.type = type;
+        if (type == Shader.ShaderType.Fragment) {
+            this.color = new Color(114, 200, 255);
+        }
+    }
+
+    private OutBusPanel(String name) {
+        super(true);
+        setBounds(0, 0, 300, 50);
+        JLabel title = new JLabel();
+        this.name = name;
+        title.setFont(new java.awt.Font("Impact", 1, 15)); // NOI18N
+        title.setForeground(new java.awt.Color(153, 153, 153));
+        title.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
+        title.setText(name);
+        setOpaque(false);
+        panel = new InnerPanel();
+
+        javax.swing.GroupLayout outBusPanel1Layout = new javax.swing.GroupLayout(this);
+        this.setLayout(outBusPanel1Layout);
+        outBusPanel1Layout.setHorizontalGroup(
+                outBusPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, outBusPanel1Layout.createSequentialGroup()
+                .addContainerGap(70, 70)
+                .addComponent(panel, 20, 200, Short.MAX_VALUE)
+                .addComponent(title, GroupLayout.PREFERRED_SIZE, 70, GroupLayout.PREFERRED_SIZE)
+                .addGap(30, 30, 30)));
+        outBusPanel1Layout.setVerticalGroup(
+                outBusPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(outBusPanel1Layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(title, javax.swing.GroupLayout.DEFAULT_SIZE, 28, Short.MAX_VALUE)
+                .addContainerGap())
+                .addGroup(outBusPanel1Layout.createSequentialGroup()
+                .addContainerGap(20, 20)
+                .addComponent(panel, 10, 10, 10)
+                .addContainerGap()));
+
+        preview = new MatPanel();
+        addComponentListener(preview);
+
+    }
+
+    @Override
+    public void setDiagram(Diagram diagram) {
+        super.setDiagram(diagram);
+        // preview.setBounds(350,300,128,100);
+        diagram.add(preview);
+        preview.update(this);
+    }
+
+    @Override
+    protected void paintComponent(Graphics g1) {
+        Graphics2D g = (Graphics2D) g1;
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
+                RenderingHints.VALUE_ANTIALIAS_ON);
+
+        int width = getWidth();
+        int[] xs = {38, width - 30, width - 30, width, width - 30, width - 30, 38, 38};
+        int[] ys = {10, 10, 0, getHeight() / 2, getHeight(), getHeight() - 10, getHeight() - 10, 10};
+
+        Polygon p = new Polygon(xs, ys, 8);
+
+        if (diagram.selectedItem == this) {
+            int[] xs2 = {0, width - 30, width - 30, width, width - 32, width - 32, 0, 0};
+            int[] ys2 = {10, 10, 0, getHeight() / 2 + 2, getHeight(), getHeight() - 8, getHeight() - 8, 10};
+
+            Polygon p2 = new Polygon(xs2, ys2, 8);
+            g.setPaint(new GradientPaint(0, 0, new Color(0.6f, 0.6f, 1.0f, 0.9f), 0, getHeight(), new Color(0.6f, 0.6f, 1.0f, 0.5f)));
+            g.fillPolygon(p2);
+        }
+
+        Color c1 = new Color(100, 100, 100, 255);
+        Color c2 = new Color(100, 100, 100, 100);
+        g.setPaint(new GradientPaint(0, 0, c1, width, 0, c2));
+        g.fillPolygon(p);
+        g.fillRect(0, 10, 3, getHeight() - 20);
+        g.fillRect(5, 10, 6, getHeight() - 20);
+        g.fillRect(13, 10, 9, getHeight() - 20);
+        g.fillRect(24, 10, 12, getHeight() - 20);
+
+
+    }
+
+    @Override
+    public void componentResized(ComponentEvent e) {
+        setSize(e.getComponent().getWidth(), 50);
+    }
+
+    @Override
+    public void componentMoved(ComponentEvent e) {
+    }
+
+    @Override
+    public void componentShown(ComponentEvent e) {
+    }
+
+    @Override
+    public void componentHidden(ComponentEvent e) {
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        if (dispatchToInnerPanel(e)) {
+            return;
+        }
+        super.mousePressed(e);
+        diagram.select(this);
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        if (panel.dragging == false) {
+            super.mouseDragged(e);
+        }
+    }
+
+    protected void draggingDot(MouseEvent e) {
+        Point p = SwingUtilities.convertPoint(this, e.getX(), e.getY(), panel);
+        if (panel.contains(p)) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(this, e, panel);
+            panel.mouseEntered(me);
+        } else {
+            MouseEvent me = SwingUtilities.convertMouseEvent(this, e, panel);
+            panel.mouseExited(me);
+        }
+    }
+
+    public Dot getConnectPoint() {
+        return panel;
+    }
+
+    public void updatePreview(Material mat) {
+        if (type == Shader.ShaderType.Fragment) {
+            preview.showMaterial(mat);
+        } else {
+            Material vmat = mat.clone();
+            vmat.getAdditionalRenderState().setWireframe(true);
+            preview.showMaterial(vmat);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return "Global";
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        diagram.fixSize();
+        MouseEvent me = SwingUtilities.convertMouseEvent(this, e, panel);
+        panel.mouseReleased(me);
+        getDiagram().getEditorParent().savePositionToMetaData(getKey(), 0, getLocation().y);
+
+    }
+
+    public String getKey() {
+        return name;
+    }
+
+    private boolean dispatchToInnerPanel(MouseEvent e) {
+        Point p = SwingUtilities.convertPoint(this, e.getX(), e.getY(), panel);
+        if (panel.contains(p)) {
+            MouseEvent me = SwingUtilities.convertMouseEvent(this, e, panel);
+            panel.dispatchEvent(me);
+            if (me.isConsumed()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void addInputMapping(InputMappingBlock block) {
+    }
+
+    public void removeInputMapping(InputMappingBlock block) {
+    }
+
+    public void addOutputMapping(OutputMappingBlock block) {
+    }
+
+    public void removeOutputMapping(OutputMappingBlock block) {
+    }
+
+    class InnerPanel extends Dot {
+
+        boolean over = false;
+        boolean dragging = false;
+
+        public InnerPanel() {
+            setOpaque(false);
+            setNode(OutBusPanel.this);
+            setParamType(Dot.ParamType.Both);
+            setType("vec4");
+            setText(name);
+        }
+
+        @Override
+        protected void paintComponent(Graphics g1) {
+            Graphics2D g = (Graphics2D) g1;
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias!
+                    RenderingHints.VALUE_ANTIALIAS_ON);
+
+            Color c1 = new Color(color.getRed(), color.getGreen(), color.getBlue(), 255);
+            Color c2 = new Color(color.getRed(), color.getGreen(), color.getBlue(), 0);
+            g.setPaint(new LinearGradientPaint(0, 0, 0, getHeight(), new float[]{0, 0.5f, 1}, new Color[]{c2, c1, c2}));
+            g.fillRoundRect(1, 1, getWidth() - 1, getHeight() - 1, 10, 10);
+
+        }
+
+        @Override
+        public void mousePressed(MouseEvent e) {
+            super.mousePressed(e);
+            dragging = true;
+        }
+
+        @Override
+        public void mouseDragged(MouseEvent e) {
+            e.consume();
+        }
+
+        @Override
+        public void mouseReleased(MouseEvent e) {
+            super.mouseReleased(e);
+            dragging = false;
+        }
+
+        @Override
+        public void mouseEntered(MouseEvent e) {
+            if (!over) {
+                super.mouseEntered(e);
+                over = true;
+            }
+        }
+
+        @Override
+        public void mouseExited(MouseEvent e) {
+            if (over) {
+                super.mouseExited(e);
+                over = false;
+            }
+        }
+    }
+}

+ 16 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/Selectable.java

@@ -0,0 +1,16 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.editor;
+
+/**
+ *
+ * @author m327836
+ */
+public interface Selectable {
+    
+    public String getKey();    
+   
+    
+}

+ 22 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/FragmentShaderNodesBlock.java

@@ -0,0 +1,22 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.util.blockparser.Statement;
+
+/**
+ *
+ * @author Nehon
+ */
+public class FragmentShaderNodesBlock extends ShaderNodesBlock {
+
+    public FragmentShaderNodesBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public FragmentShaderNodesBlock(Statement sta) {
+        super(sta);
+    }
+}

+ 42 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/InputMappingsBlock.java

@@ -0,0 +1,42 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.*;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.util.blockparser.Statement;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class InputMappingsBlock extends UberStatement {
+
+    protected InputMappingsBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public InputMappingsBlock(Statement sta, String nodeName) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            addStatement(new InputMappingBlock(statement, nodeName));
+        }
+    }
+
+    public List<InputMappingBlock> getMappings() {
+        return getBlocks(InputMappingBlock.class);
+    }
+
+    public void addMapping(InputMappingBlock mapping) {
+        addStatement(mapping);
+    }
+
+    public void removeMapping(InputMappingBlock mapping) {
+        contents.remove(mapping);
+    }
+}

+ 94 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/MatDefBlock.java

@@ -0,0 +1,94 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.UnsupportedStatement;
+import com.jme3.util.blockparser.Statement;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MatDefBlock extends UberStatement {
+
+    public static final String ADD_MAT_PARAM = "addMatParam";
+    public static final String REMOVE_MAT_PARAM = "removeMatParam";
+    protected String name;
+
+    protected MatDefBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public MatDefBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            if (statement.getLine().trim().startsWith("MaterialParameters")) {
+                addStatement(new MaterialParametersBlock(statement));
+            } else if (statement.getLine().trim().startsWith("Technique")) {
+                addStatement(new TechniqueBlock(statement));
+            } else {
+                addStatement(new UnsupportedStatement(statement));
+            }
+        }
+        String[] s = line.split("\\s");
+        name = s[1];
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        String oldName = this.name;
+        this.name = name;
+        line = "MaterialDef " + name;
+        fire("name", oldName, name);
+    }
+
+    protected MaterialParametersBlock getMaterialParameters() {
+        return getBlock(MaterialParametersBlock.class);
+    }
+
+    public List<MatParamBlock> getMatParams() {
+        return getMaterialParameters().getMatParams();
+    }
+
+    public void addMatParam(MatParamBlock matParam) {
+        MaterialParametersBlock mpBlock = getMaterialParameters();
+        if (mpBlock == null) {
+            mpBlock = new MaterialParametersBlock(0, "MaterialParameters");
+            addStatement(0, mpBlock);
+        }
+        mpBlock.addMatParam(matParam);
+        fire(ADD_MAT_PARAM, null, matParam);
+    }
+
+    public void removeMatParam(MatParamBlock matParam) {
+        MaterialParametersBlock mpBlock = getMaterialParameters();
+        if (mpBlock == null) {
+            return;
+        }
+        mpBlock.removeMatParam(matParam);
+
+        for (TechniqueBlock techniqueBlock : getTechniques()) {
+            VertexShaderNodesBlock vblock = techniqueBlock.getBlock(VertexShaderNodesBlock.class);
+            FragmentShaderNodesBlock fblock = techniqueBlock.getBlock(FragmentShaderNodesBlock.class);
+            techniqueBlock.cleanMappings(vblock, "MatParam", matParam.getName());
+            techniqueBlock.cleanMappings(fblock, "MatParam", matParam.getName());
+        }
+        fire(REMOVE_MAT_PARAM, null, matParam);
+    }
+
+    public List<TechniqueBlock> getTechniques() {
+        return getBlocks(TechniqueBlock.class);
+    }
+
+    public void addTechnique(TechniqueBlock techniqueBlock) {
+        addStatement(techniqueBlock);
+        fire("technique", null, techniqueBlock);
+    }
+}

+ 40 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/MaterialParametersBlock.java

@@ -0,0 +1,40 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.util.blockparser.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MaterialParametersBlock extends UberStatement {
+
+    protected MaterialParametersBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public MaterialParametersBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            addStatement(new MatParamBlock(statement));
+        }
+    }
+
+    public List<MatParamBlock> getMatParams() {        
+        return getBlocks(MatParamBlock.class);
+    }
+    
+    public void addMatParam(MatParamBlock matParam){
+         addStatement(matParam);        
+    }
+    
+    public void removeMatParam(MatParamBlock matParam){
+         contents.remove(matParam);        
+    }
+}

+ 39 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/OutputMappingsBlock.java

@@ -0,0 +1,39 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.util.blockparser.Statement;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class OutputMappingsBlock extends UberStatement {
+
+    protected OutputMappingsBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public OutputMappingsBlock(Statement sta, String nodeName) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            addStatement(new OutputMappingBlock(statement, nodeName));
+        }
+    }
+
+    public List<OutputMappingBlock> getMappings() {
+        return getBlocks(OutputMappingBlock.class);
+    }
+
+    public void addMapping(OutputMappingBlock mapping) {
+        addStatement(mapping);
+    }
+    
+    public void removeMapping(OutputMappingBlock mapping) {
+        contents.remove(mapping);
+    }
+}

+ 250 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/ShaderNodeBlock.java

@@ -0,0 +1,250 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.ConditionBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.DefinitionBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.shader.ShaderNodeDefinition;
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ShaderNodeBlock extends UberStatement implements Comparable<ShaderNodeBlock>, PropertyChangeListener {
+
+    public final static String POSITION = "position";
+    public final static String INPUT = "input";
+    public final static String OUTPUT = "output";
+    public static final String ADD_MAPPING = "addMapping";
+    public static final String REMOVE_MAPPING = "removeMapping";
+    protected String name;
+    //built up data for fast sorting 
+    protected int spatialOrder;
+    protected List<String> inputNodes = new ArrayList<String>();
+    protected boolean globalInput = false;
+    protected boolean globalOutput = false;
+
+    protected ShaderNodeBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public ShaderNodeBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        String[] s = line.split("\\s");
+        name = s[1];
+        for (Statement statement : sta.getContents()) {
+            if (statement.getLine().trim().startsWith("Definition")) {
+                addStatement(new DefinitionBlock(statement));
+            } else if (statement.getLine().trim().startsWith("Condition")) {
+                addStatement(new ConditionBlock(statement));
+            } else if (statement.getLine().trim().startsWith("InputMapping")) {
+                addStatement(new InputMappingsBlock(statement, name));
+            } else if (statement.getLine().trim().startsWith("OutputMapping")) {
+                addStatement(new OutputMappingsBlock(statement, name));
+            } else {
+                addStatement(statement);
+            }
+        }
+
+    }
+
+    public ShaderNodeBlock(ShaderNodeDefinition def, String path) {
+        super(0, "ShaderNode " + def.getName());
+        name = def.getName();
+        DefinitionBlock b = new DefinitionBlock(name, path);
+        addStatement(0, b);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        String oldName = this.name;
+        this.name = name;
+        line = "ShaderNode " + name;
+        fire("name", oldName, name);
+    }
+
+    public void setSpatialOrder(int spatialOrder) {
+        this.spatialOrder = spatialOrder;
+    }
+
+    public void addInputNode(String name) {
+        if (!inputNodes.contains(name)) {
+            inputNodes.add(name);
+        }
+    }
+
+    public void setGlobalInput(boolean globalInput) {
+        this.globalInput = globalInput;
+    }
+
+    public void setGlobalOutput(boolean globalOutput) {
+        this.globalOutput = globalOutput;
+    }
+
+    public DefinitionBlock getDefinition() {
+        return getBlock(DefinitionBlock.class);
+    }
+
+    public void setDefinition(String name, String path) {
+        DefinitionBlock b = getDefinition();
+
+        if (b == null) {
+            b = new DefinitionBlock(name, path);
+            addStatement(0, b);
+        } else {
+            b.setName(name);
+            b.setPath(path);
+        }
+        fire("definition", null, b);
+    }
+
+    public String getCondition() {
+        ConditionBlock b = getBlock(ConditionBlock.class);
+        if (b == null) {
+            return null;
+        }
+        return b.getCondition();
+    }
+
+    public void setCondition(String condition) {
+        ConditionBlock b = getBlock(ConditionBlock.class);
+        String prevCond = null;
+        if (condition.trim().length() == 0 || condition.equals("<null value>")) {
+            condition = null;
+        }
+        if (b == null) {
+            if (condition == null) {
+                return;
+            }
+            b = new ConditionBlock(condition);
+            addStatement(b);
+        } else {
+            if (condition == null) {
+                contents.remove(b);
+            }
+            prevCond = b.getCondition();
+            b.setCondition(condition);
+        }
+        fire("condition", prevCond, condition);
+    }
+
+    public List<InputMappingBlock> getInputs() {
+        InputMappingsBlock b = getBlock(InputMappingsBlock.class);
+        if (b == null) {
+            return null;
+        }
+        return b.getMappings();
+    }
+
+    public List<OutputMappingBlock> getOutputs() {
+        OutputMappingsBlock b = getBlock(OutputMappingsBlock.class);
+        if (b == null) {
+            return null;
+        }
+        return b.getMappings();
+    }
+
+    public void addInputMapping(InputMappingBlock mapping) {
+        InputMappingsBlock b = getBlock(InputMappingsBlock.class);
+        if (b == null) {
+            b = new InputMappingsBlock(0, "InputMappings");
+            addStatement(b);
+        }
+        b.addMapping(mapping);
+        fire(ADD_MAPPING, null, mapping);
+    }
+
+    public void addOutputMapping(OutputMappingBlock mapping) {
+        OutputMappingsBlock b = getBlock(OutputMappingsBlock.class);
+        if (b == null) {
+            b = new OutputMappingsBlock(0, "OutputMappings");
+            addStatement(b);
+        }
+        b.addMapping(mapping);
+        fire(ADD_MAPPING, null, mapping);
+    }
+
+    public void removeInputMapping(InputMappingBlock mapping) {
+        InputMappingsBlock b = getBlock(InputMappingsBlock.class);
+        if (b != null) {
+            b.removeMapping(mapping);
+        }
+        fire(REMOVE_MAPPING, mapping, null);
+    }
+
+    public void removeOutputMapping(OutputMappingBlock mapping) {
+        OutputMappingsBlock b = getBlock(OutputMappingsBlock.class);
+        if (b != null) {
+            b.removeMapping(mapping);
+        }
+        fire(REMOVE_MAPPING, mapping, null);
+    }
+
+    public int compareTo(ShaderNodeBlock o) {
+        if (inputNodes.contains(o.getName())) {
+            return 1;
+        }
+        if (o.inputNodes.contains(name)) {
+            return -1;
+        }
+        if ((globalInput && o.globalOutput) || (o.globalInput && globalOutput)) {
+            return (int) Math.signum(spatialOrder - o.spatialOrder);
+        }
+        return 0;
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getPropertyName().equals(POSITION)) {
+            spatialOrder = (Integer) evt.getNewValue();
+            fire("order", null, null);
+        } else if (evt.getPropertyName().equals(INPUT)) {
+            InputMappingBlock mapping = (InputMappingBlock) evt.getNewValue();
+            if (mapping != null) {
+                if (mapping.getRightNameSpace().equals("Global")) {
+                    globalInput = true;
+                } else {
+                    inputNodes.add(mapping.getRightNameSpace());
+                }
+                addInputMapping(mapping);
+            } else {
+                InputMappingBlock oldMapping = (InputMappingBlock) evt.getOldValue();
+                if (oldMapping.getRightNameSpace().equals("Global")) {
+                    globalInput = false;
+                } else {
+                    inputNodes.remove(oldMapping.getRightNameSpace());
+                }
+                removeInputMapping(oldMapping);
+            }
+            fire("order", null, null);
+
+        } else if (evt.getPropertyName().equals(OUTPUT)) {
+            OutputMappingBlock mapping = (OutputMappingBlock) evt.getNewValue();
+            if (mapping != null) {
+                if (mapping.getLeftNameSpace().equals("Global")) {
+                    globalOutput = true;
+                }
+                addOutputMapping(mapping);
+            } else {
+                OutputMappingBlock oldMapping = (OutputMappingBlock) evt.getOldValue();
+
+                if (oldMapping.getLeftNameSpace().equals("Global")) {
+                    globalOutput = false;
+                }
+                removeOutputMapping(oldMapping);
+            }
+            fire("order", null, null);
+        }
+    }
+}

+ 59 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/ShaderNodesBlock.java

@@ -0,0 +1,59 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Collections;
+import java.util.List;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ShaderNodesBlock extends UberStatement implements PropertyChangeListener {
+    
+    protected ShaderNodesBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+    
+    public ShaderNodesBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            ShaderNodeBlock b = new ShaderNodeBlock(statement);
+            b.addPropertyChangeListener(WeakListeners.propertyChange(this, b));
+            addStatement(b);
+        }
+    }
+    
+    public List<ShaderNodeBlock> getShaderNodes() {        
+        return getBlocks(ShaderNodeBlock.class);
+    }
+    
+    public void addShaderNode(ShaderNodeBlock shaderNodeBlock) {
+        addStatement(shaderNodeBlock);      
+        shaderNodeBlock.addPropertyChangeListener(WeakListeners.propertyChange(this, shaderNodeBlock));
+    }
+    
+    public boolean removeShaderNode(ShaderNodeBlock shaderNodeBlock) {
+        return contents.remove(shaderNodeBlock);
+    }
+    
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getPropertyName().equals("order")) {
+            sort();
+        }        
+    }
+    
+    public void sort() {
+        List<ShaderNodeBlock> list = getShaderNodes();
+        Collections.sort(list);        
+        contents.clear();
+        contents.addAll(list);
+        fire("reorder", null, null);
+    }
+}

+ 239 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/TechniqueBlock.java

@@ -0,0 +1,239 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.FragmentShaderFileBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.LightModeBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.ShaderFileBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.UnsupportedStatement;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.VertexShaderFileBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.WorldParamBlock;
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TechniqueBlock extends UberStatement {
+
+    public static final String ADD_SHADER_NODE = "addShaderNode";
+    public static final String REMOVE_SHADER_NODE = "removeShaderNode";
+    public static final String ADD_WORLD_PARAM = "addWorldParam";
+    public static final String REMOVE_WORLD_PARAM = "removeWorldParam";
+    protected String name;
+
+    protected TechniqueBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public TechniqueBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            if (statement.getLine().trim().startsWith("WorldParameters")) {
+                addStatement(new WorldParametersBlock(statement));
+            } else if (statement.getLine().trim().startsWith("LightMode")) {
+                addStatement(new LightModeBlock(statement));
+            } else if (statement.getLine().trim().startsWith("VertexShader ")) {
+                addStatement(new VertexShaderFileBlock(statement));
+            } else if (statement.getLine().trim().startsWith("FragmentShader ")) {
+                addStatement(new FragmentShaderFileBlock(statement));
+            } else if (statement.getLine().trim().startsWith("VertexShaderNodes")) {
+                addStatement(new VertexShaderNodesBlock(statement));
+            } else if (statement.getLine().trim().startsWith("FragmentShaderNodes")) {
+                addStatement(new FragmentShaderNodesBlock(statement));
+            } else {
+                addStatement(new UnsupportedStatement(statement));
+            }
+        }
+        String[] s = line.split("\\s");
+        if (s.length == 1) {
+            name = "Default";
+        } else {
+            name = s[1];
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        String oldName = this.name;
+        this.name = name;
+        line = "Technique " + (name.equals("Default") ? "" : name);
+        fire("name", oldName, name);
+    }
+
+    public String getLightMode() {
+        LightModeBlock l = getBlock(LightModeBlock.class);
+        if (l == null) {
+            return null;
+        }
+        return l.getLightMode();
+    }
+
+    public void setLightMode(String lightMode) {
+        String oldLightMode = null;
+        LightModeBlock l = getBlock(LightModeBlock.class);
+        if (l == null) {
+            l = new LightModeBlock(lightMode);
+            addStatement(0, l);
+        } else {
+            oldLightMode = l.getLightMode();
+            l.setLightMode(lightMode);
+        }
+        fire("lightMode", oldLightMode, lightMode);
+    }
+
+    protected WorldParametersBlock getWorldParameters() {
+        return getBlock(WorldParametersBlock.class);
+    }
+
+    public List<WorldParamBlock> getWorldParams() {
+        return getWorldParameters().getWorldParams();
+    }
+
+    public void addWorldParam(WorldParamBlock block) {
+        WorldParametersBlock wpBlock = getBlock(WorldParametersBlock.class);
+        if (wpBlock == null) {
+            wpBlock = new WorldParametersBlock(0, "WorldParameters");
+            addStatement(0, wpBlock);
+        }
+        wpBlock.addWorldParam(block);
+        fire(ADD_WORLD_PARAM, null, block);
+    }
+
+    public void addVertexShaderNode(ShaderNodeBlock block) {
+        VertexShaderNodesBlock vblock = getBlock(VertexShaderNodesBlock.class);
+
+        if (vblock == null) {
+            vblock = new VertexShaderNodesBlock(0, "VertexShaderNodes ");
+            addStatement(0, vblock);
+        }
+        vblock.addShaderNode(block);
+        fire(ADD_SHADER_NODE, null, block);
+    }
+
+    public void addFragmentShaderNode(ShaderNodeBlock block) {
+        FragmentShaderNodesBlock fblock = getBlock(FragmentShaderNodesBlock.class);
+
+        if (fblock == null) {
+            fblock = new FragmentShaderNodesBlock(0, "FragmentShaderNodes ");
+            addStatement(0, fblock);
+        }
+        fblock.addShaderNode(block);
+        fire(ADD_SHADER_NODE, null, block);
+    }
+
+    public void removeShaderNode(ShaderNodeBlock block) {
+        VertexShaderNodesBlock vblock = getBlock(VertexShaderNodesBlock.class);
+        FragmentShaderNodesBlock fblock = getBlock(FragmentShaderNodesBlock.class);
+        boolean removed = false;
+        String eventToFire = null;
+        if (vblock != null) {
+            removed = vblock.removeShaderNode(block);
+            eventToFire = REMOVE_SHADER_NODE;
+        }
+        if (fblock != null) {
+            if (!removed) {
+                removed = fblock.removeShaderNode(block);
+                eventToFire = REMOVE_SHADER_NODE;
+            }
+        }
+
+        if (removed) {
+            cleanMappings(vblock, block);
+            cleanMappings(fblock, block);
+        }
+
+        if (eventToFire != null) {
+            fire(eventToFire, block, null);
+        }
+    }
+
+    public void removeWorldParam(WorldParamBlock worldParam) {
+        WorldParametersBlock wpBlock = getWorldParameters();
+        if (wpBlock == null) {
+            return;
+        }
+        wpBlock.removeWorldParam(worldParam);
+
+        VertexShaderNodesBlock vblock = getBlock(VertexShaderNodesBlock.class);
+        FragmentShaderNodesBlock fblock = getBlock(FragmentShaderNodesBlock.class);
+        cleanMappings(vblock, "WorldParam", worldParam.getName());
+        cleanMappings(fblock, "WorldParam", worldParam.getName());
+
+        fire(REMOVE_WORLD_PARAM, null, worldParam);
+    }
+
+    public List<ShaderNodeBlock> getShaderNodes() {
+        List<ShaderNodeBlock> list = new ArrayList<ShaderNodeBlock>();
+        list.addAll(getBlock(VertexShaderNodesBlock.class).getShaderNodes());
+        list.addAll(getBlock(FragmentShaderNodesBlock.class).getShaderNodes());
+        return list;
+    }
+
+    public ShaderFileBlock getVertexShader() {
+        return getBlock(VertexShaderFileBlock.class);
+    }
+
+    public ShaderFileBlock getFragmentShader() {
+        return getBlock(FragmentShaderFileBlock.class);
+    }
+
+    @Override
+    public void addPropertyChangeListener(PropertyChangeListener pcl) {
+        super.addPropertyChangeListener(pcl);
+        VertexShaderNodesBlock vblock = getBlock(VertexShaderNodesBlock.class);
+        if (vblock != null) {
+            vblock.addPropertyChangeListener(WeakListeners.propertyChange(pcl, vblock));
+        }
+        FragmentShaderNodesBlock fblock = getBlock(FragmentShaderNodesBlock.class);
+        if (fblock != null) {
+            fblock.addPropertyChangeListener(WeakListeners.propertyChange(pcl, fblock));
+        }
+    }
+
+    private void cleanMappings(ShaderNodesBlock vblock, ShaderNodeBlock block) {
+        if (vblock != null) {
+            for (ShaderNodeBlock shaderNodeBlock : vblock.getShaderNodes()) {
+                List<InputMappingBlock> lInput = shaderNodeBlock.getInputs();
+                if (lInput != null) {
+                    for (InputMappingBlock inputMappingBlock : lInput) {
+                        if (inputMappingBlock.getRightNameSpace().equals(block.getName())) {
+                            shaderNodeBlock.removeInputMapping(inputMappingBlock);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    protected void cleanMappings(ShaderNodesBlock vblock, String nameSpace, String name) {
+        if (vblock != null) {
+            for (ShaderNodeBlock shaderNodeBlock : vblock.getShaderNodes()) {
+                List<InputMappingBlock> lInput = shaderNodeBlock.getInputs();
+                if (lInput != null) {
+                    for (InputMappingBlock inputMappingBlock : lInput) {
+                        if (inputMappingBlock.getRightNameSpace().equals(nameSpace) && inputMappingBlock.getRightVar().equals(name)) {
+                            shaderNodeBlock.removeInputMapping(inputMappingBlock);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    public void cleanupMappings(String nameSpace, String name) {
+        cleanMappings(getBlock(VertexShaderNodesBlock.class), nameSpace, name);
+        cleanMappings(getBlock(FragmentShaderNodesBlock.class), nameSpace, name);
+    }
+}

+ 61 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/UberStatement.java

@@ -0,0 +1,61 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class UberStatement extends Statement {
+
+    private List listeners = Collections.synchronizedList(new LinkedList());
+
+    public void addPropertyChangeListener(PropertyChangeListener pcl) {
+        listeners.add(pcl);
+    }
+
+    public void removePropertyChangeListener(PropertyChangeListener pcl) {
+        listeners.remove(pcl);
+    }
+
+    protected void fire(String propertyName, Object old, Object nue) {
+        //Passing 0 below on purpose, so you only synchronize for one atomic call:
+        PropertyChangeListener[] pcls = (PropertyChangeListener[]) listeners.toArray(new PropertyChangeListener[0]);
+        for (int i = 0; i < pcls.length; i++) {
+            pcls[i].propertyChange(new PropertyChangeEvent(this, propertyName, old, nue));
+        }
+    }
+
+    public UberStatement(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    protected <T extends Statement> T getBlock(Class<T> blockType) {
+        for (Statement statement : contents) {
+            if (statement.getClass().isAssignableFrom(blockType)) {
+                return (T) statement;
+            }
+        }
+        return null;
+    }
+
+    protected <T extends Statement> List<T> getBlocks(Class<T> blockType) {
+        List<T> blocks = new ArrayList<T>();
+        for (Statement statement : contents) {
+            if (statement.getClass().isAssignableFrom(blockType)) {
+                blocks.add((T) statement);
+            }
+        }
+        return blocks;
+    }
+}

+ 23 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/VertexShaderNodesBlock.java

@@ -0,0 +1,23 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.util.blockparser.Statement;
+
+/**
+ *
+ * @author Nehon
+ */
+public class VertexShaderNodesBlock extends ShaderNodesBlock {
+
+    public VertexShaderNodesBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public VertexShaderNodesBlock(Statement sta) {
+        super(sta);
+    }
+}
+ 

+ 39 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/WorldParametersBlock.java

@@ -0,0 +1,39 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure;
+
+import com.jme3.gde.materialdefinition.fileStructure.leaves.WorldParamBlock;
+import com.jme3.util.blockparser.Statement;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class WorldParametersBlock extends UberStatement {
+    
+    protected WorldParametersBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+    
+    public WorldParametersBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        for (Statement statement : sta.getContents()) {
+            addStatement(new WorldParamBlock(statement));
+        }
+    }
+    
+    public List<WorldParamBlock> getWorldParams() {
+        return getBlocks(WorldParamBlock.class);
+    }
+    
+    public void addWorldParam(WorldParamBlock block) {
+        contents.add(block);
+    }
+    
+    public void removeWorldParam(WorldParamBlock block) {
+        contents.remove(block);
+    }
+}

+ 57 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/ConditionBlock.java

@@ -0,0 +1,57 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ConditionBlock extends LeafStatement {
+
+    protected String condition;    
+
+    protected ConditionBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public ConditionBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+        updateLine();
+    }
+
+    public ConditionBlock(String condition) {
+        super(0, "");
+        this.condition = condition;       
+        updateLine();
+    }
+
+    private void updateLine() {
+        this.line = "Condition : " + condition;
+    }
+
+    public String getCondition() {
+        return condition;
+    }
+
+    public void setCondition(String condition) {
+        this.condition = condition;
+        updateLine();
+    }
+
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split(":");       
+            condition = split[1].trim();
+
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Logger.getLogger(ConditionBlock.class.getName()).log(Level.WARNING, "Parsing error line " + sta.getLineNumber() + " : " + sta.getLine());
+        }
+    }
+}

+ 72 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/DefinitionBlock.java

@@ -0,0 +1,72 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class DefinitionBlock extends LeafStatement {
+
+    protected String name;
+    protected String path;
+
+    protected DefinitionBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public DefinitionBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+         updateLine();
+    }
+
+    public DefinitionBlock(String name, String path) {
+        super(0, "");
+        this.name = name;
+        this.path = path;
+        updateLine();
+    }
+
+    private void updateLine() {
+        this.line = "Definition : " + name + (path == null ? "" : " : " + path);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+        updateLine();
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+        updateLine();
+    }
+
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split(":");
+            if (split.length > 2) {
+                path = split[2].trim();
+            }
+            name = split[1].trim();
+
+
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Logger.getLogger(DefinitionBlock.class.getName()).log(Level.WARNING, "Parsing error line " + sta.getLineNumber() + " : " + sta.getLine());
+        }
+    }
+}

+ 32 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/FragmentShaderFileBlock.java

@@ -0,0 +1,32 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+
+/**
+ *
+ * @author Nehon
+ */
+public class FragmentShaderFileBlock extends ShaderFileBlock {
+
+    public FragmentShaderFileBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public FragmentShaderFileBlock(Statement sta) {
+        super(sta);
+    }
+
+    public FragmentShaderFileBlock(String language, String path, int lineNumber, String line) {
+        super(language, path, lineNumber, line);
+    }
+
+    @Override
+    protected void updateLine() {
+        super.updateLine();
+        line = "Fragment" + line;
+    }
+}

+ 69 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/InputmappingBlock.java

@@ -0,0 +1,69 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class InputMappingBlock extends MappingBlock {
+
+    protected InputMappingBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public InputMappingBlock(Statement sta, String name) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+        leftNameSpace = name;
+        updateLine();
+    }
+
+    public InputMappingBlock(String leftVar, String rightVar, String leftVarSwizzle, String rightVarSwizzle, String leftNameSpace,String rightNameSpace, String condition) {
+        super(0, "");
+        this.leftVar = leftVar;
+        this.rightVar = rightVar;
+        this.leftVarSwizzle = leftVarSwizzle;
+        this.leftNameSpace = leftNameSpace;
+        this.rightVarSwizzle = rightVarSwizzle;
+        this.rightNameSpace = rightNameSpace;        
+        this.condition = condition;
+        updateLine();
+    }
+
+    @Override
+    protected final void updateLine() {
+        this.line = leftVar + (leftVarSwizzle == null ? "" : "." + leftVarSwizzle) + " = " + rightNameSpace + "." + rightVar + (rightVarSwizzle == null ? "" : "." + rightVarSwizzle) + (condition != null ? " : " + condition : "");
+    }
+
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split(":");
+            if (split.length > 1) {
+                condition = split[1].trim();
+            }
+            String mapping = split[0].trim();
+            String[] s = mapping.split("=");
+            String[] left = s[0].split("\\.");
+            leftVar = left[0].trim();
+            if (left.length > 1) {
+                leftVarSwizzle = left[1].trim();
+            }
+            String[] right = s[1].split("\\.");
+            rightNameSpace = right[0].trim();
+            rightVar = right[1].trim();
+            if (right.length > 2) {
+                rightVarSwizzle = right[2].trim();
+            }
+
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Logger.getLogger(InputMappingBlock.class.getName()).log(Level.WARNING, "Parsing error line " + sta.getLineNumber() + " : " + sta.getLine());
+        }
+    }
+}

+ 42 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/LeafStatement.java

@@ -0,0 +1,42 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class LeafStatement extends Statement {
+
+    private List listeners = Collections.synchronizedList(new LinkedList());
+
+    public void addPropertyChangeListener(PropertyChangeListener pcl) {
+        listeners.add(pcl);
+    }
+
+    public void removePropertyChangeListener(PropertyChangeListener pcl) {
+        listeners.remove(pcl);
+    }
+
+    protected void fire(String propertyName, Object old, Object nue) {
+        //Passing 0 below on purpose, so you only synchronize for one atomic call:
+        PropertyChangeListener[] pcls = (PropertyChangeListener[]) listeners.toArray(new PropertyChangeListener[0]);
+        for (int i = 0; i < pcls.length; i++) {
+            pcls[i].propertyChange(new PropertyChangeEvent(this, propertyName, old, nue));
+        }
+    }
+
+    public LeafStatement(int lineNumber, String line) {
+        super(lineNumber, line);
+        contents = null;
+    }
+}

+ 62 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/LightModeBlock.java

@@ -0,0 +1,62 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.material.TechniqueDef;
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class LightModeBlock extends LeafStatement {
+
+    protected String lightMode;
+
+    protected LightModeBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public LightModeBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+         updateLine();
+    }
+
+    public LightModeBlock(String lightMode) {
+        super(0, "");
+        this.lightMode = lightMode;
+        updateLine();
+    }
+
+    private void updateLine() {
+        this.line = "LightMode " + lightMode;
+    }
+
+    public String getLightMode() {
+        return lightMode;
+    }
+
+    public void setLightMode(String lightMode) {
+        this.lightMode = lightMode;
+        updateLine();
+    }
+
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split("\\s");
+            lightMode = split[1].trim();            
+            
+            TechniqueDef.LightMode.valueOf(lightMode);
+
+        } catch (ArrayIndexOutOfBoundsException e) {           
+            Logger.getLogger(LightModeBlock.class.getName()).log(Level.WARNING, "Parsing error line " + sta.getLineNumber() + " : " + sta.getLine());
+        } catch (IllegalArgumentException ex){
+            Logger.getLogger(LightModeBlock.class.getName()).log(Level.WARNING, "Invalid light mode : " + sta.getLineNumber() + " : " + sta.getLine());
+        }
+    }
+}

+ 112 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/MappingBlock.java

@@ -0,0 +1,112 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+/**
+ *
+ * @author Nehon
+ */
+public abstract class MappingBlock extends LeafStatement {
+
+    protected String leftVar;
+    protected String rightVar;
+    protected String leftVarSwizzle;
+    protected String rightVarSwizzle;
+    protected String leftNameSpace;
+    protected String rightNameSpace;
+    protected String condition;
+
+    public MappingBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    protected abstract void updateLine();
+
+    public String getCondition() {
+        return condition;
+    }
+
+    public void setCondition(String condition) {
+        String old = this.condition;
+        if (condition.trim().length() == 0 || condition.equals("<null value>")) {
+            condition = null;
+        }
+        this.condition = condition;
+        updateLine();
+        fire("condition", old, condition);
+    }
+
+    public String getLeftVar() {
+        return leftVar;
+    }
+
+    public void setLeftVar(String leftVar) {
+        this.leftVar = leftVar;
+        updateLine();
+    }
+
+    public String getRightVar() {
+        return rightVar;
+    }
+
+    public void setRightVar(String rightVar) {
+        this.rightVar = rightVar;
+        updateLine();
+    }
+
+    public String getRightNameSpace() {
+        return rightNameSpace;
+    }
+
+    public void setRightNameSpace(String rightnameSpace) {
+        String old = this.rightNameSpace;
+        this.rightNameSpace = rightnameSpace;
+        updateLine();
+        fire("rightNameSpace", old, rightnameSpace);
+    }
+
+    public String getLeftVarSwizzle() {
+        return leftVarSwizzle;
+    }
+
+    public void setLeftVarSwizzle(String leftVarSwizzle) {
+        String old = this.leftVarSwizzle;
+        if (leftVarSwizzle.trim().length() == 0) {
+            leftVarSwizzle = null;
+        }
+        this.leftVarSwizzle = leftVarSwizzle;
+        updateLine();
+        fire("leftVarSwizzle", old, leftVarSwizzle);
+    }
+
+    public String getRightVarSwizzle() {
+        return rightVarSwizzle;
+    }
+
+    public void setRightVarSwizzle(String rightVarSwizzle) {
+        String old = this.rightVarSwizzle;
+        this.rightVarSwizzle = rightVarSwizzle;
+        if (rightVarSwizzle.trim().length() == 0) {
+            rightVarSwizzle = null;
+        }
+        this.rightVarSwizzle = rightVarSwizzle;
+        updateLine();
+        fire("rightVarSwizzle", old, rightVarSwizzle);
+    }
+
+    public String getLeftNameSpace() {
+        return leftNameSpace;
+    }
+
+    public void setLeftNameSpace(String leftNameSpace) {
+        String old = this.leftNameSpace;
+        this.leftNameSpace = leftNameSpace;
+        updateLine();
+        fire("leftNameSpace", old, leftNameSpace);
+    }
+  
+    
+    
+}

+ 128 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/MatParamBlock.java

@@ -0,0 +1,128 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MatParamBlock extends LeafStatement {
+
+    protected String type;
+    protected String name;
+    protected String fixedFuncBinding;
+    protected String defaultValue;
+
+    protected MatParamBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public MatParamBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);     
+         updateLine();
+    }
+
+    public MatParamBlock(String type, String name, String fixedPipelineBinding, String defaultValue) {
+        super(0, "");
+        this.type = type;
+        this.name = name;
+        this.fixedFuncBinding = fixedPipelineBinding;
+        this.defaultValue = defaultValue;
+        updateLine();
+    }
+
+    private void updateLine() {
+        this.line = type + " " + name + (fixedFuncBinding != null ? " (" + fixedFuncBinding + ") " : "") + (defaultValue != null ? " : " + defaultValue : "");
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+        updateLine();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+        updateLine();
+    }
+
+    public String getFixedPipelineBinding() {
+        return fixedFuncBinding;
+    }
+
+    public void setFixedPipelineBinding(String fixedPipelineBinding) {
+        this.fixedFuncBinding = fixedPipelineBinding;
+        updateLine();
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+        updateLine();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if(!(obj instanceof MatParamBlock)){
+            return false;
+        }
+        return ((MatParamBlock)obj).getName().equals(name);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
+        return hash;
+    }
+    
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split(":");
+            String statement = split[0].trim();
+
+            // Parse default val
+            if (split.length > 1) {
+                
+                defaultValue = split[1].trim();
+            }
+
+            // Parse ffbinding
+            int startParen = statement.indexOf("(");
+            if (startParen != -1) {
+                // get content inside parentheses
+                int endParen = statement.indexOf(")", startParen);
+                fixedFuncBinding = statement.substring(startParen + 1, endParen).trim();
+                statement = statement.substring(0, startParen);
+            }
+
+            // Parse type + name
+            split = statement.split("\\p{javaWhitespace}+");
+
+            type = split[0];
+            name = split[1];
+
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            Logger.getLogger(MatParamBlock.class.getName()).log(Level.WARNING, "Parsing error line " + sta.getLineNumber() + " : " + sta.getLine());
+        }
+    }
+}

+ 73 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/OutputMappingBlock.java

@@ -0,0 +1,73 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class OutputMappingBlock extends MappingBlock {
+
+    protected OutputMappingBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public OutputMappingBlock(Statement sta, String name) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+        rightNameSpace = name;
+        updateLine();
+    }
+
+    public OutputMappingBlock(String leftVar, String rightVar, String leftVarSwizzle, String rightVarSwizzle, String leftNameSpace, String rightNameSpace, String condition) {
+        super(0, "");
+        this.leftVar = leftVar;
+        this.rightVar = rightVar;
+        this.leftVarSwizzle = leftVarSwizzle;
+        this.rightVarSwizzle = rightVarSwizzle;
+        this.leftNameSpace = leftNameSpace;
+        this.rightNameSpace = rightNameSpace;
+        this.condition = condition;
+        updateLine();
+    }
+
+    @Override
+    protected final void updateLine() {
+        this.line = leftNameSpace + "." + leftVar + (leftVarSwizzle == null ? "" : "." + leftVarSwizzle) + " = " + rightVar + (rightVarSwizzle == null ? "" : "." + rightVarSwizzle) + (condition != null ? " : " + condition : "");
+    }
+
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split(":");
+            if (split.length > 1) {
+                condition = split[1].trim();
+            }
+            String mapping = split[0].trim();
+            String[] s = mapping.split("=");
+
+            String[] left = s[0].split("\\.");
+            leftNameSpace = left[0].trim();
+            leftVar = left[1].trim();
+            if (left.length > 2) {
+                leftVarSwizzle = left[2].trim();
+            }
+
+            String[] right = s[1].split("\\.");
+
+            rightVar = right[0].trim();
+            if (right.length > 1) {
+                rightVarSwizzle = right[1].trim();
+            }
+
+
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Logger.getLogger(OutputMappingBlock.class.getName()).log(Level.WARNING, "Parsing error line {0} : {1}", new Object[]{sta.getLineNumber(), sta.getLine()});
+        }
+    }
+}

+ 53 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/ShaderFileBlock.java

@@ -0,0 +1,53 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ShaderFileBlock extends LeafStatement {
+
+    protected String language;
+    protected String path;
+
+    protected ShaderFileBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public ShaderFileBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+        updateLine();
+    }
+
+    public ShaderFileBlock(String language, String path, int lineNumber, String line) {
+        super(lineNumber, line);
+        this.language = language;
+        this.path = path;
+        updateLine();
+    }
+
+    protected void updateLine() {
+        this.line = "Shader " + language + ": " + path;
+    }
+
+    private void parse(Statement sta) {
+        try {
+            String[] split = sta.getLine().split("\\s");
+            language = split[1].trim();
+            path = split[2].trim();
+
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Logger.getLogger(ShaderFileBlock.class.getName()).log(Level.WARNING, "Parsing error line " + sta.getLineNumber() + " : " + sta.getLine());
+        } catch (IllegalArgumentException ex) {
+            Logger.getLogger(ShaderFileBlock.class.getName()).log(Level.WARNING, "Invalid light mode : " + sta.getLineNumber() + " : " + sta.getLine());
+        }
+    }
+}

+ 22 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/UnsupportedStatement.java

@@ -0,0 +1,22 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+
+/**
+ *
+ * @author Nehon
+ */
+public class UnsupportedStatement extends Statement {
+
+    public UnsupportedStatement(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public UnsupportedStatement(Statement sta) {
+        super(sta.getLineNumber(), sta.getLine());
+    }
+}

+ 32 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/VertexShaderFileBlock.java

@@ -0,0 +1,32 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+
+/**
+ *
+ * @author Nehon
+ */
+public class VertexShaderFileBlock extends ShaderFileBlock {
+
+    public VertexShaderFileBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public VertexShaderFileBlock(Statement sta) {
+        super(sta);
+    }
+
+    public VertexShaderFileBlock(String language, String path, int lineNumber, String line) {
+        super(language, path, lineNumber, line);
+    }
+
+    @Override
+    protected void updateLine() {
+        super.updateLine();
+        line = "Vertex" + line;
+    }
+}

+ 64 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/WorldParamBlock.java

@@ -0,0 +1,64 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.fileStructure.leaves;
+
+import com.jme3.util.blockparser.Statement;
+
+/**
+ *
+ * @author Nehon
+ */
+public class WorldParamBlock extends LeafStatement {
+
+    protected String name;
+
+    protected WorldParamBlock(int lineNumber, String line) {
+        super(lineNumber, line);
+    }
+
+    public WorldParamBlock(Statement sta) {
+        this(sta.getLineNumber(), sta.getLine());
+        parse(sta);
+         updateLine();
+    }
+
+    public WorldParamBlock(String name) {
+        super(0, "");
+        this.name = name;
+        updateLine();
+    }
+
+    private void updateLine() {
+        this.line = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+        updateLine();
+    }
+
+    private void parse(Statement sta) {
+        name = sta.getLine().trim();
+    }
+    
+      @Override
+    public boolean equals(Object obj) {
+        if(!(obj instanceof WorldParamBlock)){
+            return false;
+        }
+        return ((WorldParamBlock)obj).getName().equals(name);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
+        return hash;
+    }
+}

+ 30 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/Icons.java

@@ -0,0 +1,30 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.icons;
+
+import javax.swing.ImageIcon;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Icons {
+
+    public final static ImageIcon node = new ImageIcon(Icons.class.getResource("node.png"));
+    public final static ImageIcon output = new ImageIcon(Icons.class.getResource("output.png"));
+    public final static ImageIcon world = new ImageIcon(Icons.class.getResource("earth.png"));
+    public final static ImageIcon attrib = new ImageIcon(Icons.class.getResource("attrib.png"));
+    public final static ImageIcon mat = new ImageIcon(Icons.class.getResource("mat.png"));
+    public final static ImageIcon vert = new ImageIcon(Icons.class.getResource("vert.png"));
+    public final static ImageIcon frag = new ImageIcon(Icons.class.getResource("fragment.png"));
+    public final static ImageIcon imgGrey = new ImageIcon(Icons.class.getResource("dot.png"));
+    public final static ImageIcon imgGreen = new ImageIcon(Icons.class.getResource("dotGreen.png"));
+    public final static ImageIcon imgOrange = new ImageIcon(Icons.class.getResource("dotOrange.png"));
+    public final static ImageIcon imgRed = new ImageIcon(Icons.class.getResource("dotRed.png"));
+    public final static ImageIcon matDef = new ImageIcon(Icons.class.getResource("matdef.png"));
+    public final static ImageIcon tech = new ImageIcon(Icons.class.getResource("tech.png"));
+    public final static ImageIcon in = new ImageIcon(Icons.class.getResource("in.png"));
+    public final static ImageIcon out = new ImageIcon(Icons.class.getResource("out.png"));
+}

BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/Quad.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/Sphere.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/attrib.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/busOutput.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/cube.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/cube.psd


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dot.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dotGreen.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dotOrange.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/dotRed.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/earth.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/earth_1.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/expend.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/flip.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/fragment.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/in.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/inputOutput.psd


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/mat.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/matdef.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/node.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/node.psd


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/ouptut.psd


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/out.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/output.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/pirates.wmv


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/previewIcons.psd


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/reload.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/repeat.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/tech.png


BIN
jme3-materialeditor/src/com/jme3/gde/materialdefinition/icons/vert.png


+ 37 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/MatDefNavigatorPanel.form

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jScrollPane1" alignment="0" pref="400" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jScrollPane1" alignment="0" pref="300" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new BeanTreeView()"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+    </Container>
+  </SubComponents>
+</Form>

+ 182 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/MatDefNavigatorPanel.java

@@ -0,0 +1,182 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator;
+
+import com.jme3.gde.materialdefinition.MatDefDataObject;
+import java.util.Collection;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import org.netbeans.spi.navigator.NavigatorPanel;
+import org.openide.explorer.ExplorerManager;
+import org.openide.explorer.ExplorerUtils;
+import org.openide.explorer.view.BeanTreeView;
+import org.openide.nodes.Node;
+import org.openide.util.Lookup;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+
+/**
+ *
+ * @author Nehon
+ */
[email protected](mimeType = "text/jme-materialdefinition", displayName = "Material Def")
+public class MatDefNavigatorPanel extends JPanel implements NavigatorPanel, ExplorerManager.Provider {
+
+    /**
+     * template for finding data in given context.
+     */
+    private static final Lookup.Template MY_DATA = new Lookup.Template(MatDefDataObject.class);
+
+    /**
+     * current context to work on
+     */
+    private Lookup.Result curContext;
+    private Lookup lookup;
+    /**
+     * listener to context changes
+     */
+    private LookupListener contextL;
+    private final ExplorerManager mgr = new ExplorerManager();
+
+    /**
+     * Creates new form MatDefNavigatorPanel
+     */
+    public MatDefNavigatorPanel() {
+        initComponents();
+        lookup = ExplorerUtils.createLookup(mgr, getActionMap());
+
+    }
+
+    public String getDisplayHint() {
+        return "Material definition outline view";
+    }
+
+    @Override
+    public String getDisplayName() {
+        return "Bla";
+    }
+
+    public JComponent getComponent() {
+        return this;
+    }
+
+    public void panelActivated(Lookup context) {
+        // lookup context and listen to result to get notified about context changes
+        curContext = context.lookup(MY_DATA);
+        //lookup = context;
+        curContext.addLookupListener(getContextListener());
+        // get actual data and recompute content
+        Collection data = curContext.allInstances();
+        setNewContent(data);
+
+        //  ExplorerUtils.activateActions(mgr, true);
+
+    }
+
+    public void panelDeactivated() {
+        Collection data = curContext.allInstances();
+        if (!data.isEmpty()) {
+            MatDefDataObject obj = (MatDefDataObject) data.iterator().next();
+            obj.getLookupContents().remove(this);
+        }
+        curContext.removeLookupListener(getContextListener());
+        curContext = null;
+        mgr.setRootContext(Node.EMPTY);
+        //ExplorerUtils.activateActions(mgr, false);
+    }
+
+    public Lookup getLookup() {
+        // go with default activated Node strategy
+        return lookup;
+    }
+
+    /**
+     * *********** non - public part ***********
+     */
+    private void setNewContent(Collection newData) {
+        if (!newData.isEmpty()) {
+            MatDefDataObject data = (MatDefDataObject) newData.iterator().next();
+            data.getLookupContents().add(this);
+            if (data.isLoaded()) {
+                updateData(data);
+            } else {
+                mgr.setRootContext(Node.EMPTY);
+            }            
+        }        
+    }
+
+    /**
+     * Accessor for listener to context
+     */
+    private LookupListener getContextListener() {
+        if (contextL == null) {
+            contextL = new ContextListener();
+        }
+        return contextL;
+    }
+
+    public ExplorerManager getExplorerManager() {
+        return mgr;
+    }
+
+    public void updateData(MatDefDataObject data) {
+        if (data != null) {
+            data.getEditableFile().buildOverview(mgr);
+        } else {
+            mgr.setRootContext(Node.EMPTY);
+        }
+    }
+
+    /**
+     * Listens to changes of context and triggers proper action
+     */
+    private class ContextListener implements LookupListener {
+
+        public void resultChanged(LookupEvent ev) {
+            Collection data = ((Lookup.Result) ev.getSource()).allInstances();
+            setNewContent(data);
+        }
+    } // end of ContextListener
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jScrollPane1 = new BeanTreeView();
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
+        );
+    }// </editor-fold>//GEN-END:initComponents
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JScrollPane jScrollPane1;
+    // End of variables declaration//GEN-END:variables
+
+    /**
+     * Gets default instance. Do not use directly: reserved for *.settings files
+     * only, i.e. deserialization routines; otherwise you could get a
+     * non-deserialized instance. To obtain the singleton instance, use
+     * {@link #findInstance}.
+     */
+    public static synchronized MatDefNavigatorPanel getDefault() {
+        if (instance == null) {
+            instance = new MatDefNavigatorPanel();
+        }
+        return instance;
+    }
+    private static MatDefNavigatorPanel instance;
+}

+ 66 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/AbstractMatDefNode.java

@@ -0,0 +1,66 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node;
+
+import com.jme3.gde.materialdefinition.fileStructure.MatDefBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.gde.materialdefinition.navigator.node.properties.MatParamProperty;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import javax.swing.Action;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.Children;
+import org.openide.nodes.Sheet;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author Nehon
+ */
+public class AbstractMatDefNode extends AbstractNode {
+
+    protected Lookup lookup;
+
+    public AbstractMatDefNode(Children children, Lookup lookup) {
+        super(children, lookup);
+        this.lookup = lookup;
+        MatDefBlock block = lookup.lookup(MatDefBlock.class);
+        block.addPropertyChangeListener(new PropertyChangeListener() {
+            public void propertyChange(PropertyChangeEvent evt) {
+                if (evt.getPropertyName().equals(MatDefBlock.ADD_MAT_PARAM) || evt.getPropertyName().equals(MatDefBlock.REMOVE_MAT_PARAM)) {
+                    setSheet(createSheet());
+                    firePropertySetsChange(null, null);
+                }
+            }
+        });
+
+    }
+
+    
+    @Override
+    public Action[] getActions(boolean popup) {
+        return new Action[]{};
+    }
+      
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        MatDefBlock def = lookup.lookup(MatDefBlock.class);
+        List<MatParamBlock> params = def.getMatParams();
+
+        Sheet.Set set = new Sheet.Set();
+        set.setName("MaterialParameters");
+        set.setDisplayName("Material Parameters");
+        for (MatParamBlock matParamBlock : params) {
+            set.put(MatParamProperty.makeProperty(matParamBlock, lookup));
+        }
+
+        sheet.put(set);
+
+        return sheet;
+
+    }
+}

+ 112 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/MappingNode.java

@@ -0,0 +1,112 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node;
+
+import com.jme3.gde.materialdefinition.MatDefDataObject;
+import com.jme3.gde.materialdefinition.editor.Selectable;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MappingBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.gde.materialdefinition.navigator.node.properties.DefaultProperty;
+import com.jme3.gde.materialdefinition.utils.MaterialUtils;
+import java.awt.Image;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import org.openide.nodes.Children;
+import org.openide.nodes.Sheet;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MappingNode extends AbstractMatDefNode implements Selectable, PropertyChangeListener {
+
+    MappingBlock mapping; 
+    String key;
+
+    public MappingNode(final MappingBlock mapping) {
+        this(null, mapping);
+    }
+
+    public MappingNode(Lookup lookup, final MappingBlock mapping) {
+        super(Children.LEAF, lookup);
+        this.mapping = mapping;
+        setName(makeName(mapping));      
+        key = makeKey();
+    }
+
+    private String makeName(MappingBlock mapping) {
+//        ShaderNodeVariable left = mapping.getLeftVar();
+//        ShaderNodeVariable right = mapping.getRightVariable();
+//        String rightName = right.getName().replaceAll("g_", "").replaceAll("m_", "");
+//        String leftName = left.getName().replaceAll("g_", "").replaceAll("m_", "");
+//        String leftSwizzle = mapping.getLeftSwizzling().length() > 0 ? "." + mapping.getLeftSwizzling() : "";
+//        String rightSwizzle = mapping.getRightSwizzling().length() > 0 ? "." + mapping.getRightSwizzling() : "";
+//        if (isInput()) {
+//            return leftName + leftSwizzle + " = " + right.getNameSpace() + "." + rightName + rightSwizzle;
+//        }
+        return mapping.toString();//right.getNameSpace() + "." + leftName + leftSwizzle + " = " + rightName + rightSwizzle;
+    }
+
+    private boolean isInput() {
+        return (mapping instanceof InputMappingBlock);
+    }
+
+    @Override
+    public Image getIcon(int type) {
+        if (isInput()) {
+            return Icons.in.getImage();
+        }
+        return Icons.out.getImage();
+    }
+
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        Sheet.Set set = new Sheet.Set();
+        set.setName(mapping.getLeftVar() + " <- " + mapping.getRightVar());
+        set.setDisplayName(mapping.getLeftVar() + " <- " + mapping.getRightVar());
+
+
+        try {
+            set.put(new DefaultProperty<String>(mapping, String.class, "Condition", "getCondition", "setCondition"));
+
+            set.put(new DefaultProperty<String>(mapping, String.class, "Left name space", "getLeftNameSpace", "setLeftNameSpace", true));
+            set.put(new DefaultProperty<String>(mapping, String.class, "Left variable", "getLeftVar", "setLeftVar", true));
+            set.put(new DefaultProperty<String>(mapping, String.class, "Left swizzle", "getLeftVarSwizzle", "setLeftVarSwizzle"));
+
+            set.put(new DefaultProperty<String>(mapping, String.class, "Right name space", "getRightNameSpace", "setRightNameSpace", true));
+            set.put(new DefaultProperty<String>(mapping, String.class, "Right variable", "getRightVar", "setRightVar", true));
+            set.put(new DefaultProperty<String>(mapping, String.class, "Right swizzle", "getRightVarSwizzle", "setRightVarSwizzle"));
+
+
+        } catch (NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+
+        mapping.addPropertyChangeListener(WeakListeners.propertyChange(this, mapping));
+
+
+        sheet.put(set);
+
+        return sheet;
+    }
+
+    private String makeKey() {  
+        return MaterialUtils.makeKey(mapping, lookup.lookup(MatDefDataObject.class).getEditableFile().getCurrentTechnique().getName());
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        setName(makeName(mapping));        
+        key = makeKey();
+    }
+}

+ 103 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/MatDefNode.java

@@ -0,0 +1,103 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node;
+
+import com.jme3.gde.materialdefinition.editor.Selectable;
+import com.jme3.gde.materialdefinition.fileStructure.MatDefBlock;
+import com.jme3.gde.materialdefinition.fileStructure.TechniqueBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.gde.materialdefinition.navigator.node.properties.DefaultProperty;
+import java.awt.Image;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import javax.swing.Action;
+import org.openide.actions.RenameAction;
+import org.openide.nodes.ChildFactory;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.nodes.Sheet;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.WeakListeners;
+import org.openide.util.actions.SystemAction;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MatDefNode extends AbstractMatDefNode implements Selectable, PropertyChangeListener {
+  
+
+    public MatDefNode(final Lookup lookup) {
+        super(Children.create(new ChildFactory<TechniqueBlock>() {
+            @Override
+            protected boolean createKeys(List<TechniqueBlock> list) {
+                list.addAll(lookup.lookup(MatDefBlock.class).getTechniques());
+                return true;
+            }
+
+            @Override
+            protected Node createNodeForKey(TechniqueBlock key) {
+                return new TechniqueNode(lookup, key);
+            }
+        }, true),lookup);
+
+        setName(lookup.lookup(MatDefBlock.class).getName());
+
+    }
+
+  
+
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        MatDefBlock def = lookup.lookup(MatDefBlock.class);
+
+        Sheet.Set set = new Sheet.Set();
+        set.setName(def.getName() + "MaterialDefinition");
+        set.setDisplayName(def.getName() + " Material Definition");
+        set.setShortDescription(def.getName() + " Material Definition");
+        try {
+            DefaultProperty<String> prop = new DefaultProperty<String>(def, String.class, "Name", "getName", "setName");
+            set.put(prop);
+
+        } catch (NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+
+        def.addPropertyChangeListener(WeakListeners.propertyChange(this, def));
+ 
+//        for (MatParam matParam : def.getMaterialParams()) {
+//            set.put(MatParamProperty.makeProperty(matParam, lookup));
+//        }
+        sheet.put(set);
+
+        return sheet;
+
+    }
+
+    @Override
+    public Image getIcon(int type) {
+        return Icons.matDef.getImage();
+    }
+
+    @Override
+    public Image getOpenedIcon(int type) {
+        return Icons.matDef.getImage();
+    }
+
+    public String getKey() {
+        return getName();
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if(evt.getPropertyName().equals("name")){
+            setName((String)evt.getNewValue());
+            setDisplayName((String)evt.getNewValue());
+        }
+
+    }
+}

+ 177 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/ShaderNodeNode.java

@@ -0,0 +1,177 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.materialdefinition.MatDefDataObject;
+import com.jme3.gde.materialdefinition.editor.Selectable;
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MappingBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.gde.materialdefinition.navigator.node.properties.DefaultProperty;
+import com.jme3.gde.materialdefinition.utils.MaterialUtils;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderNodeDefinition;
+import java.awt.Image;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.nodes.Sheet;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ShaderNodeNode extends AbstractMatDefNode implements Selectable, PropertyChangeListener {
+
+    ShaderNodeBlock shaderNode;
+    ShaderNodeDefinition def;    
+    String key = "";
+
+
+    public ShaderNodeNode(final Lookup lookup, final ShaderNodeBlock shaderNode) {
+//        super(Children.create(new ChildFactory<MappingBlock>() {
+//            @Override
+//            protected boolean createKeys(List<MappingBlock> list) {
+//                list.addAll(shaderNode.getInputs());
+//                List<OutputMappingBlock> out = shaderNode.getOutputs();
+//                if (out != null) {
+//                    list.addAll(shaderNode.getOutputs());
+//                }
+//
+//                return true;
+//            }
+//
+//            @Override
+//            protected Node createNodeForKey(MappingBlock key) {
+//                return new MappingNode(lookup, key);
+//            }
+//        }, true), lookup);
+        super(new MappingNodeChildren(lookup, shaderNode), lookup);        
+        this.shaderNode = shaderNode;
+        setName(shaderNode.getName());
+        key = makeKey();
+        ProjectAssetManager manager = lookup.lookup(ProjectAssetManager.class);
+        def = MaterialUtils.loadShaderNodeDefinition(shaderNode, manager);
+
+    }
+
+    @Override
+    public Image getIcon(int type) {
+        return getImageIcon();
+    }
+
+    @Override
+    protected Sheet createSheet() {
+
+        Sheet sheet = super.createSheet();
+        Sheet.Set set = new Sheet.Set();
+        set.setName(shaderNode.getName() + "ShaderNode");
+        set.setDisplayName(shaderNode.getName() + " ShaderNode");
+        set.setShortDescription(def.getDocumentation());
+        try {
+            set.put(new DefaultProperty<String>(shaderNode, String.class, "Name", "getName", "setName"));
+            set.put(new DefaultProperty<String>(shaderNode, String.class, "Condition", "getCondition", "setCondition"));
+
+        } catch (NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+
+        shaderNode.addPropertyChangeListener(WeakListeners.propertyChange(this, shaderNode));
+
+
+        sheet.put(set);
+
+        return sheet;
+
+
+    }
+
+    @Override
+    public String getShortDescription() {
+        return def.getDocumentation();
+    }
+
+    @Override
+    public Image getOpenedIcon(int type) {
+        return getImageIcon();
+    }
+
+    private Image getImageIcon() {
+        if (def.getType() == Shader.ShaderType.Vertex) {
+            return Icons.vert.getImage();
+        } else {
+            return Icons.frag.getImage();
+        }
+    }
+
+    private String makeKey() {
+        String defName = lookup.lookup(MatDefDataObject.class).getEditableFile().getCurrentTechnique().getName();
+        return defName + "/" + getName();
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public ShaderNodeDefinition getShaderNodeDefinition() {
+        return def;
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getPropertyName().equals("name")) {
+            setName((String) evt.getNewValue());
+            setDisplayName((String) evt.getNewValue());
+            key = makeKey();
+        } else if (evt.getPropertyName().equals(ShaderNodeBlock.ADD_MAPPING)
+                || evt.getPropertyName().equals(ShaderNodeBlock.REMOVE_MAPPING)) {
+            ((MappingNodeChildren) getChildren()).reload();
+        }
+    }
+
+    public static class MappingNodeChildren extends Children.Keys<MappingBlock> {
+
+        Lookup lookup;
+        ShaderNodeBlock node;
+
+        public MappingNodeChildren(Lookup lookup, ShaderNodeBlock node) {
+            this.lookup = lookup;
+            this.node = node;
+        }
+
+        @Override
+        protected void addNotify() {
+            super.addNotify();
+            setKeys(createKeys());
+        }
+
+        public void reload() {
+            setKeys(createKeys());
+        }
+
+        public List<MappingBlock> createKeys() {
+            List<MappingBlock> l = new ArrayList<MappingBlock>();
+            if (node.getInputs() != null) {
+                l.addAll(node.getInputs());
+            }
+            if (node.getOutputs() != null) {
+                l.addAll(node.getOutputs());
+            }
+
+            return l;
+        }
+
+        @Override
+        protected Node[] createNodes(MappingBlock key) {
+            return new Node[]{new MappingNode(lookup, key)};
+        }
+    }
+}

+ 118 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/TechniqueNode.java

@@ -0,0 +1,118 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node;
+
+import com.jme3.gde.materialdefinition.editor.Selectable;
+import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
+import com.jme3.gde.materialdefinition.fileStructure.TechniqueBlock;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.gde.materialdefinition.navigator.node.properties.DefaultProperty;
+import java.awt.Image;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.nodes.Sheet;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author Nehon
+ */
+public class TechniqueNode extends AbstractMatDefNode implements Selectable, PropertyChangeListener {
+
+    TechniqueBlock def;
+
+    public TechniqueNode(final Lookup lookup, final TechniqueBlock def) {
+        super(new ShaderNodeChildren(lookup, def), lookup);
+        this.def = def;
+        def.addPropertyChangeListener(WeakListeners.propertyChange(this, def));
+        setName(def.getName());
+    }
+
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        Sheet.Set set = Sheet.createPropertiesSet();
+        set.setName("Technique");
+        try {
+            DefaultProperty<String> prop = new DefaultProperty<String>(def, String.class, "Name", "getName", "setName");
+            set.put(prop);
+        } catch (NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+
+        sheet.put(set);
+
+
+
+        return sheet;
+
+    }
+
+    @Override
+    public Image getIcon(int type) {
+        return Icons.tech.getImage();
+    }
+
+    @Override
+    public Image getOpenedIcon(int type) {
+        return Icons.tech.getImage();
+    }
+
+    public String getKey() {
+        return getName();
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (evt.getPropertyName().equals("name")) {
+            setName((String) evt.getNewValue());
+            setDisplayName((String) evt.getNewValue());
+        }
+        if (evt.getPropertyName().equals("reorder")
+                || evt.getPropertyName().equals(TechniqueBlock.ADD_SHADER_NODE)
+                || evt.getPropertyName().equals(TechniqueBlock.REMOVE_SHADER_NODE)) {
+            ((ShaderNodeChildren) getChildren()).reload();
+        }
+    }
+
+    public static class ShaderNodeChildren extends Children.Keys<ShaderNodeBlock> {
+
+        Lookup lookup;
+        TechniqueBlock def;
+
+        public ShaderNodeChildren(Lookup lookup, TechniqueBlock def) {
+            this.lookup = lookup;
+            this.def = def;
+        }
+
+        @Override
+        protected void addNotify() {
+            super.addNotify();
+            setKeys(createKeys());
+        }
+
+        public void reload() {
+            setKeys(createKeys());
+        }
+
+        public List<ShaderNodeBlock> createKeys() {
+            List<ShaderNodeBlock> l = new ArrayList<ShaderNodeBlock>();
+            if (def.getVertexShader() == null) {
+                l.addAll(def.getShaderNodes());
+            }
+            return l;
+        }
+
+        @Override
+        protected Node[] createNodes(ShaderNodeBlock key) {
+            return new Node[]{new ShaderNodeNode(lookup, key)};
+        }
+    }
+}

+ 32 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/DefaultProperty.java

@@ -0,0 +1,32 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node.properties;
+
+import org.openide.nodes.PropertySupport;
+
+/**
+ *
+ * @author Nehon
+ */
+public class DefaultProperty<T> extends PropertySupport.Reflection<T> {
+
+    private boolean readOnly = false;
+
+    public DefaultProperty(Object instance, Class<T> valueType, String displayName, String getter, String setter) throws NoSuchMethodException {
+        super(instance, valueType, getter, setter);
+        setName(displayName);
+        setDisplayName(displayName);
+    }
+
+    public DefaultProperty(Object instance, Class<T> valueType, String displayName, String getter, String setter, boolean readOnly) throws NoSuchMethodException {
+        this(instance, valueType, displayName, getter, setter);
+        this.readOnly = readOnly;
+    }
+
+    @Override
+    public boolean canWrite() {
+        return !readOnly;
+    }
+}

+ 208 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/MatParamProperty.java

@@ -0,0 +1,208 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node.properties;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.properties.ColorRGBAPropertyEditor;
+import com.jme3.gde.core.properties.Matrix3fPropertyEditor;
+import com.jme3.gde.core.properties.QuaternionPropertyEditor;
+import com.jme3.gde.core.properties.TexturePropertyEditor;
+import com.jme3.gde.core.properties.Vector2fPropertyEditor;
+import com.jme3.gde.core.properties.Vector3fPropertyEditor;
+import com.jme3.gde.materialdefinition.MatDefDataObject;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.MatParamBlock;
+import com.jme3.material.MatParam;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix3f;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyEditor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.openide.nodes.Node;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author Nehon
+ */
+public class MatParamProperty<T> extends Node.Property<T> {
+
+    private Lookup lookup;
+    private String type;
+    private Class<T> valueType;   
+
+    public MatParamProperty(String name, String type, Class<T> valueType, Lookup lookup) {
+        super(valueType);
+        this.valueType = valueType;
+        setName(name);
+        this.type = type;
+        this.lookup = lookup;
+        setDisplayName(name);
+    }
+
+    @Override
+    public boolean canRead() {
+        return true;
+    }
+    
+    
+
+    @Override
+    public T getValue() throws IllegalAccessException, InvocationTargetException {
+        MatParam param = lookup.lookup(Material.class).getParam(getName());
+        if (param != null) {
+            return (T) param.getValue();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean canWrite() {
+        return true;
+    }
+
+    @Override
+    public void setValue(T val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        Object v = val;
+        if (valueType == Vector3f.class) {
+            float[] f = getFloatArrayValue((String) val, 3);
+            v = new Vector3f(f[0], f[1], f[2]);
+        } else if (valueType == Quaternion.class) {
+            float[] f = getFloatArrayValue((String) val, 4);
+            v = new Quaternion(f[0], f[1], f[2], f[3]);
+        } else if (valueType == Vector4f.class) {
+            float[] f = getFloatArrayValue((String) val, 4);
+            v = new Vector4f(f[0], f[1], f[2], f[3]);
+        } else if (valueType == Vector2f.class) {
+            float[] f = getFloatArrayValue((String) val, 2);
+            v = new Vector2f(f[0], f[1]);
+        }
+        VarType vType = MatParamProperty.getVarType(type);
+        
+        Material m = lookup.lookup(Material.class);
+        m.setParam(getName(), vType, v);
+        MatDefDataObject obj = lookup.lookup(MatDefDataObject.class);
+        obj.getLookupContents().remove(m);
+        obj.getLookupContents().add(m);
+        
+    }
+    
+    
+
+    public float[] getFloatArrayValue(String value, int capacity) {
+        float[] ret = new float[capacity];
+        String[] vals = extractValues(value);
+        try {
+            for (int i = 0; i < vals.length; i++) {
+                ret[i] = Float.parseFloat(vals[i]);
+            }
+        } catch (NumberFormatException e) {
+            Logger.getLogger(VectorTextField.class.getName()).log(Level.WARNING, "Invalid format");
+        }
+        return ret;
+    }
+
+    private String[] extractValues(String value) {
+        String text = value.replaceAll("[\\[\\]]", "");
+        String[] values = text.split(",");
+        return values;
+    }
+
+    public String getStringFromFloatArray(float[] vals) {
+        String t = "[";
+        for (float f : vals) {
+            t += f + ",";
+        }
+        t = t.substring(0, t.length() - 1) + "]";
+        return t;
+    }
+
+    @Override
+    public PropertyEditor getPropertyEditor() {
+        if (valueType == Vector3f.class) {
+            return new Vector3fPropertyEditor();
+        } else if (valueType == Quaternion.class) {
+            return new QuaternionPropertyEditor();
+        } else if (valueType == Matrix3f.class) {
+            return new Matrix3fPropertyEditor();
+        } else if (valueType == ColorRGBA.class) {
+            return new ColorRGBAPropertyEditor();
+        } else if (valueType == Vector2f.class) {
+            return new Vector2fPropertyEditor();
+        } else if (valueType == Texture.class) {
+            return new TexturePropertyEditor(lookup.lookup(ProjectAssetManager.class));
+        }
+
+
+        return super.getPropertyEditor();
+    }
+
+    public static MatParamProperty<?> makeProperty(MatParamBlock param, Lookup lookup) {
+
+        VarType vType = MatParamProperty.getVarType(param.getType());
+        switch (vType) {
+            case Boolean:
+                return new MatParamProperty<Boolean>(param.getName(), param.getType(), Boolean.class, lookup);
+            case Float:
+                return new MatParamProperty<Float>(param.getName(), param.getType(), Float.class, lookup);
+            case FloatArray:
+                return new MatParamProperty<Object>(param.getName(), param.getType(), Object.class, lookup);
+            case Int:
+                return new MatParamProperty<Integer>(param.getName(), param.getType(), Integer.class, lookup);
+            case Matrix3:
+                return new MatParamProperty<Matrix3f>(param.getName(), param.getType(), Matrix3f.class, lookup);
+            case Matrix3Array:
+                return new MatParamProperty<Object>(param.getName(), param.getType(), Object.class, lookup);
+            case Matrix4:
+                return new MatParamProperty<Matrix4f>(param.getName(), param.getType(), Matrix4f.class, lookup);
+            case Matrix4Array:
+                return new MatParamProperty<Object>(param.getName(), param.getType(), Object.class, lookup);
+            case Texture2D:
+            case Texture3D:
+            case TextureArray:
+            case TextureBuffer:
+            case TextureCubeMap:
+                return new MatParamProperty<Texture>(param.getName(), param.getType(), Texture.class, lookup);
+            case Vector2:
+                return new MatParamProperty<Vector2f>(param.getName(), param.getType(), Vector2f.class, lookup);
+            case Vector2Array:
+                return new MatParamProperty<Object>(param.getName(), param.getType(), Object.class, lookup);
+            case Vector3:
+                return new MatParamProperty<Vector3f>(param.getName(), param.getType(), Vector3f.class, lookup);
+            case Vector3Array:
+                return new MatParamProperty<Object>(param.getName(), param.getType(), Object.class, lookup);
+            case Vector4:
+                if (param.getType().equals("Color")) {
+                    return new MatParamProperty<ColorRGBA>(param.getName(), param.getType(), ColorRGBA.class, lookup);
+                }
+                return new MatParamProperty<Vector4f>(param.getName(), param.getType(), Vector4f.class, lookup);
+            case Vector4Array:
+                return new MatParamProperty<Object>(param.getName(), param.getType(), Object.class, lookup);
+            default:
+                return null;
+        }
+    }
+
+    private static VarType getVarType(String type) {
+        VarType vType = null;
+        if (type.equals("Color")) {
+            vType = VarType.Vector4;
+        } else {
+            vType = VarType.valueOf(type);
+        }
+        return vType;
+    }
+
+    
+}

+ 94 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/VectorInplaceEditor.java

@@ -0,0 +1,94 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node.properties;
+
+import com.jme3.math.Vector2f;
+import java.awt.Component;
+import java.awt.event.ActionListener;
+import java.beans.PropertyEditor;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
+import org.openide.explorer.propertysheet.InplaceEditor;
+import org.openide.explorer.propertysheet.PropertyEnv;
+import org.openide.explorer.propertysheet.PropertyModel;
+
+/**
+ *
+ * @author Nehon
+ */
+public class VectorInplaceEditor implements InplaceEditor {
+
+    private VectorTextField field;
+    private PropertyEditor editor = null;
+    private PropertyModel model;
+    private PropertyEnv env;
+
+    public VectorInplaceEditor(int capacity) {
+        field = new VectorTextField(capacity);
+    }
+
+    public void connect(PropertyEditor pe, PropertyEnv env) {
+        editor = pe;
+        this.env = env;
+        reset();
+    }
+
+    public JComponent getComponent() {
+        return field;
+    }
+
+    public void clear() {
+        editor = null;
+        model = null;
+    }
+
+    public Object getValue() {
+        return field.getText();
+    }
+
+    public void setValue(Object o) {
+        field.setText((String) o);
+    }
+
+    public boolean supportsTextEntry() {
+        return true;
+    }
+
+    public void reset() {
+        if (editor.getValue() != null) {
+            field.setText((String) editor.getValue());
+        }
+    }
+
+    public void addActionListener(ActionListener al) {
+        field.addActionListener(al);
+    }
+
+    public void removeActionListener(ActionListener al) {
+        field.removeActionListener(al);
+    }
+
+    public KeyStroke[] getKeyStrokes() {
+        return new KeyStroke[0];
+    }
+
+    public PropertyEditor getPropertyEditor() {
+        return editor;
+    }
+
+    public PropertyModel getPropertyModel() {
+        return model;
+    }
+
+    public void setPropertyModel(PropertyModel pm) {
+        model = pm;
+    }
+
+    public boolean isKnownComponent(Component cmpnt) {
+        return cmpnt == field || field.isAncestorOf(cmpnt);
+    }
+}

+ 47 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/VectorPropertyEditor.java

@@ -0,0 +1,47 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node.properties;
+
+import java.beans.PropertyEditorSupport;
+import org.openide.explorer.propertysheet.ExPropertyEditor;
+import org.openide.explorer.propertysheet.InplaceEditor;
+import org.openide.explorer.propertysheet.PropertyEnv;
+
+/**
+ *
+ * @author Nehon
+ */
+public class VectorPropertyEditor extends PropertyEditorSupport implements ExPropertyEditor, InplaceEditor.Factory {
+
+    private PropertyEnv env;
+    private VectorInplaceEditor editor;
+
+    public VectorPropertyEditor(int capacity) {
+        editor = new VectorInplaceEditor(capacity);
+    }
+
+    public void attachEnv(PropertyEnv env) {
+        this.env = env;
+        env.registerInplaceEditorFactory(this);
+    }
+
+    public InplaceEditor getInplaceEditor() {
+        return editor;
+    }
+
+    @Override
+    public String getAsText() {
+        return (String) editor.getValue();
+    }
+
+    
+    
+    @Override
+    public void setAsText(String text) throws IllegalArgumentException {
+        editor.setValue(text);
+    }
+
+
+}

+ 170 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/properties/VectorTextField.java

@@ -0,0 +1,170 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.navigator.node.properties;
+
+import java.awt.KeyboardFocusManager;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JTextField;
+
+/**
+ *
+ * @author Nehon
+ */
+public class VectorTextField extends JTextField implements KeyListener, MouseListener, FocusListener {
+
+    private int index = 0;
+    private int capacity = 4;
+    private boolean pressed = false;
+    private String backup;
+
+    public VectorTextField(int capacity) {
+        setFocusTraversalKeysEnabled(false);
+        addKeyListener(this);
+        addFocusListener(this);
+        addMouseListener(this);
+        setFocusTraversalKeysEnabled(false);
+        this.capacity = capacity;
+        backup = "[";
+        for (int i = 0; i < capacity; i++) {
+            backup += "0";
+            if (i != capacity - 1) {
+                backup += ",";
+            }
+        }
+        backup += "]";
+    }
+
+    @Override
+    public void keyTyped(KeyEvent e) {
+    }
+
+    @Override
+    public void keyPressed(KeyEvent e) {
+        //
+        if (e.getKeyCode() == KeyEvent.VK_TAB) {
+            if (checkValidity()) {
+                backup = getText();
+                if (e.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK) {
+                    index = index - 1;
+
+                } else {
+                    index = index + 1;
+                }
+                if (index == capacity) {
+                    KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
+                    return;
+                }
+                if (index == -1) {
+                    KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent();
+                    return;
+                }
+
+            } else {
+                setText(backup);
+                Logger.getLogger(VectorTextField.class.getName()).log(Level.WARNING, "Invalid format");
+            }
+            findSelection(0);
+        }
+    }
+
+    private boolean checkValidity() {
+        String[] values = extractValues();
+        return values.length == capacity;
+
+    }
+
+    @Override
+    public void keyReleased(KeyEvent e) {
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        init();
+        pressed = true;
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        init();
+        index = findSelection(getCaretPosition());
+        pressed = false;
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+    }
+
+    @Override
+    public void focusGained(FocusEvent e) {
+        if (!pressed) {
+            init();
+            findSelection(0);
+        }
+    }
+
+    @Override
+    public void focusLost(FocusEvent e) {
+    }
+
+    private void init() {
+        index = 0;
+        if (getText().trim().equals("")) {
+            setText(backup);
+            setCaretPosition(1);
+        }
+
+    }
+
+  
+
+    public void clear() {
+        setText("");
+    }
+
+    private int findSelection(int caretPosition) {
+        String[] values = extractValues();
+        int start = 0;// = 1;
+        int end = 0;// = values[0].length() + start;
+        if (caretPosition == getText().length()) {
+            caretPosition--;
+        }
+        int i;
+        if (caretPosition != 0) {
+            for (i = 0; caretPosition > end; i++) {
+                start = end + 1;
+                end = values[i].length() + start;
+            }
+            i--;
+        } else {
+            for (i = 0; i <= index; i++) {
+                start = end + 1;
+                end = values[i].length() + start;
+            }
+        }
+        select(start, end);
+        return i;
+    }
+
+    private String[] extractValues() {
+        String text = getText().replaceAll("[\\[\\]]", "");
+        String[] values = text.split(",");
+        return values;
+    }
+}

+ 3 - 19
jme3-materialeditor/src/com/jme3/gde/materials/JMEMaterialDefinitionDataObject.java → jme3-materialeditor/src/com/jme3/gde/materialdefinition/package-info.java

@@ -29,23 +29,7 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package com.jme3.gde.materials;
+@TemplateRegistration(folder = "Material", content = "MatDef.j3md",displayName="Material Definition Template")
+package com.jme3.gde.materialdefinition;
 
-import java.io.IOException;
-import org.openide.filesystems.FileObject;
-import org.openide.loaders.DataObjectExistsException;
-import org.openide.loaders.MultiDataObject;
-import org.openide.loaders.MultiFileLoader;
-import org.openide.nodes.CookieSet;
-import org.openide.nodes.Node;
-import org.openide.text.DataEditorSupport;
-
-public class JMEMaterialDefinitionDataObject extends MultiDataObject {
-
-    public JMEMaterialDefinitionDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
-        super(pf, loader);
-        CookieSet cookies = getCookieSet();
-        cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies));
-    }
-
-}
+import org.netbeans.api.templates.TemplateRegistration;

+ 4 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/Bundle.properties

@@ -0,0 +1,4 @@
+ShaderVisualToolBar.jLabel1.text=Version : 
+ShaderVisualToolBar.vertButton.toolTipText=Vertex Shader
+ShaderVisualToolBar.fragButton.actionCommand=
+ShaderVisualToolBar.fragButton.toolTipText=Fragment Shader

+ 44 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/MatDefShaderElement.form

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jScrollPane1" alignment="0" pref="300" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JEditorPane" name="jEditorPane1">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+              <Font name="Monospaced" size="13" style="0"/>
+            </Property>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 169 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/MatDefShaderElement.java

@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.gde.materialdefinition.shadervisual;
+
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.materialdefinition.EditableMatDefFile;
+import com.jme3.gde.materialdefinition.MatDefDataObject;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import org.netbeans.core.spi.multiview.CloseOperationState;
+import org.netbeans.core.spi.multiview.MultiViewElement;
+import org.netbeans.core.spi.multiview.MultiViewElementCallback;
+import org.openide.awt.UndoRedo;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle.Messages;
+import org.openide.windows.TopComponent;
+
[email protected](
+    displayName = "#LBL_MatDef_SHADER",
+iconBase = "com/jme3/gde/materialdefinition/icons/matdef.png",
+mimeType = "text/jme-materialdefinition",
+persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED,
+preferredID = "MatDefVisual",
+position = 3000)
+@Messages("LBL_MatDef_SHADER=Shader")
+public final class MatDefShaderElement extends JPanel implements MultiViewElement {
+
+    private MatDefDataObject obj;
+    private ShaderVisualToolBar toolbar = new ShaderVisualToolBar();
+    private transient MultiViewElementCallback callback;    
+
+    public MatDefShaderElement(Lookup lkp) {
+        obj = lkp.lookup(MatDefDataObject.class);
+        assert obj != null;
+        initComponents();
+        toolbar.setParent(this);       
+        refresh();
+    }
+
+    public final void refresh() {
+        jEditorPane1.setText(obj.getEditableFile().getShaderCode(toolbar.getVersion(), toolbar.getType()));
+    }
+
+    @Override
+    public String getName() {
+        return "MatDefVisualElement";
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jScrollPane1 = new javax.swing.JScrollPane();
+        jEditorPane1 = new javax.swing.JEditorPane();
+
+        jEditorPane1.setEditable(false);
+        jEditorPane1.setFont(new java.awt.Font("Monospaced", 0, 13)); // NOI18N
+        jScrollPane1.setViewportView(jEditorPane1);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane1)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
+        );
+    }// </editor-fold>//GEN-END:initComponents
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JEditorPane jEditorPane1;
+    private javax.swing.JScrollPane jScrollPane1;
+    // End of variables declaration//GEN-END:variables
+
+    @Override
+    public JComponent getVisualRepresentation() {
+        return this;
+    }
+
+    @Override
+    public JComponent getToolbarRepresentation() {
+        return toolbar;
+    }
+
+    @Override
+    public Action[] getActions() {
+        return new Action[0];
+    }
+
+    @Override
+    public Lookup getLookup() {
+        return obj.getLookup();
+    }
+
+    @Override
+    public void componentOpened() {
+    }
+
+    @Override
+    public void componentClosed() {
+    }
+
+    @Override
+    public void componentShowing() {     
+    }
+
+    @Override
+    public void componentHidden() {
+    }
+
+    @Override
+    public void componentActivated() {
+        refresh();
+    }
+
+    @Override
+    public void componentDeactivated() {
+    }
+
+    @Override
+    public UndoRedo getUndoRedo() {
+        return UndoRedo.NONE;
+    }
+
+    @Override
+    public void setMultiViewCallback(MultiViewElementCallback callback) {
+        this.callback = callback;
+    }
+
+    @Override
+    public CloseOperationState canCloseElement() {
+        return CloseOperationState.STATE_OK;
+    }
+}

+ 105 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/ShaderVisualToolBar.form

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <NonVisualComponents>
+    <Component class="javax.swing.ButtonGroup" name="btnGroup">
+    </Component>
+  </NonVisualComponents>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <Component id="vertButton" min="-2" pref="25" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="fragButton" min="-2" pref="25" max="-2" attributes="0"/>
+              <EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
+              <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="versionList" min="-2" pref="131" max="-2" attributes="0"/>
+              <EmptySpace pref="154" max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="fragButton" alignment="1" min="-2" pref="25" max="-2" attributes="0"/>
+          <Group type="103" groupAlignment="3" attributes="0">
+              <Component id="jLabel1" alignment="3" max="-2" attributes="0"/>
+              <Component id="versionList" alignment="3" min="-2" max="-2" attributes="0"/>
+              <Component id="vertButton" alignment="3" min="-2" pref="25" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JLabel" name="jLabel1">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/shadervisual/Bundle.properties" key="ShaderVisualToolBar.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="versionList">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="2">
+            <StringItem index="0" value="GLSL100"/>
+            <StringItem index="1" value="GLSL150"/>
+          </StringArray>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="versionListActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JToggleButton" name="vertButton">
+      <Properties>
+        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+          <ComponentRef name="btnGroup"/>
+        </Property>
+        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="Icons.vert" type="code"/>
+        </Property>
+        <Property name="selected" type="boolean" value="true"/>
+        <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/shadervisual/Bundle.properties" key="ShaderVisualToolBar.vertButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+        <Property name="iconTextGap" type="int" value="0"/>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="vertButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JToggleButton" name="fragButton">
+      <Properties>
+        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+          <ComponentRef name="btnGroup"/>
+        </Property>
+        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="Icons.frag" type="code"/>
+        </Property>
+        <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/shadervisual/Bundle.properties" key="ShaderVisualToolBar.fragButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+        <Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materialdefinition/shadervisual/Bundle.properties" key="ShaderVisualToolBar.fragButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fragButtonActionPerformed"/>
+      </Events>
+    </Component>
+  </SubComponents>
+</Form>

+ 128 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/shadervisual/ShaderVisualToolBar.java

@@ -0,0 +1,128 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.shadervisual;
+
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.shader.Shader;
+
+/**
+ *
+ * @author Nehon
+ */
+public class ShaderVisualToolBar extends javax.swing.JPanel {
+
+    private MatDefShaderElement parent;
+
+    /**
+     * Creates new form ShaderVisualToolBar
+     */
+    public ShaderVisualToolBar() {
+        initComponents();
+    }
+
+    public void setParent(MatDefShaderElement parent) {
+        this.parent = parent;
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        btnGroup = new javax.swing.ButtonGroup();
+        jLabel1 = new javax.swing.JLabel();
+        versionList = new javax.swing.JComboBox();
+        vertButton = new javax.swing.JToggleButton();
+        fragButton = new javax.swing.JToggleButton();
+
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(ShaderVisualToolBar.class, "ShaderVisualToolBar.jLabel1.text")); // NOI18N
+
+        versionList.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "GLSL100", "GLSL150" }));
+        versionList.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                versionListActionPerformed(evt);
+            }
+        });
+
+        btnGroup.add(vertButton);
+        vertButton.setIcon(Icons.vert);
+        vertButton.setSelected(true);
+        vertButton.setToolTipText(org.openide.util.NbBundle.getMessage(ShaderVisualToolBar.class, "ShaderVisualToolBar.vertButton.toolTipText")); // NOI18N
+        vertButton.setIconTextGap(0);
+        vertButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                vertButtonActionPerformed(evt);
+            }
+        });
+
+        btnGroup.add(fragButton);
+        fragButton.setIcon(Icons.frag);
+        fragButton.setToolTipText(org.openide.util.NbBundle.getMessage(ShaderVisualToolBar.class, "ShaderVisualToolBar.fragButton.toolTipText")); // NOI18N
+        fragButton.setActionCommand(org.openide.util.NbBundle.getMessage(ShaderVisualToolBar.class, "ShaderVisualToolBar.fragButton.actionCommand")); // NOI18N
+        fragButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                fragButtonActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addComponent(vertButton, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(fragButton, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(10, 10, 10)
+                .addComponent(jLabel1)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(versionList, javax.swing.GroupLayout.PREFERRED_SIZE, 131, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addContainerGap(154, Short.MAX_VALUE))
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(fragButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
+            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                .addComponent(jLabel1)
+                .addComponent(versionList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addComponent(vertButton, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void vertButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_vertButtonActionPerformed
+        parent.refresh();
+    }//GEN-LAST:event_vertButtonActionPerformed
+
+    private void fragButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fragButtonActionPerformed
+        parent.refresh();
+    }//GEN-LAST:event_fragButtonActionPerformed
+
+    private void versionListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_versionListActionPerformed
+        parent.refresh();
+    }//GEN-LAST:event_versionListActionPerformed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.ButtonGroup btnGroup;
+    private javax.swing.JToggleButton fragButton;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JComboBox versionList;
+    private javax.swing.JToggleButton vertButton;
+    // End of variables declaration//GEN-END:variables
+
+    public String getVersion() {
+        return versionList.getSelectedItem().toString();
+    }
+
+    public Shader.ShaderType getType() {
+        if (btnGroup.isSelected(vertButton.getModel())) {
+            return Shader.ShaderType.Vertex;
+        } else {
+            return Shader.ShaderType.Fragment;
+        }
+    }
+}

+ 97 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/utils/DocFormatter.java

@@ -0,0 +1,97 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.materialdefinition.utils;
+
+import com.jme3.gde.materialdefinition.dialog.AddNodeDialog;
+import com.jme3.gde.materialdefinition.icons.Icons;
+import com.jme3.shader.ShaderNodeDefinition;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.StyledDocument;
+
+/**
+ *
+ * @author m327836
+ */
+public class DocFormatter {
+
+    private static DocFormatter instance;
+    private Style regStyle = StyleContext.getDefaultStyleContext().
+            getStyle(StyleContext.DEFAULT_STYLE);
+
+    private DocFormatter() {
+    }
+
+    public static DocFormatter getInstance() {
+        if (instance == null) {
+            instance = new DocFormatter();
+        }
+        return instance;
+    }
+
+    private void makeStyles(StyledDocument doc) {
+        Style s1 = doc.addStyle("regular", regStyle);
+        StyleConstants.setFontFamily(s1, "SansSerif");
+        Style s2 = doc.addStyle("bold", s1);       
+        StyleConstants.setBold(s2, true);
+        Style icon = doc.addStyle("input", s1);
+        StyleConstants.setAlignment(icon, StyleConstants.ALIGN_CENTER);
+        StyleConstants.setSpaceAbove(icon, 8);
+        StyleConstants.setIcon(icon, Icons.in);
+        Style icon2 = doc.addStyle("output", s1);
+        StyleConstants.setAlignment(icon2, StyleConstants.ALIGN_CENTER);
+        StyleConstants.setSpaceAbove(icon2, 8);
+        StyleConstants.setIcon(icon2, Icons.out);
+
+
+    }
+
+    public static void addDoc(ShaderNodeDefinition def, StyledDocument doc) {
+
+        if (doc.getStyle("regular") == null) {
+            getInstance().makeStyles(doc);
+        }
+
+        try {
+            String[] lines = def.getDocumentation().split("\\n");
+            doc.insertString(doc.getLength(), "Shader type : " + def.getType().toString() + "\n", doc.getStyle("regular"));
+
+            for (int i = 0; i < def.getShadersLanguage().size(); i++) {
+                doc.insertString(doc.getLength(), "Shader : " + def.getShadersLanguage().get(i) + " " + def.getShadersPath().get(i) + "\n", doc.getStyle("regular"));
+            }
+//            doc.insertString(doc.getLength(), "\n", doc.getStyle("regular"));
+
+            for (String string : lines) {
+                String l = string.trim() + "\n";
+                if (l.startsWith("@input")) {
+                    l = l.substring(6).trim();
+                    int spaceIdx = l.indexOf(' ');
+                    doc.insertString(doc.getLength(), "\n", doc.getStyle("regular"));
+                    doc.insertString(doc.getLength(), " ", doc.getStyle("input"));
+                    doc.insertString(doc.getLength(), l.substring(0, spaceIdx), doc.getStyle("bold"));
+                    doc.insertString(doc.getLength(), l.substring(spaceIdx), doc.getStyle("regular"));
+
+                } else if (l.startsWith("@output")) {
+                    l = l.substring(7).trim();
+                    int spaceIdx = l.indexOf(' ');
+                    doc.insertString(doc.getLength(), "\n", doc.getStyle("regular"));
+                    doc.insertString(doc.getLength(), " ", doc.getStyle("output"));
+                    doc.insertString(doc.getLength(), l.substring(0, spaceIdx), doc.getStyle("bold"));
+                    doc.insertString(doc.getLength(), l.substring(spaceIdx), doc.getStyle("regular"));
+
+                } else {
+                    doc.insertString(doc.getLength(), l, doc.getStyle("regular"));
+                }
+
+            }
+        } catch (BadLocationException ex) {
+            Logger.getLogger(AddNodeDialog.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно