/** * OBJWriter.java * * Copyright (c) 2013-2016, F(X)yz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of F(X)yz, any associated website, nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL F(X)yz BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.fxyz3d.io; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.IntStream; import javafx.embed.swing.SwingFXUtils; import javafx.geometry.Point2D; import javafx.scene.image.Image; import javafx.scene.paint.Color; import javafx.scene.shape.TriangleMesh; import javax.imageio.ImageIO; import org.fxyz3d.geometry.Point3D; import org.fxyz3d.scene.paint.Palette; import org.fxyz3d.scene.paint.Patterns; import org.fxyz3d.shapes.primitives.helper.TriangleMeshHelper.TextureType; /** * * @author jpereda */ public class OBJWriter { private final String newline = System.getProperty("line.separator"); private float[] points0, texCoord0; private int[] faces0, sm0; private BufferedWriter writer = null; private final TriangleMesh mesh; private final String fileName; private String diffuseMap; private String diffuseColor="0.0 0.0 0.0"; // black private TextureType defaultTexture=TextureType.NONE; public OBJWriter(TriangleMesh mesh, String fileName){ this.mesh=mesh; this.fileName=fileName; } public void setMaterialColor(Color color){ diffuseColor=""+((float)(color.getRed()))+" "+((float)(color.getGreen()))+" "+((float)(color.getBlue())); } public void setTextureColors(int numColors){ if(numColors>0){ defaultTexture=TextureType.COLORED_VERTICES_3D; Palette palette=new Palette(numColors); palette.createPalette(true); diffuseMap="palette_"+numColors+".png"; } } public void setTexturePattern(){ defaultTexture=TextureType.PATTERN; Patterns pattern = new Patterns(12, 12); pattern.createPattern(true); diffuseMap="patterne_12x12.png"; } public void setTextureImage(String image){ try { // save ImageIO.write(SwingFXUtils.fromFXImage(new Image(image), null), "png", new File("image.png")); } catch (IOException ex) { System.out.println("Error saving image"); } diffuseMap="image.png"; } public void exportMesh(){ File objFile = new File(fileName+".obj"); try{ writer = new BufferedWriter(new FileWriter(objFile)); writer.write("# Material"+newline); writer.write("mtllib "+fileName+".mtl"+newline); points0=new float[mesh.getPoints().size()]; mesh.getPoints().toArray(points0); List<Point3D> points1 = IntStream.range(0, points0.length/3) .mapToObj(i -> new Point3D(points0[3*i], points0[3*i+1], points0[3*i+2])) .collect(Collectors.toList()); writer.write("# Vertices ("+points1.size()+")"+newline); points1.forEach(p->{ try { writer.write("v "+p.x+" "+p.y+" "+p.z+""+newline); } catch (IOException ex) { System.out.println("Error writting vertex "+ex); } }); writer.write("# End Vertices"+newline); writer.write(newline); texCoord0=new float[mesh.getTexCoords().size()]; mesh.getTexCoords().toArray(texCoord0); List<Point2D> texCoord1 = IntStream.range(0, texCoord0.length/2) .mapToObj(i -> new Point2D(texCoord0[2*i], texCoord0[2*i+1])) .collect(Collectors.toList()); writer.write("# Textures Coordinates ("+texCoord1.size()+")"+newline); texCoord1.forEach(t->{ try { // objimporter u->u, v->(1-v) writer.write("vt "+((float)t.getX())+" "+((float)(1d-t.getY()))+""+newline); } catch (IOException ex) { System.out.println("Error writting texture coordinate "+ex); } }); writer.write("# End Texture Coordinates "+newline); writer.write(newline); faces0=new int[mesh.getFaces().size()]; mesh.getFaces().toArray(faces0); List<Integer[]> faces1 = IntStream.range(0, faces0.length/6) .mapToObj(i -> new Integer[]{faces0[6*i], faces0[6*i+1], faces0[6*i+2], faces0[6*i+3], faces0[6*i+4], faces0[6*i+5]}) .collect(Collectors.toList()); writer.write("# Faces ("+faces1.size()+")"+newline); writer.write("# Material"+newline); writer.write("usemtl "+fileName+""+newline); sm0=new int[mesh.getFaces().size()]; mesh.getFaceSmoothingGroups().toArray(sm0); if(sm0[0]>0){ writer.write("s "+sm0[0]+""+newline); } AtomicInteger count = new AtomicInteger(); faces1.forEach(f->{ try { writer.write("f "+(f[0]+1)+"/"+(f[1]+1)+ " "+(f[2]+1)+"/"+(f[3]+1)+ " "+(f[4]+1)+"/"+(f[5]+1)+""+newline); if(sm0[count.getAndIncrement()]!=sm0[count.get()]){ writer.write("s "+(sm0[count.get()]>0?sm0[count.get()]:"off")+""+newline); } } catch (IOException ex) { System.out.println("Error writting face "+ex); } }); writer.write("# End Faces "+newline); writer.write(newline); } catch(IOException io){ System.out.println("Error creating writer obj "+io); } finally { try { if(writer!=null){ writer.close(); } } catch (Exception e) {} } File mtlFile = new File(fileName+".mtl"); try{ writer = new BufferedWriter(new FileWriter(mtlFile)); writer.write("# Material "+fileName+""+newline); writer.write("newmtl "+fileName+""+newline); writer.write("illum 4"+newline); // Illumination [0-10] writer.write("Kd "+diffuseColor+""+newline); // diffuse color black writer.write("Ka 0.10 0.10 0.10"+newline); // ambient color writer.write("Tf 1.00 1.00 1.00"+newline); // Transmission filter if(diffuseMap!=null){ writer.write("map_Kd "+diffuseMap+""+newline); } writer.write("Ni 1.00"+newline); // optical density writer.write("Ks 1.00 1.00 1.00"+newline); // specular reflectivity writer.write("Ns 32.00"+newline); // specular exponent } catch(IOException io){ System.out.println("Error creating writer mtl "+io); } finally { try { if(writer!=null){ writer.close(); } } catch (Exception e) {} } } }