From 09fee09a0181206f9895de8d33da0e2df6562976 Mon Sep 17 00:00:00 2001 From: pahimar Date: Wed, 10 Apr 2013 15:21:55 -0400 Subject: [PATCH] Switch out String.matches in favour of Pattern/Matcher usage, and a tad more documentation. Fixed a bug in texture coordinate parsing (can have 2-3 values per entry, instead of the 3-4 I had before) --- .../client/model/obj/WavefrontObject.java | 246 ++++++++++++++---- 1 file changed, 189 insertions(+), 57 deletions(-) diff --git a/client/net/minecraftforge/client/model/obj/WavefrontObject.java b/client/net/minecraftforge/client/model/obj/WavefrontObject.java index 42934f8c7..09d56f581 100644 --- a/client/net/minecraftforge/client/model/obj/WavefrontObject.java +++ b/client/net/minecraftforge/client/model/obj/WavefrontObject.java @@ -5,8 +5,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; -import java.text.ParseException; import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.DataFormatException; import net.minecraft.client.renderer.Tessellator; @@ -15,32 +17,43 @@ import org.lwjgl.opengl.GL11; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +/** + * Wavefront Object importer + * Based heavily off of the specifications found at http://en.wikipedia.org/wiki/Wavefront_.obj_file + */ @SideOnly(Side.CLIENT) public class WavefrontObject { - private static final String REGEX_VERTEX = "(v( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(v( (\\-){0,1}\\d+\\.\\d+){3,4} *$)"; - private static final String REGEX_VERTEX_NORMAL = "(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *$)"; - private static final String REGEX_TEXTURE_COORDINATE = "(vt( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(vt( (\\-){0,1}\\d+\\.\\d+){3,4} *$)"; - private static final String REGEX_FACE_VERTEX_TEXTURECOORD_VERTEXNORMAL = "(f( \\d+/\\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+/\\d+){3,4} *$)"; - private static final String REGEX_FACE_VERTEX_TEXTURECOORD = "(f( \\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+){3,4} *$)"; - private static final String REGEX_FACE_VERTEX_VERTEXNORMAL = "(f( \\d+//\\d+){3,4} *\\n)|(f( \\d+//\\d+){3,4} *$)"; - private static final String REGEX_FACE_VERTEX = "(f( \\d+){3,4} *\\n)|(f( \\d+){3,4} *$)"; - private static final String REGEX_GROUP_OBJECT = "([go]( [\\w\\d]+) *\\n)|([go]( [\\w\\d]+) *$)"; + private static Pattern vertexPattern = Pattern.compile("(v( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(v( (\\-){0,1}\\d+\\.\\d+){3,4} *$)"); + private static Pattern vertexNormalPattern = Pattern.compile("(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *$)"); + private static Pattern textureCoordinatePattern = Pattern.compile("(vt( (\\-){0,1}\\d+\\.\\d+){2,3} *\\n)|(vt( (\\-){0,1}\\d+\\.\\d+){2,3} *$)"); + private static Pattern face_V_VT_VN_Pattern = Pattern.compile("(f( \\d+/\\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+/\\d+){3,4} *$)"); + private static Pattern face_V_VT_Pattern = Pattern.compile("(f( \\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+){3,4} *$)"); + private static Pattern face_V_VN_Pattern = Pattern.compile("(f( \\d+//\\d+){3,4} *\\n)|(f( \\d+//\\d+){3,4} *$)"); + private static Pattern face_V_Pattern = Pattern.compile("(f( \\d+){3,4} *\\n)|(f( \\d+){3,4} *$)"); + private static Pattern groupObjectPattern = Pattern.compile("([go]( [\\w\\d]+) *\\n)|([go]( [\\w\\d]+) *$)"); + + private static Matcher vertexMatcher, vertexNormalMatcher, textureCoordinateMatcher; + private static Matcher face_V_VT_VN_Matcher, face_V_VT_Matcher, face_V_VN_Matcher, face_V_Matcher; + private static Matcher groupObjectMatcher; public ArrayList vertices = new ArrayList(); public ArrayList vertexNormals = new ArrayList(); public ArrayList textureCoordinates = new ArrayList(); public ArrayList groupObjects = new ArrayList(); private GroupObject currentGroupObject; + private String fileName; public WavefrontObject(String fileName) { + this.fileName = fileName; + try { parseObjModel(this.getClass().getResource(fileName)); } - catch (ParseException e) + catch (DataFormatException e) { e.printStackTrace(); } @@ -48,17 +61,19 @@ public class WavefrontObject public WavefrontObject(URL fileURL) { + this.fileName = fileURL.getFile(); + try { parseObjModel(fileURL); } - catch (ParseException e) + catch (DataFormatException e) { e.printStackTrace(); } } - private void parseObjModel(URL fileURL) throws ParseException + private void parseObjModel(URL fileURL) throws DataFormatException { BufferedReader reader = null; InputStream inputStream = null; @@ -82,39 +97,27 @@ public class WavefrontObject } else if (currentLine.startsWith("v ")) { - Vertex vertex = parseVertex(currentLine); + Vertex vertex = parseVertex(currentLine, lineCount); if (vertex != null) { vertices.add(vertex); } - else - { - throw new ParseException("Error parsing entry ('" + currentLine + "'" + ", line " + lineCount + ") in file '" + fileURL.getFile() + "'", lineCount); - } } else if (currentLine.startsWith("vn ")) { - Vertex vertex = parseVertexNormal(currentLine); + Vertex vertex = parseVertexNormal(currentLine, lineCount); if (vertex != null) { vertexNormals.add(vertex); } - else - { - throw new ParseException("Error parsing entry ('" + currentLine + "'" + ", line " + lineCount + ") in file '" + fileURL.getFile() + "'", lineCount); - } } else if (currentLine.startsWith("vt ")) { - TextureCoordinate textureCoordinate = parseTextureCoordinate(currentLine); + TextureCoordinate textureCoordinate = parseTextureCoordinate(currentLine, lineCount); if (textureCoordinate != null) { textureCoordinates.add(textureCoordinate); } - else - { - throw new ParseException("Error parsing entry ('" + currentLine + "'" + ", line " + lineCount + ") in file '" + fileURL.getFile() + "'", lineCount); - } } else if (currentLine.startsWith("f ")) { @@ -124,20 +127,16 @@ public class WavefrontObject currentGroupObject = new GroupObject("Default"); } - Face face = parseFace(currentLine); + Face face = parseFace(currentLine, lineCount); if (face != null) { currentGroupObject.faces.add(face); } - else - { - throw new ParseException("Error parsing entry ('" + currentLine + "'" + ", line " + lineCount + ") in file '" + fileURL.getFile() + "'", lineCount); - } } else if (currentLine.startsWith("g ") | currentLine.startsWith("o ")) { - GroupObject group = parseGroupObject(currentLine); + GroupObject group = parseGroupObject(currentLine, lineCount); if (group != null) { @@ -146,10 +145,6 @@ public class WavefrontObject groupObjects.add(currentGroupObject); } } - else - { - throw new ParseException("Error parsing entry ('" + currentLine + "'" + ", line " + lineCount + ") in file '" + fileURL.getFile() + "'", lineCount); - } currentGroupObject = group; } @@ -171,6 +166,7 @@ public class WavefrontObject { e.printStackTrace(); } + try { inputStream.close(); @@ -231,7 +227,7 @@ public class WavefrontObject } } - private Vertex parseVertex(String line) + private Vertex parseVertex(String line, int lineCount) throws DataFormatException { Vertex vertex = null; @@ -243,18 +239,28 @@ public class WavefrontObject try { if (tokens.length == 3) + { return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2])); + } + else if (tokens.length == 4) + { + return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), Float.parseFloat(tokens[3])); + } } catch (NumberFormatException e) { e.printStackTrace(); } } + else + { + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + } return vertex; } - private Vertex parseVertexNormal(String line) + private Vertex parseVertexNormal(String line, int lineCount) throws DataFormatException { Vertex vertexNormal = null; @@ -273,11 +279,15 @@ public class WavefrontObject e.printStackTrace(); } } + else + { + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + } return vertexNormal; } - private TextureCoordinate parseTextureCoordinate(String line) + private TextureCoordinate parseTextureCoordinate(String line, int lineCount) throws DataFormatException { TextureCoordinate textureCoordinate = null; @@ -298,11 +308,15 @@ public class WavefrontObject e.printStackTrace(); } } + else + { + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + } return textureCoordinate; } - private Face parseFace(String line) + private Face parseFace(String line, int lineCount) throws DataFormatException { Face face = null; @@ -322,7 +336,7 @@ public class WavefrontObject } else if (currentGroupObject.glDrawingMode != GL11.GL_TRIANGLES) { - return null; + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 4, found " + tokens.length + ")"); } } else if (tokens.length == 4) @@ -333,7 +347,7 @@ public class WavefrontObject } else if (currentGroupObject.glDrawingMode != GL11.GL_QUADS) { - return null; + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 3, found " + tokens.length + ")"); } } @@ -342,7 +356,7 @@ public class WavefrontObject face.vertexNormals = new Vertex[tokens.length]; // f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ... - if (line.matches(REGEX_FACE_VERTEX_TEXTURECOORD_VERTEXNORMAL)) + if (isValidFace_V_VT_VN_Line(line)) { for (int i = 0; i < tokens.length; ++i) { @@ -356,7 +370,7 @@ public class WavefrontObject face.faceNormal = face.calculateFaceNormal(); } // f v1/vt1 v2/vt2 v3/vt3 ... - else if (line.matches(REGEX_FACE_VERTEX_TEXTURECOORD)) + else if (isValidFace_V_VT_Line(line)) { for (int i = 0; i < tokens.length; ++i) { @@ -369,7 +383,7 @@ public class WavefrontObject face.faceNormal = face.calculateFaceNormal(); } // f v1//vn1 v2//vn2 v3//vn3 ... - else if (line.matches(REGEX_FACE_VERTEX_VERTEXNORMAL)) + else if (isValidFace_V_VN_Line(line)) { for (int i = 0; i < tokens.length; ++i) { @@ -382,7 +396,7 @@ public class WavefrontObject face.faceNormal = face.calculateFaceNormal(); } // f v1 v2 v3 ... - else if (line.matches(REGEX_FACE_VERTEX)) + else if (isValidFace_V_Line(line)) { for (int i = 0; i < tokens.length; ++i) { @@ -392,13 +406,19 @@ public class WavefrontObject face.faceNormal = face.calculateFaceNormal(); } else - throw new IllegalArgumentException(); + { + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + } + } + else + { + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); } return face; } - private GroupObject parseGroupObject(String line) + private GroupObject parseGroupObject(String line, int lineCount) throws DataFormatException { GroupObject group = null; @@ -411,37 +431,149 @@ public class WavefrontObject group = new GroupObject(trimmedLine); } } + else + { + throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + } return group; } + /*** + * Verifies that the given line from the model file is a valid vertex + * @param line the line being validated + * @return true if the line is a valid vertex, false otherwise + */ private static boolean isValidVertexLine(String line) { - - return line.matches(REGEX_VERTEX); + if (vertexMatcher != null) + { + vertexMatcher.reset(); + } + + vertexMatcher = vertexPattern.matcher(line); + return vertexMatcher.matches(); } + /*** + * Verifies that the given line from the model file is a valid vertex normal + * @param line the line being validated + * @return true if the line is a valid vertex normal, false otherwise + */ private static boolean isValidVertexNormalLine(String line) { + if (vertexNormalMatcher != null) + { + vertexNormalMatcher.reset(); + } - return line.matches(REGEX_VERTEX_NORMAL); + vertexNormalMatcher = vertexNormalPattern.matcher(line); + return vertexNormalMatcher.matches(); } + /*** + * Verifies that the given line from the model file is a valid texture coordinate + * @param line the line being validated + * @return true if the line is a valid texture coordinate, false otherwise + */ private static boolean isValidTextureCoordinateLine(String line) { + if (textureCoordinateMatcher != null) + { + textureCoordinateMatcher.reset(); + } - return line.matches(REGEX_TEXTURE_COORDINATE); + textureCoordinateMatcher = textureCoordinatePattern.matcher(line); + return textureCoordinateMatcher.matches(); } - - private static boolean isValidFaceLine(String line) + + /*** + * Verifies that the given line from the model file is a valid face that is described by vertices, texture coordinates, and vertex normals + * @param line the line being validated + * @return true if the line is a valid face that matches the format "f v1/vt1/vn1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise + */ + private static boolean isValidFace_V_VT_VN_Line(String line) { + if (face_V_VT_VN_Matcher != null) + { + face_V_VT_VN_Matcher.reset(); + } - return line.matches(REGEX_FACE_VERTEX_TEXTURECOORD_VERTEXNORMAL) || line.matches(REGEX_FACE_VERTEX_TEXTURECOORD) || line.matches(REGEX_FACE_VERTEX_VERTEXNORMAL) || line.matches(REGEX_FACE_VERTEX); + face_V_VT_VN_Matcher = face_V_VT_VN_Pattern.matcher(line); + return face_V_VT_VN_Matcher.matches(); + } + + /*** + * Verifies that the given line from the model file is a valid face that is described by vertices and texture coordinates + * @param line the line being validated + * @return true if the line is a valid face that matches the format "f v1/vt1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise + */ + private static boolean isValidFace_V_VT_Line(String line) + { + if (face_V_VT_Matcher != null) + { + face_V_VT_Matcher.reset(); + } + + face_V_VT_Matcher = face_V_VT_Pattern.matcher(line); + return face_V_VT_Matcher.matches(); + } + + /*** + * Verifies that the given line from the model file is a valid face that is described by vertices and vertex normals + * @param line the line being validated + * @return true if the line is a valid face that matches the format "f v1//vn1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise + */ + private static boolean isValidFace_V_VN_Line(String line) + { + if (face_V_VN_Matcher != null) + { + face_V_VN_Matcher.reset(); + } + + face_V_VN_Matcher = face_V_VN_Pattern.matcher(line); + return face_V_VN_Matcher.matches(); + } + + /*** + * Verifies that the given line from the model file is a valid face that is described by only vertices + * @param line the line being validated + * @return true if the line is a valid face that matches the format "f v1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise + */ + private static boolean isValidFace_V_Line(String line) + { + if (face_V_Matcher != null) + { + face_V_Matcher.reset(); + } + + face_V_Matcher = face_V_Pattern.matcher(line); + return face_V_Matcher.matches(); } + /*** + * Verifies that the given line from the model file is a valid face of any of the possible face formats + * @param line the line being validated + * @return true if the line is a valid face that matches any of the valid face formats, false otherwise + */ + private static boolean isValidFaceLine(String line) + { + return isValidFace_V_VT_VN_Line(line) || isValidFace_V_VT_Line(line) || isValidFace_V_VN_Line(line) || isValidFace_V_Line(line); + } + + /*** + * Verifies that the given line from the model file is a valid group (or object) + * @param line the line being validated + * @return true if the line is a valid group (or object), false otherwise + */ private static boolean isValidGroupObjectLine(String line) { + if (groupObjectMatcher != null) + { + groupObjectMatcher.reset(); + } - return line.matches(REGEX_GROUP_OBJECT); + groupObjectMatcher = groupObjectPattern.matcher(line); + return groupObjectMatcher.matches(); } }