Browse Source

Previews for matparams (#334)

* Immediate updates
AssetManager static hack to load textures in preview...

* Previews for all matparams(?)
Updatable floats and colors

* Previews for all matparams(?)
Updatable floats and colors
(SDK Core project)

* Rich editors for colors, texture and float nodes. Simple editors for vecs and bools.

* Revert "Merge branch 'master' into previews_for_matparams"

This reverts commit 2d4920e95620534b64e5ab8a0f3cd2329851c93c, reversing
changes made to bed7dea707538130b5a97d3e16dcb0dd601303e8.

* Merge branch 'master' into HEAD

# Conflicts:
#	jme3-core/src/com/jme3/gde/core/editor/nodes/NodePanel.java

* fixing disastreous merge commit

* one more file

* mending

* mending

* sorting for shadernodeblock. inclusive?

* live updates for backpanel

* clean up

* fixes #339 Attribute nodes are expected to be named "Attr"

* fixes #335 properly. better sorting and sorting on new mapping

* Revert "Immediate updates AssetManager static hack to load textures in preview..."

This reverts commit d08c87f08455ef4699576af6d15097003805c8b2.

* Clean up and documentation

* Reverted some unnecessary changes

* fixing line endings

* fixing line endings

* line endings
Rickard Edén 3 years ago
parent
commit
33831b7e37
27 changed files with 1932 additions and 497 deletions
  1. 4 0
      gradlew
  2. 7 0
      jme3-core/src/com/jme3/gde/core/editor/nodes/NodeEditor.java
  3. 47 21
      jme3-core/src/com/jme3/gde/core/editor/nodes/NodePanel.java
  4. 2 2
      jme3-core/src/com/jme3/gde/core/scene/controller/SceneToolController.java
  5. 344 402
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/EditableMatDefFile.java
  6. 117 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatStructChangeListener.java
  7. 15 50
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatDefEditorlElement.java
  8. 32 9
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/ShaderNodePanel.java
  9. 75 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/BasePreview.java
  10. 64 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/BoolPreview.java
  11. 100 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/ColorPreview.java
  12. 27 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/FloatPreview.java
  13. 64 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/PreviewFactory.java
  14. 72 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/TexturePreview.java
  15. 93 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/VecPreview.java
  16. 105 0
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/util/MatDefEditorUtil.java
  17. 19 1
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/MatDefBlock.java
  18. 2 2
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/ShaderNodesBlock.java
  19. 6 2
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/MatParamBlock.java
  20. 0 3
      jme3-materialeditor/src/com/jme3/gde/materialdefinition/navigator/node/MatDefNode.java
  21. 7 4
      jme3-materialeditor/src/com/jme3/gde/materials/MaterialPreviewRenderer.java
  22. 4 0
      jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/Bundle.properties
  23. 1 1
      jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/FloatPanel.java
  24. 90 0
      jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/FloatPanelSmall.form
  25. 137 0
      jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/FloatPanelSmall.java
  26. 186 0
      jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.form
  27. 312 0
      jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.java

+ 4 - 0
gradlew

@@ -274,6 +274,10 @@ public abstract class Diagram extends JPanel implements MouseListener,
         parent.notifyRemoveNode(node);
         parent.notifyRemoveNode(node);
     }
     }
     
     
+    public void updateDefaultValue(String name, String value){
+        parent.notifyDefaultValueUpdated(name, value);
+    }
+    
     public List<Selectable> getSelectedItems() {
     public List<Selectable> getSelectedItems() {
         return selectedItems;
         return selectedItems;
     }
     }

+ 7 - 0
jme3-core/src/com/jme3/gde/core/editor/nodes/NodeEditor.java

@@ -85,4 +85,11 @@ public interface NodeEditor {
      * @param selectable The selected item
      * @param selectable The selected item
      */
      */
     void selectionChanged(Selectable selectable);
     void selectionChanged(Selectable selectable);
+    
+    /**
+     * Called when a default value of a MatParam is updated
+     * @param name name of the MatParam
+     * @param value the new default value
+     */
+    void notifyDefaultValueUpdated(String name, String value);
 }
 }

+ 47 - 21
jme3-core/src/com/jme3/gde/core/editor/nodes/NodePanel.java

@@ -46,7 +46,11 @@ import java.awt.event.MouseEvent;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 import javax.swing.GroupLayout;
 import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.ParallelGroup;
+import javax.swing.GroupLayout.SequentialGroup;
 import javax.swing.Icon;
 import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JPanel;
 import javax.swing.LayoutStyle;
 import javax.swing.LayoutStyle;
@@ -58,16 +62,18 @@ import javax.swing.SwingUtilities;
  * @author Nehon
  * @author Nehon
  */
  */
 public abstract class NodePanel extends DraggablePanel implements Selectable, KeyListener {
 public abstract class NodePanel extends DraggablePanel implements Selectable, KeyListener {
-    protected List<JLabel> inputLabels = new ArrayList<JLabel>();
-    protected List<JLabel> outputLabels = new ArrayList<JLabel>();
-    protected List<ConnectionEndpoint> inputDots = new ArrayList<ConnectionEndpoint>();
-    protected List<ConnectionEndpoint> outputDots = new ArrayList<ConnectionEndpoint>();
+    protected List<JLabel> inputLabels = new ArrayList<>();
+    protected List<JLabel> outputLabels = new ArrayList<>();
+    protected List<ConnectionEndpoint> inputDots = new ArrayList<>();
+    protected List<ConnectionEndpoint> outputDots = new ArrayList<>();
     protected JPanel content;
     protected JPanel content;
     protected JLabel header;
     protected JLabel header;
+    protected List<JComponent> previews = new ArrayList<>();
     protected Color color;
     protected Color color;
-    protected Color backgroundColor = new Color(170, 170, 170, 120);
+    protected Color backgroundColor = new Color(170, 170, 170);
     protected String name;
     protected String name;
     protected NodeToolBar toolBar = null;
     protected NodeToolBar toolBar = null;
+    
 
 
     /**
     /**
      * Creates new form NodePanel
      * Creates new form NodePanel
@@ -86,7 +92,7 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
     public void setToolbar(NodeToolBar toolBar) {
     public void setToolbar(NodeToolBar toolBar) {
         this.toolBar = toolBar;
         this.toolBar = toolBar;
     }
     }
-
+    
     /**
     /**
      * Set this node's name, title and tooltipText
      * Set this node's name, title and tooltipText
      * Note: This name is different from AWTs setName()
      * Note: This name is different from AWTs setName()
@@ -94,9 +100,9 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
      */
      */
     public void setNameAndTitle(String s) {
     public void setNameAndTitle(String s) {
         name = s;
         name = s;
-        setTitle(name);
+        setTitle(s);
     }
     }
-
+    
     public void setTitle(String s) {
     public void setTitle(String s) {
         header.setText(s);
         header.setText(s);
         header.setToolTipText(s);
         header.setToolTipText(s);
@@ -235,7 +241,7 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
         header.addMouseListener(labelMouseMotionListener);
         header.addMouseListener(labelMouseMotionListener);
         header.addMouseMotionListener(labelMouseMotionListener);
         header.addMouseMotionListener(labelMouseMotionListener);
         header.setHorizontalAlignment(SwingConstants.LEFT);
         header.setHorizontalAlignment(SwingConstants.LEFT);
-        header.setFont(new Font("Tahoma", Font.BOLD, 11));
+        header.setFont(new Font("Tahoma", Font.BOLD, 10));
         
         
         if (getLayout() instanceof GroupLayout) {
         if (getLayout() instanceof GroupLayout) {
             header.setText(oldHeaderText);
             header.setText(oldHeaderText);
@@ -248,14 +254,25 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
         content.setLayout(contentLayout);
         content.setLayout(contentLayout);
         int txtLength = 100;
         int txtLength = 100;
         GroupLayout.ParallelGroup grpHoriz = contentLayout.createParallelGroup(GroupLayout.Alignment.LEADING);
         GroupLayout.ParallelGroup grpHoriz = contentLayout.createParallelGroup(GroupLayout.Alignment.LEADING);
-
         for (int i = 0; i < outputDots.size(); i++) {
         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));
+            SequentialGroup group = contentLayout.createSequentialGroup();
+            
+            group.addGap(0, 0, Short.MAX_VALUE);
+            if(outputDots.size() > 1){
+                group.addComponent(outputLabels.get(i), 0, txtLength, GroupLayout.PREFERRED_SIZE);
+                group.addGap(2, 2, 2);
+            }
+            if(i == 0 && !previews.isEmpty()){
+                JComponent preview = previews.get(0);
+                group.addComponent(preview, preview.getWidth(), preview.getWidth(), GroupLayout.PREFERRED_SIZE);
+                group.addGap(2, 2, 2);
+            }
+            group.addComponent(outputDots.get(i), GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE);
+
+            grpHoriz.addGroup(GroupLayout.Alignment.TRAILING, group);
         }
         }
+        
+        
         for (int i = 0; i < inputDots.size(); i++) {
         for (int i = 0; i < inputDots.size(); i++) {
             grpHoriz.addGroup(GroupLayout.Alignment.LEADING, contentLayout.createSequentialGroup()
             grpHoriz.addGroup(GroupLayout.Alignment.LEADING, contentLayout.createSequentialGroup()
                     .addComponent(inputDots.get(i), GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
                     .addComponent(inputDots.get(i), GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
@@ -274,9 +291,18 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
                     .addComponent(inputLabels.get(i))).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
                     .addComponent(inputLabels.get(i))).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
         }
         }
         for (int i = 0; i < outputDots.size(); i++) {
         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);
+            ParallelGroup group = contentLayout.createParallelGroup(GroupLayout.Alignment.LEADING);
+            
+            group.addComponent(outputDots.get(i), GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE);
+            if(outputDots.size() > 1){
+                group.addComponent(outputLabels.get(i));
+            }
+            if(i == 0 && !previews.isEmpty()){
+                JComponent preview = previews.get(0);
+                group.addComponent(preview, preview.getHeight(), preview.getHeight(), GroupLayout.PREFERRED_SIZE);
+            }
+            grp.addGroup(group);
+            grp.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
         }
         }
 
 
         grpVert.addGroup(GroupLayout.Alignment.TRAILING, grp);
         grpVert.addGroup(GroupLayout.Alignment.TRAILING, grp);
@@ -297,9 +323,9 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
                 layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                 layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                 .addGroup(layout.createSequentialGroup()
                 .addGroup(layout.createSequentialGroup()
                         .addComponent(header, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                         .addComponent(header, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
-                        .addGap(10, 10, 10)
+                        .addGap(6, 6, 6)
                         .addComponent(content, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                         .addComponent(content, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
-                .addGap(10, 10, 10));
+                .addGap(12, 12, 12));
     }
     }
     
     
     @Override
     @Override
@@ -333,7 +359,7 @@ public abstract class NodePanel extends DraggablePanel implements Selectable, Ke
         g.setColor(borderColor);
         g.setColor(borderColor);
 
 
         g.drawRoundRect(4, 0, getWidth() - 9, getHeight() - 6, 15, 15);
         g.drawRoundRect(4, 0, getWidth() - 9, getHeight() - 6, 15, 15);
-        g.setColor(new Color(170, 170, 170, 120));
+        g.setColor(backgroundColor);
         g.fillRect(4, 1, 10, 10);
         g.fillRect(4, 1, 10, 10);
         g.setColor(borderColor);
         g.setColor(borderColor);
         g.drawLine(4, 0, 14, 0);
         g.drawLine(4, 0, 14, 0);

+ 2 - 2
jme3-core/src/com/jme3/gde/core/scene/controller/SceneToolController.java

@@ -67,7 +67,7 @@ public class SceneToolController extends AbstractAppState {
     protected AssetManager manager;
     protected AssetManager manager;
     protected Material blueMat;
     protected Material blueMat;
     protected AbstractCameraController camController;
     protected AbstractCameraController camController;
-    
+
     private SceneToolControllerListener toolListener;
     private SceneToolControllerListener toolListener;
     
     
     /**
     /**
@@ -211,7 +211,7 @@ public class SceneToolController extends AbstractAppState {
         }
         }
         if (toolListener != null) {
         if (toolListener != null) {
             toolListener.onSetCursorLocation(location);
             toolListener.onSetCursorLocation(location);
-        }
+    }
     }
     }
 
 
     public void snapCursorToSelection() {
     public void snapCursorToSelection() {

+ 344 - 402
jme3-materialeditor/src/com/jme3/gde/materialdefinition/EditableMatDefFile.java

@@ -1,402 +1,344 @@
-/*
- *  Copyright (c) 2009-2018 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.asset.AssetKey;
-import com.jme3.gde.core.assets.ProjectAssetManager;
-import com.jme3.gde.core.errorreport.ExceptionUtils;
-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.MatParamBlock;
-import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
-import com.jme3.gde.materialdefinition.navigator.node.MatDefNode;
-import com.jme3.material.Material;
-import com.jme3.material.MaterialDef;
-import com.jme3.material.TechniqueDef;
-import com.jme3.material.plugins.J3MLoader;
-import com.jme3.material.plugins.MatParseException;
-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.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-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;
-
-/**
- * This is the MatDef representation in the editor. It will update the file with
- * any changes.
- * 
- * @author Nehon
- */
-public class EditableMatDefFile {
-
-    private FileObject matDefFile;
-    private final MatDefDataObject obj;
-    private Material material;
-    private MatDefBlock matDefStructure;
-    private TechniqueBlock currentTechnique;
-    private MaterialDef materialDef;
-    private ProjectAssetManager assetManager;
-    private ShaderGenerator glsl100;
-    private ShaderGenerator glsl150;    
-    private final static String GLSL100 = "GLSL100";    
-    private Lookup lookup;
-    private boolean loaded = false;
-    private boolean dirty = false;
-
-    public EditableMatDefFile(Lookup lookup) {
-        obj = lookup.lookup(MatDefDataObject.class);
-        load(lookup);
-
-    }
-
-    public final void load(Lookup lookup) {
-        loaded = false;
-        this.matDefFile = obj.getPrimaryFile();
-        this.assetManager = lookup.lookup(ProjectAssetManager.class);
-        this.glsl100 = new Glsl100ShaderGenerator(assetManager);
-        this.glsl150 = new Glsl150ShaderGenerator(assetManager);
-        this.lookup = lookup;
-
-        if (matDefStructure != null) {
-            obj.getLookupContents().remove(matDefStructure);
-            matDefStructure = null;
-        }
-        if (materialDef != null) {
-            obj.getLookupContents().remove(materialDef);
-            materialDef = null;
-        }
-        if (material != null) {
-            obj.getLookupContents().remove(material);
-            matToRemove = material;
-            material = null;
-        }
-        FileLock lock = null;
-        InputStream in = null;
-        boolean matParseError = false;        
-        try {
-            lock = matDefFile.lock();
-            in = obj.getPrimaryFile().getInputStream();
-            List<Statement> sta = BlockLanguageParser.parse(in);
-            matDefStructure = new MatDefBlock(sta.get(0));
-            if (assetManager != null) {
-                AssetKey<MaterialDef> matDefKey = new AssetKey<MaterialDef>(assetManager.getRelativeAssetPath(assetManager.getRelativeAssetPath(matDefFile.getPath())));
-                assetManager.deleteFromCache(matDefKey);
-                materialDef = (MaterialDef) assetManager.loadAsset(assetManager.getRelativeAssetPath(matDefFile.getPath()));
-            }
-        } catch (Exception ex) {
-            Throwable t = ex.getCause();
-
-            while (t != null) {
-                if (t instanceof MatParseException) {
-                    Logger.getLogger(EditableMatDefFile.class.getName()).log(Level.SEVERE, t.getMessage());
-                    matParseError = true;
-                }
-                t = t.getCause();
-            }
-            if (matParseError) {
-                // Show an Exception if it's already in the console?
-                ExceptionUtils.caughtException(ex, "This means the related "
-                        + "j3md file contained a syntax error of some sort. "
-                        + "It has also been logged into the Console.", false);
-            } else {
-                ExceptionUtils.caughtException(ex, "This means that there was "
-                        + "an unexpected exception when parsing the j3md. "
-                        + "If this was due to you opening a j3md from a jar "
-                        + "(org.openide.filesystems.FSException) then don't "
-                        + "report it to us, if it differs, do so!", true);
-            }
-        } finally {
-            if (lock != null) {
-                lock.releaseLock();
-            }
-            if (in != null) {
-                try {
-                    in.close();
-                } catch (IOException ex) {
-                    Exceptions.printStackTrace(ex);
-                }
-            }
-        }
-        if (materialDef != null && !matParseError) {
-            if(currentTechnique == null){
-                currentTechnique = matDefStructure.getTechniques().get(0);
-            }
-            registerListener(matDefStructure);
-
-            obj.getLookupContents().add(matDefStructure);
-            updateLookupWithMaterialData(obj);
-            loaded = true;
-        }
-    }
-
-    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(currentTechnique.getName(), SceneApplication.getApplication().getRenderManager());
-            Shader s;
-            StringBuilder sb = new StringBuilder();
-            TechniqueDef def = material.getActiveTechnique().getDef();
-            sb.append(def.getShaderPrologue());
-            material.getActiveTechnique().getDynamicDefines().generateSource(sb, Arrays.asList(def.getDefineNames()), Arrays.asList(def.getDefineTypes()));
-            
-            if (version.equals(GLSL100)) {
-                glsl100.initialize(material.getActiveTechnique().getDef());
-                s = glsl100.generateShader(sb.toString());
-            } else {
-                glsl150.initialize(material.getActiveTechnique().getDef());
-                s = glsl150.generateShader(sb.toString());
-            }
-            for (Shader.ShaderSource source : s.getSources()) {
-                if (source.getType() == type) {
-                    return source.getSource();
-                }
-            }
-            return "";
-        } catch (Exception e) {
-            Exceptions.printStackTrace(e);
-            return "Error generating shader: " + e.getMessage();
-        }
-    }
-
-    public TechniqueBlock getCurrentTechnique() {
-        return currentTechnique;
-    }
-    
-    public void setCurrentTechnique(TechniqueBlock tech){
-        this.currentTechnique = tech;
-    }
-
-    public MatDefBlock getMatDefStructure() {
-        return matDefStructure;
-    }
-    private final 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) {
-                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;
-        }
-    }
-
-    public boolean isLoaded() {
-        return loaded;
-    }
-
-    public boolean isDirty() {
-        return dirty;
-    }
-
-    public void setDirty(boolean dirty) {
-        this.dirty = dirty;
-    }
-
-    public void setLoaded(boolean loaded) {
-        this.loaded = loaded;
-    }
-
-    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.REMOVE_MAT_PARAM)) {
-                MatParamBlock oldValue = (MatParamBlock) evt.getOldValue();
-
-                for (ShaderNodeBlock shaderNodeBlock : currentTechnique.getShaderNodes()) {
-
-                    if (shaderNodeBlock.getCondition() != null && shaderNodeBlock.getCondition().contains(oldValue.getName())) {
-                        shaderNodeBlock.setCondition(shaderNodeBlock.getCondition().replaceAll(oldValue.getName(), "").trim());
-                    }
-                    List<InputMappingBlock> lin = shaderNodeBlock.getInputs();
-                    if (lin != null) {
-                        for (InputMappingBlock inputMappingBlock : shaderNodeBlock.getInputs()) {
-                            if (inputMappingBlock.getCondition() != null && inputMappingBlock.getCondition().contains(oldValue.getName())) {
-                                inputMappingBlock.setCondition(inputMappingBlock.getCondition().replaceAll(oldValue.getName(), "").trim());
-                            }
-                        }
-                    }
-                    List<OutputMappingBlock> l = shaderNodeBlock.getOutputs();
-                    if (l != null) {
-                        for (OutputMappingBlock outputMappingBlock : l) {
-                            if (outputMappingBlock.getCondition() != null && outputMappingBlock.getCondition().contains(oldValue.getName())) {
-                                outputMappingBlock.setCondition(outputMappingBlock.getCondition().replaceAll(oldValue.getName(), "").trim());
-                            }
-                        }
-                    }
-                }
-            }
-            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 = 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);
-    }
-    
-    public void cleanup(){
-        if (matDefStructure != null) {
-            obj.getLookupContents().remove(matDefStructure);
-            matDefStructure = null;
-        }
-        if (materialDef != null) {
-            obj.getLookupContents().remove(materialDef);
-            materialDef = null;
-        }
-        if (material != null) {
-            obj.getLookupContents().remove(material);
-            matToRemove = material;
-            material = null;
-        }
-        
-        setCurrentTechnique(null);
-        setLoaded(false);
-    }
-    
-}
+/*
+ *  Copyright (c) 2009-2018 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.asset.AssetKey;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.errorreport.ExceptionUtils;
+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.MatParamBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.gde.materialdefinition.navigator.node.MatDefNode;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.TechniqueDef;
+import com.jme3.material.plugins.J3MLoader;
+import com.jme3.material.plugins.MatParseException;
+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.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+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;
+
+/**
+ * This is the MatDef representation in the editor. It will update the file with
+ * any changes.
+ * 
+ * @author Nehon
+ */
+public class EditableMatDefFile {
+
+    private FileObject matDefFile;
+    private final MatDefDataObject obj;
+    private Material material;
+    private MatDefBlock matDefStructure;
+    private TechniqueBlock currentTechnique;
+    private MaterialDef materialDef;
+    private static ProjectAssetManager assetManager;
+    private ShaderGenerator glsl100;
+    private ShaderGenerator glsl150;    
+    private final static String GLSL100 = "GLSL100";    
+    private Lookup lookup;
+    private boolean loaded = false;
+    private boolean dirty = false;
+    private final MatStructChangeListener changeListener = new MatStructChangeListener(this);
+
+    public EditableMatDefFile(Lookup lookup) {
+        obj = lookup.lookup(MatDefDataObject.class);
+        load(lookup);
+
+    }
+
+    public final void load(Lookup lookup) {
+        loaded = false;
+        this.matDefFile = obj.getPrimaryFile();
+        this.assetManager = lookup.lookup(ProjectAssetManager.class);
+        this.glsl100 = new Glsl100ShaderGenerator(assetManager);
+        this.glsl150 = new Glsl150ShaderGenerator(assetManager);
+        this.lookup = lookup;
+
+        if (matDefStructure != null) {
+            obj.getLookupContents().remove(matDefStructure);
+            matDefStructure = null;
+        }
+        if (materialDef != null) {
+            obj.getLookupContents().remove(materialDef);
+            materialDef = null;
+        }
+        if (material != null) {
+            obj.getLookupContents().remove(material);
+            matToRemove = material;
+            material = null;
+        }
+        FileLock lock = null;
+        InputStream in = null;
+        boolean matParseError = false;        
+        try {
+            lock = matDefFile.lock();
+            in = obj.getPrimaryFile().getInputStream();
+            List<Statement> sta = BlockLanguageParser.parse(in);
+            matDefStructure = new MatDefBlock(sta.get(0));
+            if (assetManager != null) {
+                AssetKey<MaterialDef> matDefKey = new AssetKey<MaterialDef>(assetManager.getRelativeAssetPath(assetManager.getRelativeAssetPath(matDefFile.getPath())));
+                assetManager.deleteFromCache(matDefKey);
+                materialDef = (MaterialDef) assetManager.loadAsset(assetManager.getRelativeAssetPath(matDefFile.getPath()));
+            }
+        } catch (Exception ex) {
+            Throwable t = ex.getCause();
+
+            while (t != null) {
+                if (t instanceof MatParseException) {
+                    Logger.getLogger(EditableMatDefFile.class.getName()).log(Level.SEVERE, t.getMessage());
+                    matParseError = true;
+                }
+                t = t.getCause();
+            }
+            if (matParseError) {
+                // Show an Exception if it's already in the console?
+                ExceptionUtils.caughtException(ex, "This means the related "
+                        + "j3md file contained a syntax error of some sort. "
+                        + "It has also been logged into the Console.", false);
+            } else {
+                ExceptionUtils.caughtException(ex, "This means that there was "
+                        + "an unexpected exception when parsing the j3md. "
+                        + "If this was due to you opening a j3md from a jar "
+                        + "(org.openide.filesystems.FSException) then don't "
+                        + "report it to us, if it differs, do so!", true);
+            }
+        } finally {
+            if (lock != null) {
+                lock.releaseLock();
+            }
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        }
+        if (materialDef != null && !matParseError) {
+            if(currentTechnique == null){
+                currentTechnique = matDefStructure.getTechniques().get(0);
+            }
+            registerListener(matDefStructure);
+
+            obj.getLookupContents().add(matDefStructure);
+            updateLookupWithMaterialData(obj);
+            loaded = true;
+        }
+    }
+
+    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(currentTechnique.getName(), SceneApplication.getApplication().getRenderManager());
+            Shader s;
+            StringBuilder sb = new StringBuilder();
+            TechniqueDef def = material.getActiveTechnique().getDef();
+            sb.append(def.getShaderPrologue());
+            material.getActiveTechnique().getDynamicDefines().generateSource(sb, Arrays.asList(def.getDefineNames()), Arrays.asList(def.getDefineTypes()));
+            
+            if (version.equals(GLSL100)) {
+                glsl100.initialize(material.getActiveTechnique().getDef());
+                s = glsl100.generateShader(sb.toString());
+            } else {
+                glsl150.initialize(material.getActiveTechnique().getDef());
+                s = glsl150.generateShader(sb.toString());
+            }
+            for (Shader.ShaderSource source : s.getSources()) {
+                if (source.getType() == type) {
+                    return source.getSource();
+                }
+            }
+            return "";
+        } catch (Exception e) {
+            Exceptions.printStackTrace(e);
+            return "Error generating shader: " + e.getMessage();
+        }
+    }
+
+    public TechniqueBlock getCurrentTechnique() {
+        return currentTechnique;
+    }
+    
+    public void setCurrentTechnique(TechniqueBlock tech){
+        this.currentTechnique = tech;
+    }
+
+    public MatDefBlock getMatDefStructure() {
+        return matDefStructure;
+    }
+    
+    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) {
+                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;
+        }
+    }
+
+    public boolean isLoaded() {
+        return loaded;
+    }
+
+    public boolean isDirty() {
+        return dirty;
+    }
+
+    public void setDirty(boolean dirty) {
+        this.dirty = dirty;
+    }
+
+    public void setLoaded(boolean loaded) {
+        this.loaded = loaded;
+    }
+
+    Material matToRemove;
+
+    protected void applyChange() {
+
+        try {
+            EditorCookie ec = 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);
+    }
+    
+    public void cleanup(){
+        if (matDefStructure != null) {
+            obj.getLookupContents().remove(matDefStructure);
+            matDefStructure = null;
+        }
+        if (materialDef != null) {
+            obj.getLookupContents().remove(materialDef);
+            materialDef = null;
+        }
+        if (material != null) {
+            obj.getLookupContents().remove(material);
+            matToRemove = material;
+            material = null;
+        }
+        
+        setCurrentTechnique(null);
+        setLoaded(false);
+    }
+    
+    public static ProjectAssetManager getAssetManager(){
+        return assetManager;
+    }
+}

+ 117 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/MatStructChangeListener.java

@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2009-2022 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.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.MatParamBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
+import com.jme3.util.blockparser.Statement;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+
+/**
+ * Listens for structural changes to the MatDef (Broken out from EditableMatDefFile.
+ * 
+ * @author rickard
+ */
+class MatStructChangeListener implements PropertyChangeListener {
+    
+    private final EditableMatDefFile matDefFile;
+
+    MatStructChangeListener(final EditableMatDefFile matDefFile) {
+        this.matDefFile = matDefFile;
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        final TechniqueBlock currentTechnique = matDefFile.getCurrentTechnique();
+        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.REMOVE_MAT_PARAM)) {
+            MatParamBlock oldValue = (MatParamBlock) evt.getOldValue();
+            for (ShaderNodeBlock shaderNodeBlock : currentTechnique.getShaderNodes()) {
+                if (shaderNodeBlock.getCondition() != null && shaderNodeBlock.getCondition().contains(oldValue.getName())) {
+                    shaderNodeBlock.setCondition(shaderNodeBlock.getCondition().replaceAll(oldValue.getName(), "").trim());
+                }
+                List<InputMappingBlock> lin = shaderNodeBlock.getInputs();
+                if (lin != null) {
+                    for (InputMappingBlock inputMappingBlock : shaderNodeBlock.getInputs()) {
+                        if (inputMappingBlock.getCondition() != null && inputMappingBlock.getCondition().contains(oldValue.getName())) {
+                            inputMappingBlock.setCondition(inputMappingBlock.getCondition().replaceAll(oldValue.getName(), "").trim());
+                        }
+                    }
+                }
+                List<OutputMappingBlock> l = shaderNodeBlock.getOutputs();
+                if (l != null) {
+                    for (OutputMappingBlock outputMappingBlock : l) {
+                        if (outputMappingBlock.getCondition() != null && outputMappingBlock.getCondition().contains(oldValue.getName())) {
+                            outputMappingBlock.setCondition(outputMappingBlock.getCondition().replaceAll(oldValue.getName(), "").trim());
+                        }
+                    }
+                }
+            }
+        }
+        if (evt.getPropertyName().equals(MatDefBlock.ADD_MAT_PARAM) || evt.getPropertyName().equals(TechniqueBlock.ADD_SHADER_NODE) || evt.getPropertyName().equals(ShaderNodeBlock.ADD_MAPPING)) {
+            matDefFile.registerListener((Statement) evt.getNewValue());
+        }
+        matDefFile.applyChange();
+    }
+    
+}

+ 15 - 50
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/MatDefEditorlElement.java

@@ -37,13 +37,13 @@ import com.jme3.gde.core.editor.nodes.NodeEditor;
 import com.jme3.gde.core.editor.nodes.Diagram;
 import com.jme3.gde.core.editor.nodes.Diagram;
 import com.jme3.gde.core.editor.nodes.NodePanel;
 import com.jme3.gde.core.editor.nodes.NodePanel;
 import com.jme3.gde.core.editor.nodes.Selectable;
 import com.jme3.gde.core.editor.nodes.Selectable;
-import com.jme3.asset.ShaderNodeDefinitionKey;
 import com.jme3.gde.core.assets.ProjectAssetManager;
 import com.jme3.gde.core.assets.ProjectAssetManager;
 import com.jme3.gde.core.errorreport.ExceptionPanel;
 import com.jme3.gde.core.errorreport.ExceptionPanel;
 import com.jme3.gde.materialdefinition.EditableMatDefFile;
 import com.jme3.gde.materialdefinition.EditableMatDefFile;
 import com.jme3.gde.materialdefinition.MatDefDataObject;
 import com.jme3.gde.materialdefinition.MatDefDataObject;
 import com.jme3.gde.materialdefinition.MatDefMetaData;
 import com.jme3.gde.materialdefinition.MatDefMetaData;
 import com.jme3.gde.materialdefinition.editor.ShaderNodePanel.NodeType;
 import com.jme3.gde.materialdefinition.editor.ShaderNodePanel.NodeType;
+import com.jme3.gde.materialdefinition.editor.util.MatDefEditorUtil;
 import com.jme3.gde.materialdefinition.fileStructure.MatDefBlock;
 import com.jme3.gde.materialdefinition.fileStructure.MatDefBlock;
 import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
 import com.jme3.gde.materialdefinition.fileStructure.ShaderNodeBlock;
 import com.jme3.gde.materialdefinition.fileStructure.TechniqueBlock;
 import com.jme3.gde.materialdefinition.fileStructure.TechniqueBlock;
@@ -275,7 +275,7 @@ public final class MatDefEditorlElement extends JPanel implements
             });
             });
         }
         }
     }
     }
-
+    
     public void switchTechnique(TechniqueBlock tech) {
     public void switchTechnique(TechniqueBlock tech) {
         obj.getEditableFile().setCurrentTechnique(tech);        
         obj.getEditableFile().setCurrentTechnique(tech);        
         reload(obj.getEditableFile(), obj.getLookup());
         reload(obj.getEditableFile(), obj.getLookup());
@@ -323,7 +323,7 @@ public final class MatDefEditorlElement extends JPanel implements
         Lookup.Result<Material> resMat = obj.getLookup().lookupResult(Material.class);
         Lookup.Result<Material> resMat = obj.getLookup().lookupResult(Material.class);
         Collection<? extends Material> col = (Collection<? extends Material>) resMat.allInstances();
         Collection<? extends Material> col = (Collection<? extends Material>) resMat.allInstances();
         if (!col.isEmpty()) {
         if (!col.isEmpty()) {
-            Material material = col.iterator().next();            
+            Material material = col.iterator().next();     
             diagram1.refreshPreviews(material,obj.getEditableFile().getCurrentTechnique().getName());
             diagram1.refreshPreviews(material,obj.getEditableFile().getCurrentTechnique().getName());
         }
         }
 
 
@@ -527,31 +527,7 @@ public final class MatDefEditorlElement extends JPanel implements
 
 
     @Override
     @Override
     public void makeMapping(Connection conn) {
     public void makeMapping(Connection conn) {
-        InOut startNode = (InOut) conn.getStart().getNode();
-        InOut endNode = (InOut) conn.getEnd().getNode();
-        String leftVarName = conn.getEnd().getText();
-        String rightVarName = conn.getStart().getText();
-        String leftVarSwizzle = null;
-        String rightVarSwizzle = null;
-
-        int endCard = ShaderUtils.getCardinality(conn.getEnd().getType(), "");
-        int startCard = ShaderUtils.getCardinality(conn.getStart().getType(), "");
-        String swizzle = "xyzw";
-        if (startCard > endCard) {
-            rightVarSwizzle = swizzle.substring(0, endCard);
-        } else if (endCard > startCard) {
-            leftVarSwizzle = swizzle.substring(0, startCard);
-        }
-
-        if (endNode instanceof ShaderOutBusPanel) {
-            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());
-        }
+        MatDefEditorUtil.makeMapping(conn, diagram1.getCurrentTechniqueName());
     }
     }
 
 
     @Override
     @Override
@@ -575,28 +551,7 @@ public final class MatDefEditorlElement extends JPanel implements
     }
     }
 
 
     public void notifyAddTechnique(TechniqueBlock tech) {
     public void notifyAddTechnique(TechniqueBlock tech) {
-        String path = "Common/MatDefs/ShaderNodes/Common/Unshaded.j3sn";
-        ShaderNodeDefinitionKey key =  new ShaderNodeDefinitionKey(path);
-        List<ShaderNodeDefinition> defs = getAssetManager().loadAsset(key);
-        ShaderNodeBlock node = new ShaderNodeBlock(defs.get(0), path);
-        tech.addFragmentShaderNode(node);
-        node.addOutputMapping(new OutputMappingBlock("color", "color", "", "", "Global", "Unshaded", null));
-        
-        path = "Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn";
-        key =  new ShaderNodeDefinitionKey(path);
-        defs = getAssetManager().loadAsset(key);
-        node = new ShaderNodeBlock(defs.get(0), path);
-        tech.addVertexShaderNode(node);
-        
-        node.addInputMapping(new InputMappingBlock("worldViewProjectionMatrix", "WorldViewProjectionMatrix", "", "", "CommonVert", "WorldParam", null));
-        node.addInputMapping(new InputMappingBlock("modelPosition", "position", "", "xyz", "CommonVert", "Global", null));        
-        
-        node.addOutputMapping(new OutputMappingBlock("position", "projPosition", "", "", "Global", "CommonVert", null));
-        
-        
-        WorldParamBlock param = new WorldParamBlock("WorldViewProjectionMatrix");
-        tech.addWorldParam(param);
-        
+        MatDefEditorUtil.notifyAddTechnique(getAssetManager(), tech);
         obj.getEditableFile().getMatDefStructure().addTechnique(tech);     
         obj.getEditableFile().getMatDefStructure().addTechnique(tech);     
         
         
     }
     }
@@ -644,6 +599,13 @@ public final class MatDefEditorlElement extends JPanel implements
             }
             }
         }
         }
     }
     }
+    
+    @Override
+    public void notifyDefaultValueUpdated(String matParamName, String value){
+        MatDefBlock matDef = obj.getLookup().lookup(MatDefBlock.class);
+        matDef.getMatParam(matParamName).setDefaultValue(value);
+        refresh();
+    }
 
 
     private ConnectionEndpoint findConnectPoint(String nameSpace, String name, boolean isInput) {
     private ConnectionEndpoint findConnectPoint(String nameSpace, String name, boolean isInput) {
 
 
@@ -723,6 +685,9 @@ public final class MatDefEditorlElement extends JPanel implements
 
 
         for (MatParamBlock matParamBlock : matDef.getMatParams()) {
         for (MatParamBlock matParamBlock : matDef.getMatParams()) {
             ShaderNodeVariable var = new ShaderNodeVariable("", "MatParam", matParamBlock.getName());
             ShaderNodeVariable var = new ShaderNodeVariable("", "MatParam", matParamBlock.getName());
+            if(matParamBlock.getDefaultValue() != null){
+                var.setDefaultValue(matParamBlock.getDefaultValue());
+            }
             var.setType(MaterialUtils.getMatParamType(matParamBlock));
             var.setType(MaterialUtils.getMatParamType(matParamBlock));
             uniforms.add(var);
             uniforms.add(var);
         }
         }

+ 32 - 9
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/ShaderNodePanel.java

@@ -38,6 +38,8 @@ import com.jme3.gde.materialdefinition.fileStructure.leaves.DefinitionBlock;
 import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
 import com.jme3.gde.materialdefinition.fileStructure.leaves.InputMappingBlock;
 import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
 import com.jme3.gde.materialdefinition.fileStructure.leaves.OutputMappingBlock;
 import com.jme3.gde.core.editor.icons.Icons;
 import com.jme3.gde.core.editor.icons.Icons;
+import com.jme3.gde.materialdefinition.editor.previews.BasePreview;
+import com.jme3.gde.materialdefinition.editor.previews.PreviewFactory;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader;
 import com.jme3.shader.ShaderNodeDefinition;
 import com.jme3.shader.ShaderNodeDefinition;
 import com.jme3.shader.ShaderNodeVariable;
 import com.jme3.shader.ShaderNodeVariable;
@@ -93,7 +95,6 @@ public class ShaderNodePanel extends NodePanel implements InOut,
         } else {
         } else {
             type = NodeType.Fragment;
             type = NodeType.Fragment;
         }
         }
-        
         node.addPropertyChangeListener(WeakListeners.propertyChange(this, node));
         node.addPropertyChangeListener(WeakListeners.propertyChange(this, node));
         this.addPropertyChangeListener(WeakListeners.propertyChange(node, this));
         this.addPropertyChangeListener(WeakListeners.propertyChange(node, this));
         
         
@@ -116,7 +117,14 @@ public class ShaderNodePanel extends NodePanel implements InOut,
     }
     }
     
     
     private void init(List<ShaderNodeVariable> inputs, List<ShaderNodeVariable> outputs) {
     private void init(List<ShaderNodeVariable> inputs, List<ShaderNodeVariable> outputs) {
-        setBounds(0, 0, 120, 30 + inputs.size() * 20 + outputs.size() * 20);
+        if(outputs.size() == 1 && outputs.get(0).getType().startsWith("sampler")){
+            setBounds(0, 0, 120, 80);
+        } else if(type == NodeType.WorldParam || type == NodeType.Attribute){
+            setBounds(0, 0, 120, 45);
+        } else {
+            setBounds(0, 0, 120, 40 + inputs.size() * 20 + outputs.size() * 20);
+        }
+        
 
 
         for (ShaderNodeVariable input : inputs) {
         for (ShaderNodeVariable input : inputs) {
             JLabel label = createLabel(input.getType(), input.getName(), ConnectionEndpoint.ParamType.Input);
             JLabel label = createLabel(input.getType(), input.getName(), ConnectionEndpoint.ParamType.Input);
@@ -130,13 +138,22 @@ public class ShaderNodePanel extends NodePanel implements InOut,
             JLabel label = createLabel(output.getType(), outName, ConnectionEndpoint.ParamType.Output);
             JLabel label = createLabel(output.getType(), outName, ConnectionEndpoint.ParamType.Output);
             ConnectionEndpoint dot = createConnectionEndpoint(output.getType(), ConnectionEndpoint.ParamType.Output, outName);
             ConnectionEndpoint dot = createConnectionEndpoint(output.getType(), ConnectionEndpoint.ParamType.Output, outName);
             dot.setIndex(index++);
             dot.setIndex(index++);
+            
             outputLabels.add(label);
             outputLabels.add(label);
             outputDots.add(dot);
             outputDots.add(dot);
+            if(type == NodeType.MatParam){
+                BasePreview c = PreviewFactory.createPreviewComponent(output);
+                
+                if(c != null){
+                    c.addPropertyChangeListener(this);
+                    previews.add(c);
+                }
+            }
+            
         }
         }
-
         initComponents();
         initComponents();
         updateType();
         updateType();
-        setOpaque(false);
+        setOpaque(true);
     }
     }
 
 
     @Override
     @Override
@@ -149,7 +166,9 @@ public class ShaderNodePanel extends NodePanel implements InOut,
 
 
     @Override
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
     public void propertyChange(PropertyChangeEvent evt) {
-        if (evt.getPropertyName().equals("name")) {
+        if(evt.getSource() instanceof BasePreview && evt.getNewValue() instanceof String){
+            diagram.updateDefaultValue(evt.getPropertyName(), (String) evt.getNewValue());
+        } else if (evt.getPropertyName().equals("name")) {
             refresh((ShaderNodeBlock) evt.getSource());
             refresh((ShaderNodeBlock) evt.getSource());
         }
         }
     }
     }
@@ -194,15 +213,18 @@ public class ShaderNodePanel extends NodePanel implements InOut,
                 break;
                 break;
             case Attribute:
             case Attribute:
                 header.setIcon(Icons.attrib);
                 header.setIcon(Icons.attrib);
-                setNameAndTitle("Attr");
+                name = "Attr";
+                setTitle(outputLabels.get(0).getText() ); // sets text _and_ tooltip the same
                 break;
                 break;
             case WorldParam:
             case WorldParam:
                 header.setIcon(Icons.world);
                 header.setIcon(Icons.world);
-                setNameAndTitle("WorldParam");
+                name = "WorldParam";
+                setTitle(outputLabels.get(0).getText());
                 break;
                 break;
             case MatParam:
             case MatParam:
                 header.setIcon(Icons.mat);
                 header.setIcon(Icons.mat);
-                setNameAndTitle("MatParam");
+                name = "MatParam";
+                setTitle(outputLabels.get(0).getText());
                 break;
                 break;
         }
         }
         color = type.getColor();
         color = type.getColor();
@@ -231,7 +253,7 @@ public class ShaderNodePanel extends NodePanel implements InOut,
      */
      */
     protected JLabel createLabel(String glslType, String txt, ConnectionEndpoint.ParamType type) {
     protected JLabel createLabel(String glslType, String txt, ConnectionEndpoint.ParamType type) {
         JLabel label = super.createLabel(txt, type);
         JLabel label = super.createLabel(txt, type);
-        label.setToolTipText(glslType + " " + txt);
+        label.setToolTipText(type + " " + glslType + " " + txt);
         return label;
         return label;
     }
     }
     
     
@@ -267,4 +289,5 @@ public class ShaderNodePanel extends NodePanel implements InOut,
     public void removeOutputMapping(OutputMappingBlock block) {
     public void removeOutputMapping(OutputMappingBlock block) {
         firePropertyChange(ShaderNodeBlock.OUTPUT, block, null);
         firePropertyChange(ShaderNodeBlock.OUTPUT, block, null);
     }
     }
+    
 }
 }

+ 75 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/BasePreview.java

@@ -0,0 +1,75 @@
+/*
+ *  Copyright (c) 2009-2022 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.previews;
+
+import com.jme3.gde.materials.MaterialProperty;
+import com.jme3.gde.materials.multiview.widgets.MaterialWidgetListener;
+import com.jme3.shader.ShaderNodeVariable;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.util.logging.Logger;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * Base component for all previews.
+ *
+ * @author rickard
+ */
+public abstract class BasePreview extends JPanel implements MaterialWidgetListener {
+
+    private ShaderNodeVariable output;
+    protected Logger logger = Logger.getLogger(BasePreview.class.getName());
+
+    public interface OnDefaultValueChangedListener {
+
+        void onDefaultValueChanged(String value);
+    }
+
+    public BasePreview(ShaderNodeVariable output) {
+        this.output = output;
+        setBorder(new EmptyBorder(0, 0, 0, 0));
+        setBackground(new Color(170, 170, 170));
+        ((FlowLayout) getLayout()).setVgap(0);
+    }
+
+    protected void onDefaultValueChanged(String value) {
+        output.setDefaultValue(value);
+        firePropertyChange(output.getName(), "", value);
+    }
+
+    @Override
+    public void propertyChanged(MaterialProperty property) {
+        onDefaultValueChanged(property.getValue());
+    }
+
+}

+ 64 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/BoolPreview.java

@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) 2009-2022 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.previews;
+
+import com.jme3.shader.ShaderNodeVariable;
+import java.awt.event.ActionEvent;
+import javax.swing.JCheckBox;
+
+/**
+ * A component for displaying and editing a Bool MatParam
+ *
+ * @author rickard
+ */
+public class BoolPreview extends BasePreview {
+
+    private final JCheckBox checkbox;
+
+    public BoolPreview(ShaderNodeVariable output) {
+        super(output);
+        boolean checked = false;
+        String value = output.getDefaultValue();
+        if (value != null && value.isEmpty()) {
+            checked = Boolean.parseBoolean(value);
+        }
+        checkbox = new JCheckBox("", checked);
+
+        checkbox.setSize(20, 20);
+        checkbox.addActionListener((ActionEvent e) -> {
+            onDefaultValueChanged(((JCheckBox) e.getSource()).isSelected() ? "true" : "false");
+        });
+
+        add(checkbox);
+    }
+
+}

+ 100 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/ColorPreview.java

@@ -0,0 +1,100 @@
+/*
+ *  Copyright (c) 2009-2022 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.previews;
+
+import com.jme3.gde.materials.multiview.widgets.ColorRGBADialog;
+import com.jme3.shader.ShaderNodeVariable;
+import java.awt.Color;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import javax.swing.JFrame;
+import javax.swing.border.LineBorder;
+
+/**
+ * Component for previewing and changing a default Color value for a MatParam.
+ *
+ * @author rickard
+ */
+public class ColorPreview extends BasePreview implements MouseListener {
+
+    private final float[] rgba = new float[]{0f, 0f, 0f, 1f};
+
+    public ColorPreview(ShaderNodeVariable output) {
+        super(output);
+        String color = output.getDefaultValue();
+        populateRGBA(color);
+        setBackground(new Color(rgba[0], rgba[1], rgba[2], rgba[3]));
+        addMouseListener(this);
+        setBorder(new LineBorder(Color.DARK_GRAY));
+        setSize(20, 20);
+    }
+
+    private void populateRGBA(String colorString) {
+        if(colorString != null && !colorString.isEmpty()){
+            String[] split = colorString.split(" ");
+            rgba[0] = Float.parseFloat(split[0]);
+            rgba[1] = Float.parseFloat(split[1]);
+            rgba[2] = Float.parseFloat(split[2]);
+            rgba[3] = Float.parseFloat(split[3]);
+        }
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        ColorRGBADialog dialog = new ColorRGBADialog(new JFrame(), true);
+        dialog.setLocationRelativeTo(null);
+        dialog.setColor(new Color(rgba[0], rgba[1], rgba[2], rgba[3]));
+        dialog.setVisible(true);
+        if (dialog.getColorAsText() != null) {
+            setBackground(dialog.getColor());
+            populateRGBA(dialog.getColorAsText());
+            onDefaultValueChanged(dialog.getColorAsText());
+        }
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+    }
+
+}

+ 27 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/FloatPreview.java

@@ -0,0 +1,27 @@
+package com.jme3.gde.materialdefinition.editor.previews;
+
+import com.jme3.gde.materials.MaterialProperty;
+import com.jme3.gde.materials.multiview.widgets.FloatPanelSmall;
+import com.jme3.shader.ShaderNodeVariable;
+
+/**
+ * Component for previewing and changing a default Float value for a MatParam.
+ *
+ * @author rickard
+ */
+public class FloatPreview extends BasePreview {
+
+    public FloatPreview(ShaderNodeVariable output) {
+        super(output);
+        MaterialProperty p = new MaterialProperty();
+        p.setName(output.getName());
+        p.setType("float");
+        p.setValue(output.getDefaultValue());
+        FloatPanelSmall floatPanel = new FloatPanelSmall();
+        floatPanel.setProperty(p);
+        floatPanel.registerChangeListener(this);
+        add(floatPanel);
+        setSize(floatPanel.getPreferredSize());
+    }
+
+}

+ 64 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/PreviewFactory.java

@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) 2009-2022 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.previews;
+
+import com.jme3.shader.ShaderNodeVariable;
+
+/**
+ *
+ * @author rickard
+ */
+public class PreviewFactory {
+    
+    public static BasePreview createPreviewComponent(ShaderNodeVariable output){
+        switch (output.getType()) {
+            case "bool":
+                return new BoolPreview(output);
+            case "vec4":
+                if(output.getName().toLowerCase().contains("color")){
+                    return new ColorPreview(output);
+                } else {
+                    return new VecPreview(output, 4);
+                }
+            case "vec3":
+                return new VecPreview(output, 3);
+            case "vec2":
+                return new VecPreview(output, 2);
+            case "float":
+                return new FloatPreview(output);
+            case "sampler2D|sampler2DShadow":
+                return new TexturePreview(output);
+            default:
+                return null;
+        }
+    }
+}

+ 72 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/TexturePreview.java

@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2009-2022 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.previews;
+
+import com.jme3.gde.materialdefinition.EditableMatDefFile;
+import com.jme3.gde.materials.MaterialProperty;
+import com.jme3.gde.materials.multiview.widgets.TexturePanelSquare;
+import com.jme3.shader.ShaderNodeVariable;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+
+/**
+ * Component for previewing and changing a default Texture2D value for a
+ * MatParam.
+ *
+ * @author rickard
+ */
+public class TexturePreview extends BasePreview {
+
+    private final TexturePanelSquare texturePanel;
+
+    public TexturePreview(ShaderNodeVariable output) {
+        super(output);
+        String texture = output.getDefaultValue();
+        texturePanel = new TexturePanelSquare(EditableMatDefFile.getAssetManager());
+        MaterialProperty property = new MaterialProperty(output.getType(), output.getName(), texture != null ? texture : "");
+        texturePanel.setProperty(property);
+        texturePanel.registerChangeListener(this);
+        add(texturePanel);
+        setSize(texturePanel.getPreferredSize());
+    }
+
+    private static BufferedImage resize(BufferedImage image, int width, int height) {
+        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
+        Graphics2D g2d = (Graphics2D) bi.createGraphics();
+        g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
+        g2d.drawImage(image, 0, 0, width, height, null);
+        g2d.dispose();
+        return bi;
+    }
+
+}

+ 93 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/previews/VecPreview.java

@@ -0,0 +1,93 @@
+/*
+ *  Copyright (c) 2009-2022 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.previews;
+
+import com.jme3.shader.ShaderNodeVariable;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+
+/**
+ * Component for previewing and changing a default Vector4 value for a MatParam.
+ *
+ * @author rickard
+ */
+public class VecPreview extends BasePreview {
+
+    private final int components;
+
+    public VecPreview(ShaderNodeVariable output, int components) {
+        super(output);
+        this.components = components;
+        JTextField textField = new JTextField(output.getDefaultValue());
+        textField.addActionListener((ActionEvent e) -> {
+            String value = ((JTextField) e.getSource()).getText();
+            if (verifyString(value)) {
+                onDefaultValueChanged(value);
+            }
+        });
+        add(textField);
+        JLabel content = new JLabel(output.getDefaultValue());
+        add(content);
+        setBackground(new Color(55, 55, 55));
+        setSize(90, 20);
+    }
+
+    private boolean verifyString(String value) {
+        String[] split = value.split(" ");
+        if (split.length != components) {
+            logger.warning(String.format("Value should contain {0} components", components));
+            return false;
+        }
+        for (String s : split) {
+            if (!verifyFloatString(s)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean verifyFloatString(String value) {
+        try {
+            Float.parseFloat(value);
+        } catch (NumberFormatException ex) {
+            logger.warning("Value is not valid float");
+            return false;
+        } catch (NullPointerException ex) {
+            logger.warning("Value is null");
+            return false;
+        }
+        return true;
+    }
+
+}

+ 105 - 0
jme3-materialeditor/src/com/jme3/gde/materialdefinition/editor/util/MatDefEditorUtil.java

@@ -0,0 +1,105 @@
+/*
+ *  Copyright (c) 2009-2022 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.util;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.ShaderNodeDefinitionKey;
+import com.jme3.gde.core.editor.nodes.Connection;
+import com.jme3.gde.materialdefinition.editor.InOut;
+import com.jme3.gde.materialdefinition.editor.ShaderOutBusPanel;
+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.OutputMappingBlock;
+import com.jme3.gde.materialdefinition.fileStructure.leaves.WorldParamBlock;
+import com.jme3.shader.ShaderNodeDefinition;
+import com.jme3.shader.ShaderUtils;
+import java.util.List;
+
+/**
+ *
+ * @author rickard
+ */
+public class MatDefEditorUtil {
+    
+    public static void notifyAddTechnique(AssetManager assetManager, TechniqueBlock tech) {
+        String path = "Common/MatDefs/ShaderNodes/Common/Unshaded.j3sn";
+        ShaderNodeDefinitionKey key = new ShaderNodeDefinitionKey(path);
+        List<ShaderNodeDefinition> defs = assetManager.loadAsset(key);
+        ShaderNodeBlock node = new ShaderNodeBlock(defs.get(0), path);
+        tech.addFragmentShaderNode(node);
+        node.addOutputMapping(new OutputMappingBlock("color", "color", "", "", "Global", "Unshaded", null));
+
+        path = "Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn";
+        key = new ShaderNodeDefinitionKey(path);
+        defs = assetManager.loadAsset(key);
+        node = new ShaderNodeBlock(defs.get(0), path);
+        tech.addVertexShaderNode(node);
+
+        node.addInputMapping(new InputMappingBlock("worldViewProjectionMatrix", "WorldViewProjectionMatrix", "", "", "CommonVert", "WorldParam", null));
+        node.addInputMapping(new InputMappingBlock("modelPosition", "position", "", "xyz", "CommonVert", "Global", null));
+
+        node.addOutputMapping(new OutputMappingBlock("position", "projPosition", "", "", "Global", "CommonVert", null));
+
+        WorldParamBlock param = new WorldParamBlock("WorldViewProjectionMatrix");
+        tech.addWorldParam(param);
+    }
+
+    public static void makeMapping(Connection conn, String currentTechniqueName) {
+        InOut startNode = (InOut) conn.getStart().getNode();
+        InOut endNode = (InOut) conn.getEnd().getNode();
+        String leftVarName = conn.getEnd().getText();
+        String rightVarName = conn.getStart().getText();
+        String leftVarSwizzle = null;
+        String rightVarSwizzle = null;
+
+        int endCard = ShaderUtils.getCardinality(conn.getEnd().getType(), "");
+        int startCard = ShaderUtils.getCardinality(conn.getStart().getType(), "");
+        String swizzle = "xyzw";
+        if (startCard > endCard) {
+            rightVarSwizzle = swizzle.substring(0, endCard);
+        } else if (endCard > startCard) {
+            leftVarSwizzle = swizzle.substring(0, startCard);
+        }
+
+        if (endNode instanceof ShaderOutBusPanel) {
+            OutputMappingBlock mapping = new OutputMappingBlock(leftVarName, rightVarName, leftVarSwizzle, rightVarSwizzle, endNode.getName(), startNode.getName(), null);
+            startNode.addOutputMapping(mapping);
+            conn.makeKey(mapping, currentTechniqueName);
+        } else {
+            InputMappingBlock mapping = new InputMappingBlock(leftVarName, rightVarName, leftVarSwizzle, rightVarSwizzle, endNode.getName(), startNode.getName(), null);
+            endNode.addInputMapping(mapping);
+            conn.makeKey(mapping, currentTechniqueName);
+        }
+    }
+}

+ 19 - 1
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/MatDefBlock.java

@@ -110,7 +110,7 @@ public class MatDefBlock extends UberStatement {
         }
         }
         fire(REMOVE_MAT_PARAM, matParam, null);
         fire(REMOVE_MAT_PARAM, matParam, null);
     }
     }
-
+    
     public List<TechniqueBlock> getTechniques() {
     public List<TechniqueBlock> getTechniques() {
         return getBlocks(TechniqueBlock.class);
         return getBlocks(TechniqueBlock.class);
     }
     }
@@ -119,4 +119,22 @@ public class MatDefBlock extends UberStatement {
         addStatement(techniqueBlock);
         addStatement(techniqueBlock);
         fire("technique", null, techniqueBlock);
         fire("technique", null, techniqueBlock);
     }
     }
+    
+    
+    public MatParamBlock getMatParam(String name){
+        for(MatParamBlock block:  getMaterialParameters().getMatParams()){
+            if(block.getName().equals(name)){
+                return block;
+            }
+        }
+        return null;
+    }
+    
+    public void updateMatParam(MatParamBlock matParam, String value){
+        for(MatParamBlock block:  getMaterialParameters().getMatParams()){
+            if(block.getName().equals(matParam.getName())){
+                block.setDefaultValue(value);
+            }
+        }
+    }
 }
 }

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

@@ -45,7 +45,7 @@ public class ShaderNodesBlock extends UberStatement implements PropertyChangeLis
     public boolean removeShaderNode(ShaderNodeBlock shaderNodeBlock) {
     public boolean removeShaderNode(ShaderNodeBlock shaderNodeBlock) {
         return contents.remove(shaderNodeBlock);
         return contents.remove(shaderNodeBlock);
     }
     }
-
+    
     @Override
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
     public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getPropertyName().equals(ShaderNodeBlock.ADD_MAPPING) || evt.getPropertyName().equals("order")) {
         if (evt.getPropertyName().equals(ShaderNodeBlock.ADD_MAPPING) || evt.getPropertyName().equals("order")) {
@@ -66,7 +66,7 @@ public class ShaderNodesBlock extends UberStatement implements PropertyChangeLis
         contents.addAll(list);
         contents.addAll(list);
         fire("reorder", null, null);
         fire("reorder", null, null);
     }
     }
-
+    
     /**
     /**
      * Sorts nodes so that they are initialized after their input dependencies.
      * Sorts nodes so that they are initialized after their input dependencies.
      * Will look higher up in hierarchy and move node incrementally. This may require several passes
      * Will look higher up in hierarchy and move node incrementally. This may require several passes

+ 6 - 2
jme3-materialeditor/src/com/jme3/gde/materialdefinition/fileStructure/leaves/MatParamBlock.java

@@ -74,8 +74,12 @@ public class MatParamBlock extends LeafStatement {
     }
     }
 
 
     public void setDefaultValue(String defaultValue) {
     public void setDefaultValue(String defaultValue) {
-        this.defaultValue = defaultValue;
-        updateLine();
+        if(!defaultValue.equals(this.defaultValue)){
+            String oldValue = this.defaultValue;
+            this.defaultValue = defaultValue;
+            updateLine();
+            fire("defaultValue", oldValue, defaultValue);
+        }
     }
     }
 
 
     @Override
     @Override

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

@@ -13,8 +13,6 @@ import java.awt.Image;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeListener;
 import java.util.List;
 import java.util.List;
-import javax.swing.Action;
-import org.openide.actions.RenameAction;
 import org.openide.nodes.ChildFactory;
 import org.openide.nodes.ChildFactory;
 import org.openide.nodes.Children;
 import org.openide.nodes.Children;
 import org.openide.nodes.Node;
 import org.openide.nodes.Node;
@@ -22,7 +20,6 @@ import org.openide.nodes.Sheet;
 import org.openide.util.Exceptions;
 import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
 import org.openide.util.Lookup;
 import org.openide.util.WeakListeners;
 import org.openide.util.WeakListeners;
-import org.openide.util.actions.SystemAction;
 
 
 /**
 /**
  *
  *

+ 7 - 4
jme3-materialeditor/src/com/jme3/gde/materials/MaterialPreviewRenderer.java

@@ -43,7 +43,7 @@ public class MaterialPreviewRenderer implements SceneListener {
     private Geometry currentGeom;
     private Geometry currentGeom;
     private Material currentMaterial;
     private Material currentMaterial;
     private boolean init = false;
     private boolean init = false;
-    final JLabel label;
+    private final JLabel label;
     private final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(5);
     private final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(5);
     private boolean previewRequested;
     private boolean previewRequested;
 
 
@@ -60,9 +60,9 @@ public class MaterialPreviewRenderer implements SceneListener {
 
 
     private void init() {
     private void init() {
         SceneApplication.getApplication().addSceneListener(this);
         SceneApplication.getApplication().addSceneListener(this);
-        Sphere sphMesh = new Sphere(64, 64, 2.5f);
-        sphMesh.setTextureMode(Sphere.TextureMode.Polar);
-        sphMesh.updateGeometry(64, 64, 2.5f, false, false);
+        Sphere sphMesh = new Sphere(32, 32, 2.5f);
+        sphMesh.setTextureMode(Sphere.TextureMode.Projected);
+        sphMesh.updateGeometry(32, 32, 2.5f, false, false);
         Logger log = Logger.getLogger(TangentBinormalGenerator.class.getName());
         Logger log = Logger.getLogger(TangentBinormalGenerator.class.getName());
         log.setLevel(Level.SEVERE);
         log.setLevel(Level.SEVERE);
         TangentBinormalGenerator.generate(sphMesh);
         TangentBinormalGenerator.generate(sphMesh);
@@ -261,6 +261,9 @@ public class MaterialPreviewRenderer implements SceneListener {
         return previewRequested;
         return previewRequested;
     }
     }
     
     
+    /**
+     * A more lightweight refresh than showMaterials that doesn't rebuild the material
+     */
     public void refreshOnly(){
     public void refreshOnly(){
         previewRequested = true;
         previewRequested = true;
         SceneApplication.getApplication().enqueue((Callable<Object>) () -> {
         SceneApplication.getApplication().enqueue((Callable<Object>) () -> {

+ 4 - 0
jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/Bundle.properties

@@ -47,3 +47,7 @@ ColorPanel.bLabel.text=0.2545
 ColorPanel.aLabel.text=0.2545
 ColorPanel.aLabel.text=0.2545
 TexturePanel.jButton2.toolTipText=Remove this texture
 TexturePanel.jButton2.toolTipText=Remove this texture
 TexturePanel.texturePreview.text=
 TexturePanel.texturePreview.text=
+TexturePanelSquare.jCheckBox2.text=repeat
+TexturePanelSquare.jCheckBox1.text=flip
+TexturePanelSquare.AccessibleContext.accessibleName=
+TexturePanelSquare.text=

+ 1 - 1
jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/FloatPanel.java

@@ -21,7 +21,7 @@ public class FloatPanel extends MaterialPropertyWidget {
     /** Creates new form NumberPanel */
     /** Creates new form NumberPanel */
     public FloatPanel() {
     public FloatPanel() {
         initComponents();
         initComponents();
-           jSpinner1.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.0f), null, null, Float.valueOf(0.1f)));
+        jSpinner1.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.0f), null, null, Float.valueOf(0.1f)));
     }
     }
 
 
     /** This method is called from within the constructor to
     /** This method is called from within the constructor to

+ 90 - 0
jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/FloatPanelSmall.form

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <Properties>
+    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+      <Color blue="aa" green="aa" red="aa" type="rgb"/>
+    </Property>
+  </Properties>
+  <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="false"/>
+    <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="jToolBar1" alignment="0" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jToolBar1" min="-2" max="-2" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JToolBar" name="jToolBar1">
+      <Properties>
+        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="aa" green="aa" red="aa" type="rgb"/>
+        </Property>
+        <Property name="floatable" type="boolean" value="false"/>
+        <Property name="rollover" type="boolean" value="true"/>
+      </Properties>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
+      <SubComponents>
+        <Container class="javax.swing.JPanel" name="jPanel1">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="aa" green="aa" red="aa" type="rgb"/>
+            </Property>
+            <Property name="opaque" type="boolean" value="false"/>
+          </Properties>
+
+          <Layout>
+            <DimensionLayout dim="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="jSpinner1" min="-2" pref="62" max="-2" attributes="0"/>
+                      <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+            </DimensionLayout>
+            <DimensionLayout dim="1">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="1" attributes="0">
+                      <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+                      <Component id="jSpinner1" min="-2" pref="24" max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+            </DimensionLayout>
+          </Layout>
+          <SubComponents>
+            <Component class="javax.swing.JSpinner" name="jSpinner1">
+              <Properties>
+                <Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
+                  <SpinnerModel initial="0.0" numberType="java.lang.Float" stepSize="1.0" type="number"/>
+                </Property>
+                <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[70, 20]"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="valueChanged"/>
+              </Events>
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 137 - 0
jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/FloatPanelSmall.java

@@ -0,0 +1,137 @@
+/*
+ *  Copyright (c) 2009-2022 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.materials.multiview.widgets;
+
+import com.jme3.gde.materials.MaterialProperty;
+
+/**
+ * A smaller FloatPanel, designed for shader nodes editor
+ * @author rickard
+ */
+public class FloatPanelSmall extends MaterialPropertyWidget {
+
+    /** Creates new form NumberPanel */
+    public FloatPanelSmall() {
+        initComponents();
+           jSpinner1.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.0f), null, null, Float.valueOf(0.1f)));
+    }
+
+    /** 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() {
+
+        jToolBar1 = new javax.swing.JToolBar();
+        jPanel1 = new javax.swing.JPanel();
+        jSpinner1 = new javax.swing.JSpinner();
+
+        setBackground(new java.awt.Color(170, 170, 170));
+
+        jToolBar1.setBackground(new java.awt.Color(170, 170, 170));
+        jToolBar1.setFloatable(false);
+        jToolBar1.setRollover(true);
+
+        jPanel1.setBackground(new java.awt.Color(170, 170, 170));
+        jPanel1.setOpaque(false);
+
+        jSpinner1.setModel(new javax.swing.SpinnerNumberModel(0.0f, null, null, 1.0f));
+        jSpinner1.setPreferredSize(new java.awt.Dimension(70, 20));
+        jSpinner1.addChangeListener(new javax.swing.event.ChangeListener() {
+            public void stateChanged(javax.swing.event.ChangeEvent evt) {
+                valueChanged(evt);
+            }
+        });
+
+        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+        jPanel1.setLayout(jPanel1Layout);
+        jPanel1Layout.setHorizontalGroup(
+            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(jPanel1Layout.createSequentialGroup()
+                .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 62, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(0, 0, Short.MAX_VALUE))
+        );
+        jPanel1Layout.setVerticalGroup(
+            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
+                .addGap(0, 0, Short.MAX_VALUE)
+                .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE))
+        );
+
+        jToolBar1.add(jPanel1);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void valueChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_valueChanged
+        if (property == null) {
+            return;
+        }
+        property.setValue(jSpinner1.getValue() + "");
+        fireChanged();
+    }//GEN-LAST:event_valueChanged
+
+    @Override
+    protected void readProperty() {
+        java.awt.EventQueue.invokeLater(new Runnable() {
+
+            public void run() {
+                setToolTipText(property.getName());
+                MaterialProperty prop = property;
+                property = null;
+                try {
+                    jSpinner1.setValue(Float.parseFloat(prop.getValue()));
+                } catch (Exception e) {
+                    jSpinner1.setValue(0);
+                }
+                property = prop;
+            }
+        });
+    }
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JSpinner jSpinner1;
+    private javax.swing.JToolBar jToolBar1;
+    // End of variables declaration//GEN-END:variables
+}

+ 186 - 0
jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.form

@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.4" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <Properties>
+    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+      <Color blue="aa" green="aa" red="aa" type="rgb"/>
+    </Property>
+    <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[95, 65]"/>
+    </Property>
+    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[40, 40]"/>
+    </Property>
+    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[95, 45]"/>
+    </Property>
+  </Properties>
+  <AccessibilityProperties>
+    <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+      <ResourceString bundle="com/jme3/gde/materials/multiview/widgets/Bundle.properties" key="TexturePanelSquare.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+    </Property>
+  </AccessibilityProperties>
+  <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="false"/>
+    <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">
+              <Component id="texturePreview" min="-2" max="-2" attributes="0"/>
+              <EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jCheckBox2" min="-2" max="-2" attributes="0"/>
+                  <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+          </Group>
+          <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <EmptySpace min="0" pref="94" max="32767" attributes="0"/>
+                  <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace min="0" pref="1" max="32767" attributes="0"/>
+              </Group>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="texturePreview" min="-2" max="-2" attributes="1"/>
+                  <Group type="102" attributes="0">
+                      <Component id="jCheckBox1" min="-2" max="-2" attributes="1"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="jCheckBox2" min="-2" pref="17" max="-2" attributes="1"/>
+                  </Group>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+          </Group>
+          <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <EmptySpace min="0" pref="52" max="32767" attributes="0"/>
+                  <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+              </Group>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="jPanel1">
+      <Properties>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[10, 0]"/>
+        </Property>
+      </Properties>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <EmptySpace min="0" pref="10" max="32767" attributes="0"/>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+    </Container>
+    <Component class="javax.swing.JCheckBox" name="jCheckBox1">
+      <Properties>
+        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="aa" green="aa" red="aa" type="rgb"/>
+        </Property>
+        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+          <Font name="Lucida Grande" size="10" style="0"/>
+        </Property>
+        <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="0" green="0" red="0" type="rgb"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materials/multiview/widgets/Bundle.properties" key="TexturePanelSquare.jCheckBox1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+        <Property name="focusable" type="boolean" value="false"/>
+        <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
+          <Insets value="[0, 0, 0, 0]"/>
+        </Property>
+        <Property name="verticalTextPosition" type="int" value="3"/>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jCheckBox1ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JCheckBox" name="jCheckBox2">
+      <Properties>
+        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="aa" green="aa" red="aa" type="rgb"/>
+        </Property>
+        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+          <Font name="Lucida Grande" size="10" style="0"/>
+        </Property>
+        <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+          <Color blue="0" green="0" red="0" type="rgb"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materials/multiview/widgets/Bundle.properties" key="TexturePanelSquare.jCheckBox2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+        <Property name="focusable" type="boolean" value="false"/>
+        <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
+          <Insets value="[0, 0, 0, 0]"/>
+        </Property>
+        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[53, 15]"/>
+        </Property>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[53, 15]"/>
+        </Property>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[53, 15]"/>
+        </Property>
+        <Property name="verticalTextPosition" type="int" value="3"/>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jCheckBox2ActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="texturePreview">
+      <Properties>
+        <Property name="horizontalAlignment" type="int" value="0"/>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/materials/multiview/widgets/Bundle.properties" key="TexturePanelSquare.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+        <Property name="verticalAlignment" type="int" value="1"/>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
+            <LineBorder>
+              <Color PropertyName="color" blue="99" green="99" red="99" type="rgb"/>
+            </LineBorder>
+          </Border>
+        </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="[60, 60]"/>
+        </Property>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[40, 40]"/>
+        </Property>
+        <Property name="name" type="java.lang.String" value="" noResource="true"/>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[40, 40]"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>

+ 312 - 0
jme3-materialeditor/src/com/jme3/gde/materials/multiview/widgets/TexturePanelSquare.java

@@ -0,0 +1,312 @@
+/*
+ *  Copyright (c) 2009-2022 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.materials.multiview.widgets;
+
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.properties.TexturePropertyEditor;
+import com.jme3.gde.core.properties.preview.TexturePreview;
+import com.jme3.gde.materials.MaterialProperty;
+import com.jme3.gde.materials.multiview.MaterialEditorTopComponent;
+import java.awt.Component;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A more compact texture panel designed for the shader node editor.
+ * @author rickard
+ */
+public class TexturePanelSquare extends MaterialPropertyWidget {
+
+    private final TexturePropertyEditor editor;
+    private final ProjectAssetManager manager;
+    private boolean flip = false;
+    private boolean repeat = false;
+    protected String textureName = null; // always enclosed with ""
+    private TexturePreview texPreview;
+    private final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
+
+    /** Creates new form SelectionPanel */
+    public TexturePanelSquare(ProjectAssetManager manager) {
+        this.manager = manager;
+        editor = new TexturePropertyEditor(manager);
+        initComponents();
+        texturePreview.addMouseListener(new MouseListener() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                Component view = editor.getCustomEditor();
+                view.setVisible(true);
+                if (editor.getValue() != null) {
+                    textureName = "\"" + editor.getAsText() + "\"";
+                    displayPreview();
+                    updateFlipRepeat();
+                    fireChanged();
+                } else { // "No Texture" has been clicked
+                    textureName = "\"\"";
+                    texturePreview.setIcon(null);
+                    texturePreview.setToolTipText("");
+                    property.setValue("");
+                    fireChanged();
+                }
+            }
+
+            @Override
+            public void mousePressed(MouseEvent e) {
+            }
+
+            @Override
+            public void mouseReleased(MouseEvent e) {
+            }
+
+            @Override
+            public void mouseEntered(MouseEvent e) {
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+            }
+        });
+    }
+
+    private void displayPreview() {
+        if (!"".equals(textureName)) {
+            exec.execute(new Runnable() {
+                @Override
+                public void run() {
+                    try{
+                        if (texPreview == null) {
+                            texPreview = new TexturePreview(manager);
+                        }
+                        texPreview.requestPreview(stripQuotes(textureName), "", 80, 25, texturePreview, null);
+                    } catch (AssetNotFoundException a) {
+                        Logger.getLogger(MaterialEditorTopComponent.class.getName()).log(Level.WARNING, "Could not load texture {0}", textureName);
+                    }
+                }
+            });
+        }
+    }
+    
+    private String stripQuotes(String s) {
+        return s.substring(1, s.length() - 1);
+    }
+
+    private void updateFlipRepeat() {
+        if (flip && repeat) {
+            property.setValue("Flip Repeat " + textureName);
+            texturePreview.setToolTipText("Flip Repeat " + textureName);
+        } else if (flip) {
+            property.setValue("Flip " + textureName);
+            texturePreview.setToolTipText("Flip " + textureName);
+        } else if (repeat) {
+            property.setValue("Repeat " + textureName);
+            texturePreview.setToolTipText("Repeat " + textureName);
+        } else {
+            property.setValue(textureName);
+            texturePreview.setToolTipText(textureName);
+        }
+    }
+
+    /** 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() {
+
+        jPanel1 = new javax.swing.JPanel();
+        jCheckBox1 = new javax.swing.JCheckBox();
+        jCheckBox2 = new javax.swing.JCheckBox();
+        texturePreview = new javax.swing.JLabel();
+
+        setBackground(new java.awt.Color(170, 170, 170));
+        setMaximumSize(new java.awt.Dimension(95, 65));
+        setMinimumSize(new java.awt.Dimension(40, 40));
+        setPreferredSize(new java.awt.Dimension(95, 45));
+
+        jPanel1.setPreferredSize(new java.awt.Dimension(10, 0));
+
+        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+        jPanel1.setLayout(jPanel1Layout);
+        jPanel1Layout.setHorizontalGroup(
+            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 10, Short.MAX_VALUE)
+        );
+        jPanel1Layout.setVerticalGroup(
+            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 0, Short.MAX_VALUE)
+        );
+
+        jCheckBox1.setBackground(new java.awt.Color(170, 170, 170));
+        jCheckBox1.setFont(new java.awt.Font("Lucida Grande", 0, 10)); // NOI18N
+        jCheckBox1.setForeground(new java.awt.Color(0, 0, 0));
+        jCheckBox1.setText(org.openide.util.NbBundle.getMessage(TexturePanelSquare.class, "TexturePanelSquare.jCheckBox1.text")); // NOI18N
+        jCheckBox1.setFocusable(false);
+        jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0));
+        jCheckBox1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jCheckBox1ActionPerformed(evt);
+            }
+        });
+
+        jCheckBox2.setBackground(new java.awt.Color(170, 170, 170));
+        jCheckBox2.setFont(new java.awt.Font("Lucida Grande", 0, 10)); // NOI18N
+        jCheckBox2.setForeground(new java.awt.Color(0, 0, 0));
+        jCheckBox2.setText(org.openide.util.NbBundle.getMessage(TexturePanelSquare.class, "TexturePanelSquare.jCheckBox2.text")); // NOI18N
+        jCheckBox2.setFocusable(false);
+        jCheckBox2.setMargin(new java.awt.Insets(0, 0, 0, 0));
+        jCheckBox2.setMaximumSize(new java.awt.Dimension(53, 15));
+        jCheckBox2.setMinimumSize(new java.awt.Dimension(53, 15));
+        jCheckBox2.setPreferredSize(new java.awt.Dimension(53, 15));
+        jCheckBox2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jCheckBox2ActionPerformed(evt);
+            }
+        });
+
+        texturePreview.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+        texturePreview.setText(org.openide.util.NbBundle.getMessage(TexturePanelSquare.class, "TexturePanelSquare.text")); // NOI18N
+        texturePreview.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+        texturePreview.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(153, 153, 153)));
+        texturePreview.setFocusable(false);
+        texturePreview.setIconTextGap(0);
+        texturePreview.setMaximumSize(new java.awt.Dimension(60, 60));
+        texturePreview.setMinimumSize(new java.awt.Dimension(40, 40));
+        texturePreview.setName(""); // NOI18N
+        texturePreview.setPreferredSize(new java.awt.Dimension(40, 40));
+
+        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(texturePreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(2, 2, 2)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jCheckBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jCheckBox1))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(layout.createSequentialGroup()
+                    .addGap(0, 94, Short.MAX_VALUE)
+                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addGap(0, 1, Short.MAX_VALUE)))
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(texturePreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(jCheckBox1)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(jCheckBox2, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE)))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(layout.createSequentialGroup()
+                    .addGap(0, 52, Short.MAX_VALUE)
+                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addGap(0, 0, Short.MAX_VALUE)))
+        );
+
+        getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(TexturePanelSquare.class, "TexturePanelSquare.AccessibleContext.accessibleName")); // NOI18N
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox1ActionPerformed
+        if (property == null) {
+            return;
+        }
+        flip = jCheckBox1.isSelected();
+        updateFlipRepeat();
+        fireChanged();
+    }//GEN-LAST:event_jCheckBox1ActionPerformed
+
+    private void jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox2ActionPerformed
+        if (property == null) {
+            return;
+        }
+        repeat = jCheckBox2.isSelected();
+        updateFlipRepeat();
+        fireChanged();
+    }//GEN-LAST:event_jCheckBox2ActionPerformed
+
+    @Override
+    protected void readProperty() {
+        java.awt.EventQueue.invokeLater(new Runnable() {
+
+            @Override
+            public void run() {
+                if (property.getValue().startsWith("Flip Repeat ")) {
+                    flip = true;
+                    repeat = true;
+                    textureName = property.getValue().replaceFirst("Flip Repeat ", "").trim();
+                } else if (property.getValue().startsWith("Flip ")) {
+                    flip = true;
+                    textureName = property.getValue().replaceFirst("Flip ", "").trim();
+                } else if (property.getValue().startsWith("Repeat ")) {
+                    repeat = true;
+                    textureName = property.getValue().replaceFirst("Repeat ", "").trim();
+                } else {
+                    textureName = property.getValue();
+                }
+                displayPreview();
+                texturePreview.setToolTipText(property.getValue());
+                MaterialProperty prop = property;
+                property = null;
+                jCheckBox1.setSelected(flip);
+                jCheckBox2.setSelected(repeat);
+                property = prop;
+            }
+        });
+    }
+
+    @Override
+    public void cleanUp() {
+        if (texPreview != null) {
+            texPreview.cleanUp();
+        }
+        exec.shutdownNow();
+    }
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBox jCheckBox1;
+    private javax.swing.JCheckBox jCheckBox2;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JLabel texturePreview;
+    // End of variables declaration//GEN-END:variables
+}