Jelajahi Sumber

Merge pull request #293 from davidB/david_materiel_no_render

add TechniqueDef.noRender
Kirill Vainer 10 tahun lalu
induk
melakukan
328966ba79

+ 77 - 76
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -69,7 +69,7 @@ import java.util.logging.Logger;
  * Setting the parameters can modify the behavior of a
  * shader.
  * <p/>
- * 
+ *
  * @author Kirill Vainer
  */
 public class Material implements CloneableSmartAsset, Cloneable, Savable {
@@ -146,7 +146,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
     public String getName() {
         return name;
     }
-    
+
     /**
      * This method sets the name of the material.
      * The name is not the same as the asset name.
@@ -222,11 +222,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
     }
 
     /**
-     * Compares two materials and returns true if they are equal. 
+     * Compares two materials and returns true if they are equal.
      * This methods compare definition, parameters, additional render states.
-     * Since materials are mutable objects, implementing equals() properly is not possible, 
+     * Since materials are mutable objects, implementing equals() properly is not possible,
      * hence the name contentEquals().
-     * 
+     *
      * @param otherObj the material to compare to this material
      * @return true if the materials are equal.
      */
@@ -234,15 +234,15 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         if (!(otherObj instanceof Material)) {
             return false;
         }
-        
+
         Material other = (Material) otherObj;
-        
+
         // Early exit if the material are the same object
         if (this == other) {
             return true;
         }
 
-        // Check material definition        
+        // Check material definition
         if (this.getMaterialDef() != other.getMaterialDef()) {
             return false;
         }
@@ -251,12 +251,12 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         if (this.paramValues.size() != other.paramValues.size()) {
             return false;
         }
-        
+
         // Checking technique
         if (this.technique != null || other.technique != null) {
             // Techniques are considered equal if their names are the same
-            // E.g. if user chose custom technique for one material but 
-            // uses default technique for other material, the materials 
+            // E.g. if user chose custom technique for one material but
+            // uses default technique for other material, the materials
             // are not equal.
             String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default";
             String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default";
@@ -290,7 +290,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                 return false;
             }
         }
-        
+
         return true;
     }
 
@@ -305,7 +305,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         hash = 29 * hash + (this.additionalState != null ? this.additionalState.contentHashCode() : 0);
         return hash;
     }
-    
+
     /**
      * Returns the currently active technique.
      * <p>
@@ -436,7 +436,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
     public Collection<MatParam> getParams() {
         return paramValues.values();
     }
-    
+
     /**
      * Returns the ListMap of all parameters set on this material.
      *
@@ -473,7 +473,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
      */
     public void setParam(String name, VarType type, Object value) {
         checkSetParam(type, name);
-        
+
         if (type.isTextureType()) {
             setTextureParam(name, type, (Texture)value);
         } else {
@@ -501,7 +501,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         if (matParam == null) {
             return;
         }
-        
+
         paramValues.remove(name);
         if (matParam instanceof MatParamTexture) {
             int texUnit = ((MatParamTexture) matParam).getUnit();
@@ -728,7 +728,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             renderer.renderMesh(mesh, lodLevel, 1, null);
         }
     }
-    
+
     /**
      * Uploads the lights in the light list as two uniform arrays.<br/><br/> *
      * <p>
@@ -747,30 +747,30 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             return 0;
         }
 
-        Uniform lightData = shader.getUniform("g_LightData");        
-        lightData.setVector4Length(numLights * 3);//8 lights * max 3        
+        Uniform lightData = shader.getUniform("g_LightData");
+        lightData.setVector4Length(numLights * 3);//8 lights * max 3
         Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
-        
 
-        if (startIndex != 0) {        
+
+        if (startIndex != 0) {
             // apply additive blending for 2nd and future passes
             rm.getRenderer().applyRenderState(additiveLight);
-            ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);            
+            ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
         }else{
             ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList,true));
         }
-        
+
         int lightDataIndex = 0;
         TempVars vars = TempVars.get();
         Vector4f tmpVec = vars.vect4f1;
         int curIndex;
         int endIndex = numLights + startIndex;
         for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
-                
-                
-                Light l = lightList.get(curIndex);              
+
+
+                Light l = lightList.get(curIndex);
                 if(l.getType() == Light.Type.Ambient){
-                    endIndex++;    
+                    endIndex++;
                     continue;
                 }
                 ColorRGBA color = l.getColor();
@@ -781,14 +781,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                         l.getType().getId(),
                         lightDataIndex);
                 lightDataIndex++;
-                
+
                 switch (l.getType()) {
                     case Directional:
                         DirectionalLight dl = (DirectionalLight) l;
-                        Vector3f dir = dl.getDirection();                        
+                        Vector3f dir = dl.getDirection();
                         //Data directly sent in view space to avoid a matrix mult for each pixel
                         tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
-                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);      
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
 //                        tmpVec.divideLocal(tmpVec.w);
 //                        tmpVec.normalizeLocal();
                         lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
@@ -802,7 +802,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                         Vector3f pos = pl.getPosition();
                         float invRadius = pl.getInvRadius();
                         tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
-                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);    
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
                         //tmpVec.divideLocal(tmpVec.w);
                         lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
                         lightDataIndex++;
@@ -810,37 +810,37 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                         lightData.setVector4InArray(0,0,0,0, lightDataIndex);
                         lightDataIndex++;
                         break;
-                    case Spot:                      
+                    case Spot:
                         SpotLight sl = (SpotLight) l;
                         Vector3f pos2 = sl.getPosition();
                         Vector3f dir2 = sl.getDirection();
                         float invRange = sl.getInvSpotRange();
                         float spotAngleCos = sl.getPackedAngleCos();
                         tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(),  1.0f);
-                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);   
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
                        // tmpVec.divideLocal(tmpVec.w);
                         lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
                         lightDataIndex++;
-                        
+
                         //We transform the spot direction in view space here to save 5 varying later in the lighting shader
                         //one vec4 less and a vec4 that becomes a vec3
                         //the downside is that spotAngleCos decoding happens now in the frag shader.
                         tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(),  0.0f);
-                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);                           
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
                         tmpVec.normalizeLocal();
                         lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
                         lightDataIndex++;
-                        break;                    
+                        break;
                     default:
                         throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
                 }
         }
-        vars.release();        
+        vars.release();
         //Padding of unsued buffer space
         while(lightDataIndex < numLights * 3) {
             lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
-            lightDataIndex++;             
-        } 
+            lightDataIndex++;
+        }
         return curIndex;
     }
 
@@ -887,10 +887,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                 case Directional:
                     DirectionalLight dl = (DirectionalLight) l;
                     Vector3f dir = dl.getDirection();
-                    //FIXME : there is an inconstency here due to backward 
+                    //FIXME : there is an inconstency here due to backward
                     //compatibility of the lighting shader.
-                    //The directional light direction is passed in the 
-                    //LightPosition uniform. The lighting shader needs to be 
+                    //The directional light direction is passed in the
+                    //LightPosition uniform. The lighting shader needs to be
                     //reworked though in order to fix this.
                     tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
                     lightPos.setValue(VarType.Vector4, tmpLightPosition);
@@ -987,11 +987,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                 for (TechniqueDef techDef : techDefs) {
                     if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
                         // use the first one that supports all the caps
-                        tech = new Technique(this, techDef);                        
+                        tech = new Technique(this, techDef);
                         techniques.put(name, tech);
                         if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() ||
                                tech.getDef().getLightMode() == LightMode.Disable){
-                            break;  
+                            break;
                         }
                     }
                     lastTech = techDef;
@@ -1078,7 +1078,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             Uniform u = uniforms.getValue(i);
             if (!u.isSetByCurrentMaterial()) {
                 if (u.getName().charAt(0) != 'g') {
-                    // Don't reset world globals! 
+                    // Don't reset world globals!
                     // The benefits gained from this are very minimal
                     // and cause lots of matrix -> FloatBuffer conversions.
                     u.clearValue();
@@ -1093,21 +1093,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
      * <p>
      * The material is rendered as follows:
      * <ul>
-     * <li>Determine which technique to use to render the material - 
-     * either what the user selected via 
-     * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) 
-     * Material.selectTechnique()}, 
-     * or the first default technique that the renderer supports 
+     * <li>Determine which technique to use to render the material -
+     * either what the user selected via
+     * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+     * Material.selectTechnique()},
+     * or the first default technique that the renderer supports
      * (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering capabilities})<ul>
-     * <li>If the technique has been changed since the last frame, then it is notified via 
-     * {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean, java.util.EnumSet) 
-     * Technique.makeCurrent()}. 
-     * If the technique wants to use a shader to render the model, it should load it at this part - 
-     * the shader should have all the proper defines as declared in the technique definition, 
-     * including those that are bound to material parameters. 
-     * The technique can re-use the shader from the last frame if 
+     * <li>If the technique has been changed since the last frame, then it is notified via
+     * {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean, java.util.EnumSet)
+     * Technique.makeCurrent()}.
+     * If the technique wants to use a shader to render the model, it should load it at this part -
+     * the shader should have all the proper defines as declared in the technique definition,
+     * including those that are bound to material parameters.
+     * The technique can re-use the shader from the last frame if
      * no changes to the defines occurred.</li></ul>
-     * <li>Set the {@link RenderState} to use for rendering. The render states are 
+     * <li>Set the {@link RenderState} to use for rendering. The render states are
      * applied in this order (later RenderStates override earlier RenderStates):<ol>
      * <li>{@link TechniqueDef#getRenderState() Technique Definition's RenderState}
      * - i.e. specific renderstate that is required for the shader.</li>
@@ -1120,22 +1120,22 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
      * <li>Uniforms bound to material parameters are updated based on the current material parameter values.</li>
      * <li>Uniforms bound to world parameters are updated from the RenderManager.
      * Internally {@link UniformBindingManager} is used for this task.</li>
-     * <li>Uniforms bound to textures will cause the texture to be uploaded as necessary. 
+     * <li>Uniforms bound to textures will cause the texture to be uploaded as necessary.
      * The uniform is set to the texture unit where the texture is bound.</li></ul>
-     * <li>If the technique uses a shader, the model is then rendered according 
+     * <li>If the technique uses a shader, the model is then rendered according
      * to the lighting mode specified on the technique definition.<ul>
-     * <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays 
+     * <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays
      * with the first 4 lights and renders the model once.</li>
-     * <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times, 
-     * for the first light it is rendered opaque, on subsequent lights it is 
+     * <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times,
+     * for the first light it is rendered opaque, on subsequent lights it is
      * rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth writing disabled.</li>
      * </ul>
-     * <li>For techniques that do not use shaders, 
+     * <li>For techniques that do not use shaders,
      * fixed function OpenGL is used to render the model (see {@link GL1Renderer} interface):<ul>
      * <li>OpenGL state ({@link FixedFuncBinding}) that is bound to material parameters is updated. </li>
-     * <li>The texture set on the material is uploaded and bound. 
+     * <li>The texture set on the material is uploaded and bound.
      * Currently only 1 texture is supported for fixed function techniques.</li>
-     * <li>If the technique uses lighting, then OpenGL lighting state is updated 
+     * <li>If the technique uses lighting, then OpenGL lighting state is updated
      * based on the light list on the geometry, otherwise OpenGL lighting is disabled.</li>
      * <li>The mesh is uploaded and rendered.</li>
      * </ul>
@@ -1147,10 +1147,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
      */
     public void render(Geometry geom, LightList lights, RenderManager rm) {
         autoSelectTechnique(rm);
+        TechniqueDef techDef = technique.getDef();
 
-        Renderer r = rm.getRenderer();
+        if (techDef.isNoRender()) return;
 
-        TechniqueDef techDef = technique.getDef();
+        Renderer r = rm.getRenderer();
 
         if (rm.getForcedRenderState() != null) {
             r.applyRenderState(rm.getForcedRenderState());
@@ -1169,7 +1170,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         // reset unchanged uniform flag
         clearUniformsSetByCurrent(technique.getShader());
         rm.updateUniformBindings(technique.getWorldBindUniforms());
-        
+
 
         // setup textures and uniforms
         for (int i = 0; i < paramValues.size(); i++) {
@@ -1212,24 +1213,24 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         // any unset uniforms will be set to 0
         resetUniformsNotSetByCurrent(shader);
         r.setShader(shader);
-        
+
         renderMeshFromGeometry(r, geom);
     }
 
     /**
      * Called by {@link RenderManager} to render the geometry by
      * using this material.
-     * 
+     *
      * Note that this version of the render method
      * does not perform light filtering.
-     * 
+     *
      * @param geom The geometry to render
      * @param rm The render manager requesting the rendering
      */
     public void render(Geometry geom, RenderManager rm) {
         render(geom, geom.getWorldLightList(), rm);
     }
-    
+
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);
         oc.write(def.getAssetName(), "material_def", null);
@@ -1304,14 +1305,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                     continue;
                 }
             }
-            
+
             if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) {
                 // Ancient version of jME3 ...
                 param.setName(param.getName().substring(2));
             }
-            
+
             if (def.getMaterialParam(param.getName()) == null) {
-                logger.log(Level.WARNING, "The material parameter is not defined: {0}. Ignoring..", 
+                logger.log(Level.WARNING, "The material parameter is not defined: {0}. Ignoring..",
                                           param.getName());
             } else {
                 checkSetParam(param.getVarType(), param.getName());

+ 80 - 55
jme3-core/src/main/java/com/jme3/material/TechniqueDef.java

@@ -40,7 +40,7 @@ import java.util.*;
 
 /**
  * Describes a technique definition.
- * 
+ *
  * @author Kirill Vainer
  */
 public class TechniqueDef implements Savable {
@@ -49,7 +49,7 @@ public class TechniqueDef implements Savable {
      * Version #1: Separate shader language for each shader source.
      */
     public static final int SAVABLE_VERSION = 1;
-    
+
     /**
      * Describes light rendering mode.
      */
@@ -58,15 +58,15 @@ public class TechniqueDef implements Savable {
          * Disable light-based rendering
          */
         Disable,
-        
+
         /**
-         * Enable light rendering by using a single pass. 
+         * Enable light rendering by using a single pass.
          * <p>
          * An array of light positions and light colors is passed to the shader
          * containing the world light list for the geometry being rendered.
          */
         SinglePass,
-        
+
         /**
          * Enable light rendering by using multi-pass rendering.
          * <p>
@@ -77,7 +77,7 @@ public class TechniqueDef implements Savable {
          * passes have it set to black.
          */
         MultiPass,
-        
+
         /**
          * @deprecated OpenGL1 is not supported anymore
          */
@@ -96,15 +96,16 @@ public class TechniqueDef implements Savable {
 
     private EnumMap<Shader.ShaderType,String> shaderLanguages;
     private EnumMap<Shader.ShaderType,String> shaderNames;
-    
+
     private DefineList presetDefines;
     private boolean usesNodes = false;
     private List<ShaderNode> shaderNodes;
     private ShaderGenerationInfo shaderGenerationInfo;
 
+    private boolean noRender = false;
     private RenderState renderState;
     private RenderState forcedRenderState;
-    
+
     private LightMode lightMode   = LightMode.Disable;
     private ShadowMode shadowMode = ShadowMode.Disable;
 
@@ -115,7 +116,7 @@ public class TechniqueDef implements Savable {
      * Creates a new technique definition.
      * <p>
      * Used internally by the J3M/J3MD loader.
-     * 
+     *
      * @param name The name of the technique, should be set to <code>null</code>
      * for default techniques.
      */
@@ -135,7 +136,7 @@ public class TechniqueDef implements Savable {
     /**
      * Returns the name of this technique as specified in the J3MD file.
      * Default techniques have the name "Default".
-     * 
+     *
      * @return the name of this technique
      */
     public String getName(){
@@ -153,9 +154,9 @@ public class TechniqueDef implements Savable {
 
     /**
      * Set the light mode
-     * 
+     *
      * @param lightMode the light mode
-     * 
+     *
      * @see LightMode
      */
     public void setLightMode(LightMode lightMode) {
@@ -172,9 +173,9 @@ public class TechniqueDef implements Savable {
 
     /**
      * Set the shadow mode.
-     * 
+     *
      * @param shadowMode the shadow mode.
-     * 
+     *
      * @see ShadowMode
      */
     public void setShadowMode(ShadowMode shadowMode) {
@@ -184,7 +185,7 @@ public class TechniqueDef implements Savable {
     /**
      * Returns the render state that this technique is using
      * @return the render state that this technique is using
-     * @see #setRenderState(com.jme3.material.RenderState) 
+     * @see #setRenderState(com.jme3.material.RenderState)
      */
     public RenderState getRenderState() {
         return renderState;
@@ -192,15 +193,37 @@ public class TechniqueDef implements Savable {
 
     /**
      * Sets the render state that this technique is using.
-     * 
+     *
      * @param renderState the render state that this technique is using.
-     * 
+     *
      * @see RenderState
      */
     public void setRenderState(RenderState renderState) {
         this.renderState = renderState;
     }
 
+    /**
+     * Sets if this technique should not be used to render.
+     *
+     * @param noRender not render or render ?
+     *
+     * @see NoRender
+     */
+    public void setNoRender(boolean noRender) {
+        this.noRender = noRender;
+    }
+
+    /**
+     * Returns true if this technique should not be used to render.
+     * (eg. to not render a material with default technique)
+     *
+     * @return true if this technique should not be rendered, false otherwise.
+     *
+     */
+    public boolean isNoRender(){
+        return noRender;
+    }
+
     /**
      * @deprecated jME3 always requires shaders now
      */
@@ -208,12 +231,12 @@ public class TechniqueDef implements Savable {
     public boolean isUsingShaders(){
         return true;
     }
-    
+
     /**
      * Returns true if this technique uses Shader Nodes, false otherwise.
-     * 
+     *
      * @return true if this technique uses Shader Nodes, false otherwise.
-     * 
+     *
      */
     public boolean isUsingShaderNodes(){
         return usesNodes;
@@ -222,7 +245,7 @@ public class TechniqueDef implements Savable {
     /**
      * Gets the {@link Caps renderer capabilities} that are required
      * by this technique.
-     * 
+     *
      * @return the required renderer capabilities
      */
     public EnumSet<Caps> getRequiredCaps() {
@@ -231,7 +254,7 @@ public class TechniqueDef implements Savable {
 
     /**
      * Sets the shaders that this technique definition will use.
-     * 
+     *
      * @param vertexShader The name of the vertex shader
      * @param fragmentShader The name of the fragment shader
      * @param vertLanguage The vertex shader language
@@ -242,7 +265,7 @@ public class TechniqueDef implements Savable {
         this.shaderNames.put(Shader.ShaderType.Vertex, vertexShader);
         this.shaderLanguages.put(Shader.ShaderType.Fragment, fragLanguage);
         this.shaderNames.put(Shader.ShaderType.Fragment, fragmentShader);
-        
+
         requiredCaps.clear();
         Caps vertCap = Caps.valueOf(vertLanguage);
         requiredCaps.add(vertCap);
@@ -259,17 +282,17 @@ public class TechniqueDef implements Savable {
      */
     public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> shaderLanguages) {
         requiredCaps.clear();
-        
+
         for (Shader.ShaderType shaderType : shaderNames.keySet()) {
             String language = shaderLanguages.get(shaderType);
             String shaderFile = shaderNames.get(shaderType);
-            
+
             this.shaderLanguages.put(shaderType, language);
             this.shaderNames.put(shaderType, shaderFile);
-            
+
             Caps vertCap = Caps.valueOf(language);
             requiredCaps.add(vertCap);
-            
+
             if (shaderType.equals(Shader.ShaderType.Geometry)) {
                 requiredCaps.add(Caps.GeometryShader);
             } else if (shaderType.equals(Shader.ShaderType.TessellationControl)) {
@@ -280,11 +303,11 @@ public class TechniqueDef implements Savable {
 
     /**
      * Returns the define name which the given material parameter influences.
-     * 
+     *
      * @param paramName The parameter name to look up
      * @return The define name
-     * 
-     * @see #addShaderParamDefine(java.lang.String, java.lang.String) 
+     *
+     * @see #addShaderParamDefine(java.lang.String, java.lang.String)
      */
     public String getShaderParamDefine(String paramName){
         if (defineParams == null) {
@@ -297,11 +320,11 @@ public class TechniqueDef implements Savable {
      * Adds a define linked to a material parameter.
      * <p>
      * Any time the material parameter on the parent material is altered,
-     * the appropriate define on the technique will be modified as well. 
-     * See the method 
+     * the appropriate define on the technique will be modified as well.
+     * See the method
      * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
      * on the exact details of how the material parameter changes the define.
-     * 
+     *
      * @param paramName The name of the material parameter to link to.
      * @param defineName The name of the define parameter, e.g. USE_LIGHTING
      */
@@ -314,26 +337,26 @@ public class TechniqueDef implements Savable {
 
     /**
      * Returns the {@link DefineList} for the preset defines.
-     * 
+     *
      * @return the {@link DefineList} for the preset defines.
-     * 
-     * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object) 
+     *
+     * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
      */
     public DefineList getShaderPresetDefines() {
         return presetDefines;
     }
-    
+
     /**
-     * Adds a preset define. 
+     * Adds a preset define.
      * <p>
      * Preset defines do not depend upon any parameters to be activated,
      * they are always passed to the shader as long as this technique is used.
-     * 
+     *
      * @param defineName The name of the define parameter, e.g. USE_LIGHTING
-     * @param type The type of the define. See 
+     * @param type The type of the define. See
      * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
      * to see why it matters.
-     * 
+     *
      * @param value The value of the define
      */
     public void addShaderPresetDefine(String defineName, VarType type, Object value){
@@ -346,18 +369,18 @@ public class TechniqueDef implements Savable {
     /**
      * Returns the name of the fragment shader used by the technique, or null
      * if no fragment shader is specified.
-     * 
+     *
      * @return the name of the fragment shader to be used.
      */
     public String getFragmentShaderName() {
         return shaderNames.get(Shader.ShaderType.Fragment);
     }
 
-    
+
     /**
      * Returns the name of the vertex shader used by the technique, or null
      * if no vertex shader is specified.
-     * 
+     *
      * @return the name of the vertex shader to be used.
      */
     public String getVertexShaderName() {
@@ -370,7 +393,7 @@ public class TechniqueDef implements Savable {
     public String getFragmentShaderLanguage() {
         return shaderLanguages.get(Shader.ShaderType.Fragment);
     }
-    
+
     /**
      * Returns the language of the vertex shader used in this technique.
      */
@@ -390,10 +413,10 @@ public class TechniqueDef implements Savable {
     public String getShaderProgramName(Shader.ShaderType shaderType){
         return shaderNames.get(shaderType);
     }
-    
+
     /**
      * Adds a new world parameter by the given name.
-     * 
+     *
      * @param name The world parameter to add.
      * @return True if the world parameter name was found and added
      * to the list of world parameters, false otherwise.
@@ -402,7 +425,7 @@ public class TechniqueDef implements Savable {
         if (worldBinds == null){
             worldBinds = new ArrayList<UniformBinding>();
         }
-        
+
         try {
             worldBinds.add( UniformBinding.valueOf(name) );
             return true;
@@ -418,11 +441,11 @@ public class TechniqueDef implements Savable {
     public void setForcedRenderState(RenderState forcedRenderState) {
         this.forcedRenderState = forcedRenderState;
     }
-    
+
     /**
      * Returns a list of world parameters that are used by this
      * technique definition.
-     * 
+     *
      * @return The list of world parameters
      */
     public List<UniformBinding> getWorldBindings() {
@@ -448,10 +471,11 @@ public class TechniqueDef implements Savable {
         oc.write(lightMode, "lightMode", LightMode.Disable);
         oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
         oc.write(renderState, "renderState", null);
+        oc.write(noRender, "noRender", false);
         oc.write(usesNodes, "usesNodes", false);
         oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null);
         oc.write(shaderGenerationInfo, "shaderGenerationInfo", null);
-        
+
         // TODO: Finish this when Map<String, String> export is available
 //        oc.write(defineParams, "defineParams", null);
         // TODO: Finish this when List<Enum> export is available
@@ -470,7 +494,8 @@ public class TechniqueDef implements Savable {
         lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
         shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
         renderState = (RenderState) ic.readSavable("renderState", null);
-        
+        noRender = ic.readBoolean("noRender", false);
+
         if (ic.getSavableVersion(TechniqueDef.class) == 0) {
             // Old version
             shaderLanguages.put(Shader.ShaderType.Vertex,ic.readString("shaderLang", null));
@@ -483,7 +508,7 @@ public class TechniqueDef implements Savable {
             shaderLanguages.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlLanguage", null));
             shaderLanguages.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalLanguage", null));
         }
-        
+
         usesNodes = ic.readBoolean("usesNodes", false);
         shaderNodes = ic.readSavableArrayList("shaderNodes", null);
         shaderGenerationInfo = (ShaderGenerationInfo) ic.readSavable("shaderGenerationInfo", null);
@@ -525,6 +550,6 @@ public class TechniqueDef implements Savable {
     //todo: make toString return something usefull
     @Override
     public String toString() {
-        return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + '}';
-    }    
+        return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + ", noRender=" + noRender + '}';
+    }
 }

+ 48 - 46
jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java

@@ -63,7 +63,7 @@ public class J3MLoader implements AssetLoader {
    // private ErrorLogger errors;
     private ShaderNodeLoaderDelegate nodesLoaderDelegate;
     boolean isUseNodes = false;
-    
+
     private AssetManager assetManager;
     private AssetKey key;
 
@@ -168,7 +168,7 @@ public class J3MLoader implements AssetLoader {
             if (tex != null){
                 if (repeat){
                     tex.setWrap(WrapMode.Repeat);
-                }                
+                }
             }else{
                 tex = new Texture2D(PlaceholderAssets.getPlaceholderImage(assetManager));
                 if (repeat){
@@ -176,7 +176,7 @@ public class J3MLoader implements AssetLoader {
                 }
                 tex.setKey(texKey);
                 tex.setName(texKey.getName());
-            }         
+            }
             return tex;
         }else{
             String[] split = value.trim().split(whitespacePattern);
@@ -222,15 +222,15 @@ public class J3MLoader implements AssetLoader {
             }
         }
     }
-    
-    // <TYPE> <NAME> [ "(" <FFBINDING> ")" ] [-LINEAR] [ ":" <DEFAULTVAL> ] 
+
+    // <TYPE> <NAME> [ "(" <FFBINDING> ")" ] [-LINEAR] [ ":" <DEFAULTVAL> ]
     private void readParam(String statement) throws IOException{
         String name;
         String defaultVal = null;
         ColorSpace colorSpace = null;
-        
+
         String[] split = statement.split(":");
-        
+
         // Parse default val
         if (split.length == 1){
             // Doesn't contain default value
@@ -239,14 +239,14 @@ public class J3MLoader implements AssetLoader {
                 throw new IOException("Parameter statement syntax incorrect");
             }
             statement = split[0].trim();
-            defaultVal = split[1].trim();           
+            defaultVal = split[1].trim();
         }
-        
+
         if (statement.endsWith("-LINEAR")) {
             colorSpace = ColorSpace.Linear;
             statement = statement.substring(0, statement.length() - "-LINEAR".length());
         }
-        
+
         // Parse ffbinding
         int startParen = statement.indexOf("(");
         if (startParen != -1){
@@ -256,32 +256,32 @@ public class J3MLoader implements AssetLoader {
             // don't care about bindingStr
             statement = statement.substring(0, startParen);
         }
-        
+
         // Parse type + name
         split = statement.split(whitespacePattern);
         if (split.length != 2){
             throw new IOException("Parameter statement syntax incorrect");
         }
-        
+
         VarType type;
         if (split[0].equals("Color")){
             type = VarType.Vector4;
         }else{
             type = VarType.valueOf(split[0]);
         }
-        
+
         name = split[1];
-        
+
         Object defaultValObj = null;
-        if (defaultVal != null){ 
+        if (defaultVal != null){
             defaultValObj = readValue(type, defaultVal);
         }
         if(type.isTextureType()){
-            materialDef.addMaterialParamTexture(type, name, colorSpace);    
+            materialDef.addMaterialParamTexture(type, name, colorSpace);
         }else{
             materialDef.addMaterialParam(type, name, defaultValObj);
         }
-        
+
     }
 
     private void readValueParam(String statement) throws IOException{
@@ -376,7 +376,7 @@ public class J3MLoader implements AssetLoader {
         technique.setRenderState(renderState);
         renderState = null;
     }
-    
+
     private void readForcedRenderState(List<Statement> renderStates) throws IOException{
         renderState = new RenderState();
         for (Statement statement : renderStates){
@@ -385,7 +385,7 @@ public class J3MLoader implements AssetLoader {
         technique.setForcedRenderState(renderState);
         renderState = null;
     }
-    
+
     // <DEFINENAME> [ ":" <PARAMNAME> ]
     private void readDefine(String statement) throws IOException{
         String[] split = statement.split(":");
@@ -405,9 +405,9 @@ public class J3MLoader implements AssetLoader {
         }
 
     }
-    
+
     private void readTechniqueStatement(Statement statement) throws IOException{
-        String[] split = statement.getLine().split("[ \\{]");       
+        String[] split = statement.getLine().split("[ \\{]");
         if (split[0].equals("VertexShader") ||
                 split[0].equals("FragmentShader") ||
                 split[0].equals("GeometryShader") ||
@@ -420,12 +420,12 @@ public class J3MLoader implements AssetLoader {
             readShadowMode(statement.getLine());
         }else if (split[0].equals("WorldParameters")){
             readWorldParams(statement.getContents());
-        }else if (split[0].equals("RenderState")){  
+        }else if (split[0].equals("RenderState")){
             readRenderState(statement.getContents());
-        }else if (split[0].equals("ForcedRenderState")){  
+        }else if (split[0].equals("ForcedRenderState")){
             readForcedRenderState(statement.getContents());
-        }else if (split[0].equals("Defines")){           
-            readDefines(statement.getContents());         
+        }else if (split[0].equals("Defines")){
+            readDefines(statement.getContents());
         } else if (split[0].equals("ShaderNodesDefinitions")) {
             initNodesLoader();
             if (isUseNodes) {
@@ -438,14 +438,16 @@ public class J3MLoader implements AssetLoader {
             }
         } else if (split[0].equals("FragmentShaderNodes")) {
             initNodesLoader();
-            if (isUseNodes) {                
+            if (isUseNodes) {
                 nodesLoaderDelegate.readFragmentShaderNodes(statement.getContents());
             }
+        } else if (split[0].equals("NoRender")) {
+            technique.setNoRender(true);
         } else {
             throw new MatParseException(null, split[0], statement);
         }
     }
-    
+
     private void readTransparentStatement(String statement) throws IOException{
         String[] split = statement.split(whitespacePattern);
         if (split.length != 2){
@@ -465,11 +467,11 @@ public class J3MLoader implements AssetLoader {
         } else {
             throw new IOException("Technique statement syntax incorrect");
         }
-        
+
         for (Statement statement : techStat.getContents()){
             readTechniqueStatement(statement);
         }
-        
+
         if(isUseNodes){
             nodesLoaderDelegate.computeConditions();
             //used for caching later, the shader here is not a file.
@@ -479,14 +481,14 @@ public class J3MLoader implements AssetLoader {
         if (shaderName.containsKey(Shader.ShaderType.Vertex) && shaderName.containsKey(Shader.ShaderType.Fragment)) {
             technique.setShaderFile(shaderName, shaderLanguage);
         }
-        
+
         materialDef.addTechniqueDef(technique);
         technique = null;
         shaderLanguage.clear();
         shaderName.clear();
     }
 
-    private void loadFromRoot(List<Statement> roots) throws IOException{       
+    private void loadFromRoot(List<Statement> roots) throws IOException{
         if (roots.size() == 2){
             Statement exception = roots.get(0);
             String line = exception.getLine();
@@ -498,7 +500,7 @@ public class J3MLoader implements AssetLoader {
         }else if (roots.size() != 1){
             throw new IOException("Too many roots in J3M/J3MD file");
         }
-               
+
         boolean extending = false;
         Statement materialStat = roots.get(0);
         String materialName = materialStat.getLine();
@@ -511,16 +513,16 @@ public class J3MLoader implements AssetLoader {
         }else{
             throw new IOException("Specified file is not a Material file");
         }
-        
+
         String[] split = materialName.split(":", 2);
-        
+
         if (materialName.equals("")){
-            throw new MatParseException("Material name cannot be empty", materialStat);         
+            throw new MatParseException("Material name cannot be empty", materialStat);
         }
 
         if (split.length == 2){
             if (!extending){
-                throw new MatParseException("Must use 'Material' when extending.", materialStat); 
+                throw new MatParseException("Must use 'Material' when extending.", materialStat);
             }
 
             String extendedMat = split[1].trim();
@@ -535,15 +537,15 @@ public class J3MLoader implements AssetLoader {
 //            material.setAssetName(fileName);
         }else if (split.length == 1){
             if (extending){
-                throw new MatParseException("Expected ':', got '{'", materialStat);               
+                throw new MatParseException("Expected ':', got '{'", materialStat);
             }
             materialDef = new MaterialDef(assetManager, materialName);
             // NOTE: pass file name for defs so they can be loaded later
             materialDef.setAssetName(key.getName());
         }else{
-            throw new MatParseException("Cannot use colon in material name/path", materialStat);   
+            throw new MatParseException("Cannot use colon in material name/path", materialStat);
         }
-        
+
         for (Statement statement : materialStat.getContents()){
             split = statement.getLine().split("[ \\{]");
             String statType = split[0];
@@ -561,16 +563,16 @@ public class J3MLoader implements AssetLoader {
                 }else if (statType.equals("MaterialParameters")){
                     readMaterialParams(statement.getContents());
                 }else{
-                    throw new MatParseException("Expected material statement, got '"+statType+"'", statement);                       
+                    throw new MatParseException("Expected material statement, got '"+statType+"'", statement);
                 }
             }
         }
     }
 
-    public Object load(AssetInfo info) throws IOException {       
+    public Object load(AssetInfo info) throws IOException {
         this.assetManager = info.getManager();
-        
-        InputStream in = info.openStream();        
+
+        InputStream in = info.openStream();
         try {
             key = info.getKey();
             if (key.getExtension().equals("j3m") && !(key instanceof MaterialKey)) {
@@ -584,7 +586,7 @@ public class J3MLoader implements AssetLoader {
                 in.close();
             }
         }
-        
+
         if (material != null){
             // material implementation
             return material;
@@ -593,7 +595,7 @@ public class J3MLoader implements AssetLoader {
             return materialDef;
         }
     }
-    
+
     public MaterialDef loadMaterialDef(List<Statement> roots, AssetManager manager, AssetKey key) throws IOException {
         this.key = key;
         this.assetManager = manager;
@@ -615,6 +617,6 @@ public class J3MLoader implements AssetLoader {
                 nodesLoaderDelegate.setAssetManager(assetManager);
             }
         }
-    }   
+    }
 
 }