001/*
002
003*/
004
005package renderer.models;
006import  renderer.scene.*;
007
008import java.util.Scanner;
009import java.io.File;
010import java.io.FileInputStream;
011import java.io.IOException;
012import java.io.FileNotFoundException;
013import java.util.regex.*;
014import java.util.ArrayList;
015
016/**
017<p>
018   A simple demonstration of loading and drawing a basic OBJ file.
019<p>
020   A basic OBJ file is a text file that contains three kinds of lines:
021   lines that begin with the character {@code 'v'}, lines that begin
022   with the character {@code 'f'}, and lines that begin with the
023   character {@code '#'}.
024<p>
025   A line in an OBJ file that begins with {@code '#'} is a comment line
026   and can be ignored.
027<p>
028   A line in an OBJ file that begins with {@code 'v'} is a line that
029   describes a vertex in 3-dimensional space. The {@code 'v'} will always
030   be followed on the line by three doubles, the {@code x}, {@code y},
031   and {@code z} coordinates of the vertex.
032<p>
033   A line in an OBJ file that begins with {@code 'f'} is a line that
034   describes a "face". The {@code 'f'} will be followed on the line by
035   a sequence of positive integers. The integers are the indices of the
036   vertices that make up the face. The "index" of a vertex is the order
037   in which the vertex was listed in the OBJ file. So a line like this
038<pre>{@code
039      f  2  4  1
040}</pre>
041   would represent a triangle made up of the 2nd vertex read from the file,
042   the 4th vertex read from the file, and the 1st vertex read from the file.
043   And a line like this
044<pre>{@code
045      f  2  4  3  5
046}</pre>
047   would represent a quadrilateral made up of the 2nd vertex read from the file,
048   the 4th vertex read from the file, the 3rd vertex read from the file, and
049   the 5th vertex read from the file.
050<p>
051   See <a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file" target="_top">
052                https://en.wikipedia.org/wiki/Wavefront_.obj_file</a>
053*/
054public class ObjSimpleModel extends Model
055{
056   /**
057      Create a wireframe model from the contents of an OBJ file.
058
059      @param objFile  {@link File} object for the OBJ data file
060   */
061   public ObjSimpleModel(File objFile)
062   {
063      super();
064
065      // Open the OBJ file.
066      String objName = null;
067      FileInputStream fis = null;
068      try
069      {
070         objName = objFile.getCanonicalPath();
071         fis = new FileInputStream( objFile );
072      }
073      catch (FileNotFoundException e)
074      {
075         e.printStackTrace(System.err);
076         System.err.printf("ERROR! Could not find OBJ file: %s\n", objName);
077         System.exit(-1);
078      }
079      catch (IOException e)
080      {
081         e.printStackTrace(System.err);
082         System.err.printf("ERROR! Could not open OBJ file: %s\n", objName);
083         System.exit(-1);
084      }
085
086      // Get the geometry from the OBJ file.
087      ArrayList<Vertex> verts = new ArrayList<Vertex>();
088      try
089      {
090         // Pattern for parsing lines that start with "f"
091         Pattern p = Pattern.compile("^(\\d*)[/]?(\\d*)[/]?(\\d*)");
092
093         Scanner scanner = new Scanner(fis);
094         while ( scanner.hasNext() )
095         {
096            String token = scanner.next();
097            if ( token.startsWith("#")
098              || token.startsWith("vt")
099              || token.startsWith("vn")
100              || token.startsWith("s")
101              || token.startsWith("g")
102              || token.startsWith("o")
103              || token.startsWith("usemtl")
104              || token.startsWith("mtllib") )
105            {
106               scanner.nextLine(); // skip over these lines
107            }
108            else if ( token.startsWith("v") )
109            {
110               double x = scanner.nextDouble();
111               double y = scanner.nextDouble();
112               double z = scanner.nextDouble();
113               Vertex v = new Vertex(x, y, z);
114               verts.add( v );
115            }// parse vertex
116            else if ( token.startsWith("f") )
117            {
118               LineSegment ls;
119               // tokenize the rest of the line
120               String restOfLine = scanner.nextLine();
121               Scanner scanner2 = new Scanner( restOfLine );
122               // parse three vertices and make two line segments
123               int[] v = new int[3];
124               for (int i = 0; i < 3; i++)
125               {
126                  // parse a "v/vt/vn" group
127                  String faceGroup = scanner2.next();
128                  Matcher m = p.matcher( faceGroup );
129                  if ( m.find() )
130                  {
131                     v[i] = Integer.parseInt( m.group(1) );
132                     String vt = m.group(2);  // don't need
133                     String vn = m.group(3);  // don't need
134                  }
135                  else
136                     System.err.println("Error: bad face: " + faceGroup);
137               }
138               this.addLineSegment( verts.get(v[0]-1), verts.get(v[1]-1) );
139               this.addLineSegment( verts.get(v[1]-1), verts.get(v[2]-1) );
140
141               // parse another vertex (if there is one) and make a line segment
142               while (scanner2.hasNext())
143               {
144                  v[1] = v[2];
145                  String faceGroup = scanner2.next();
146                  Matcher m = p.matcher( faceGroup );
147                  if ( m.find() )
148                  {
149                     v[2] = Integer.parseInt( m.group(1) );
150                     String vt = m.group(2);  // don't need
151                     String vn = m.group(3);  // don't need
152                  }
153                  else
154                     System.err.println("Error: bad face: " + faceGroup);
155
156                  this.addLineSegment( verts.get(v[1]-1), verts.get(v[2]-1) );
157               }
158               // close the line loop around this face
159               this.addLineSegment( verts.get(v[2]-1), verts.get(v[0]-1) );
160            }// parse face
161         }// parse one line
162         fis.close();
163      }
164      catch (Exception e)
165      {
166         e.printStackTrace(System.err);
167         System.err.printf("ERROR! Could not read OBJ file: %s\n", objName);
168         System.exit(-1);
169      }
170   }
171}//ObjSimpleModel