/* * __ .__ .__ ._____. * _/ |_ _______ __|__| ____ | | |__\_ |__ ______ * \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/ * | | ( <_> > <| \ \___| |_| || \_\ \\___ \ * |__| \____/__/\_ \__|\___ >____/__||___ /____ > * \/ \/ \/ \/ * * Copyright (c) 2006-2011 Karsten Schmidt * * This library 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. * * http://creativecommons.org/licenses/LGPL/2.1/ * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ package toxi.geom.mesh; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; import toxi.geom.Vec3D; /** * A simple, but flexible and memory efficient exporter for binary STL files. * Custom color support is implemented via the STLcolorModel interface and the * exporter comes with the 2 most common format variations defined by the * DEFAULT and MATERIALISE constants. * * The minimal design of this exporter means it does not build an extra list of * faces in RAM and so is able to easily export models with millions of faces. * * http://en.wikipedia.org/wiki/STL_(file_format) */ public class STLWriter { /** * */ protected static final Logger logger = Logger.getLogger(STLWriter.class .getName()); /** * */ public static final int DEFAULT_RGB = -1; /** * */ public static final STLColorModel DEFAULT = new DefaultSTLColorModel(); /** * */ public static final STLColorModel MATERIALISE = new MaterialiseSTLColorModel( 0xffffffff); /** * */ public static final int DEFAULT_BUFFER = 0x10000; /** * */ protected OutputStream ds; /** * */ protected byte[] buf = new byte[4]; /** * */ protected int bufferSize; /** * */ protected Vec3D scale = new Vec3D(1, 1, 1); /** * */ protected boolean useInvertedNormals = false; /** * */ protected STLColorModel colorModel; /** * */ public STLWriter() { this(DEFAULT, DEFAULT_BUFFER); } /** * * @param cm * @param bufSize */ public STLWriter(STLColorModel cm, int bufSize) { colorModel = cm; this.bufferSize = bufSize; } /** * * @param stream * @param numFaces */ public void beginSave(OutputStream stream, int numFaces) { try { logger.info("starting to save STL data to output stream..."); ds = new BufferedOutputStream(new DataOutputStream(stream), bufferSize); writeHeader(numFaces); } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); } } /** * * @param fn * @param numFaces */ public void beginSave(String fn, int numFaces) { logger.log(Level.INFO, "saving mesh to: {0}", fn); try { beginSave(new FileOutputStream(fn), numFaces); } catch (FileNotFoundException ex) { logger.log(Level.SEVERE, null, ex); } } /** * */ public void endSave() { try { ds.flush(); ds.close(); } catch (Exception e) { logger.log(Level.SEVERE, null, e); } } /** * * @param a * @param b * @param c */ public void face(Vec3D a, Vec3D b, Vec3D c) { face(a, b, c, DEFAULT_RGB); } /** * * @param a * @param b * @param c * @param rgb */ public void face(Vec3D a, Vec3D b, Vec3D c, int rgb) { Vec3D normal = b.sub(a).crossSelf(c.sub(a)).normalize(); if (useInvertedNormals) { normal.invert(); } face(a, b, c, normal, rgb); } /** * * @param a * @param b * @param c * @param normal * @param rgb */ public void face(Vec3D a, Vec3D b, Vec3D c, Vec3D normal, int rgb) { try { writeVector(normal); // vertices writeScaledVector(a); writeScaledVector(b); writeScaledVector(c); // vertex attrib (color) if (rgb != DEFAULT_RGB) { writeShort(colorModel.formatRGB(rgb)); } else { writeShort(colorModel.getDefaultRGB()); } } catch (IOException e) { logger.log(Level.SEVERE, null, e); } } private void prepareBuffer(int a) { buf[3] = (byte) (a >>> 24); buf[2] = (byte) (a >> 16 & 0xff); buf[1] = (byte) (a >> 8 & 0xff); buf[0] = (byte) (a & 0xff); } /** * * @param s */ public void setScale(float s) { scale.set(s, s, s); } /** * * @param s */ public void setScale(Vec3D s) { scale.set(s); } /** * * @param state */ public void useInvertedNormals(boolean state) { useInvertedNormals = state; } /** * * @param a * @throws IOException */ protected void writeFloat(float a) throws IOException { prepareBuffer(Float.floatToRawIntBits(a)); ds.write(buf, 0, 4); } /** * * @param num * @throws IOException */ protected void writeHeader(int num) throws IOException { byte[] header = new byte[80]; colorModel.formatHeader(header); ds.write(header, 0, 80); writeInt(num); } /** * * @param a * @throws IOException */ protected void writeInt(int a) throws IOException { prepareBuffer(a); ds.write(buf, 0, 4); } /** * * @param v */ protected void writeScaledVector(Vec3D v) { try { writeFloat(v.x * scale.x); writeFloat(v.y * scale.y); writeFloat(v.z * scale.z); } catch (IOException e) { logger.log(Level.SEVERE, null, e); } } /** * * @param a * @throws IOException */ protected void writeShort(int a) throws IOException { buf[0] = (byte) (a & 0xff); buf[1] = (byte) (a >> 8 & 0xff); ds.write(buf, 0, 2); } /** * * @param v */ protected void writeVector(Vec3D v) { try { writeFloat(v.x); writeFloat(v.y); writeFloat(v.z); } catch (IOException e) { logger.log(Level.SEVERE, null, e); } } }