private int countTexVerts(String filepath) {
    int texVertCount = 0; // The running total of how many texture verticies are in the file
    String line; // This will hold individual lines from the file as it is being read
    // Unfortunately, the readLine() method will occasionally throw an IOException,
    // so it must be inside this try-catch block...just in case
    try {
      FileReader fr = new FileReader(filepath); // First, setup the file for reading
      BufferedReader objFile =
          new BufferedReader(fr); // The setup a BufferedReader for things like readLine
      line = objFile.readLine(); // Read the first line in the file
      // readLine sets line to null when it has reached the end of the file
      // so continue to read until then.
      while (line != null) {
        // No sense in checking the line if it's blank...besides, the program
        // crashes if you don't check for this first :-)
        // Also, we ONLY want to count lines that begin with "vt" as specified
        // by the OBJ Specification
        if (line.length() > 0)
          if ((line.charAt(0) == 'v') && (line.charAt(1) == 't')) texVertCount++;

        // Grab the next line in the file
        line = objFile.readLine();
      } // end while
      // Done with the files for now, so we close them
      objFile.close();
      fr.close();
    } // end try
    catch (IOException exception) {
      System.out.println("Error reading texture coordinates from file: " + filepath);
    } // end catch
    // System.out.println("\n" + texVertCount + " texture vertices found");
    return texVertCount;
  } // end countTexVerts method
 public int countMaterials(String fileNameList, String filepath) {
   int matCount = 0;
   String line;
   FileReader fr;
   BufferedReader mtlFile;
   StringTokenizer fileNames = new StringTokenizer(fileNameList.toString());
   try {
     while (fileNames.hasMoreTokens()) {
       fr =
           new FileReader(
               filepath.substring(0, (filepath.length() - objName.length()))
                   + fileNames.nextToken());
       mtlFile = new BufferedReader(fr);
       line = mtlFile.readLine();
       while (line != null) {
         if (line.length() > 6) {
           if (line.substring(0, 6).toLowerCase().compareTo("newmtl") == 0) matCount++;
         }
         line = mtlFile.readLine();
       }
       mtlFile.close();
       fr.close();
     }
   } catch (FileNotFoundException exception) {
     System.out.println("Error opening material library for counting");
     return 0;
   } catch (IOException exception) {
     System.out.println("Error counting materials from material library");
     return 0;
   }
   // DEBUG
   System.out.println(matCount + " materials found");
   // END DEBUG
   return matCount;
 } // end countMaterials
  private int countVerts(String filepath) {
    int vertCount = 0; // Running total of how many vertices there are
    String line; // This will hold individual lines from the file as it is being read

    // readLine sometimes returns an IOException error, so the try-catch is needed
    try {
      FileReader fr = new FileReader(filepath); // First, setup the file for reading
      BufferedReader objFile =
          new BufferedReader(fr); // The setup a BufferedReader for things like readLine

      line = objFile.readLine(); // Read the first line in the file

      // readLine sets line to null when it has reached the end of the file
      // so continue to read until then.
      while (line != null) {
        // No sense in checking the line if it's blank...besides, the program
        // crashes if you don't check for this first :-)
        // Also, we ONLY want to count lines that begin with "v " as specified
        // by the OBJ Specification
        if (line.length() > 0) if ((line.charAt(0) == 'v') && (line.charAt(1) == ' ')) vertCount++;

        // Grab the next line in the file
        line = objFile.readLine();
      } // end while

      // Done with the files for now, so we close them
      objFile.close();
      fr.close();
    } // end try
    catch (IOException exception) {
      System.out.println("Error counting verticies from file: " + filepath);
    } // end catch

    // System.out.println("\n" + vertCount + " vertices found");

    return vertCount; // Return the number of vertices in the file
  } // end countVerts method
  private int readMaterials(String filepath) {
    int matNo = 0;
    // The current material number - index into the materials array
    String line;
    // This will hold individual lines from the file as it is being read
    String name;
    StringBuffer fileNameList;
    // This (temporarily) holds the file names listed in the obj file
    StringTokenizer fileNames;
    // This is a tokenized list of files for counting the materials
    StringTokenizer tokens;
    // This is merely for parsing through the read lines to grab the filename
    FileReader fr;
    // This will be for opening the files
    BufferedReader objFile;
    // This will hold the open file so we can read entire lines from it at once
    BufferedReader mtlFile;
    // This will hold the open file so we can read entire lines from it at once

    // First, we'll try to retrieve a list of the material libraries
    // from the obj file
    // this is in a try-catch block since the readLine method can
    // sometimes throw an IOException
    try {
      fr = new FileReader(filepath);
      objFile = new BufferedReader(fr);
      fileNameList = new StringBuffer();
      line = objFile.readLine(); // Read the first line in the file
      // readLine sets line to null when it has reached the end of the file
      // so continue to read until then.
      while (line != null) {
        line = line.trim();
        // No sense in checking the line if it's blank...besides, the program
        // crashes if you don't check for this first :-)
        if (line.length() > 6) {
          String[] splits = line.split(" ");
          if (splits[0].equals("mtllib")) { // next token is file name
            mtlFileName = splits[1];
            fileNameList.append(splits[1]);
            for (int i = 2; i < splits.length; i++) {
              fileNameList.append(" " + splits[i]);
            }
          }
        }
        // Grab the next line in the file
        line = objFile.readLine();
      } // end while

      // Done with the files for now, so we close them
      objFile.close();
      fr.close();
      fileNames = new StringTokenizer(fileNameList.toString());
      // DEBUG
      StringTokenizer debug = new StringTokenizer(fileNameList.toString());
      System.out.println("\nThe following material libraries were found:");
      while (debug.hasMoreTokens()) {
        System.out.println("   * " + debug.nextToken());
      }
      System.out.println();
      // END DEBUG
    } // end try
    catch (IOException exception) {
      System.out.println("Error reading material libraries from: " + filepath);
      return -1;
    } // end catch

    // This returns how many materials are in the file(s) and adds it to the one default that
    // always exists
    numMats += countMaterials(fileNameList.toString(), filepath);
    materials = new MaterialCell[numMats];
    for (int i = 0; i < numMats; i++) materials[i] = new MaterialCell();
    // Now that the material library filenames have been retrieved
    // (and stored in the StringTokenizer called fileNames) we can
    // procede to open them and add the materials
    while (fileNames.hasMoreTokens()) {
      try {
        fr =
            new FileReader(
                filepath.substring(0, (filepath.length() - objName.length()))
                    + fileNames.nextToken());
        mtlFile = new BufferedReader(fr);
      } catch (FileNotFoundException exception) {
        System.out.println("Error opening material library for reading");
        return -1;
      }
      try {
        line = mtlFile.readLine();
      } catch (IOException exception) {
        System.out.println("Error reading from material library");
        return -1;
      }
      while (line != null) {
        line = line.trim();
        if (line.length() > 0) {
          // Things to recall at this point in the code:
          //   * materials[] is the array of MaterialCells in the PMesh that
          //     this class extends
          //   * matNo is the current material being read in.  This gets
          //     incremented every time a newmtl line is found.  It is also
          //     the index into the materials[] array
          //   * materials[0] is already set to a default material (thanks
          //     to the constructor for MaterialCell).  It will remain THE
          //     default material, and all subsequent materials will be added
          //     beginning at index 1
          switch (line.charAt(0)) {
            case '#': // Comment
            case '!':
            case '$':
            case '\n':
              break;
            case 'n': // (newmtl) new material
              tokens = new StringTokenizer(line); // Holds the line for parsing to get information,

              matNo++; // Working on a new material
              name = tokens.nextToken();
              name = tokens.nextToken(); // Eat up the "newmtl" keyword
              materials[matNo].materialName = new String(name); // Assign the material name
              break;
            case 'K': // (Ka, Kd, Ks) color coefficients
              tokens = new StringTokenizer(line);
              try {
                switch (line.charAt(1)) {
                  case 'a': // (Ka) ambient color coefficients
                    line = tokens.nextToken();
                    materials[matNo].ka.r = Double.parseDouble(tokens.nextToken());
                    materials[matNo].ka.g = Double.parseDouble(tokens.nextToken());
                    materials[matNo].ka.b = Double.parseDouble(tokens.nextToken());
                    if (tokens.hasMoreTokens()) // Test for the existance of a possible alpha value
                    materials[matNo].ka.a = Double.parseDouble(tokens.nextToken());
                    break;
                  case 'd': // (Kd) diffuse color coefficients
                    line = tokens.nextToken();
                    materials[matNo].kd.r = Double.parseDouble(tokens.nextToken());
                    materials[matNo].kd.g = Double.parseDouble(tokens.nextToken());
                    materials[matNo].kd.b = Double.parseDouble(tokens.nextToken());
                    if (tokens.hasMoreTokens()) // Test for the existance of a possible alpha value
                    materials[matNo].kd.a = Double.parseDouble(tokens.nextToken());
                    break;
                  case 's': // (Ks) specular color coefficients
                    line = tokens.nextToken();
                    materials[matNo].ks.r = Double.parseDouble(tokens.nextToken());
                    materials[matNo].ks.g = Double.parseDouble(tokens.nextToken());
                    materials[matNo].ks.b = Double.parseDouble(tokens.nextToken());
                    if (tokens.hasMoreTokens()) // Test for the existance of a possible alpha value
                    materials[matNo].ks.a = Double.parseDouble(tokens.nextToken());
                    break;
                  default:
                    break;
                }
              } // end try for reading color coefficients
              catch (NumberFormatException exception) {
                System.out.println("Error while reading color coefficients from material file");
                return -1;
              } // end catch for reading color coefficients
              break;
            case 'I':
              tokens = new StringTokenizer(line);
              switch (line.charAt(1)) {
                case 'r': // reflectivity - one or three values should be specified
                  int count = 0;
                  line = tokens.nextToken();
                  do {
                    materials[matNo].reflectivity.r = Double.parseDouble(tokens.nextToken());
                    count++;
                  } while (tokens.hasMoreTokens() && count < 3);
                  if (count == 1) {
                    // materials[matNo].reflOneValue = true;
                    materials[matNo].reflectivity.g = materials[matNo].reflectivity.r;
                    materials[matNo].reflectivity.b = materials[matNo].reflectivity.r;
                  }
                  // else if(count ==3) materials[matNo].reflOneValue = false;
                  else System.out.printf("Error reading reflectivity: count=%d\n", count);
                  break; // end case 'r'

                case 't':
                  int cnt = 0;
                  line = tokens.nextToken();
                  do {
                    materials[matNo].refractivity.r = Double.parseDouble(tokens.nextToken());
                    cnt++;
                  } while (tokens.hasMoreTokens() && cnt < 3);

                  if (cnt == 1) {
                    // materials[matNo].refrOneValue = true;
                    materials[matNo].refractivity.g = materials[matNo].refractivity.r;
                    materials[matNo].refractivity.b = materials[matNo].refractivity.r;
                  }
                  // else if(cnt ==3) materials[matNo].refrOneValue = false;
                  else System.out.printf("Error reading refractivity: count=%d\n", cnt);
                  break; // end case 't'
              }
              break; // end case 'I'

            case 'm': // (map_Ka, map_Kd, map_Ks, map_d) texture maps
              tokens = new StringTokenizer(line);
              switch (line.charAt(4)) {
                case 'K': // (map_Ka, map_Kd, map_Ks) texture maps
                  switch (line.charAt(5)) {
                    case 'a': // (map_Ka) ambient texture map
                      line = tokens.nextToken();
                      materials[matNo].mapKa = tokens.nextToken();
                      break;
                    case 'd': // (map_Kd) diffuse texture map
                      line = tokens.nextToken();
                      materials[matNo].mapKd = tokens.nextToken();
                      break;
                    case 's': // (map_Ks) specular texture map
                      line = tokens.nextToken();
                      materials[matNo].mapKs = tokens.nextToken();
                      break;
                    default:
                      break;
                  }
                  break;
                case 'd': // (map_d) transparency map
                  line = tokens.nextToken();
                  materials[matNo].mapD = tokens.nextToken();
                  break;
                default:
                  break;
              }
              break;
            case 'e': // (emm) emissive color coefficients
              tokens = new StringTokenizer(line);
              try {
                line = tokens.nextToken();
                materials[matNo].emmColor.r = Double.parseDouble(tokens.nextToken());
                materials[matNo].emmColor.g = Double.parseDouble(tokens.nextToken());
                materials[matNo].emmColor.b = Double.parseDouble(tokens.nextToken());
              } // end try for reading emissive color
              catch (NumberFormatException exception) {
                System.out.println("Error while reading emissive color from material file");
                return -1;
              } // end catch for reading emissive color
              break;
            case 'T': // (Tf) transmission filter
              tokens = new StringTokenizer(line);
              try {
                line = tokens.nextToken();
                materials[matNo].transmissionFilter.r = Double.parseDouble(tokens.nextToken());
                materials[matNo].transmissionFilter.g = Double.parseDouble(tokens.nextToken());
                materials[matNo].transmissionFilter.b = Double.parseDouble(tokens.nextToken());
              } // end try for reading transparency
              catch (NumberFormatException exception) {
                System.out.println("Error while reading transmissionFilter from material file");
                return -1;
              } // end catch for reading transparency
              break;
            case 'N': // (Ns, Ni) shininess or refraction index
              tokens = new StringTokenizer(line);
              try {
                switch (line.charAt(1)) {
                  case 's': // (Ns) shininess
                    line = tokens.nextToken();
                    materials[matNo].shiny = Double.parseDouble(tokens.nextToken());
                    // ASSUME shininess is in OpenGL form [0, 128]
                    // materials[matNo].shiny /= 1000.0;	//Wavefront shininess is from [0, 1000]
                    // materials[matNo].shiny *= 128.0;	//So rescale for OpenGL... [0, 128]
                    break;
                  case 'i': // (Ni) refraction index
                    line = tokens.nextToken();
                    materials[matNo].refractiveIndex = Double.parseDouble(tokens.nextToken());
                    break;
                  default:
                    break;
                }
              } // end try for reading shininess or refraction index
              catch (NumberFormatException exception) {
                System.out.println(
                    "Error while reading shininess or refraction index from material file");
                return -1;
              } // end catch for reading shininess or refraction index
              break;
            case 'L': // (Lc) line color
              tokens = new StringTokenizer(line);
              try {
                line = tokens.nextToken();
                materials[matNo].lineColor.r = Double.parseDouble(tokens.nextToken());
                materials[matNo].lineColor.g = Double.parseDouble(tokens.nextToken());
                materials[matNo].lineColor.b = Double.parseDouble(tokens.nextToken());
                if (tokens.hasMoreTokens())
                  materials[matNo].lineColor.a = Double.parseDouble(tokens.nextToken());
              } // end try for reading line color
              catch (NumberFormatException exception) {
                System.out.println("Error while reading line color from material file");
                return -1;
              } // end catch for reading line color
              break;
            case 'd': // (ds) double sided
              if (line.charAt(1) == 's') {
                tokens = new StringTokenizer(line);
                try {
                  line = tokens.nextToken();
                  if (Integer.parseInt(tokens.nextToken()) == 1)
                    materials[matNo].doubleSided = true;
                  // else it defaults to "false"
                } // end try for reading double sided flag
                catch (NumberFormatException exception) {
                  System.out.println("Error while reading double sided flag from material file");
                  return -1;
                } // end catch for reading double sided flag
              }
              break;
            default:
              break;
          } // end switch
        } // end if(line.length() > 0)

        try {
          line = mtlFile.readLine();
        } catch (IOException exception) {
          System.out.println("Error reading from material library");
          return -1;
        }
      } // end while(line != null)
      try {
        mtlFile.close();
        fr.close();
      } catch (IOException exception) {
        System.out.println("Error closing file...somehow");
        return -1;
      }
    }

    return 0;
  } // end readMaterials
  private void readSurfaces(String filepath) {
    // System.out.println("\nBuilding surfaces...");
    // This method is the last to be called out of all of the methods in this class.  Once all
    // of the vertex information has been read in, as well as the materials, they are arranged
    // into the surfaces and polygons that form that object itself...See the included file
    // CustomOBJSpecs.txt for more details

    int curMat = 0; // this is a reference to the current material to be used
    // int curIndex = 0;			//This is a temporary variable for reading in vertices of polygons
    SurfCell curSurf; // the current surface being worked with
    // PolyCell curPoly;			//the current polygon in the surface
    String matName; // This is the material name from the obj file.  It will be used to find the
    // material in the list
    String line; // the current line being analyzed
    StringTokenizer token; // For tokenizing the string to grab the different components of the line
    // StringTokenizer vertTokens;	//For tokenizing the vertex entries in a polygon (surface) line
    FileReader fr; // Just a file reading stream for the file
    BufferedReader objFile; // this wraps around the file reader so we can do things like readLine()
    boolean inSmooth = false; // a flag for whether or not a given face is in a surface

    try {
      fr = new FileReader(filepath);
      objFile = new BufferedReader(fr);
      line = objFile.readLine();
      curSurf = surfHead;
      while (line != null) {
        if (line.length() > 0) {
          switch (line.charAt(0)) {
            case '#':
            case '!':
            case '$':
            case '\n':
            case 'v': // These are all comments or vertex info...skip them :-)
              break;
            case 'u':
              token = new StringTokenizer(line);
              line = token.nextToken(); // "eat" up the usemtl keyword
              matName = token.nextToken(); // actually grab the material name
              boolean found = false;
              int i = 0;
              while (!found && i < numMats)
              // for(int i = 0; i < numMats; i++)
              {
                // simply compare the stored material name to the name retrieved from the OBJ file.
                // if they match, set curMat to whatever index that material is at
                if (materials[i].materialName.toUpperCase().compareTo(matName.toUpperCase()) == 0) {
                  curMat = i; // Set curMat to the current material index
                  found = true;
                }
                i++;
              }
              if (!found) {
                System.out.printf(
                    "Group %s material %s not found - using default\n", curSurf.name, matName);
                curSurf.material = 0;
              } else curSurf.material = curMat;
              break;
            case 's':
              token = new StringTokenizer(line);
              line = token.nextToken(); // "eat" up the s at the beginning of the line
              // If there are more tokens on the line (specifically, the word "off")
              // then we will read them in.  The only one that we really care about is if it's
              // an "off" but it must be read if it's there.
              if (token.hasMoreTokens()) line = token.nextToken();
              // if smooth groups are turned off and no new smooth group is specified...
              if (line.toUpperCase().compareTo("OFF") == 0) {
                inSmooth = false;
              } else // We are simply starting a new smooth group
              {
                inSmooth = true;
                // curSurf.smooth = inSmooth;
              } // end else
              break;
            case 'f':
              if (curSurf != null) {
                addPolyToSurf(curSurf, line, inSmooth);
              } else // no active surface - create a "default" surface and add this poly to it
              {
                System.out.printf("ReadSurfaces: No active surface available\n");
                System.out.printf("Creating a default surface\n");
                if (inSmooth) surfHead = new SurfCell("default");
                else surfHead = new SurfCell("default");
                curSurf = surfHead;
                addPolyToSurf(curSurf, line, inSmooth);
              }
              curSurf.numPoly++; // Surface level count of the polygons
              numPolys++; // PMesh level count of the polygons
              break;

            case 'g': // Starts a new surface - if inSmooth is true set the smooth flag in
              // that surface
              token = new StringTokenizer(line);
              line = token.nextToken(); // "eat" up the g at the beginning of the line
              // If there are more tokens on the line (specifically a group name)
              // then we will read it in.
              if (token.hasMoreTokens()) line = token.nextToken();
              if (line == null) line = new String("Group" + numSurf);
              if (surfHead == null) // Create first surface
              {
                surfHead = new SurfCell(line);
                curSurf = surfHead;
              } else // Advance to next surface
              {
                curSurf.next = new SurfCell(line);
                curSurf = curSurf.next;
              }
              // Assign beginning variables
              numSurf++; // PMesh level count of surfaces							break;
            default:
              break;
          } // end switch
        } // end if(line.length() > 0)
        line = objFile.readLine(); // grab the next line for reading
      } // end while(line != null)
      objFile.close();
      fr.close();
    } // end try
    catch (IOException exception) {
      System.out.println("Error while reading surface data from: " + filepath);
    } // end catch
    System.out.printf("\n %d vertices  %d surfaces\n", numVerts, numSurf);
    // System.out.println("\n" + numSurf + " surfaces found");
  } // end method readSurfaces
  /*	public String getMaterialFile(String filepath)
  	{
  		String line, tmpName = null;		//This will hold individual lines from the file as it is being read
  		//readLine sometimes returns an IOException error, so the try-catch is needed
  		try
  		{
  			FileReader fr = new FileReader(filepath);			//First, setup the file for reading
  			BufferedReader objFile = new BufferedReader(fr);	//The setup a BufferedReader for things like readLine
  			String objFileName = Utils.fileFromPath(filepath);
  			/// BEFORE returning need to strip the obj file name off file path and replace with mtlFileName
  			String filePath = filepath.replaceAll(objFileName, "");
  			line = objFile.readLine(); //Read the first line in the file
  			//readLine sets line to null when it has reached the end of the file
  			//so continue to read until then.
  			while(line != null)
  			{
  				//No sense in checking the line if it's blank...besides, the program
  				//crashes if you don't check for this first :-)
  				String [] splits = line.split(" ");
  				if(splits[0].equals("mtllib")){//next token is file name
  					tmpName = splits[1];
  				}
  				if(tmpName != null)
  					return filePath+tmpName;
  				//Grab the next line in the file
  				line = objFile.readLine();
  			}//end while
  			//Done with the files for now, so we close them
  			objFile.close();
  			fr.close();
  		}//end try
  		catch(IOException exception)
  		{
  			System.out.println("Error retrieving material file name from file: " + filepath);
  		}//end catch
  		return null;  //Return the material file name - null is not found
  	}//end countVerts method
  */
  @SuppressWarnings("unused")
  private void readTexVerts(String filepath) {
    String line; // This will hold individual lines from the file as it is being read
    StringTokenizer
        tokens; // The lines are copied into the StringTokenizer for parsing of information
    int texNo = 0; // Just a counter for placing textures in the texVertArray

    numTex = countTexVerts(filepath);

    // Only bother creating an array of VertListCells if there
    // are some verticies to put in it :-)
    if (numTex > 0) {
      texVertArray = new ArrayList<Double3D>();
      // Interestingly enough, the above call will not create all of the objects, it
      // merely creates an array of references to the objects.  Here is where
      // they get created
      for (int i = 0; i < numTex; i++) texVertArray.add(i, new Double3D());
    } // end if(numTex > 0)

    // Must use the try-catch because readLine will occasionally throw an IOException
    try {
      FileReader fr = new FileReader(filepath); // First, open the file
      BufferedReader objFile = new BufferedReader(fr); // Then, assign it to a BufferedReader
      // This allows for things like reading entire lines
      line = objFile.readLine(); // Grab the first line of the file
      // readLine will set line equal to null when it reaches the end of the file,
      // so continue reading verticies until that point
      while (line != null) {
        // We cannot check any characters of the line if the line is blank, so
        // we don't even bother (besides, the program crashes without this)
        if (line.length() > 0) {
          // ONLY parse lines beginning with "vt" as specified by the OBJ Specification
          if (line.charAt(0) == 'v' && line.charAt(1) == 't') {
            // A tokenizer allows for parsing through the line to pick out the numbers
            // or anything else we want for that matter :-)
            tokens = new StringTokenizer(line);
            line = tokens.nextToken(); // Clear the "vt" out of the tokens
            // unfortunately, parsing for a number will sometimes raise an exception
            // so we need this try statement
            try {
              texVertArray.get(texNo).x = Double.parseDouble(tokens.nextToken());
              texVertArray.get(texNo).y = Double.parseDouble(tokens.nextToken());
              if (tokens.hasMoreTokens())
                texVertArray.get(texNo).z = Double.parseDouble(tokens.nextToken());
              texNo++;
            } // end try
            catch (NumberFormatException exception) {
              System.out.println("Formatting error in file: " + filepath);
            } // end catch
          } // end if
        } // end if(line.length() > 0)
        line = objFile.readLine();
      } // end while
      // Done with the files for now, so we close them
      objFile.close();
      fr.close();
    } // end try
    catch (IOException exception) {
      System.out.println("Error reading verticies from file: " + filepath);
    } // end catch
  } // end readTexVerts method
  private void readVerts(String filepath) {
    String line; // This will hold individual lines from the file as it is being read
    StringTokenizer
        tokens; // The lines are copied into the StringTokenizer for parsing of information
    int vertNo = 0; // Just a counter
    double xSum = 0.0, ySum = 0.0, zSum = 0.0;

    // System.out.println("About to count the vertices");

    numVerts = countVerts(filepath); // Begin by counting all of the vertices

    // Only create an array of VertListCells if there are vertices to put in it
    if (numVerts > 0) {
      vertArray = new ArrayList<VertCell>();
      vertUsedArray = new ArrayList<SurfCell>();
      for (int i = 0; i < numVerts; i++) {
        vertArray.add(i, new VertCell());
        vertUsedArray.add(i, null);
      }
    } // end if

    // Must use the try-catch because readLine will occasionally throw an IOException
    try {
      FileReader fr = new FileReader(filepath);
      BufferedReader objFile = new BufferedReader(fr);
      line = objFile.readLine(); // Grab the first line of the file

      // readLine will set line equal to null when it reaches the end of the file,
      // so continue reading verticies until that point
      while (line != null) {
        if (line.length() > 0) // ignoe blank lines
        {
          // ONLY parse lines beginning with "v " as specified by the OBJ Specification
          if (line.charAt(0) == 'v' && line.charAt(1) == ' ') {
            tokens = new StringTokenizer(line);
            line = tokens.nextToken(); // Clear the "v" out of the tokens
            try // parsing for a number will sometimes raise an exception
            {
              vertArray.get(vertNo).worldPos.x = Double.parseDouble(tokens.nextToken());
              xSum += vertArray.get(vertNo).worldPos.x;
              vertArray.get(vertNo).worldPos.y = Double.parseDouble(tokens.nextToken());
              ySum += vertArray.get(vertNo).worldPos.y;
              vertArray.get(vertNo).worldPos.z = Double.parseDouble(tokens.nextToken());
              zSum += vertArray.get(vertNo).worldPos.z;
              vertNo++;
            } // end try
            catch (NumberFormatException exception) {
              System.out.println("Formatting error in file: " + filepath);
            } // end catch
          } // end if
        } // end if
        line = objFile.readLine();
      } // end while
      objFile.close();
      fr.close();
    } // end try
    catch (IOException exception) {
      System.out.println("Error reading verticies from file: " + filepath);
    } // end catch
    center.x = xSum / (double) numVerts;
    center.y = ySum / (double) numVerts;
    center.z = zSum / (double) numVerts;
  } // end readVerts method