public static Mesh readMeshFromObj(String path) throws IOException {
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String mtlLibPath = "";
		String materialName = "";

		ArrayList<Vector3d> vertices = new ArrayList<Vector3d>();
		ArrayList<Point3f> points = new ArrayList<Point3f>();
		ArrayList<Triangle> triangles = new ArrayList<Triangle>();
		ArrayList<Integer> coordIndices = new ArrayList<Integer>();
		while (true) {
			String line = reader.readLine();
			if (line == null) {
				reader.close();
				break;
			}
			if (line.startsWith("mtllib ")) {// mtllib location
				mtlLibPath = path.substring(0,
						path.lastIndexOf(File.separator) + 1)
						+ line.substring(7);
			} else if (line.startsWith("v ")) {// vertex
				double[] coords = parseDoubles(line.substring(2));
				Vector3d vertex = new Vector3d(coords);
				Point3f point = new Point3f(vertex);
				vertices.add(vertex);
				points.add(point);
			} else if (line.startsWith("usemtl ")) {
				materialName = line.substring(7);
			} else if (line.startsWith("f ")) {// face (triangle)
				line = line.substring(2);
				String[] coords = line.split(" ");
				Vector<Integer> triangle = new Vector<Integer>();
				for (String coord : coords) {
					String[] coordInfos = coord.split("/");
					Integer coordIndex = Integer.parseInt(coordInfos[0]) - 1;
					triangle.add(coordIndex);
					coordIndices.add(coordIndex);
				}
				triangles.add(new Triangle(triangle.get(0), triangle.get(1),
						triangle.get(2)));
			}
		}

		int[] coordinateIndices = new int[coordIndices.size()];
		for (int j = 0; j < coordinateIndices.length; j++) {
			coordinateIndices[j] = coordIndices.get(j);
		}
		IndexedTriangleArray geometry = new IndexedTriangleArray(
				vertices.size(), IndexedTriangleArray.COORDINATES,
				coordIndices.size());
		geometry.setCoordinates(0, points.toArray(new Point3f[0]));
		geometry.setCoordinateIndices(0, coordinateIndices);
		GeometryInfo geometryInfo = new GeometryInfo(geometry);
		NormalGenerator normalGenerator = new NormalGenerator(
				(float) Math.toRadians(10));
		normalGenerator.generateNormals(geometryInfo);

		// Material
		reader = new BufferedReader(new FileReader(new File(mtlLibPath)));

		// Default
		Color3f ambientColour = new Color3f(0.2f, 0.2f, 0.2f);
		Color3f diffuseColour = new Color3f(0.8f, 0.8f, 0.8f);
		Color3f specularColour = new Color3f(0.5f, 0.5f, 0.5f);
		Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
		float shininess = 50.0f;

		String currentMaterial = "";
		while (true) {
			String line = reader.readLine();
			if (line == null) {
				reader.close();
				break;
			}
			if (line.startsWith("newmtl ")) {
				currentMaterial = line.substring(7);
			} else if (currentMaterial.equals(materialName)) {
				if (line.startsWith("Ka ")) {
					// ambient color
					ambientColour = new Color3f(parseFloats(line.substring(3)));
				} else if (line.startsWith("Kd ")) {
					// diffuse color
					diffuseColour = new Color3f(parseFloats(line.substring(3)));
				} else if (line.startsWith("Ks ")) {
					// specular color
					specularColour = new Color3f(parseFloats(line.substring(3)));
				} else if (line.startsWith("Ns ")) {
					// specular coefficient
					shininess = Float.parseFloat(line.substring(3));
				}
			}
		}

		Appearance app = new Appearance();
		app.setMaterial(new Material(ambientColour, emissiveColour,
				diffuseColour, specularColour, shininess));
		Shape3D shape = new Shape3D(geometryInfo.getGeometryArray(), app);
		shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);

		Mesh mesh = new Mesh(vertices.toArray(new Vector3d[0]),
				triangles.toArray(new Triangle[0]), ambientColour,
				diffuseColour, specularColour, emissiveColour, shininess, shape);
		return mesh;
	}