init
authoradam <adam@megacz.com>
Mon, 26 Nov 2007 03:43:57 +0000 (19:43 -0800)
committeradam <adam@megacz.com>
Mon, 26 Nov 2007 03:43:57 +0000 (19:43 -0800)
darcs-hash:20071126034357-5007d-538cebe4569498197ec1e1a53f79348b55540f15.gz

Makefile [new file with mode: 0644]
src/Geom.java [new file with mode: 0644]
src/StlFile.java [new file with mode: 0644]
src/StlFileParser.java [new file with mode: 0644]
teapot.stl [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..a9c9949
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+all:
+       mkdir -p build
+       javac -d build `find src -name \*.java`
+       java -cp build Geom
diff --git a/src/Geom.java b/src/Geom.java
new file mode 100644 (file)
index 0000000..fec07b5
--- /dev/null
@@ -0,0 +1,164 @@
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.media.opengl.*;
+import javax.media.opengl.glu.*;
+
+public class Geom implements GLEventListener {
+
+    public static StlFile stlf = null;
+
+    /**
+     * Take care of initialization here.
+     */
+    public void init(GLAutoDrawable gld) {
+        GL gl = gld.getGL();
+        GLU glu = new GLU();
+        gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        gl.glViewport(0, 0, 500, 300);
+        gl.glMatrixMode(GL.GL_PROJECTION);
+        gl.glLoadIdentity();
+        //glu.gluOrtho2D(0.0, 500.0, 0.0, 300.0);
+        display(gld);
+    }
+
+
+    /**
+     * Take care of drawing here.
+     */
+    public void display(GLAutoDrawable drawable) {
+        float red = 0.0f;
+        float green = 0.0f;
+        float blue = 0.0f;
+        GL gl = drawable.getGL();
+        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
+        gl.glPointSize(5.0f);
+
+        for(int i=0; i<stlf.coordArray.length; i+=3) {
+            red -= .09f;
+            green -= .12f;
+            blue -= .15f;
+            if (red < 0.15) red = 1.0f;
+            if (green < 0.15) green = 1.0f;
+            if (blue < 0.15) blue = 1.0f;
+            gl.glColor3f(red, green, blue);
+
+            gl.glBegin(GL.GL_TRIANGLES);
+            for(int j=0; j<3; j++)
+                gl.glVertex3f(stlf.coordArray[i+j].x,
+                              stlf.coordArray[i+j].y,
+                              stlf.coordArray[i+j].z);
+            gl.glEnd();
+        }
+    }
+
+
+    public void reshape(
+                        GLAutoDrawable drawable,
+                        int x,
+                        int y,
+                        int width,
+                     int height
+                        ) {}
+    public void displayChanged(
+                               GLAutoDrawable drawable,
+                               boolean modeChanged,
+                            boolean deviceChanged
+                               ) {}
+
+    /** point in 3-space */
+    public static class P {
+        public final float x, y, z;
+        public P(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
+        public V minus(P p) { return null; }
+    }
+
+    /** vector in 3-space */
+    public static class V {
+        private final float x, y, z;
+        public V(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
+        public V norm() { return null; /* FIXME */ }
+        public boolean sameDirection(V v) { throw new Error(); }
+        public V cross(V v) { return null; }
+    }
+
+    /** an edge */
+    public static class E {
+        public final P p1, p2;
+        private T t1, t2;
+        public E(P p1, P p2) { this.p1 = p1; this.p2 = p2; }
+        public boolean equals(Object o) {
+            if (o==null || !(o instanceof E)) return false;
+            E e = (E)o;
+            if (this.p1 == e.p1 && this.p2 == e.p2) return true;
+            if (this.p2 == e.p1 && this.p1 == e.p2) return true;
+            return false;
+        }
+        public P shared(E e) { return null; }
+    }
+
+    /** a triangle (face) */
+    public static class T {
+        public final E e1, e2, e3;
+        public T(E e1, E e2, E e3, V normal) {
+            P p1 = e1.shared(e2);
+            P p2 = e2.shared(e3);
+            P p3 = e3.shared(e1);
+            V norm = p2.minus(p1).cross(p3.minus(p1));
+            if (norm.sameDirection(normal)) {
+                this.e1 = e1;
+                this.e2 = e2;
+                this.e3 = e3;
+            } else {
+                this.e1 = e3;
+                this.e2 = e2;
+                this.e3 = e1;
+            }
+        }
+        public V norm() {
+            P p1 = e1.shared(e2);
+            P p2 = e2.shared(e3);
+            P p3 = e3.shared(e1);
+            return p2.minus(p1).cross(p3.minus(p1)).norm();
+        }
+        public T(E e1, E e2, E e3) { this.e1 = e1; this.e2 = e2; this.e3 = e3; }
+    }
+
+    /** matrix */
+    public static class M {
+    }
+
+    public static void main(String[] s) throws Exception {
+        stlf = new StlFile();
+        stlf.load("teapot.stl");
+        /*
+        for(int i=0; i<stlf.coordArray.length; i+=3) {
+            System.out.println("triangle: (" +
+                               stlf.coordArray[i+0] + ", " +
+                               stlf.coordArray[i+1] + ", " +
+                               stlf.coordArray[i+2] + ")");
+        }
+
+    gi.setCoordinates();
+    gi.setNormals(normArray);
+    gi.setStripCounts(stripCounts);
+
+        GeometryInfo gi = ;
+        ga.convertToIndexedTriangles();
+        GeometryArray ga = ;
+        ga.convertToIndexedTriangles();
+        */
+
+        Geom geom = new Geom();
+        
+
+        Frame f = new Frame();
+        GLCapabilities glcaps = new GLCapabilities();
+        GLCanvas glcanvas = new GLCanvas();
+        glcanvas.addGLEventListener(geom);
+        f.add(glcanvas, BorderLayout.CENTER);
+        f.show();
+        f.setSize(500, 300);
+    }
+
+}
diff --git a/src/StlFile.java b/src/StlFile.java
new file mode 100644 (file)
index 0000000..d069f48
--- /dev/null
@@ -0,0 +1,897 @@
+import com.sun.j3d.loaders.Loader;
+import com.sun.j3d.loaders.Scene;
+import com.sun.j3d.loaders.SceneBase;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import java.io.Reader;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import java.util.StringTokenizer;
+import java.util.ArrayList;
+
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.Shape3D;
+
+// New from JDK 1.4 for endian related problems
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+
+
+
+/**
+ * Title:         STL Loader
+ * Description:   STL files loader (Supports ASCII and binary files) for Java3D
+ *                Needs JDK 1.4 due to endian problems
+ * Company:       Universidad del Pais Vasco (UPV/EHU)
+ * @author:       Carlos Pedrinaci Godoy
+ * @version:      1.0
+ *
+ * Contact : xenicp@yahoo.es
+ *
+ *
+ * Things TO-DO:
+ *    1.-We can't read binary files over the net.
+ *    2.-For binary files if size is lower than expected (calculated with the number of faces)
+ *    the program will block.
+ *    3.-Improve the way for detecting the kind of stl file?
+ *    Can give us problems if the comment of the binary file begins by "solid"
+ */
+
+public class StlFile implements Loader
+{
+  private static final int DEBUG = 0;     // Sets mode to Debug: outputs every action done
+
+  // Maximum length (in chars) of basePath
+  private static final int MAX_PATH_LENGTH = 1024;
+
+  // Global variables
+  private int flag;                         // Needed cause implements Loader
+
+  private URL baseUrl = null;               // Reading files over Internet
+  private String basePath = null;           // For local files
+
+  private boolean fromUrl = false;          // Usefull for binary files
+  private boolean Ascii = true;             // File type Ascii -> true o binary -> false
+  private String fileName = null;
+
+  // Arrays with coordinates and normals
+  // Needed for reading ASCII files because its size is unknown until the end
+  private ArrayList coordList;         // Holds Point3f
+  private ArrayList normList;          // Holds Vector3f
+
+  // GeometryInfo needs Arrays
+    public Point3f[] coordArray = null;
+    public Vector3f[] normArray = null;
+
+  // Needed because TRIANGLE_STRIP_ARRAY
+  // As the number of strips = the number of faces it's filled in objectToVectorArray
+    public int[] stripCounts = null;
+
+  // Default = Not available
+  private String objectName=new String("Not available");
+
+  /**
+  *  Constructor
+  */
+  public StlFile()
+  {
+  }
+
+  /**
+   * Method that reads the EOL
+   * Needed for verifying that the file has a correct format
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readEOL(StlFileParser parser)
+  {
+    try{
+    parser.nextToken();
+    }
+    catch (IOException e)
+    {
+      System.err.println("IO Error on line " + parser.lineno() + ": " + e.getMessage());
+    }
+    if(parser.ttype != StlFileParser.TT_EOL)
+    {
+      System.err.println("Format Error:expecting End Of Line on line " + parser.lineno());
+    }
+  }
+
+  /**
+   * Method that reads the word "solid" and stores the object name.
+   * It also detects what kind of file it is
+   * TO-DO:
+   *    1.- Better way control of exceptions?
+   *    2.- Better way to decide between Ascii and Binary?
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readSolid(StlFileParser parser)
+  {
+    if( !parser.sval.equals("solid"))
+    {
+      //System.out.println("Expecting solid on line " + parser.lineno());
+      // If the first word is not "solid" then we consider the file is binary
+      // Can give us problems if the comment of the binary file begins by "solid"
+      this.setAscii(false);
+    }
+    else  // It's an ASCII file
+    {
+      try{
+          parser.nextToken();
+          }
+      catch (IOException e)
+      {
+        System.err.println("IO Error on line " + parser.lineno() + ": " + e.getMessage());
+      }
+      if( parser.ttype != StlFileParser.TT_WORD)
+      {
+        // Is the object name always provided???
+        System.err.println("Format Error:expecting the object name on line " + parser.lineno());
+      }
+      else
+      { // Store the object Name
+        this.setObjectName(new String(parser.sval));
+        if(DEBUG==1)
+        {
+          System.out.println("Object Name:" + this.getObjectName().toString());
+        }
+        this.readEOL(parser);
+      }
+    }
+  }//End of readSolid
+
+  /**
+   * Method that reads a normal
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readNormal(StlFileParser parser)
+  {
+    Vector3f v = new Vector3f();
+
+    if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("normal")))
+    {
+      System.err.println("Format Error:expecting 'normal' on line " + parser.lineno());
+    }
+    else
+    {
+      if (parser.getNumber())
+      {
+        v.x=(float)parser.nval;
+
+        if(DEBUG==1)
+        {
+          System.out.println("Normal:");
+          System.out.print("X=" + v.x + " ");
+        }
+
+        if (parser.getNumber())
+        {
+          v.y=(float)parser.nval;
+          if(DEBUG==1)
+            System.out.print("Y=" + v.y + " ");
+
+         if (parser.getNumber())
+          {
+            v.z=(float)parser.nval;
+            if(DEBUG==1)
+              System.out.println("Z=" + v.z);
+
+            // We add that vector to the Normal's array
+            this.normList.add(v);
+            this.readEOL(parser);
+         }
+          else System.err.println("Format Error:expecting coordinate on line " + parser.lineno());
+        }
+        else System.err.println("Format Error:expecting coordinate on line " + parser.lineno());
+      }
+      else System.err.println("Format Error:expecting coordinate on line " + parser.lineno());
+    }
+  }// End of Read Normal
+
+  /**
+   * Method that reads the coordinates of a vector
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readVertex(StlFileParser parser)
+  {
+    Point3f p = new Point3f();
+
+    if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("vertex")))
+    {
+      System.err.println("Format Error:expecting 'vertex' on line " + parser.lineno());
+    }
+    else
+    {
+      if (parser.getNumber())
+      {
+        p.x=(float)parser.nval;
+
+        if(DEBUG==1)
+        {
+          System.out.println("Vertex:");
+          System.out.print("X=" + p.x + " ");
+        }
+
+        if (parser.getNumber())
+        {
+          p.y=(float)parser.nval;
+          if(DEBUG==1)
+            System.out.print("Y=" + p.y + " ");
+
+         if (parser.getNumber())
+          {
+           p.z=(float)parser.nval;
+            if(DEBUG==1)
+              System.out.println("Z=" + p.z);
+
+            // We add that vertex to the array of vertex
+            coordList.add(p);
+            readEOL(parser);
+         }
+          else System.err.println("Format Error: expecting coordinate on line " + parser.lineno());
+        }
+        else System.err.println("Format Error: expecting coordinate on line " + parser.lineno());
+      }
+      else System.err.println("Format Error: expecting coordinate on line " + parser.lineno());
+    }
+  }//End of read vertex
+
+  /**
+   * Method that reads "outer loop" and then EOL
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readLoop(StlFileParser parser)
+  {
+    if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("outer")))
+    {
+      System.err.println("Format Error:expecting 'outer' on line " + parser.lineno());
+    }
+    else
+    {
+      try{
+          parser.nextToken();
+          }
+      catch (IOException e)
+      {
+        System.err.println("IO error on line " + parser.lineno() + ": " + e.getMessage());
+      }
+      if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("loop")))
+      {
+        System.err.println("Format Error:expecting 'loop' on line " + parser.lineno());
+      }
+      else readEOL(parser);
+    }
+  }//End of readLoop
+
+  /**
+   * Method that reads "endloop" then EOL
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readEndLoop(StlFileParser parser)
+  {
+    if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("endloop")))
+    {
+      System.err.println("Format Error:expecting 'endloop' on line " + parser.lineno());
+    }
+    else readEOL(parser);
+  }//End of readEndLoop
+
+  /**
+   * Method that reads "endfacet" then EOL
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readEndFacet(StlFileParser parser)
+  {
+    if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("endfacet")))
+    {
+      System.err.println("Format Error:expecting 'endfacet' on line " + parser.lineno());
+    }
+    else readEOL(parser);
+  }//End of readEndFacet
+
+  /**
+   * Method that reads a face of the object
+   * (Cares about the format)
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readFacet(StlFileParser parser)
+  {
+    if(!(parser.ttype==StlFileParser.TT_WORD && parser.sval.equals("facet")))
+    {
+      System.err.println("Format Error:expecting 'facet' on line " + parser.lineno());
+    }
+    else
+    {
+      try{
+          parser.nextToken();
+          readNormal(parser);
+
+          parser.nextToken();
+          readLoop(parser);
+
+          parser.nextToken();
+          readVertex(parser);
+
+          parser.nextToken();
+          readVertex(parser);
+
+          parser.nextToken();
+          readVertex(parser);
+
+          parser.nextToken();
+          readEndLoop(parser);
+
+          parser.nextToken();
+          readEndFacet(parser);
+      }
+      catch (IOException e)
+      {
+        System.err.println("IO Error on line " + parser.lineno() + ": " + e.getMessage());
+      }
+    }
+  }// End of readFacet
+
+  /**
+   * Method that reads a face in binary files
+   * All binary versions of the methods end by 'B'
+   * As in binary files we can read the number of faces, we don't need
+   * to use coordArray and normArray (reading binary files should be faster)
+   *
+   * @param in The ByteBuffer with the data of the object.
+   * @param index The facet index
+   *
+   * @throws IOException
+   */
+  private void readFacetB(ByteBuffer in, int index) throws IOException
+  {
+    //File structure: Normal Vertex1 Vertex2 Vertex3
+    Vector3f normal = new Vector3f();
+    Point3f vertex = new Point3f();
+
+    if(DEBUG==1)
+      System.out.println("Reading face number " + index);
+
+    // Read the Normal
+    normArray[index]=new Vector3f();
+    normArray[index].x=in.getFloat();
+    normArray[index].y=in.getFloat();
+    normArray[index].z=in.getFloat();
+
+    if(DEBUG==1)
+      System.out.println("Normal: X=" + normArray[index].x + " Y=" + normArray[index].y + " Z=" + normArray[index].z);
+
+    // Read vertex1
+    coordArray[index*3] = new Point3f();
+    coordArray[index*3].x=in.getFloat();
+    coordArray[index*3].y=in.getFloat();
+    coordArray[index*3].z=in.getFloat();
+
+    if(DEBUG==1)
+      System.out.println("Vertex 1: X=" + coordArray[index*3].x + " Y=" + coordArray[index*3].y + " Z=" + coordArray[index*3].z);
+
+    // Read vertex2
+    coordArray[index*3+1] = new Point3f();
+    coordArray[index*3+1].x=in.getFloat();
+    coordArray[index*3+1].y=in.getFloat();
+    coordArray[index*3+1].z=in.getFloat();
+
+    if(DEBUG==1)
+      System.out.println("Vertex 2: X=" + coordArray[index*3+1].x + " Y=" + coordArray[index*3+1].y + " Z=" + coordArray[index*3+1].z);
+
+    // Read vertex3
+    coordArray[index*3+2] = new Point3f();
+    coordArray[index*3+2].x=in.getFloat();
+    coordArray[index*3+2].y=in.getFloat();
+    coordArray[index*3+2].z=in.getFloat();
+
+    if(DEBUG==1)
+      System.out.println("Vertex 3: X=" + coordArray[index*3+2].x + " Y=" + coordArray[index*3+2].y + " Z=" + coordArray[index*3+2].z);
+
+  }// End of readFacetB
+
+  /**
+   * Method for reading binary files
+   * Execution is completly different
+   * It uses ByteBuffer for reading data and ByteOrder for retrieving the machine's endian
+   * (Needs JDK 1.4)
+   *
+   * TO-DO:
+   *  1.-Be able to read files over Internet
+   *  2.-If the amount of data expected is bigger than what is on the file then
+   *  the program will block forever
+   *
+   * @param file The name of the file
+   *
+   * @throws IOException
+   */
+  private void readBinaryFile(String file) throws IOException
+  {
+    FileInputStream data;                 // For reading the file
+    ByteBuffer dataBuffer;                // For reading in the correct endian
+    byte[] Info=new byte[80];             // Header data
+    byte[] Array_number= new byte[4];     // Holds the number of faces
+    byte[] Temp_Info;                     // Intermediate array
+
+    int Number_faces; // First info (after the header) on the file
+
+    if(DEBUG==1)
+      System.out.println("Machine's endian: " + ByteOrder.nativeOrder());
+
+    // Get file's name
+    if(fromUrl)
+    {
+      // FileInputStream can only read local files!?
+      System.out.println("This version doesn't support reading binary files from internet");
+    }
+    else
+    { // It's a local file
+      data = new FileInputStream(file);
+
+      // First 80 bytes aren't important
+      if(80 != data.read(Info))
+      { // File is incorrect
+        //System.out.println("Format Error: 80 bytes expected");
+        throw new IncorrectFormatException();
+      }
+      else
+      { // We must first read the number of faces -> 4 bytes int
+        // It depends on the endian so..
+
+        data.read(Array_number);                      // We get the 4 bytes
+        dataBuffer = ByteBuffer.wrap(Array_number);   // ByteBuffer for reading correctly the int
+        dataBuffer.order(ByteOrder.nativeOrder());    // Set the right order
+        Number_faces = dataBuffer.getInt();
+
+        Temp_Info = new byte[50*Number_faces];        // Each face has 50 bytes of data
+
+        data.read(Temp_Info);                         // We get the rest of the file
+
+        dataBuffer = ByteBuffer.wrap(Temp_Info);      // Now we have all the data in this ByteBuffer
+        dataBuffer.order(ByteOrder.nativeOrder());
+
+        if(DEBUG==1)
+          System.out.println("Number of faces= " + Number_faces);
+
+        // We can create that array directly as we know how big it's going to be
+        coordArray = new Point3f[Number_faces*3]; // Each face has 3 vertex
+        normArray = new Vector3f[Number_faces];
+        stripCounts = new int[Number_faces];
+
+        for(int i=0;i<Number_faces;i++)
+        {
+          stripCounts[i]=3;
+          try
+          {
+            readFacetB(dataBuffer,i);
+            // After each facet there are 2 bytes without information
+            // In the last iteration we dont have to skip those bytes..
+            if(i != Number_faces - 1)
+            {
+              dataBuffer.get();
+              dataBuffer.get();
+            }
+          }
+          catch (IOException e)
+          {
+            // Quitar
+            System.out.println("Format Error: iteration number " + i);
+            throw new IncorrectFormatException();
+          }
+        }//End for
+      }// End file reading
+    }// End else
+  }// End of readBinaryFile
+
+  /**
+   * Method that reads ASCII files
+   * Uses StlFileParser for correct reading and format checking
+   * The beggining of that method is common to binary and ASCII files
+   * We try to detect what king of file it is
+   *
+   * TO-DO:
+   *  1.- Find a best way to decide what kind of file it is
+   *  2.- Is that return (first catch) the best thing to do?
+   *
+   * @param parser The file parser. An instance of StlFileParser.
+   */
+  private void readFile(StlFileParser parser)
+  {
+    int t;
+
+    try{
+        parser.nextToken();
+        }
+    catch (IOException e)
+    {
+      System.err.println("IO Error on line " + parser.lineno() + ": " + e.getMessage());
+      System.err.println("File seems to be empty");
+      return;         // ????? Throw ?????
+    }
+
+    // Here we try to detect what kind of file it is (see readSolid)
+    readSolid(parser);
+
+    if(getAscii())
+    { // Ascii file
+      try
+      {
+          parser.nextToken();
+      }
+      catch (IOException e)
+      {
+       System.err.println("IO Error on line " + parser.lineno() + ": " + e.getMessage());
+      }
+
+      // Read all the facets of the object
+      while (parser.ttype != StlFileParser.TT_EOF && !parser.sval.equals("endsolid"))
+      {
+        readFacet(parser);
+        try
+        {
+          parser.nextToken();
+        }
+        catch (IOException e)
+        {
+          System.err.println("IO Error on line " + parser.lineno() + ": " + e.getMessage());
+        }
+      }// End while
+
+      // Why are we out of the while?: EOF or endsolid
+      if(parser.ttype == StlFileParser.TT_EOF)
+       System.err.println("Format Error:expecting 'endsolid', line " + parser.lineno());
+      else
+      {
+        if(DEBUG==1)
+          System.out.println("File readed");
+      }
+    }//End of Ascii reading
+
+    else
+    { // Binary file
+      try{
+        readBinaryFile(getFileName());
+      }
+      catch(IOException e)
+      {
+        System.err.println("Format Error: reading the binary file");
+      }
+    }// End of binary file
+  }//End of readFile
+
+  /**
+   * The Stl File is loaded from the .stl file specified by
+   * the filename.
+   * To attach the model to your scene, call getSceneGroup() on
+   * the Scene object passed back, and attach the returned
+   * BranchGroup to your scene graph.  For an example, see
+   * $J3D/programs/examples/ObjLoad/ObjLoad.java.
+   *
+   * @param filename The name of the file with the object to load
+   *
+   * @return Scene The scene with the object loaded.
+   *
+   * @throws FileNotFoundException
+   * @throws IncorrectFormatException
+   * @throws ParsingErrorException
+   */
+  public Scene load(String filename) throws FileNotFoundException,
+                                           IncorrectFormatException,
+                                           ParsingErrorException
+  {
+    setBasePathFromFilename(filename);
+    setFileName(filename);     // For binary files
+
+    Reader reader = new BufferedReader(new FileReader(filename));
+    return load(reader);
+  } // End of load(String)
+
+   /**
+   * The Stl file is loaded off of the web.
+   * To attach the model to your scene, call getSceneGroup() on
+   * the Scene object passed back, and attach the returned
+   * BranchGroup to your scene graph.  For an example, see
+   * $J3D/programs/examples/ObjLoad/ObjLoad.java.
+   *
+   * @param url The url to load the onject from
+   *
+   * @return Scene The scene with the object loaded.
+   *
+   * @throws FileNotFoundException
+   * @throws IncorrectFormatException
+   * @throws ParsingErrorException
+   */
+  public Scene load(URL url) throws FileNotFoundException,
+                                   IncorrectFormatException,
+                                   ParsingErrorException
+  {
+    BufferedReader reader;
+
+    setBaseUrlFromUrl(url);
+
+    try {
+      reader = new BufferedReader(new InputStreamReader(url.openStream()));
+    }
+    catch (IOException e) {
+      throw new FileNotFoundException();
+    }
+    fromUrl = true;
+    return load(reader);
+  } // End of load(URL)
+
+  /**
+   * The Stl File is loaded from the already opened file.
+   * To attach the model to your scene, call getSceneGroup() on
+   * the Scene object passed back, and attach the returned
+   * BranchGroup to your scene graph.  For an example, see
+   * $J3D/programs/examples/ObjLoad/ObjLoad.java.
+   *
+   * @param reader The reader to read the object from
+   *
+   * @return Scene The scene with the object loaded.
+   *
+   * @throws FileNotFoundException
+   * @throws IncorrectFormatException
+   * @throws ParsingErrorException
+   */
+  public Scene load(Reader reader) throws FileNotFoundException,
+                                          IncorrectFormatException,
+                                          ParsingErrorException
+  {
+    // That method calls the method that loads the file for real..
+    // Even if the Stl format is not complicated I've decided to use
+    // a parser as in the Obj's loader included in Java3D
+
+    StlFileParser st=new StlFileParser(reader);
+
+    // Initialize data
+    coordList = new ArrayList();
+    normList = new ArrayList();
+    setAscii(true);      // Default ascii
+
+    readFile(st);
+    return makeScene();
+  }
+
+  /**
+   * Method that takes the info from an ArrayList of Point3f
+   * and returns a Point3f[].
+   * Needed for ASCII files as we don't know the number of facets until the end
+   *
+   * @param inList The list to transform into Point3f[]
+   *
+   * @return Point3f[] The result.
+   */
+  private Point3f[] objectToPoint3Array(ArrayList inList)
+  {
+    Point3f outList[] = new Point3f[inList.size()];
+
+    for (int i = 0 ; i < inList.size() ; i++) {
+      outList[i] = (Point3f)inList.get(i);
+    }
+    return outList;
+  } // End of objectToPoint3Array
+
+  /**
+   * Method that takes the info from an ArrayList of Vector3f
+   * and returns a Vector3f[].
+   * Needed for ASCII files as we don't know the number of facets until the end
+   *
+   * TO-DO:
+   *  1.- Here we fill stripCounts...
+   *      Find a better place to do it?
+   *
+   * @param inList The list to transform into Point3f[]
+   *
+   * @return Vector3f[] The result.
+   */
+  private Vector3f[] objectToVectorArray(ArrayList inList)
+  {
+    Vector3f outList[] = new Vector3f[inList.size()];
+
+    if(DEBUG==1)
+      System.out.println("Number of facets of the object=" + inList.size());
+
+    // To-do
+    stripCounts = new int[inList.size()];
+    for (int i = 0 ; i < inList.size() ; i++) {
+      outList[i] = (Vector3f)inList.get(i);
+      // To-do
+      stripCounts[i]=3;
+    }
+    return outList;
+  } // End of objectToVectorArray
+
+  /**
+   * Method that creates the SceneBase with the stl file info
+   *
+   * @return SceneBase The scene
+   */
+  private SceneBase makeScene()
+  {
+    // Create Scene to pass back
+    SceneBase scene = new SceneBase();
+    BranchGroup group = new BranchGroup();
+    scene.setSceneGroup(group);
+
+    // Store the scene info on a GeometryInfo
+    GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_STRIP_ARRAY);
+
+    // Convert ArrayLists to arrays: only needed if file was not binary
+    if(this.Ascii)
+    {
+      coordArray = objectToPoint3Array(coordList);
+      normArray = objectToVectorArray(normList);
+    }
+
+    gi.setCoordinates(coordArray);
+    gi.setNormals(normArray);
+    gi.setStripCounts(stripCounts);
+
+    // Put geometry into Shape3d
+    Shape3D shape = new Shape3D();
+    shape.setGeometry(gi.getGeometryArray());
+
+    group.addChild(shape);
+    scene.addNamedObject(objectName, shape);
+
+    return scene;
+  } // end of makeScene
+
+  /////////////////////// Accessors and Modifiers ///////////////////////////
+
+  public URL getBaseUrl()
+  {
+    return baseUrl;
+  }
+
+  /**
+   * Modifier for baseUrl, if accessing internet.
+   *
+   * @param url The new url
+   */
+  public void setBaseUrl(URL url)
+  {
+    baseUrl = url;
+  }
+
+  private void setBaseUrlFromUrl(URL url)
+  {
+    StringTokenizer stok =
+      new StringTokenizer(url.toString(), "/\\", true);
+    int tocount = stok.countTokens() - 1;
+    StringBuffer sb = new StringBuffer(MAX_PATH_LENGTH);
+    for(int i = 0; i < tocount ; i++) {
+       String a = stok.nextToken();
+       sb.append(a);
+//     if((i == 0) && (!a.equals("file:"))) {
+//         sb.append(a);
+//         sb.append(java.io.File.separator);
+//         sb.append(java.io.File.separator);
+//     } else {
+//         sb.append(a);
+//         sb.append( java.io.File.separator );
+//     }
+    }
+    try {
+      baseUrl = new URL(sb.toString());
+    }
+    catch (MalformedURLException e) {
+      System.err.println("Error setting base URL: " + e.getMessage());
+    }
+  } // End of setBaseUrlFromUrl
+
+
+  public String getBasePath()
+  {
+    return basePath;
+  }
+
+  /**
+   * Set the path where files associated with this .stl file are
+   * located.
+   * Only needs to be called to set it to a different directory
+   * from that containing the .stl file.
+   *
+   * @param pathName The new Path to the file
+   */
+  public void setBasePath(String pathName)
+  {
+    basePath = pathName;
+    if (basePath == null || basePath == "")
+       basePath = "." + java.io.File.separator;
+    basePath = basePath.replace('/', java.io.File.separatorChar);
+    basePath = basePath.replace('\\', java.io.File.separatorChar);
+    if (!basePath.endsWith(java.io.File.separator))
+       basePath = basePath + java.io.File.separator;
+  } // End of setBasePath
+
+  /*
+   * Takes a file name and sets the base path to the directory
+   * containing that file.
+   */
+  private void setBasePathFromFilename(String fileName)
+  {
+    // Get ready to parse the file name
+    StringTokenizer stok =
+      new StringTokenizer(fileName, java.io.File.separator);
+
+    //  Get memory in which to put the path
+    StringBuffer sb = new StringBuffer(MAX_PATH_LENGTH);
+
+    // Check for initial slash
+    if (fileName!= null && fileName.startsWith(java.io.File.separator))
+      sb.append(java.io.File.separator);
+
+    // Copy everything into path except the file name
+    for(int i = stok.countTokens() - 1 ; i > 0 ; i--) {
+      String a = stok.nextToken();
+      sb.append(a);
+      sb.append(java.io.File.separator);
+    }
+    setBasePath(sb.toString());
+  } // End of setBasePathFromFilename
+
+  public int getFlags()
+  {
+    return flag;
+  }
+
+  public void setFlags(int parm)
+  {
+    this.flag=parm;
+  }
+
+  public boolean getAscii()
+  {
+    return this.Ascii;
+  }
+
+  public void setAscii(boolean tipo)
+  {
+    this.Ascii = tipo;
+  }
+
+
+  public String getFileName()
+  {
+    return this.fileName;
+  }
+
+  public void setFileName(String filename)
+  {
+    this.fileName=new String(filename);
+  }
+
+
+  public String getObjectName()
+  {
+    return this.objectName;
+  }
+
+  public void setObjectName(String name)
+  {
+    this.objectName = name;
+  }
+
+} // End of package stl_loader
\ No newline at end of file
diff --git a/src/StlFileParser.java b/src/StlFileParser.java
new file mode 100644 (file)
index 0000000..8743cca
--- /dev/null
@@ -0,0 +1,99 @@
+import java.io.StreamTokenizer;
+import java.io.Reader;
+import java.io.IOException;
+
+/**
+ * Title:        STL Loader
+ * Description:  STL files loader (Supports ASCII and binary files) for Java3D
+ *               Needs JDK 1.4 due to endian problems
+ * Copyright:    Copyright (c) 2001
+ * Company:      Universidad del Pais Vasco (UPV/EHU)
+ * @author:      Carlos Pedrinaci Godoy
+ * @version 1.0
+ *
+ * Contact : xenicp@yahoo.es
+ *
+ *   STL FILE PARSER:
+ *   Extends StreamTokenizer
+ *   For Ascii files
+ *   Each token returned is checked in StlFile.java
+ *   Format of an ASCII STL file:
+ *
+ *     solid /users/vis/dru/wedge.stl
+ *       facet normal -1 0 0
+ *         outer loop
+ *           vertex 0.005 1 0
+ *           vertex 0 0.543 0
+ *           vertex 0.453 1 1
+ *         endloop
+ *       endfacet
+ *            .
+ *            .
+ *            .
+ *     endsolid /users/vis/dru/wedge.stl
+ *
+ *  That Class is necessary because scientific numbers are not correctly readed by Tokenizer
+ *  we must then extend that class and define another getNumber
+ */
+
+public class StlFileParser extends StreamTokenizer
+{
+  /**
+   * Constructor: object creation and setup
+   *
+   * @param r The Reader instance
+   */
+  public StlFileParser(Reader r)
+  {
+    super(r);
+    setup();
+  }
+
+  /**
+   * Method that sets some params of the Tokenizer for reading the file correctly
+   */
+  public void setup()
+  {
+    resetSyntax();
+    eolIsSignificant(true);   // The End Of Line is important
+    lowerCaseMode(true);
+
+    // All printable ascii characters
+    wordChars('!', '~');
+
+    whitespaceChars(' ', ' ');
+    whitespaceChars('\n', '\n');
+    whitespaceChars('\r', '\r');
+    whitespaceChars('\t', '\t');
+  }// End setup
+
+  /**
+   * Gets a number from the stream.  Note that we don't recognize
+   * numbers in the tokenizer automatically because numbers might be in
+   * scientific notation, which isn't processed correctly by
+   * StreamTokenizer.  The number is returned in nval.
+   *
+   * @return boolean.
+   */
+  boolean getNumber()
+  {
+    int t;
+
+    try {
+        nextToken();
+       if (ttype != TT_WORD)
+         throw new IOException("Expected number on line " + lineno());
+       nval =  (Double.valueOf(sval)).doubleValue();
+    }
+    catch (IOException e) {
+      System.err.println(e.getMessage());
+      return false;
+    }
+    catch (NumberFormatException e) {
+      System.err.println(e.getMessage());
+      return false;
+    }
+    return true;
+  } // end of getNumber
+
+}// End of StlFileParser
diff --git a/teapot.stl b/teapot.stl
new file mode 100644 (file)
index 0000000..0c86f6d
Binary files /dev/null and b/teapot.stl differ