/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.jme3.gde.materials; import com.jme3.asset.TextureKey; import com.jme3.gde.core.assets.ProjectAssetManager; import com.jme3.gde.core.util.Beans; import com.jme3.gde.materialdefinition.utils.MaterialUtils; import com.jme3.gde.materials.wizards.StoreTextureWizardWizardAction; import com.jme3.material.MatParam; import com.jme3.material.MatParamTexture; import com.jme3.material.Material; import com.jme3.texture.Image; import com.jme3.texture.Texture; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import jme3tools.converters.ImageToAwt; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; /** * Provides an editable j3m file * * @author normenhansen */ public class EditableMaterialFile { private static final Logger logger = Logger.getLogger(EditableMaterialFile.class.getName()); private String name; private String matDefName; private FileObject material; private FileObject matDef; private Map materialParameters = new LinkedHashMap(); private Map additionalRenderStates = new HashMap(); private List matDefEntries = new ArrayList(); private ProjectAssetManager manager; private FileSystem fs; public static final String[] variableTypes = new String[]{"Int", "Boolean", "Float", "Vector2", "Vector3", "Vector4", "Color", "Texture2D", "Texture3D", "TextureArray", "TextureBuffer", "TextureCubeMap"}; public EditableMaterialFile(FileObject material, ProjectAssetManager manager) { this.material = material; this.manager = manager; } private void prepareAdditionalStates() { additionalRenderStates.clear(); additionalRenderStates.put("Wireframe", new MaterialProperty("OnOff", "Wireframe", "")); additionalRenderStates.put("DepthWrite", new MaterialProperty("OnOff", "DepthWrite", "")); additionalRenderStates.put("DepthTest", new MaterialProperty("OnOff", "DepthTest", "")); additionalRenderStates.put("ColorWrite", new MaterialProperty("OnOff", "ColorWrite", "")); additionalRenderStates.put("PointSprite", new MaterialProperty("OnOff", "PointSprite", "")); additionalRenderStates.put("FaceCull", new MaterialProperty("FaceCullMode", "FaceCull", "")); additionalRenderStates.put("Blend", new MaterialProperty("BlendMode", "Blend", "")); additionalRenderStates.put("AlphaTestFalloff", new MaterialProperty("Float", "AlphaTestFalloff", "")); additionalRenderStates.put("PolyOffset", new MaterialProperty("Float,Float", "PolyOffset", "")); } /** * loads the data from the material and matdef files */ public void read() { prepareAdditionalStates(); materialParameters.clear(); int level = 0; boolean params = false; boolean states = false; try { //scan material text for (String line : material.asLines()) { //trim line incl comments line = MaterialUtils.trimLine(line); //find and load matdef file if (line.startsWith("Material ") || line.startsWith("Material\t") && level == 0) { parseMaterialProperties(line); } //start parsing material parameters if (line.startsWith("MaterialParameters ") || line.startsWith("MaterialParameters\t") || line.startsWith("MaterialParameters{") && level == 1) { params = true; } //start parsing renderstates if (line.startsWith("AdditionalRenderState ") || line.startsWith("AdditionalRenderState\t") || line.startsWith("AdditionalRenderState{") && level == 1) { states = true; } //up a level if (line.indexOf("{") != -1) { level++; } //down a level, stop processing parameters/states if (line.indexOf("}") != -1) { level--; if (params) { params = false; } if (states) { states = false; } } //try reading parameter if (level == 2 && params) { int colonIdx = line.indexOf(":"); if (colonIdx != -1) { String[] lines = line.split(":"); MaterialProperty prop = new MaterialProperty(); prop.setName(lines[0].trim()); if (lines.length > 1) { prop.setValue(lines[lines.length - 1].trim()); } materialParameters.put(prop.getName(), prop); } } //try reading state if (level == 2 && states) { String[] lines = null; int colonIdx = line.indexOf(" "); if (colonIdx != -1) { lines = line.split(" "); } colonIdx = line.indexOf("\t"); if (colonIdx != -1) { lines = line.split("\t"); } if (lines != null) { MaterialProperty prop = new MaterialProperty(); String name = lines[0].trim(); prop.setName(name); if (additionalRenderStates.get(name) != null) { prop.setType(additionalRenderStates.get(name).getType()); if (lines.length > 1) { String value = ""; for (int i = 1; i < lines.length; i++) { value += " " + lines[i]; } prop.setValue(value.trim()); } additionalRenderStates.put(prop.getName(), prop); } } } } checkWithMatDef(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } /** * finds and loads the matdef file either from project or from base jme * * @param line */ private void parseMaterialProperties(String line) { int colonIdx = line.indexOf(":"); //find matdef file if (colonIdx != -1) { line = line.replaceFirst("Material", ""); line = line.replace("{", ""); String[] lines = line.split(":"); setName(lines[0].trim()); setMatDefName(lines[1].trim()); } } private void initMatDef() { //try to read from assets folder matDef = manager.getAssetFolder().getFileObject(getMatDefName()); //try to read from classpath if not in assets folder and store in a virtual filesystem folder if (matDef == null || !matDef.isValid()) { try { fs = FileUtil.createMemoryFileSystem(); matDef = fs.getRoot().createData(name, "j3md"); OutputStream out = matDef.getOutputStream(); InputStream in = manager.getResourceAsStream(getMatDefName()); if (in != null) { int input = in.read(); while (input != -1) { out.write(input); input = in.read(); } in.close(); } out.close(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } } /** * Finds and loads the matdef file either from project or from base jme, * then applies the parameter values to the material parameter entries. * * @param line */ private void checkWithMatDef() { //load matdef matDefEntries.clear(); boolean params = false; int level = 0; if (matDef != null && matDef.isValid()) { try { for (String defLine : matDef.asLines()) { defLine = MaterialUtils.trimLine(defLine.trim()); if (defLine.startsWith("MaterialParameters ") || defLine.startsWith("MaterialParameters\t") || defLine.startsWith("MaterialParameters{") && level == 1) { params = true; } if (defLine.indexOf("{") != -1) { level++; } if (defLine.indexOf("}") != -1) { level--; if (params) { params = false; } } //read variable types if (level == 2 && params) { for (int i = 0; i < variableTypes.length; i++) { String string = variableTypes[i]; if (defLine.startsWith(string)) { String name = MaterialUtils.trimName(defLine.replaceFirst(string, "")); matDefEntries.add(name); MaterialProperty prop = materialParameters.get(name); if (prop == null) { prop = new MaterialProperty(); prop.setName(name); prop.setValue(""); materialParameters.put(prop.getName(), prop); } prop.setType(string); } } } } } catch (IOException ex) { Exceptions.printStackTrace(ex); } } else { Logger.getLogger(EditableMaterialFile.class.getName()).log(Level.WARNING, "Could not read MaterialDef!"); } for (Iterator> it = materialParameters.entrySet().iterator(); it.hasNext();) { Map.Entry entry = it.next(); if (!matDefEntries.contains(entry.getKey())) { it.remove(); } } } /** * returns the new content of the material file, filled with the new * parameters * * @return */ public String getUpdatedContent() { boolean params = false; boolean states = false; boolean addedstates = false; int level = 0; try { List matLines = material.asLines(); StringWriter out = new StringWriter(); List setValues = new LinkedList(); List setStates = new LinkedList(); //goes through the lines of the material file and replaces the values it finds for (String line : matLines) { String newLine = line; line = MaterialUtils.trimLine(line); //write material header if (line.startsWith("Material ") || line.startsWith("Material\t") && level == 0) { String suffix = ""; if (line.indexOf("{") > -1) { suffix = "{"; } newLine = "Material " + getName() + " : " + matDefName + " " + suffix; } //start parameters if (line.startsWith("MaterialParameters ") || line.startsWith("MaterialParameters\t") || line.startsWith("MaterialParameters{") && level == 1) { params = true; } //start states if (line.startsWith("AdditionalRenderState ") || line.startsWith("AdditionalRenderState\t") || line.startsWith("AdditionalRenderState{") && level == 1) { states = true; addedstates = true; } //up a level if (line.indexOf("{") != -1) { level++; } //down a level, stop processing states and check if all parameters and states have been written if (line.indexOf("}") != -1) { level--; //find and write parameters we did not replace yet at end of parameters section if (params) { for (Iterator> it = materialParameters.entrySet().iterator(); it.hasNext();) { Map.Entry entry = it.next(); if (!setValues.contains(entry.getKey()) && matDefEntries.contains(entry.getKey())) { MaterialProperty prop = entry.getValue(); if (prop.getValue() != null && prop.getValue().length() > 0) { String myLine = " " + prop.getName() + " : " + prop.getValue() + "\n"; out.write(myLine, 0, myLine.length()); } } } params = false; } //find and write states we did not replace yet at end of states section if (states) { for (Iterator> it = additionalRenderStates.entrySet().iterator(); it.hasNext();) { Map.Entry entry = it.next(); if (!setStates.contains(entry.getKey())) { MaterialProperty prop = entry.getValue(); if (prop.getValue() != null && prop.getValue().length() > 0) { String myLine = " " + prop.getName() + " " + prop.getValue() + "\n"; out.write(myLine, 0, myLine.length()); } } } states = false; } //add renderstates if they havent been in the file yet if (level == 0) { if (!addedstates) { String myLine = " AdditionalRenderState {\n"; out.write(myLine, 0, myLine.length()); for (Iterator> it = additionalRenderStates.entrySet().iterator(); it.hasNext();) { Map.Entry entry = it.next(); if (!setStates.contains(entry.getKey())) { MaterialProperty prop = entry.getValue(); if (prop.getValue() != null && prop.getValue().length() > 0) { myLine = " " + prop.getName() + " " + prop.getValue() + "\n"; out.write(myLine, 0, myLine.length()); } } } myLine = " }\n"; out.write(myLine, 0, myLine.length()); } } } //try replacing value of parameter line with new value if (level == 2 && params) { int colonIdx = newLine.indexOf(":"); if (colonIdx != -1) { String[] lines = newLine.split(":"); String myName = lines[0].trim(); if (materialParameters.containsKey(myName)) { setValues.add(myName); MaterialProperty prop = materialParameters.get(myName); if (prop.getValue() != null && prop.getValue().length() > 0 && prop.getType() != null) { newLine = lines[0] + ": " + prop.getValue(); } else { newLine = null; } } else if (!matDefEntries.contains(myName)) { newLine = null; } } } //try replacing value of state line with new value if (level == 2 && states) { String cutLine = newLine.trim(); String[] lines = null; int colonIdx = cutLine.indexOf(" "); if (colonIdx != -1) { lines = cutLine.split(" "); } colonIdx = cutLine.indexOf("\t"); if (colonIdx != -1) { lines = cutLine.split("\t"); } if (lines != null) { String myName = lines[0].trim(); if (additionalRenderStates.containsKey(myName)) { setStates.add(myName); MaterialProperty prop = additionalRenderStates.get(myName); if (prop.getValue() != null && prop.getValue().length() > 0 && prop.getType() != null) { newLine = " " + lines[0] + " " + prop.getValue(); } else { newLine = null; } } } } if (newLine != null) { out.write(newLine + "\n", 0, newLine.length() + 1); } } out.close(); return out.toString(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } return ""; } private void createBaseMaterialFile() throws IOException { OutputStreamWriter out = new OutputStreamWriter(material.getOutputStream()); out.write("Material MyMaterial : " + matDefName + " {\n"); out.write(" MaterialParameters {\n"); out.write(" }\n"); out.write("}\n"); out.close(); } public Map getParameterMap() { return materialParameters; } public Map getStateMap() { return additionalRenderStates; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } public void setAsText(String text) throws IOException { OutputStreamWriter out = new OutputStreamWriter(material.getOutputStream()); out.write(text, 0, text.length()); out.close(); } /** * Creates the data from a material * * @param mat */ public void setAsMaterial(Material mat) throws IOException { assert (mat.getMaterialDef().getAssetName() != null); setName("MyMaterial"); setMatDefName(mat.getMaterialDef().getAssetName()); createBaseMaterialFile(); materialParameters.clear(); checkPackedTextureProps(mat); additionalRenderStates.put("Wireframe", new MaterialProperty("OnOff", "Wireframe", mat.getAdditionalRenderState().isWireframe() ? "On" : "Off")); additionalRenderStates.put("DepthWrite", new MaterialProperty("OnOff", "DepthWrite", mat.getAdditionalRenderState().isDepthWrite() ? "On" : "Off")); additionalRenderStates.put("DepthTest", new MaterialProperty("OnOff", "DepthTest", mat.getAdditionalRenderState().isDepthTest() ? "On" : "Off")); additionalRenderStates.put("ColorWrite", new MaterialProperty("OnOff", "ColorWrite", mat.getAdditionalRenderState().isColorWrite() ? "On" : "Off")); additionalRenderStates.put("PointSprite", new MaterialProperty("OnOff", "PointSprite", mat.getAdditionalRenderState().isPointSprite() ? "On" : "Off")); additionalRenderStates.put("FaceCull", new MaterialProperty("FaceCullMode", "FaceCull", mat.getAdditionalRenderState().getFaceCullMode().name())); additionalRenderStates.put("Blend", new MaterialProperty("BlendMode", "Blend", mat.getAdditionalRenderState().getBlendMode().name())); additionalRenderStates.put("AlphaTestFalloff", new MaterialProperty("Float", "AlphaTestFalloff", mat.getAdditionalRenderState().getAlphaFallOff() + "")); additionalRenderStates.put("PolyOffset", new MaterialProperty("Float,Float", "PolyOffset", mat.getAdditionalRenderState().getPolyOffsetUnits() + " " + mat.getAdditionalRenderState().getPolyOffsetFactor())); checkWithMatDef(); setAsText(getUpdatedContent()); } /** * Prompts user to save packed textures * * @param mat * @param param */ private void checkPackedTextureProps(Material mat) { Collection params = mat.getParams(); for (Iterator it = new ArrayList(params).iterator(); it.hasNext();) { MatParam param = it.next(); MaterialProperty prop = new MaterialProperty(param); if (prop.getValue() == null) { switch (param.getVarType()) { case Texture2D: case Texture3D: case TextureArray: case TextureBuffer: case TextureCubeMap: try { MatParamTexture texParam = mat.getTextureParam(param.getName()); Texture tex = texParam.getTextureValue(); Image img = tex.getImage(); if (img == null) { logger.log(Level.INFO, "No image found"); return; } BufferedImage image = ImageToAwt.convert(img, false, false, 0); ByteArrayOutputStream out = new ByteArrayOutputStream(); ImageWriter imgWrtr = ImageIO.getImageWritersByFormatName("png").next(); ImageOutputStream imgOutStrm; imgOutStrm = ImageIO.createImageOutputStream(out); imgWrtr.setOutput(imgOutStrm); ImageWriteParam jpgWrtPrm = imgWrtr.getDefaultWriteParam(); imgWrtr.write(null, new IIOImage(image, null, null), jpgWrtPrm); imgOutStrm.close(); out.close(); String texturePath = material.getName(); texturePath = "Textures/" + texturePath + "-" + param.getName() + ".png"; StoreTextureWizardWizardAction act = new StoreTextureWizardWizardAction(manager, out.toByteArray(), texturePath); act.actionPerformed(null); texturePath = act.getName(); TextureKey texKey = new TextureKey(texturePath); TextureKey oldKey = (TextureKey)tex.getKey(); if(oldKey!=null){ Beans.copyProperties(texKey, oldKey); } //TODO: seems like flip is removed due to ImageToAwt texKey.setFlipY(false); Texture texture = manager.loadTexture(texKey); MatParamTexture newParam = new MatParamTexture(texParam.getVarType(), texParam.getName(), texture, texParam.getUnit(), null); materialParameters.put(newParam.getName(), new MaterialProperty(newParam)); } catch (Exception ex) { Exceptions.printStackTrace(ex); } break; default: } } else { materialParameters.put(param.getName(), prop); } } } /** * @return the matDefName */ public String getMatDefName() { return matDefName; } /** * @param matDefName the matDefName to set */ public void setMatDefName(String matDefName) { this.matDefName = matDefName; assert (matDefName != null); initMatDef(); checkWithMatDef(); } public String getMaterialPath() { return manager.getRelativeAssetPath(material.getPath()); } }