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 //ls = new LineSegment( verts.get(v[0]-1), verts.get(v[1]-1) ); 139 ls = new LineSegment(new Vertex(verts.get(v[0]-1)), new Vertex(verts.get(v[1]-1))); 140 this.addLineSegment( ls ); 141 //ls = new LineSegment( verts.get(v[1]-1), verts.get(v[2]-1) ); 142 ls = new LineSegment(new Vertex(verts.get(v[1]-1)), new Vertex(verts.get(v[2]-1))); 143 this.addLineSegment( ls ); 144 145 // parse another vertex (if there is one) and make a line segment 146 while (scanner2.hasNext()) 147 { 148 v[1] = v[2]; 149 String faceGroup = scanner2.next(); 150 Matcher m = p.matcher( faceGroup ); 151 if ( m.find() ) 152 { 153 v[2] = Integer.parseInt( m.group(1) ); 154 String vt = m.group(2); // don't need 155 String vn = m.group(3); // don't need 156 } 157 else 158 System.err.println("Error: bad face: " + faceGroup); 159 160 //ls = new LineSegment( verts.get(v[1]-1), verts.get(v[2]-1)); 161 ls = new LineSegment(new Vertex(verts.get(v[1]-1)), new Vertex(verts.get(v[2]-1))); 162 this.addLineSegment( ls ); 163 } 164 // close the line loop around this face 165 //ls = new LineSegment( verts.get(v[2]-1), verts.get(v[0]-1)); 166 ls = new LineSegment(new Vertex(verts.get(v[2]-1)), new Vertex(verts.get(v[0]-1))); 167 this.addLineSegment( ls ); 168 }// parse face 169 }// parse one line 170 fis.close(); 171 } 172 catch (Exception e) 173 { 174 e.printStackTrace(System.err); 175 System.err.printf("ERROR! Could not read OBJ file: %s\n", objName); 176 System.exit(-1); 177 } 178 } 179}//ObjSimpleModel