diff --git a/jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java b/jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java index 2958f2aa0..fd3ce155f 100644 --- a/jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java +++ b/jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java @@ -48,7 +48,7 @@ import java.util.List; * * @author Nehon */ -public class ShaderGenerationInfo implements Savable { +public class ShaderGenerationInfo implements Savable, Cloneable { /** * the list of attributes of the vertex shader @@ -187,4 +187,36 @@ public class ShaderGenerationInfo implements Savable { vertexGlobal = (ShaderNodeVariable) ic.readSavable("vertexGlobal", null); } + + @Override + protected Object clone() throws CloneNotSupportedException { + ShaderGenerationInfo clone = (ShaderGenerationInfo) super.clone(); + + for (ShaderNodeVariable attribute : attributes) { + clone.attributes.add((ShaderNodeVariable) attribute.clone()); + } + + for (ShaderNodeVariable uniform : vertexUniforms) { + clone.vertexUniforms.add((ShaderNodeVariable) uniform.clone()); + } + + clone.vertexGlobal = (ShaderNodeVariable) vertexGlobal.clone(); + + + for (ShaderNodeVariable varying : varyings) { + clone.varyings.add((ShaderNodeVariable) varying.clone()); + } + + for (ShaderNodeVariable uniform : fragmentUniforms) { + clone.fragmentUniforms.add((ShaderNodeVariable) uniform.clone()); + } + + for (ShaderNodeVariable globals : fragmentGlobals) { + clone.fragmentGlobals.add((ShaderNodeVariable) globals.clone()); + } + + clone.unusedNodes.addAll(unusedNodes); + + return clone; + } } diff --git a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java index 62a0a0cd3..83135cf0c 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -39,6 +39,7 @@ import com.jme3.shader.*; import com.jme3.shader.Shader.ShaderType; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.*; /** @@ -46,7 +47,7 @@ import java.util.*; * * @author Kirill Vainer */ -public class TechniqueDef implements Savable { +public class TechniqueDef implements Savable, Cloneable { /** * Version #1: Separate shader language for each shader source. @@ -539,7 +540,7 @@ public class TechniqueDef implements Savable { public void setShaderFile(EnumMap shaderNames, EnumMap shaderLanguages) { requiredCaps.clear(); - int maxCap = 0; + weight = 0; for (Shader.ShaderType shaderType : shaderNames.keySet()) { String language = shaderLanguages.get(shaderType); String shaderFile = shaderNames.get(shaderType); @@ -549,7 +550,7 @@ public class TechniqueDef implements Savable { Caps cap = Caps.valueOf(language); requiredCaps.add(cap); - maxCap = Math.max(maxCap, cap.ordinal()); + weight = Math.max(weight, cap.ordinal()); if (shaderType.equals(Shader.ShaderType.Geometry)) { requiredCaps.add(Caps.GeometryShader); @@ -557,7 +558,6 @@ public class TechniqueDef implements Savable { requiredCaps.add(Caps.TesselationShader); } } - weight = maxCap; } /** @@ -776,4 +776,55 @@ public class TechniqueDef implements Savable { public void setLightSpace(LightSpace lightSpace) { this.lightSpace = lightSpace; } + + @Override + public Object clone() throws CloneNotSupportedException { + //cannot use super.clone because of the final fields instance that would be shared by the clones. + TechniqueDef clone = new TechniqueDef(name, sortId); + + clone.noRender = noRender; + clone.lightMode = lightMode; + clone.shadowMode = shadowMode; + clone.lightSpace = lightSpace; + clone.usesNodes = usesNodes; + clone.shaderPrologue = shaderPrologue; + + clone.setShaderFile(shaderNames, shaderLanguages); + + clone.defineNames = new ArrayList<>(defineNames.size()); + clone.defineNames.addAll(defineNames); + + clone.defineTypes = new ArrayList<>(defineTypes.size()); + clone.defineTypes.addAll(defineTypes); + + clone.paramToDefineId = new HashMap<>(paramToDefineId.size()); + clone.paramToDefineId.putAll(paramToDefineId); + + if (shaderNodes != null) { + for (ShaderNode shaderNode : shaderNodes) { + clone.shaderNodes.add((ShaderNode) shaderNode.clone()); + } + clone.shaderGenerationInfo = (ShaderGenerationInfo) shaderGenerationInfo.clone(); + } + + if (renderState != null) { + clone.setRenderState(renderState.clone()); + } + if (forcedRenderState != null) { + clone.setForcedRenderState(forcedRenderState.clone()); + } + + try { + clone.logic = logic.getClass().getConstructor(TechniqueDef.class).newInstance(clone); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + e.printStackTrace(); + } + + if (worldBinds != null) { + clone.worldBinds = new ArrayList<>(worldBinds.size()); + clone.worldBinds.addAll(worldBinds); + } + + return clone; + } } diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderNode.java b/jme3-core/src/main/java/com/jme3/shader/ShaderNode.java index 0b65edf15..9f8156933 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderNode.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderNode.java @@ -54,7 +54,7 @@ import java.util.List; * * @author Nehon */ -public class ShaderNode implements Savable { +public class ShaderNode implements Savable, Cloneable { private String name; private ShaderNodeDefinition definition; @@ -212,4 +212,24 @@ public class ShaderNode implements Savable { public String toString() { return "\nShaderNode{" + "\nname=" + name + ", \ndefinition=" + definition.getName() + ", \ncondition=" + condition + ", \ninputMapping=" + inputMapping + ", \noutputMapping=" + outputMapping + '}'; } + + @Override + public Object clone() throws CloneNotSupportedException { + ShaderNode clone = (ShaderNode) super.clone(); + + //No need to clone the definition. + clone.definition = definition; + + clone.inputMapping = new ArrayList<>(); + for (VariableMapping variableMapping : inputMapping) { + clone.inputMapping.add((VariableMapping) variableMapping.clone()); + } + + clone.outputMapping = new ArrayList<>(); + for (VariableMapping variableMapping : outputMapping) { + clone.outputMapping.add((VariableMapping) variableMapping.clone()); + } + + return clone; + } } diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java b/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java index d3588de34..7d0d959e3 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java @@ -243,11 +243,6 @@ public class ShaderNodeVariable implements Savable, Cloneable { return "\n" + type + ' ' + (nameSpace != null ? (nameSpace + '.') : "") + name; } - @Override - public ShaderNodeVariable clone() { - return new ShaderNodeVariable(type, nameSpace, name); - } - /** * * @return true if this variable is a shader output @@ -281,6 +276,9 @@ public class ShaderNodeVariable implements Savable, Cloneable { public void setMultiplicity(String multiplicity) { this.multiplicity = multiplicity; } - - + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } } diff --git a/jme3-core/src/main/java/com/jme3/shader/VariableMapping.java b/jme3-core/src/main/java/com/jme3/shader/VariableMapping.java index 5f134a002..61ae612fd 100644 --- a/jme3-core/src/main/java/com/jme3/shader/VariableMapping.java +++ b/jme3-core/src/main/java/com/jme3/shader/VariableMapping.java @@ -43,7 +43,7 @@ import java.io.IOException; * * @author Nehon */ -public class VariableMapping implements Savable { +public class VariableMapping implements Savable, Cloneable { private ShaderNodeVariable leftVariable; private ShaderNodeVariable rightVariable; @@ -195,4 +195,14 @@ public class VariableMapping implements Savable { public String toString() { return "\n{" + leftVariable.toString() + (leftSwizzling.length() > 0 ? ("." + leftSwizzling) : "") + " = " + rightVariable.getType() + " " + rightVariable.getNameSpace() + "." + rightVariable.getName() + (rightSwizzling.length() > 0 ? ("." + rightSwizzling) : "") + " : " + condition + "}"; } + + @Override + protected Object clone() throws CloneNotSupportedException { + VariableMapping clone = (VariableMapping) super.clone(); + + clone.leftVariable = (ShaderNodeVariable) leftVariable.clone(); + clone.rightVariable = (ShaderNodeVariable) rightVariable.clone(); + + return clone; + } } diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index b77934078..8cecbb6de 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -43,21 +43,18 @@ import com.jme3.material.logic.StaticPassLightingLogic; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.shader.DefineList; -import com.jme3.shader.Shader; -import com.jme3.shader.VarType; +import com.jme3.shader.*; import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.util.PlaceholderAssets; import com.jme3.util.blockparser.BlockLanguageParser; import com.jme3.util.blockparser.Statement; +import com.jme3.util.clone.Cloner; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -69,6 +66,7 @@ public class J3MLoader implements AssetLoader { // private ErrorLogger errors; private ShaderNodeLoaderDelegate nodesLoaderDelegate; boolean isUseNodes = false; + int langSize = 0; private AssetManager assetManager; private AssetKey key; @@ -79,13 +77,13 @@ public class J3MLoader implements AssetLoader { private RenderState renderState; private ArrayList presetDefines = new ArrayList(); - private EnumMap shaderLanguages; + private List> shaderLanguages; private EnumMap shaderNames; private static final String whitespacePattern = "\\p{javaWhitespace}+"; public J3MLoader() { - shaderLanguages = new EnumMap<>(Shader.ShaderType.class); + shaderLanguages = new ArrayList<>();// EnumMap<>(Shader.ShaderType.class); shaderNames = new EnumMap<>(Shader.ShaderType.class); } @@ -97,20 +95,29 @@ public class J3MLoader implements AssetLoader { throw new IOException("Shader statement syntax incorrect" + statement); } String[] typeAndLang = split[0].split(whitespacePattern); - if (typeAndLang.length != 2) { - throw new IOException("Shader statement syntax incorrect: " + statement); - } for (Shader.ShaderType shaderType : Shader.ShaderType.values()) { if (typeAndLang[0].equals(shaderType.toString() + "Shader")) { - readShaderDefinition(shaderType, split[1].trim(), typeAndLang[1]); + + readShaderDefinition(shaderType, split[1].trim(), Arrays.copyOfRange(typeAndLang, 1, typeAndLang.length)); } } } - private void readShaderDefinition(Shader.ShaderType shaderType, String name, String language) { + + private void readShaderDefinition(Shader.ShaderType shaderType, String name, String... languages) { shaderNames.put(shaderType, name); - shaderLanguages.put(shaderType, language); + + if (langSize != 0 && langSize != languages.length) { + throw new AssetLoadException("Technique " + technique.getName() + " must have the same number of languages for each shader type."); + } + langSize = languages.length; + for (int i = 0; i < languages.length; i++) { + if (i >= shaderLanguages.size()) { + shaderLanguages.add(new EnumMap(Shader.ShaderType.class)); + } + shaderLanguages.get(i).put(shaderType, languages[i]); + } } // LightMode @@ -608,6 +615,7 @@ public class J3MLoader implements AssetLoader { private void readTechnique(Statement techStat) throws IOException{ isUseNodes = false; String[] split = techStat.getLine().split(whitespacePattern); + Cloner cloner = new Cloner(); String name; if (split.length == 1) { @@ -625,6 +633,30 @@ public class J3MLoader implements AssetLoader { readTechniqueStatement(statement); } + technique.setShaderPrologue(createShaderPrologue(presetDefines)); + + switch (technique.getLightMode()) { + case Disable: + technique.setLogic(new DefaultTechniqueDefLogic(technique)); + break; + case MultiPass: + technique.setLogic(new MultiPassLightingLogic(technique)); + break; + case SinglePass: + technique.setLogic(new SinglePassLightingLogic(technique)); + break; + case StaticPass: + technique.setLogic(new StaticPassLightingLogic(technique)); + break; + case SinglePassAndImageBased: + technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique)); + break; + default: + throw new UnsupportedOperationException(); + } + + List techniqueDefs = new ArrayList<>(); + if(isUseNodes){ nodesLoaderDelegate.computeConditions(); @@ -634,43 +666,36 @@ public class J3MLoader implements AssetLoader { // Not sure if this is needed anymore, since shader caching // is now done by TechniqueDef. technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100"); + techniqueDefs.add(technique); }else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) { - technique.setShaderFile(shaderNames, shaderLanguages); + if (shaderLanguages.size() > 1) { + for (int i = 1; i < shaderLanguages.size(); i++) { + TechniqueDef td = cloner.clone(technique); + td.setShaderFile(shaderNames, shaderLanguages.get(i)); + techniqueDefs.add(td); + } + } + technique.setShaderFile(shaderNames, shaderLanguages.get(0)); + techniqueDefs.add(technique); + } else { technique = null; shaderLanguages.clear(); shaderNames.clear(); presetDefines.clear(); + langSize = 0; logger.log(Level.WARNING, "Fixed function technique was ignored"); logger.log(Level.WARNING, "Fixed function technique ''{0}'' was ignored for material {1}", new Object[]{name, key}); return; } - - technique.setShaderPrologue(createShaderPrologue(presetDefines)); - - switch (technique.getLightMode()) { - case Disable: - technique.setLogic(new DefaultTechniqueDefLogic(technique)); - break; - case MultiPass: - technique.setLogic(new MultiPassLightingLogic(technique)); - break; - case SinglePass: - technique.setLogic(new SinglePassLightingLogic(technique)); - break; - case StaticPass: - technique.setLogic(new StaticPassLightingLogic(technique)); - break; - case SinglePassAndImageBased: - technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique)); - break; - default: - throw new UnsupportedOperationException(); + + for (TechniqueDef techniqueDef : techniqueDefs) { + materialDef.addTechniqueDef(techniqueDef); } - materialDef.addTechniqueDef(technique); technique = null; + langSize = 0; shaderLanguages.clear(); shaderNames.clear(); presetDefines.clear();