diff --git a/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java b/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java index 1300bf361..edb780344 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java +++ b/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java @@ -36,8 +36,9 @@ import com.jme3.material.ShaderGenerationInfo; import com.jme3.material.plugins.ConditionParser; import com.jme3.shader.Shader.ShaderType; -import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * This shader Generator can generate Vertex and Fragment shaders from @@ -51,6 +52,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { * the indentation characters 1à tabulation characters */ private final static String INDENTCHAR = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + private final static Logger log = Logger.getLogger(Glsl100ShaderGenerator.class.getName()); protected ShaderNodeVariable inPosTmp; @@ -116,21 +118,16 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { } } - /** - * {@inheritDoc} - * - * if the declaration contains no code nothing is done, else it's appended - */ - @Override - protected void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) { - if (nodeSource.replaceAll("\\n", "").trim().length() > 0) { - nodeSource = updateDefinesName(nodeSource, shaderNode); + + protected void generateDeclarationSection(StringBuilder source) { + for (String defName : declaredNodes.keySet()) { + NodeDeclaration nd = declaredNodes.get(defName); source.append("\n"); unIndent(); - startCondition(shaderNode.getCondition(), source); - source.append(nodeSource); + startCondition(nd.condition, source); + source.append(nd.source); source.append("\n"); - endCondition(shaderNode.getCondition(), source); + endCondition(nd.condition, source); indent(); } } @@ -235,81 +232,112 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { @Override protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) { - nodeSource = updateDefinesName(nodeSource, shaderNode); + source.append("\n"); - comment(source, shaderNode, "Begin"); + comment(source, shaderNode, ""); startCondition(shaderNode.getCondition(), source); - final List declaredInputs = new ArrayList<>(); - - // Decalring variables with default values first final ShaderNodeDefinition definition = shaderNode.getDefinition(); - for (final ShaderNodeVariable var : definition.getInputs()) { - - if (var.getType().startsWith("sampler")) { - continue; - } - - final String fullName = shaderNode.getName() + "_" + var.getName(); - - final ShaderNodeVariable variable = new ShaderNodeVariable(var.getType(), shaderNode.getName(), - var.getName(), var.getMultiplicity()); - - if (!isVarying(info, variable)) { - declareVariable(source, variable, var.getDefaultValue(), true, null); + StringBuilder b = new StringBuilder(); + appendIndent(b); + b.append(definition.getName()).append("("); + boolean isFirst = true; + for (ShaderNodeVariable v : definition.getParams()) { + if (!isFirst) { + b.append(", "); } + if (definition.getInputs().contains(v)) { - nodeSource = replaceVariableName(nodeSource, variable); - declaredInputs.add(fullName); - } - - for (VariableMapping mapping : shaderNode.getInputMapping()) { + List maps = shaderNode.getInputMapping(v.getName()); - final ShaderNodeVariable rightVariable = mapping.getRightVariable(); - final ShaderNodeVariable leftVariable = mapping.getLeftVariable(); + boolean declared = false; + for (VariableMapping m : maps) { + // map varyings to their inputs, as the code may not do the mapping. + if (isVarying(info, m.getLeftVariable())) { + map(m, source, false); + declared = true; + } + } - String newName = shaderNode.getName() + "_" + leftVariable.getName(); - boolean isDeclared = declaredInputs.contains(newName); - //Variables fed with a sampler matparam or world param are replaced by the matparam itself - //It avoids issue with samplers that have to be uniforms. - if (rightVariable != null && isWorldOrMaterialParam(rightVariable) && rightVariable.getType().startsWith("sampler")) { - nodeSource = replace(nodeSource, leftVariable, rightVariable.getPrefix() + rightVariable.getName()); + if (maps.isEmpty()) { + //no mapping found + if (v.getDefaultValue() != null) { + // if there is a default value append it to the function call + b.append(v.getDefaultValue()); + } else { + // no default value, construct a variable with the proper type and dummy value and raise a warning + b.append(getConstructor(v.getType())); + log.log(Level.WARNING, "No input defined for variable " + v.getName() + " on shader node " + shaderNode.getName()); + } + } else if (maps.size() == 1 && !declared) { + // one mapping for this variable, directly append the + // other variable from the mapping to the function call + VariableMapping m = maps.get(0); + ShaderNodeVariable v2 = m.getRightVariable(); + b.append(getAppendableNameSpace(v2)) + .append(v2.getPrefix()) + .append(v2.getName()); + if (m.getRightSwizzling().length() > 0) { + b.append("."); + b.append(m.getRightSwizzling()); + } + } else { + // 2 possible cases here + // the variable is a varrying: we can append it directly + // or + // several mappings with different conditions: we have to declare the variable and + // map it properly before appending the variable in the function call + for (VariableMapping mapping : maps) { + map(mapping, source, true); + } + b.append(shaderNode.getName()) + .append("_") + .append(v.getName()); + } } else { - - if (leftVariable.getType().startsWith("sampler")) { - throw new IllegalArgumentException("a Sampler must be a uniform"); + // outputs + String name = shaderNode.getName() + "_" + v.getName(); + // if the output is not a varying (already declared) we declare it) + if (!isVarying(info, name)) { + appendIndent(source); + source.append(v.getType()).append(" ").append(name).append(";\n"); } - map(mapping, source, !isDeclared); - } - - if (!isDeclared) { - nodeSource = replace(nodeSource, leftVariable, newName); - declaredInputs.add(newName); + // append the variable to the function call + b.append(shaderNode.getName()) + .append("_") + .append(v.getName()); } + isFirst = false; } + b.append(");\n"); - - for (ShaderNodeVariable var : definition.getOutputs()) { - ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName(), var.getMultiplicity()); - if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) { - if (!isVarying(info, v)) { - declareVariable(source, v); - } - nodeSource = replaceVariableName(nodeSource, v); - } - } - - source.append(nodeSource); - + // Map any output to global output. for (VariableMapping mapping : shaderNode.getOutputMapping()) { - map(mapping, source, true); + map(mapping, b, false); } + source.append(b); + endCondition(shaderNode.getCondition(), source); - comment(source, shaderNode, "End"); } + /** + * Returns a proper constructor call for a given type + * @param type + * @return + */ + private String getConstructor(String type) { + if (type.startsWith("i") || type.startsWith("u")) { + return type + "(0)"; + } + if (type.equals("boolean") || type.startsWith("u")) { + return "false"; + } + return type + "(0.0)"; + } + + /** * declares a variable, embed in a conditional block if needed * @param source the StringBuilder to use @@ -510,6 +538,17 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { return isVarying; } + protected boolean isVarying(ShaderGenerationInfo info, String variableName) { + for (ShaderNodeVariable shaderNodeVariable : info.getVaryings()) { + String name = shaderNodeVariable.getNameSpace() + "_" + shaderNodeVariable.getName(); + if (name.equals(variableName)) { + return true; + } + } + return false; + } + + /** * Appends a comment to the generated code * @param source the StringBuilder to use @@ -554,10 +593,10 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { String[] lines = nodeSource.split("\\n"); ConditionParser parser = new ConditionParser(); for (String line : lines) { - - if (line.trim().startsWith("#if")) { - List params = parser.extractDefines(line.trim()); - String l = line.trim().replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");//parser.getFormattedExpression(); + line = line.trim(); + if (line.startsWith("#if")) { + List params = parser.extractDefines(line); + String l = line.replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", ""); boolean match = false; for (String param : params) { for (VariableMapping map : shaderNode.getInputMapping()) { diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java b/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java index a86340ab9..78a20c7fd 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java @@ -34,6 +34,7 @@ package com.jme3.shader; import com.jme3.asset.AssetManager; import com.jme3.material.ShaderGenerationInfo; import com.jme3.material.TechniqueDef; +import com.jme3.material.plugins.ConditionParser; import com.jme3.shader.Shader.ShaderType; import com.jme3.shader.plugins.ShaderAssetKey; @@ -72,7 +73,15 @@ public abstract class ShaderGenerator { */ Pattern extensions = Pattern.compile("(#extension.*\\s+)"); - private Map imports = new LinkedHashMap<>(); + /** + * a set of imports to append to the shader source + */ + private Set imports = new HashSet<>(); + + /** + * The nodes function and their condition to be declared in the shader + */ + protected Map declaredNodes = new LinkedHashMap<>(); /** * Build a shaderGenerator @@ -155,12 +164,6 @@ public abstract class ShaderGenerator { generateEndOfMainSection(source, info, type); - //insert imports backward - int insertIndex = sourceDeclaration.length(); - for (String importSource : imports.values()) { - sourceDeclaration.insert(insertIndex, importSource); - } - sourceDeclaration.append(source); return moveExtensionsUp(sourceDeclaration); @@ -195,56 +198,92 @@ public abstract class ShaderGenerator { * @param type the Shader type */ protected void generateDeclarationAndMainBody(List shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) { + declaredNodes.clear(); for (ShaderNode shaderNode : shaderNodes) { if (info.getUnusedNodes().contains(shaderNode.getName())) { continue; } + if (shaderNode.getDefinition().getType() == type) { int index = findShaderIndexFromVersion(shaderNode, type); String shaderPath = shaderNode.getDefinition().getShadersPath().get(index); Map sources = (Map) assetManager.loadAsset(new ShaderAssetKey(shaderPath, false)); String loadedSource = sources.get("[main]"); for (String name : sources.keySet()) { - if (!name.equals("[main]")) { - imports.put(name, sources.get(name)); + if (!name.equals("[main]") && !imports.contains(name)) { + imports.add(name); + // append the imported file in place if it hasn't been imported already. + sourceDeclaration.append(sources.get(name)); } } - appendNodeDeclarationAndMain(loadedSource, sourceDeclaration, source, shaderNode, info, shaderPath); + // Nodes are functions added to the declaration part of the shader + // Multiple nodes may use the same definition and we don't want to declare it several times. + // Also nodes can have #ifdef conditions so we need to properly merge this conditions to declare the Node function. + NodeDeclaration nd = declaredNodes.get(shaderNode.getDefinition().getName()); + loadedSource = functionize(loadedSource, shaderNode.getDefinition()); + if(nd == null){ + nd = new NodeDeclaration(shaderNode.getCondition(), loadedSource); + declaredNodes.put(shaderNode.getDefinition().getName(), nd); + } else { + nd.condition = ConditionParser.mergeConditions(nd.condition, shaderNode.getCondition(), "||"); + } + + generateNodeMainSection(source, shaderNode, loadedSource, info); } } + + generateDeclarationSection(sourceDeclaration); + } /** - * Appends declaration and main part of a node to the shader declaration and - * main part. the loadedSource is split by "void main(){" to split - * declaration from main part of the node source code.The trailing "}" is - * removed from the main part. Each part is then respectively passed to - * generateDeclarativeSection and generateNodeMainSection. - * - * @see ShaderGenerator#generateDeclarativeSection - * @see ShaderGenerator#generateNodeMainSection - * - * @param loadedSource the actual source code loaded for this node. - * @param shaderPath path to the shader file - * @param sourceDeclaration the Shader declaration part string builder. - * @param source the Shader main part StringBuilder. - * @param shaderNode the shader node. - * @param info the ShaderGenerationInfo. + * Tuns old style shader node code into a proper function so that it can be appended to the declarative sectio. + * Note that this only needed for nodes coming from a j3sn file. + * @param source + * @param def + * @return */ - protected void appendNodeDeclarationAndMain(String loadedSource, StringBuilder sourceDeclaration, StringBuilder source, ShaderNode shaderNode, ShaderGenerationInfo info, String shaderPath) { - if (loadedSource.length() > 1) { - loadedSource = loadedSource.substring(0, loadedSource.lastIndexOf("}")); - String[] sourceParts = loadedSource.split("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{"); - if(sourceParts.length<2){ - throw new IllegalArgumentException("Syntax error in "+ shaderPath +". Cannot find 'void main(){' in \n"+ loadedSource); + public static String functionize(String source, ShaderNodeDefinition def){ + StringBuffer signature = new StringBuffer(); + def.setReturnType("void"); + signature.append("void ").append(def.getName()).append("("); + boolean addParam = false; + if(def.getParams().isEmpty()){ + addParam = true; + } + + boolean isFirst = true; + for (ShaderNodeVariable v : def.getInputs()) { + if(!isFirst){ + signature.append(", "); + } + String qualifier; + qualifier = "const in"; + signature.append(qualifier).append(" ").append(v.getType()).append(" ").append(v.getName()); + isFirst = false; + if(addParam) { + def.getParams().add(v); } - generateDeclarativeSection(sourceDeclaration, shaderNode, sourceParts[0], info); - generateNodeMainSection(source, shaderNode, sourceParts[1], info); - } else { - //if source is empty, we still call generateNodeMainSection so that mappings can be done. - generateNodeMainSection(source, shaderNode, loadedSource, info); } + for (ShaderNodeVariable v : def.getOutputs()) { + if(def.getInputs().contains(v)){ + continue; + } + if(!isFirst){ + signature.append(", "); + } + signature.append("out ").append(v.getType()).append(" ").append(v.getName()); + isFirst = false; + if(addParam) { + def.getParams().add(v); + } + } + signature.append("){"); + + source = source.replaceAll("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{", signature.toString()); + + return source; } /** @@ -287,19 +326,12 @@ public abstract class ShaderGenerator { protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type); /** - * Appends the given shaderNode declarative part to the shader declarative - * part. If needed the shader type can be determined by fetching the - * shaderNode's definition type. - * - * @see ShaderNode#getDefinition() - * @see ShaderNodeDefinition#getType() + * Appends the shaderNodes function to the shader declarative + * part. * - * @param nodeDecalarationSource the declaration part of the node * @param source the StringBuilder to append generated code. - * @param shaderNode the shaderNode. - * @param info the ShaderGenerationInfo. */ - protected abstract void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeDecalarationSource, ShaderGenerationInfo info); + protected abstract void generateDeclarationSection(StringBuilder source); /** * generates the start of the shader main section. this method is @@ -363,4 +395,15 @@ public abstract class ShaderGenerator { } return index; } + + protected class NodeDeclaration{ + String condition; + String source; + + public NodeDeclaration(String condition, String source) { + this.condition = condition; + this.source = source; + } + } + } 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 5ef500ffd..15b3cc564 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderNode.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderNode.java @@ -143,6 +143,21 @@ public class ShaderNode implements Savable, Cloneable { return inputMapping; } + /** + * Returns a list of variable mapping for the given node input. + * + * @return the input mappings. + */ + public List getInputMapping(String varName) { + List list = new ArrayList<>(); + for (VariableMapping v : inputMapping) { + if (v.getLeftVariable().getName().equals(varName)){ + list.add(v); + } + } + return list; + } + /** * Sets the input mappings. * diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java b/jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java index ba5fc0161..0561d663c 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java @@ -57,8 +57,10 @@ public class ShaderNodeDefinition implements Savable { private String documentation; private List inputs = new ArrayList(); private List outputs = new ArrayList(); + private List params = new ArrayList<>(); private String path = null; private boolean noOutput = false; + private String returnType; /** * creates a ShaderNodeDefinition @@ -183,8 +185,22 @@ public class ShaderNodeDefinition implements Savable { public void setPath(String path) { this.path = path; } - - + + public String getReturnType() { + return returnType; + } + + public void setReturnType(String returnType) { + this.returnType = returnType; + } + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } /** * jme serialization (not used) @@ -194,14 +210,15 @@ public class ShaderNodeDefinition implements Savable { */ @Override public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); + OutputCapsule oc = ex.getCapsule(this); oc.write(name, "name", ""); String[] str = new String[shadersLanguage.size()]; oc.write(shadersLanguage.toArray(str), "shadersLanguage", null); oc.write(shadersPath.toArray(str), "shadersPath", null); oc.write(type, "type", null); oc.writeSavableArrayList((ArrayList) inputs, "inputs", new ArrayList()); - oc.writeSavableArrayList((ArrayList) outputs, "inputs", new ArrayList()); + oc.writeSavableArrayList((ArrayList) outputs, "outputs", new ArrayList()); + oc.writeSavableArrayList((ArrayList) params, "params", new ArrayList()); } public List getShadersLanguage() { @@ -250,6 +267,8 @@ public class ShaderNodeDefinition implements Savable { type = ic.readEnum("type", Shader.ShaderType.class, null); inputs = (List) ic.readSavableArrayList("inputs", new ArrayList()); outputs = (List) ic.readSavableArrayList("outputs", new ArrayList()); + params = (List) ic.readSavableArrayList("params", new ArrayList()); + im.getAssetManager(); } /** diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/basicGpuSkinning.vert b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/basicGpuSkinning.vert index 4c153e27f..eacf56ca8 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/basicGpuSkinning.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/basicGpuSkinning.vert @@ -1,8 +1,8 @@ +#import "Common/ShaderLib/Skinning.glsllib" -void main(){ - modModelPosition = (mat4(0.0) + - boneMatrices[int(boneIndex.x)] * boneWeight.x + - boneMatrices[int(boneIndex.y)] * boneWeight.y + - boneMatrices[int(boneIndex.z)] * boneWeight.z + - boneMatrices[int(boneIndex.w)] * boneWeight.w) * vec4(modelPosition.xyz,1.0); +void main(){ + #ifdef NUM_BONES + modModelPosition = modelPosition; + Skinning_Compute(modModelPosition); + #endif } \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/fullGpuSkinning.vert b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/fullGpuSkinning.vert index 49f8a659e..3743bd130 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/fullGpuSkinning.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/fullGpuSkinning.vert @@ -1,12 +1,10 @@ +#import "Common/ShaderLib/Skinning.glsllib" void main(){ - modModelPosition = (mat4(0.0) + - boneMatrices[int(boneIndex.x)] * boneWeight.x + - boneMatrices[int(boneIndex.y)] * boneWeight.y + - boneMatrices[int(boneIndex.z)] * boneWeight.z + - boneMatrices[int(boneIndex.w)] * boneWeight.w) * modelPosition; - - mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz); - modModelTangent = rotMat * modelTangent; - modModelNormal = rotMat * modelNormal; + #ifdef NUM_BONES + modModelPosition = modelPosition; + modModelNormal = modelNormal; + modModelTangents = modelTangents; + Skinning_Compute(modModelPosition, modModelNormal, modModelTangents); + #endif } \ No newline at end of file diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java index 07569ce90..628883a86 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java @@ -104,6 +104,28 @@ public class ConditionParser { return defines; } + /** + * merges 2 condition with the given operator + * + * @param condition1 the first condition + * @param condition2 the second condition + * @param operator the operator ("&&" or "||&) + * @return the merged condition + */ + public static String mergeConditions(String condition1, String condition2, String operator) { + if (condition1 != null) { + if (condition2 == null) { + return condition1; + } else { + String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")"; + return mergedCondition; + } + } else { + return condition2; + } + } + + /** * * @return the formatted expression previously updated by extractDefines diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java index c8c674046..b18a08904 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java @@ -580,7 +580,7 @@ public class ShaderNodeLoaderDelegate { multiplicity = multiplicity.toUpperCase(); left.setMultiplicity(multiplicity); // only declare the variable if the define is defined. - left.setCondition(mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||")); + left.setCondition(ConditionParser.mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||")); } else { throw new MatParseException("Wrong multiplicity for variable" + left.getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement); @@ -1057,26 +1057,6 @@ public class ShaderNodeLoaderDelegate { } } - /** - * merges 2 condition with the given operator - * - * @param condition1 the first condition - * @param condition2 the second condition - * @param operator the operator ("&&" or "||&) - * @return the merged condition - */ - public String mergeConditions(String condition1, String condition2, String operator) { - if (condition1 != null) { - if (condition2 == null) { - return condition1; - } else { - String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")"; - return mergedCondition; - } - } else { - return condition2; - } - } /** * Searches a variable in a list from its name and merges the conditions of the diff --git a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java index b1683fcff..9b8d0511e 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java +++ b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java @@ -4,7 +4,7 @@ import com.jme3.app.SimpleApplication; import com.jme3.material.Material; import com.jme3.material.Technique; import com.jme3.material.TechniqueDef; -import com.jme3.math.ColorRGBA; +import com.jme3.math.*; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.jme3.shader.Shader;