diff --git a/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md b/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md index a8dab82f4..ea871b507 100644 --- a/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md +++ b/engine/src/core-data/Common/MatDefs/Gui/Gui.j3md @@ -2,7 +2,7 @@ MaterialDef Default GUI { MaterialParameters { Texture2D Texture - Color Color : Color + Color Color ( Color ) Boolean VertexColor } diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag index 285247042..78002358c 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag @@ -2,7 +2,6 @@ #define ATTENUATION //#define HQ_ATTENUATION - varying vec2 texCoord; #ifdef SEPARATE_TEXCOORD varying vec2 texCoord2; @@ -51,9 +50,8 @@ varying vec3 SpecularSum; #ifdef COLORRAMP uniform sampler2D m_ColorRamp; #endif -uniform float m_AlphaDiscardThreshold; - +uniform float m_AlphaDiscardThreshold; #ifndef VERTEX_LIGHTING uniform float m_Shininess; diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md index 32502028a..d6fda8927 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md @@ -38,16 +38,16 @@ MaterialDef Phong Lighting { Boolean UseVertexColor // Ambient color - Color Ambient : MaterialAmbient + Color Ambient (MaterialAmbient) // Diffuse color - Color Diffuse : MaterialDiffuse + Color Diffuse (MaterialDiffuse) // Specular color - Color Specular : MaterialSpecular + Color Specular (MaterialSpecular) // Specular power/shininess - Float Shininess : MaterialShininess + Float Shininess (MaterialShininess) : 1 // Diffuse map Texture2D DiffuseMap diff --git a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md index c50e86116..5a33d44f6 100644 --- a/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md +++ b/engine/src/core-data/Common/MatDefs/Misc/Unshaded.j3md @@ -3,7 +3,7 @@ MaterialDef Unshaded { MaterialParameters { Texture2D ColorMap Texture2D LightMap - Color Color : Color + Color Color ( Color ) Boolean VertexColor Boolean SeparateTexCoord diff --git a/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java b/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java index 733e65330..93ae9fe1f 100644 --- a/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java +++ b/engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java @@ -47,7 +47,6 @@ import com.jme3.material.RenderState.BlendMode; import com.jme3.material.RenderState.FaceCullMode; import com.jme3.material.TechniqueDef.LightMode; import com.jme3.material.TechniqueDef.ShadowMode; -import com.jme3.shader.Shader.ShaderType; import com.jme3.shader.VarType; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; @@ -57,13 +56,13 @@ import com.jme3.util.BufferUtils; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; -import java.util.Locale; -import java.util.Scanner; +import java.util.List; +import com.jme3.util.blockparser.BlockLanguageParser; +import com.jme3.util.blockparser.Statement; public class J3MLoader implements AssetLoader { private AssetManager owner; - private Scanner scan; private AssetKey key; private MaterialDef materialDef; @@ -74,6 +73,8 @@ public class J3MLoader implements AssetLoader { private String shaderLang; private String vertName; private String fragName; + + private static final String whitespacePattern = "\\p{javaWhitespace}+"; public J3MLoader(){ } @@ -86,29 +87,6 @@ public class J3MLoader implements AssetLoader { throw new IOException("Expected '"+expected+"', got '"+got+"'!"); } - private void nextStatement(){ - while (true){ - if (scan.hasNext("\\}")){ - break; - }else if (scan.hasNext("[\n;]")){ - scan.next(); - }else if (scan.hasNext("//")){ - scan.useDelimiter("\n"); - scan.next(); - scan.useDelimiter("\\p{javaWhitespace}+"); - }else{ - break; - } - } - } - - private String readString(String end){ - scan.useDelimiter(end); - String str = scan.next(); - scan.useDelimiter("\\p{javaWhitespace}+"); - return str.trim(); - } - private Image createColorTexture(ColorRGBA color){ if (color.getAlpha() == 1.0f){ // create RGB texture @@ -128,75 +106,49 @@ public class J3MLoader implements AssetLoader { } } - private void readShaderStatement(ShaderType type) throws IOException { - String lang = readString(":"); - - String word = scan.next(); - throwIfNequal(":", word); - - word = readString("[\n;(\\})]"); // new line, semicolon, comment or brace will end a statement - // locate source code - - if (type == ShaderType.Vertex) - vertName = word; - else if (type == ShaderType.Fragment) - fragName = word; - - shaderLang = lang; + // : + private void readShaderStatement(String statement) throws IOException { + String[] split = statement.split(":"); + if (split.length != 2){ + 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); + } + shaderLang = typeAndLang[1]; + if (typeAndLang[0].equals("VertexShader")){ + vertName = split[1].trim(); + }else if (typeAndLang[0].equals("FragmentShader")){ + fragName = split[1].trim(); + } + } - private void readLightMode(){ - String mode = readString("[\n;(\\})]"); - LightMode lm = LightMode.valueOf(mode); + // LightMode + private void readLightMode(String statement) throws IOException{ + String[] split = statement.split(whitespacePattern); + if (split.length != 2){ + throw new IOException("LightMode statement syntax incorrect"); + } + LightMode lm = LightMode.valueOf(split[1]); technique.setLightMode(lm); } - private void readShadowMode(){ - String mode = readString("[\n;(\\})]"); - ShadowMode sm = ShadowMode.valueOf(mode); - technique.setShadowMode(sm); - } - - private void readParam() throws IOException{ - String word = scan.next(); - VarType type; - if (word.equals("Color")){ - type = VarType.Vector4; - }else{ - type = VarType.valueOf(word); + // ShadowMode + private void readShadowMode(String statement) throws IOException{ + String[] split = statement.split(whitespacePattern); + if (split.length != 2){ + throw new IOException("ShadowMode statement syntax incorrect"); } - - word = readString("[\n;(//)(\\})]"); - FixedFuncBinding ffBinding = null; - if (word.contains(":")){ - // contains fixed func binding - String[] split = word.split(":"); - word = split[0].trim(); - - try { - ffBinding = FixedFuncBinding.valueOf(split[1].trim()); - } catch (IllegalArgumentException ex){ - throw new IOException("FixedFuncBinding '" + - split[1] + "' does not exist!"); - } - } - // TODO: add support for default vals - materialDef.addMaterialParam(type, word, null, ffBinding); + ShadowMode sm = ShadowMode.valueOf(split[1]); + technique.setShadowMode(sm); } - private void readValueParam() throws IOException{ - String name = readString(":"); - throwIfNequal(":", scan.next()); - - // parse value - MatParam p = material.getMaterialDef().getMaterialParam(name); - if (p == null) - throw new IOException("The material parameter: "+name+" is undefined."); - - VarType type = p.getVarType(); + private Object readValue(VarType type, String value) throws IOException{ if (type.isTextureType()){ // String texturePath = readString("[\n;(//)(\\})]"); - String texturePath = readString("[\n;(\\})]"); + String texturePath = value.trim(); boolean flipY = false; boolean repeat = false; if (texturePath.startsWith("Flip Repeat ")){ @@ -219,98 +171,147 @@ public class J3MLoader implements AssetLoader { if (tex != null){ if (repeat) tex.setWrap(WrapMode.Repeat); - - material.setTextureParam(name, type, tex); } + return tex; }else{ + String[] split = value.trim().split(whitespacePattern); switch (type){ case Float: - material.setParam(name, type, scan.nextFloat()); - break; + if (split.length != 1){ + throw new IOException("Float value parameter must have 1 entry: " + value); + } + return Float.parseFloat(split[0]); case Vector2: - material.setParam(name, type, new Vector2f(scan.nextFloat(), - scan.nextFloat())); - break; + if (split.length != 2){ + throw new IOException("Vector2 value parameter must have 2 entries: " + value); + } + return new Vector2f(Float.parseFloat(split[0]), + Float.parseFloat(split[1])); case Vector3: - material.setParam(name, type, new Vector3f(scan.nextFloat(), - scan.nextFloat(), - scan.nextFloat())); - break; + if (split.length != 3){ + throw new IOException("Vector3 value parameter must have 3 entries: " + value); + } + return new Vector3f(Float.parseFloat(split[0]), + Float.parseFloat(split[1]), + Float.parseFloat(split[2])); case Vector4: - material.setParam(name, type, new ColorRGBA(scan.nextFloat(), - scan.nextFloat(), - scan.nextFloat(), - scan.nextFloat())); - break; + if (split.length != 4){ + throw new IOException("Vector4 value parameter must have 4 entries: " + value); + } + return new ColorRGBA(Float.parseFloat(split[0]), + Float.parseFloat(split[1]), + Float.parseFloat(split[2]), + Float.parseFloat(split[3])); case Int: - material.setParam(name, type, scan.nextInt()); - break; + if (split.length != 1){ + throw new IOException("Int value parameter must have 1 entry: " + value); + } + return Integer.parseInt(split[0]); case Boolean: - material.setParam(name, type, scan.nextBoolean()); - break; + if (split.length != 1){ + throw new IOException("Boolean value parameter must have 1 entry: " + value); + } + return Boolean.parseBoolean(split[0]); default: throw new UnsupportedOperationException("Unknown type: "+type); } } } - - private void readMaterialParams() throws IOException{ - nextStatement(); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); - - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; + + // [ "(" ")" ] [ ":" ] + private void readParam(String statement) throws IOException{ + String name; + String defaultVal = null; + FixedFuncBinding ffBinding = null; + + String[] split = statement.split(":"); + + // Parse default val + if (split.length == 1){ + // Doesn't contain default value + }else{ + if (split.length != 2){ + throw new IOException("Parameter statement syntax incorrect"); } - - readParam(); - nextStatement(); + statement = split[0].trim(); + defaultVal = split[1].trim(); } + + // Parse ffbinding + int startParen = statement.indexOf("("); + if (startParen != -1){ + // get content inside parentheses + int endParen = statement.indexOf(")", startParen); + String bindingStr = statement.substring(startParen+1, endParen).trim(); + try { + ffBinding = FixedFuncBinding.valueOf(bindingStr); + } catch (IllegalArgumentException ex){ + throw new IOException("FixedFuncBinding '" + + split[1] + "' does not exist!"); + } + 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){ + readValue(type, defaultVal); + } + + materialDef.addMaterialParam(type, name, defaultValObj, ffBinding); } - private void readExtendingMaterialParams() throws IOException{ - nextStatement(); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); + private void readValueParam(String statement) throws IOException{ + // Use limit=1 incase filename contains colons + String[] split = statement.split(":", 2); + if (split.length != 2){ + throw new IOException("Value parameter statement syntax incorrect"); + } + String name = split[0].trim(); - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } + // parse value + MatParam p = material.getMaterialDef().getMaterialParam(name); + if (p == null){ + throw new IOException("The material parameter: "+name+" is undefined."); + } - readValueParam(); - nextStatement(); + Object valueObj = readValue(p.getVarType(), split[1]); + if (p.getVarType().isTextureType()){ + material.setTextureParam(name, p.getVarType(), (Texture) valueObj); + }else{ + material.setParam(name, p.getVarType(), valueObj); } } - private void readWorldParams() throws IOException{ - nextStatement(); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); + private void readMaterialParams(List paramsList) throws IOException{ + for (Statement statement : paramsList){ + readParam(statement.getLine()); + } + } - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } + private void readExtendingMaterialParams(List paramsList) throws IOException{ + for (Statement statement : paramsList){ + readValueParam(statement.getLine()); + } + } - word = readString("[\n;(//)(\\})]"); - if (word != null && !word.equals("")){ - technique.addWorldParam(word); - } - nextStatement(); + private void readWorldParams(List worldParams) throws IOException{ + for (Statement statement : worldParams){ + technique.addWorldParam(statement.getLine()); } } @@ -318,174 +319,111 @@ public class J3MLoader implements AssetLoader { return word != null && word.equals("On"); } - private void readRenderStateStatement() throws IOException{ - String word = scan.next(); - if (word.equals("Wireframe")){ - renderState.setWireframe(parseBoolean(scan.next())); - }else if (word.equals("FaceCull")){ - renderState.setFaceCullMode(FaceCullMode.valueOf(scan.next())); - }else if (word.equals("DepthWrite")){ - renderState.setDepthWrite(parseBoolean(scan.next())); - }else if (word.equals("DepthTest")){ - renderState.setDepthTest(parseBoolean(scan.next())); - }else if (word.equals("Blend")){ - renderState.setBlendMode(BlendMode.valueOf(scan.next())); - }else if (word.equals("AlphaTestFalloff")){ + private void readRenderStateStatement(String statement) throws IOException{ + String[] split = statement.split(whitespacePattern); + if (split[0].equals("Wireframe")){ + renderState.setWireframe(parseBoolean(split[1])); + }else if (split[0].equals("FaceCull")){ + renderState.setFaceCullMode(FaceCullMode.valueOf(split[1])); + }else if (split[0].equals("DepthWrite")){ + renderState.setDepthWrite(parseBoolean(split[1])); + }else if (split[0].equals("DepthTest")){ + renderState.setDepthTest(parseBoolean(split[1])); + }else if (split[0].equals("Blend")){ + renderState.setBlendMode(BlendMode.valueOf(split[1])); + }else if (split[0].equals("AlphaTestFalloff")){ renderState.setAlphaTest(true); - renderState.setAlphaFallOff(scan.nextFloat()); - }else if (word.equals("PolyOffset")){ - float factor = scan.nextFloat(); - float units = scan.nextFloat(); + renderState.setAlphaFallOff(Float.parseFloat(split[1])); + }else if (split[0].equals("PolyOffset")){ + float factor = Float.parseFloat(split[1]); + float units = Float.parseFloat(split[2]); renderState.setPolyOffset(factor, units); - }else if (word.equals("ColorWrite")){ - renderState.setColorWrite(parseBoolean(scan.next())); - }else if (word.equals("PointSprite")){ - renderState.setPointSprite(parseBoolean(scan.next())); + }else if (split[0].equals("ColorWrite")){ + renderState.setColorWrite(parseBoolean(split[1])); + }else if (split[0].equals("PointSprite")){ + renderState.setPointSprite(parseBoolean(split[1])); }else{ - throwIfNequal(null, word); + throwIfNequal(null, split[0]); } } - private void readAdditionalRenderState() throws IOException{ - nextStatement(); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); - + private void readAdditionalRenderState(List renderStates) throws IOException{ renderState = material.getAdditionalRenderState(); - - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } - - readRenderStateStatement(); - nextStatement(); + for (Statement statement : renderStates){ + readRenderStateStatement(statement.getLine()); } - renderState = null; } - private void readRenderState() throws IOException{ - nextStatement(); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); - + private void readRenderState(List renderStates) throws IOException{ renderState = new RenderState(); - - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } - - readRenderStateStatement(); - nextStatement(); + for (Statement statement : renderStates){ + readRenderStateStatement(statement.getLine()); } - technique.setRenderState(renderState); renderState = null; } - private void readDefine(){ - // stops at either next statement or colon - // ways to end a statement: - /* - Block { - Statement - Statement; - Statement //comment - Statement } - */ - String defineName = readString("[\n;:(//)(\\})]"); - if (defineName.equals("")) - return; - - String matParamName = null; - if (scan.hasNext(":")){ - scan.next(); - // this time without colon - matParamName = readString("[\n;(//)(\\})]"); - // add define <-> param mapping - technique.addShaderParamDefine(matParamName, defineName); - }else{ + // [ ":" ] + private void readDefine(String statement) throws IOException{ + String[] split = statement.split(":"); + if (split.length == 1){ // add preset define - technique.addShaderPresetDefine(defineName, VarType.Boolean, true); + technique.addShaderPresetDefine(split[0].trim(), VarType.Boolean, true); + }else if (split.length == 2){ + technique.addShaderParamDefine(split[1].trim(), split[0].trim()); + }else{ + throw new IOException("Define syntax incorrect"); } } - private void readDefines() throws IOException{ - nextStatement(); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); - - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } - - readDefine(); - nextStatement(); + private void readDefines(List defineList) throws IOException{ + for (Statement statement : defineList){ + readDefine(statement.getLine()); } } - private void readTechniqueStatement() throws IOException{ - String word = scan.next(); - if (word.equals("VertexShader")){ - readShaderStatement(ShaderType.Vertex); - }else if (word.equals("FragmentShader")){ - readShaderStatement(ShaderType.Fragment); - }else if (word.equals("LightMode")){ - readLightMode(); - }else if (word.equals("ShadowMode")){ - readShadowMode(); - }else if (word.equals("WorldParameters")){ - readWorldParams(); - }else if (word.equals("RenderState")){ - readRenderState(); - }else if (word.equals("Defines")){ - readDefines(); + private void readTechniqueStatement(Statement statement) throws IOException{ + String[] split = statement.getLine().split("[ \\{]"); + if (split[0].equals("VertexShader") || + split[0].equals("FragmentShader")){ + readShaderStatement(statement.getLine()); + }else if (split[0].equals("LightMode")){ + readLightMode(statement.getLine()); + }else if (split[0].equals("ShadowMode")){ + readShadowMode(statement.getLine()); + }else if (split[0].equals("WorldParameters")){ + readWorldParams(statement.getContents()); + }else if (split[0].equals("RenderState")){ + readRenderState(statement.getContents()); + }else if (split[0].equals("Defines")){ + readDefines(statement.getContents()); }else{ - throwIfNequal(null, word); + throwIfNequal(null, split[0]); } - nextStatement(); } - private void readTransparentStatement() throws IOException{ - String on = readString("[\n;(\\})]"); - material.setTransparent(parseBoolean(on)); + private void readTransparentStatement(String statement) throws IOException{ + String[] split = statement.split(whitespacePattern); + if (split.length != 2){ + throw new IOException("Transparent statement syntax incorrect"); + } + material.setTransparent(parseBoolean(split[1])); } - private void readTechnique() throws IOException{ - String name = null; - if (!scan.hasNext("\\{")){ - name = scan.next(); + private void readTechnique(Statement techStat) throws IOException{ + String[] split = techStat.getLine().split(whitespacePattern); + if (split.length == 1){ + technique = new TechniqueDef(null); + }else if (split.length == 2){ + technique = new TechniqueDef(split[1]); + }else{ + throw new IOException("Technique statement syntax incorrect"); } - technique = new TechniqueDef(name); - - String word = scan.next(); - throwIfNequal("{", word); - - nextStatement(); - - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } - - readTechniqueStatement(); + + for (Statement statement : techStat.getContents()){ + readTechniqueStatement(statement); } if (vertName != null && fragName != null){ @@ -499,40 +437,44 @@ public class J3MLoader implements AssetLoader { shaderLang = null; } - private void loadFromScanner() throws IOException{ - nextStatement(); - + private void loadFromRoot(List roots) throws IOException{ + if (roots.size() == 2){ + Statement exception = roots.get(0); + String line = exception.getLine(); + if (line.startsWith("Exception")){ + throw new AssetLoadException(line.substring("Exception ".length())); + }else{ + throw new IOException("In multiroot material, expected first statement to be 'Exception'"); + } + }else if (roots.size() != 1){ + throw new IOException("Too many roots in J3M/J3MD file"); + } + boolean extending = false; - String name = null; - String word = scan.next(); - if (word.equals("Material")){ - extending = true; - }else if (word.equals("MaterialDef")){ + Statement materialStat = roots.get(0); + String materialName = materialStat.getLine(); + if (materialName.startsWith("MaterialDef")){ + materialName = materialName.substring("MaterialDef ".length()).trim(); extending = false; - }else if (word.equals("Exception")){ - String exception = scan.nextLine(); - throw new AssetLoadException(exception); + }else if (materialName.startsWith("Material")){ + materialName = materialName.substring("Material ".length()).trim(); + extending = true; }else{ throw new IOException("Specified file is not a Material file"); } - - nextStatement(); - - word = readString("[(\\{)(//)\n:]"); - if (word == null || word.equals("")) + + String[] split = materialName.split(":", 2); + + if (materialName.equals("")){ throw new IOException("Material name cannot be empty"); + } - name = word; - - nextStatement(); - - if (scan.hasNext(":")){ + if (split.length == 2){ if (!extending){ throw new IOException("Must use 'Material' when extending."); } - scan.next(); // skip colon - String extendedMat = readString("\\{"); + String extendedMat = split[1].trim(); MaterialDef def = (MaterialDef) owner.loadAsset(new AssetKey(extendedMat)); if (def == null) @@ -541,45 +483,35 @@ public class J3MLoader implements AssetLoader { material = new Material(def); material.setKey(key); // material.setAssetName(fileName); - }else if (scan.hasNext("\\{")){ + }else if (split.length == 1){ if (extending){ throw new IOException("Expected ':', got '{'"); } - materialDef = new MaterialDef(owner, name); + materialDef = new MaterialDef(owner, materialName); // NOTE: pass file name for defs so they can be loaded later materialDef.setAssetName(key.getName()); + }else{ + throw new IOException("Cannot use colon in material name/path"); } - scan.next(); // skip { - - nextStatement(); - - while (true){ - if (scan.hasNext("\\}")){ - scan.next(); - break; - } - - word = scan.next(); + + for (Statement statement : materialStat.getContents()){ + split = statement.getLine().split("[ \\{]"); + String statType = split[0]; if (extending){ - if (word.equals("MaterialParameters")){ - readExtendingMaterialParams(); - nextStatement(); - }else if (word.equals("AdditionalRenderState")){ - readAdditionalRenderState(); - nextStatement(); - }else if (word.equals("Transparent")){ - readTransparentStatement(); - nextStatement(); + if (statType.equals("MaterialParameters")){ + readExtendingMaterialParams(statement.getContents()); + }else if (statType.equals("AdditionalRenderState")){ + readAdditionalRenderState(statement.getContents()); + }else if (statType.equals("Transparent")){ + readTransparentStatement(statement.getLine()); } }else{ - if (word.equals("Technique")){ - readTechnique(); - nextStatement(); - }else if (word.equals("MaterialParameters")){ - readMaterialParams(); - nextStatement(); + if (statType.equals("Technique")){ + readTechnique(statement); + }else if (statType.equals("MaterialParameters")){ + readMaterialParams(statement.getContents()); }else{ - throw new IOException("Expected material statement, got '"+scan.next()+"'"); + throw new IOException("Expected material statement, got '"+statType+"'"); } } } @@ -590,10 +522,8 @@ public class J3MLoader implements AssetLoader { InputStream in = info.openStream(); try { - scan = new Scanner(in); - scan.useLocale(Locale.US); key = info.getKey(); - loadFromScanner(); + loadFromRoot(BlockLanguageParser.parse(in)); } finally { if (in != null) in.close(); diff --git a/engine/src/core/com/jme3/asset/AssetManager.java b/engine/src/core/com/jme3/asset/AssetManager.java index c57bb1b4d..49c9ef4fc 100644 --- a/engine/src/core/com/jme3/asset/AssetManager.java +++ b/engine/src/core/com/jme3/asset/AssetManager.java @@ -79,6 +79,8 @@ public interface AssetManager { * they were registered, to locate the asset by the {@link AssetKey}. * Once an {@link AssetLocator} returns a non-null AssetInfo, it is sent * to the {@link AssetLoader} to load the asset. + * Once a locator is registered, it can be removed via + * {@link #unregisterLocator(java.lang.String, java.lang.Class) }. * * @param rootPath Specifies the root path from which to locate assets * for the given {@link AssetLocator}. The purpose of this parameter @@ -87,9 +89,20 @@ public interface AssetManager { * * @see AssetLocator#setRootPath(java.lang.String) * @see AssetLocator#locate(com.jme3.asset.AssetManager, com.jme3.asset.AssetKey) + * @see #unregisterLocator(java.lang.String, java.lang.Class) */ public void registerLocator(String rootPath, Class locatorClass); + /** + * Unregisters the given locator class. This essentially undoes the operation + * done by {@link #registerLocator(java.lang.String, java.lang.Class) }. + * + * @param rootPath Should be the same as the root path specified in {@link + * #registerLocator(java.lang.String, java.lang.Class) }. + * @param locatorClass The locator class to unregister + */ + public void unregisterLocator(String rootPath, Class locatorClass); + /** * Set an {@link AssetEventListener} to receive events from this * AssetManager. There can only be one {@link AssetEventListener} diff --git a/engine/src/core/com/jme3/material/MatParam.java b/engine/src/core/com/jme3/material/MatParam.java index ddf462db6..9ae9faa22 100644 --- a/engine/src/core/com/jme3/material/MatParam.java +++ b/engine/src/core/com/jme3/material/MatParam.java @@ -128,7 +128,8 @@ public class MatParam implements Savable, Cloneable { * Returns the value of this material parameter. *

* Material parameters that are used for material definitions - * will not have a value. + * will not have a value, unless there's a default value declared + * in the definition. * * @return the value of this material parameter. */ diff --git a/engine/src/core/com/jme3/material/Material.java b/engine/src/core/com/jme3/material/Material.java index 3a692efb8..cb0dd69ca 100644 --- a/engine/src/core/com/jme3/material/Material.java +++ b/engine/src/core/com/jme3/material/Material.java @@ -53,8 +53,6 @@ import com.jme3.math.Vector4f; import com.jme3.renderer.Caps; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.shader.Shader; import com.jme3.shader.Uniform; @@ -115,6 +113,13 @@ public class Material implements Asset, Cloneable, Savable, Comparable throw new NullPointerException("Material definition cannot be null"); } this.def = def; + + // Load default values from definition (if any) + for (MatParam param : def.getMaterialParams()){ + if (param.getValue() != null){ + setParam(param.getName(), param.getVarType(), param.getValue()); + } + } } public Material(AssetManager contentMan, String defName) { diff --git a/engine/src/core/com/jme3/material/MaterialDef.java b/engine/src/core/com/jme3/material/MaterialDef.java index c13236480..b2eb55e15 100644 --- a/engine/src/core/com/jme3/material/MaterialDef.java +++ b/engine/src/core/com/jme3/material/MaterialDef.java @@ -35,6 +35,7 @@ package com.jme3.material; import com.jme3.asset.AssetManager; import com.jme3.shader.VarType; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -138,6 +139,19 @@ public class MaterialDef { public MatParam getMaterialParam(String name){ return matParams.get(name); } + + /** + * Returns a collection of all material parameters declared in this + * material definition. + *

+ * Modifying the material parameters or the collection will lead + * to undefined results. + * + * @return All material parameters declared in this definition. + */ + public Collection getMaterialParams(){ + return matParams.values(); + } /** * Adds a new technique definition to this material definition. diff --git a/engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java b/engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java new file mode 100644 index 000000000..6dfa6b8f2 --- /dev/null +++ b/engine/src/core/com/jme3/util/blockparser/BlockLanguageParser.java @@ -0,0 +1,92 @@ +package com.jme3.util.blockparser; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +public class BlockLanguageParser { + + private Reader reader; + private ArrayList statementStack = new ArrayList(); + private Statement lastStatement; + private int lineNumber = 1; + + private BlockLanguageParser(){ + } + + private void reset(){ + statementStack.clear(); + statementStack.add(new Statement(0, "")); + lastStatement = null; + lineNumber = 1; + } + + private void pushStatement(StringBuilder buffer){ + String content = buffer.toString().trim(); + if (content.length() > 0){ + // push last statement onto the list + lastStatement = new Statement(lineNumber, content); + + Statement parent = statementStack.get(statementStack.size()-1); + parent.addStatement(lastStatement); + + buffer.setLength(0); + } + } + + private void load(InputStream in) throws IOException{ + reset(); + + reader = new InputStreamReader(in); + + StringBuilder buffer = new StringBuilder(); + boolean insideComment = false; + char lastChar = '\0'; + + while (true){ + int ci = reader.read(); + char c = (char) ci; + if (c == '\r'){ + continue; + } + if (insideComment && c == '\n'){ + insideComment = false; + }else if (c == '/' && lastChar == '/'){ + buffer.deleteCharAt(buffer.length()-1); + insideComment = true; + pushStatement(buffer); + lastChar = '\0'; + }else if (!insideComment){ + if (ci == -1 || c == '{' || c == '}' || c == '\n' || c == ';'){ + pushStatement(buffer); + lastChar = '\0'; + if (c == '{'){ + // push last statement onto the stack + statementStack.add(lastStatement); + continue; + }else if (c == '}'){ + // pop statement from stack + statementStack.remove(statementStack.size()-1); + continue; + }else if (c == '\n'){ + lineNumber++; + }else if (ci == -1){ + break; + } + }else{ + buffer.append(c); + lastChar = c; + } + } + } + } + + public static List parse(InputStream in) throws IOException { + BlockLanguageParser parser = new BlockLanguageParser(); + parser.load(in); + return parser.statementStack.get(0).getContents(); + } +} diff --git a/engine/src/core/com/jme3/util/blockparser/Statement.java b/engine/src/core/com/jme3/util/blockparser/Statement.java new file mode 100644 index 000000000..d1309adcc --- /dev/null +++ b/engine/src/core/com/jme3/util/blockparser/Statement.java @@ -0,0 +1,61 @@ +package com.jme3.util.blockparser; + +import java.util.ArrayList; +import java.util.List; + +public class Statement { + + private int lineNumber; + private String line; + private List contents = new ArrayList(); + + Statement(int lineNumber, String line) { + this.lineNumber = lineNumber; + this.line = line; + } + + void addStatement(Statement statement){ +// if (contents == null){ +// contents = new ArrayList(); +// } + contents.add(statement); + } + + public int getLineNumber(){ + return lineNumber; + } + + public String getLine() { + return line; + } + + public List getContents() { + return contents; + } + + private String getIndent(int indent){ + return " ".substring(0, indent); + } + + private String toString(int indent){ + StringBuilder sb = new StringBuilder(); + sb.append(getIndent(indent)); + sb.append(line); + if (contents != null){ + sb.append(" {\n"); + for (Statement statement : contents){ + sb.append(statement.toString(indent+4)); + sb.append("\n"); + } + sb.append(getIndent(indent)); + sb.append("}"); + } + return sb.toString(); + } + + @Override + public String toString(){ + return toString(0); + } + +} diff --git a/engine/src/desktop/com/jme3/asset/DesktopAssetManager.java b/engine/src/desktop/com/jme3/asset/DesktopAssetManager.java index a9362de23..73a344d81 100644 --- a/engine/src/desktop/com/jme3/asset/DesktopAssetManager.java +++ b/engine/src/desktop/com/jme3/asset/DesktopAssetManager.java @@ -143,7 +143,7 @@ public class DesktopAssetManager implements AssetManager { } } - public void unregisterLocator(String rootPath, Class clazz){ + public void unregisterLocator(String rootPath, Class clazz){ handler.removeLocator(clazz, rootPath); if (logger.isLoggable(Level.FINER)){ logger.log(Level.FINER, "Unregistered locator: {0}", diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md index 14bd7ef3f..9ba39b1ed 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md +++ b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md @@ -3,7 +3,7 @@ MaterialDef Default GUI { MaterialParameters { Texture2D Texture Boolean UseTex - Vector4 Color : Color + Vector4 Color (Color) } Technique { diff --git a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag index bf79d84fa..e3b3d1e3c 100644 --- a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag +++ b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag @@ -654,10 +654,14 @@ void main(){ float innerAngleCos = floor(g_LightDirection.w) * 0.001; float outerAngleCos = fract(g_LightDirection.w); float innerMinusOuter = innerAngleCos - outerAngleCos; - spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0); - if(spotFallOff<=0.0){ - gl_FragColor = AmbientSum * diffuseColor; + + spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter; + + if(spotFallOff <= 0.0){ + gl_FragColor.rgb = AmbientSum * diffuseColor; return; + }else{ + spotFallOff = clamp(spotFallOff, 0.0, 1.0); } } diff --git a/engine/src/test/jme3test/asset/TestAbsoluteLocators.java b/engine/src/test/jme3test/asset/TestAbsoluteLocators.java index 0e271f094..b26a98ce1 100644 --- a/engine/src/test/jme3test/asset/TestAbsoluteLocators.java +++ b/engine/src/test/jme3test/asset/TestAbsoluteLocators.java @@ -44,7 +44,7 @@ public class TestAbsoluteLocators { public static void main(String[] args){ AssetManager am = new DesktopAssetManager(); - am.registerLoader(AWTLoader.class.getName(), "png"); + am.registerLoader(AWTLoader.class.getName(), "jpg"); am.registerLoader(WAVLoader.class.getName(), "wav"); // register absolute locator diff --git a/engine/src/test/jme3test/light/TestSpotLightTerrain.java b/engine/src/test/jme3test/light/TestSpotLightTerrain.java index 58a5c2199..27cb0c8b5 100644 --- a/engine/src/test/jme3test/light/TestSpotLightTerrain.java +++ b/engine/src/test/jme3test/light/TestSpotLightTerrain.java @@ -156,7 +156,7 @@ public class TestSpotLightTerrain extends SimpleApplication { matTerrain.setFloat("DiffuseMap_3_scale", rockScale); // RIVER ROCK texture - Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.png"); + Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"); riverRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_4", riverRock); matTerrain.setFloat("DiffuseMap_4_scale", rockScale);