/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2011 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.io; import java.io.File; import java.util.HashMap; import java.util.Map; import org.jwildfire.base.Tools; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.transform.Mesh3D; import org.jwildfire.transform.Mesh3DTransformer.Rotate; public class SunFlowWriter { private void addImage(StringBuilder pSB, Mesh3D pMesh3D) { pSB.append(" image {\n"); pSB.append(" resolution " + pMesh3D.getImageWidth() + " " + pMesh3D.getImageHeight() + "\n"); // pSB.append(" aa 1 2\n"); // pSB.append(" samples 4\n"); pSB.append(" aa 0 0\n"); pSB.append(" samples 1\n"); pSB.append(" filter gaussian\n"); pSB.append(" }\n"); pSB.append("\n"); } private void addCamera(StringBuilder pSB, Mesh3D pMesh3D) { pSB.append(" camera {\n"); pSB.append(" type pinhole\n"); pSB.append(" eye " + pMesh3D.getLastTransformation().getCamX() + " " + pMesh3D.getLastTransformation().getCamZ() + " " + pMesh3D.getLastTransformation().getCamY() + "\n"); pSB.append(" target 0 0 0\n"); pSB.append(" up 0 0 1\n"); // pSB.append(" fov 20\n"); // pSB.append(" aspect " + ((double) pMesh3D.getImageWidth() / (double) pMesh3D.getImageHeight()) + "\n"); pSB.append(" fov 10.5\n"); pSB.append(" aspect 1.25\n"); pSB.append(" }\n"); pSB.append("\n"); } private void addLight(StringBuilder pSB, double pX, double pY, double pZ, int pRed, int pGreen, int pBlue) { if (pRed > 0 || pGreen > 0 || pBlue > 0) { pSB.append(" light {\n"); pSB.append(" type point\n"); pSB.append(" color { \"sRGB nonlinear\" " + ((double) pRed / 255.0) + " " + ((double) pGreen / 255.0) + " " + ((double) pBlue / 255.0) + " }\n"); pSB.append(" power 5000000\n"); pSB.append(" p " + pX + " " + pZ + " " + pY + "\n"); pSB.append(" }\n"); pSB.append("\n"); } } private void addLights(StringBuilder pSB, Mesh3D pMesh3D) { pSB.append(" gi { type path samples 16 }\n"); pSB.append("\n"); addLight(pSB, pMesh3D.getLastTransformation().getLight1X(), pMesh3D.getLastTransformation().getLight1Y(), pMesh3D.getLastTransformation().getLight1Z(), pMesh3D.getLastTransformation().getLight1Color().getRed(), pMesh3D.getLastTransformation().getLight1Color().getGreen(), pMesh3D.getLastTransformation().getLight1Color().getBlue()); addLight(pSB, pMesh3D.getLastTransformation().getLight2X(), pMesh3D.getLastTransformation().getLight2Y(), pMesh3D.getLastTransformation().getLight2Z(), pMesh3D.getLastTransformation().getLight2Color().getRed(), pMesh3D.getLastTransformation().getLight2Color().getGreen(), pMesh3D.getLastTransformation().getLight2Color().getBlue()); addLight(pSB, pMesh3D.getLastTransformation().getLight3X(), pMesh3D.getLastTransformation().getLight3Y(), pMesh3D.getLastTransformation().getLight3Z(), pMesh3D.getLastTransformation().getLight3Color().getRed(), pMesh3D.getLastTransformation().getLight3Color().getGreen(), pMesh3D.getLastTransformation().getLight3Color().getBlue()); addLight(pSB, pMesh3D.getLastTransformation().getLight4X(), pMesh3D.getLastTransformation().getLight4Y(), pMesh3D.getLastTransformation().getLight4Z(), pMesh3D.getLastTransformation().getLight4Color().getRed(), pMesh3D.getLastTransformation().getLight4Color().getGreen(), pMesh3D.getLastTransformation().getLight4Color().getBlue()); } private void addShaders(StringBuilder pSB, Mesh3D pMesh3D, String pTexturename) { pSB.append(" shader {\n"); pSB.append(" name \"shader01\"\n"); pSB.append(" type phong\n"); pSB.append(" texture " + pTexturename + "\n"); pSB.append(" spec { \"sRGB nonlinear\" 1 1 1 } 100\n"); pSB.append(" samples 4\n"); pSB.append(" }\n"); pSB.append("\n"); pSB.append(" shader {\n"); pSB.append(" name \"shader02\"\n"); pSB.append(" type shiny\n"); pSB.append(" texture " + pTexturename + "\n"); pSB.append(" refl 0.0\n"); pSB.append(" }\n"); pSB.append("\n"); } public void saveMesh(Mesh3D pMesh3D, String pAbsolutePath) throws Exception { // create texture String textureFilename; { File outputFile = new File(pAbsolutePath); String filename = outputFile.getName(); int p = filename.lastIndexOf("."); if (p > 0 && p < filename.length() - 1) { textureFilename = filename.substring(0, p) + ".jpg"; } else { textureFilename = filename + ".jpg"; } new ImageWriter().saveAsJPEG(pMesh3D.getTexture(), new File(outputFile.getParent(), textureFilename).getAbsolutePath()); } // StringBuilder sb = new StringBuilder(); addImage(sb, pMesh3D); addCamera(sb, pMesh3D); addLights(sb, pMesh3D); addShaders(sb, pMesh3D, textureFilename); sb.append(" object {\n"); sb.append(" shader \"shader01\"\n"); sb.append(" transform {\n"); sb.append(" scaleu " + pMesh3D.getLastTransformation().getZoom() + "\n"); if (pMesh3D.getLastTransformation().getDoRotate() == Rotate.XY) { sb.append(" rotatex " + (90 - pMesh3D.getLastTransformation().getBeta()) + "\n"); sb.append(" rotatey 0\n"); sb.append(" rotatez " + (-pMesh3D.getLastTransformation().getAlpha()) + "\n"); } else { throw new Exception("Only XY rotation is currently supported"); } sb.append(" translate 0 0 0\n"); sb.append(" }\n"); sb.append(" type generic-mesh\n"); sb.append(" name \"JWildfire.mesh.001\"\n"); // points sb.append(" points "); sb.append(pMesh3D.getPCount()); sb.append("\n"); double x[] = pMesh3D.getX(), y[] = pMesh3D.getY(), z[] = pMesh3D.getZ(); double xMin = 0.0, xMax = 0.0, yMin = 0.0, yMax = 0.0, xSize, ySize; double scl = 1; for (int i = 0; i < pMesh3D.getPCount(); i++) { if (x[i] < xMin) { xMin = x[i]; } else if (x[i] > xMax) { xMax = x[i]; } if (y[i] < yMin) { yMin = y[i]; } else if (y[i] > yMax) { yMax = y[i]; } sb.append(" " + Tools.doubleToString(x[i] * scl) + " " + Tools.doubleToString(y[i] * scl) + " " + Tools.doubleToString(-z[i] * scl) + "\n"); } xSize = xMax - xMin; if (xSize < MathLib.EPSILON) { xSize = MathLib.EPSILON; } ySize = yMax - yMin; if (ySize < MathLib.EPSILON) { ySize = MathLib.EPSILON; } // triangles sb.append(" triangles "); sb.append(pMesh3D.getFCount()); sb.append("\n"); int pp1[] = pMesh3D.getPP1(), pp2[] = pMesh3D.getPP2(), pp3[] = pMesh3D.getPP3(); for (int i = 0; i < pMesh3D.getFCount(); i++) { sb.append(" " + pp1[i] + " " + pp2[i] + " " + pp3[i] + "\n"); } // normals // sb.append(" normals none\n"); sb.append(" normals vertex\n"); Map<Integer, AvgNormal> normals = new HashMap<Integer, AvgNormal>(); for (int i = 0; i < pMesh3D.getFCount(); i++) { int p1 = pp1[i], p2 = pp2[i], p3 = pp3[i]; double x1 = x[p1]; double y1 = y[p1]; double z1 = z[p1]; double x2 = x[p2]; double y2 = y[p2]; double z2 = z[p2]; double x3 = x[p3]; double y3 = y[p3]; double z3 = z[p3]; double vax = x2 - x1; double vay = y2 - y1; double vaz = z2 - z1; double vbx = x3 - x1; double vby = y3 - y1; double vbz = z3 - z1; double nfx = vay * vbz - vaz * vby; double nfy = vaz * vbx - vax * vbz; double nfz = vax * vby - vay * vbx; double rr = Math.sqrt(nfx * nfx + nfy * nfy + nfz * nfz); if (Math.abs(rr) <= MathLib.EPSILON) { nfx = nfy = 0.0; nfz = -1.0; } else { nfx = 0.0 - nfx / rr; nfy = 0.0 - nfy / rr; nfz = 0.0 - nfz / rr; } AvgNormal n; n = normals.get(p1); if (n == null) { n = new AvgNormal(); normals.put(p1, n); } n.sumNX += nfx; n.sumNY += nfy; n.sumNZ += nfz; n.count++; n = normals.get(p2); if (n == null) { n = new AvgNormal(); normals.put(p2, n); } n.sumNX += nfx; n.sumNY += nfy; n.sumNZ += nfz; n.count++; n = normals.get(p3); if (n == null) { n = new AvgNormal(); normals.put(p3, n); } n.sumNX += nfx; n.sumNY += nfy; n.sumNZ += nfz; n.count++; } for (int i = 0; i < pMesh3D.getPCount(); i++) { AvgNormal n = normals.get(i); if (n == null) { n = new AvgNormal(); n.count++; } sb.append(" " + Tools.doubleToString(n.sumNX / (double) n.count) + " " + Tools.doubleToString(n.sumNY / (double) n.count) + " " + Tools.doubleToString(n.sumNZ / (double) n.count) + "\n"); } sb.append(" uvs vertex\n"); for (int i = 0; i < pMesh3D.getPCount(); i++) { // double x1 = x[i]; // double y1 = y[i]; // double xx = (x1 - xMin) / xSize; // double yy = (y1 - yMin) / ySize; // sb.append(" " + xx + " " + yy + "\n"); double u = pMesh3D.getU()[i]; double v = pMesh3D.getV()[i]; sb.append(" " + u + " " + v + "\n"); } sb.append(" }\n"); Tools.writeUTF8Textfile(pAbsolutePath, sb.toString()); } private static class AvgNormal { public double sumNX, sumNY, sumNZ; public int count; } }