One can now define several versions for the shader in a Technique in a J3md file, ie:

VertexShader GLSL150 GLSL110 : "path/to/shader/file.vert"
FragmentShader GLSL150 GLSL110 : "path/to/shader/file.frag"

Versions must be separated with spaces. They will be matched together when creating the technique so they have to follow the same order for different shaders.
native-compilation-test
Nehon 8 years ago
parent 4777c591b1
commit 12a2f0f63c
  1. 34
      jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java
  2. 59
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  3. 22
      jme3-core/src/main/java/com/jme3/shader/ShaderNode.java
  4. 10
      jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java
  5. 12
      jme3-core/src/main/java/com/jme3/shader/VariableMapping.java
  6. 97
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.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;
}
}

@ -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<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> 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;
}
}

@ -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;
}
}

@ -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
@ -282,5 +277,8 @@ public class ShaderNodeVariable implements Savable, Cloneable {
this.multiplicity = multiplicity;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

@ -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;
}
}

@ -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<String> presetDefines = new ArrayList<String>();
private EnumMap<Shader.ShaderType, String> shaderLanguages;
private List<EnumMap<Shader.ShaderType, String>> shaderLanguages;
private EnumMap<Shader.ShaderType, String> 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, String>(Shader.ShaderType.class));
}
shaderLanguages.get(i).put(shaderType, languages[i]);
}
}
// LightMode <MODE>
@ -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<TechniqueDef> 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();

Loading…
Cancel
Save