From 1ae7fa0080deceabf1253bd047acd41ab5301c31 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 10 Apr 2013 18:24:34 -0400 Subject: [PATCH] Add in a generic factory interface, allowing for additional model support to be added at runtime. --- .../client/model/AdvancedModelLoader.java | 82 ++++++++++++ .../client/model/IModelCustom.java | 10 +- .../client/model/IModelCustomLoader.java | 31 +++++ .../client/model/ModelFormatException.java | 31 +++++ .../client/model/obj/ObjModelLoader.java | 31 +++++ .../client/model/obj/WavefrontObject.java | 119 ++++++++---------- 6 files changed, 228 insertions(+), 76 deletions(-) create mode 100644 client/net/minecraftforge/client/model/AdvancedModelLoader.java create mode 100644 client/net/minecraftforge/client/model/IModelCustomLoader.java create mode 100644 client/net/minecraftforge/client/model/ModelFormatException.java create mode 100644 client/net/minecraftforge/client/model/obj/ObjModelLoader.java diff --git a/client/net/minecraftforge/client/model/AdvancedModelLoader.java b/client/net/minecraftforge/client/model/AdvancedModelLoader.java new file mode 100644 index 000000000..d2e888237 --- /dev/null +++ b/client/net/minecraftforge/client/model/AdvancedModelLoader.java @@ -0,0 +1,82 @@ +package net.minecraftforge.client.model; + +import java.io.InputStream; +import java.net.URL; +import java.util.Collection; +import java.util.Map; + +import net.minecraftforge.client.model.obj.ObjModelLoader; + +import com.google.common.collect.Maps; + +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +/** + * Common interface for advanced model loading from files, based on file suffix + * Model support can be queried through the {@link #getSupportedSuffixes()} method. + * Instances can be created by calling {@link #loadModel(String)} with a class-loadable-path + * + * @author cpw + * + */ +@SideOnly(Side.CLIENT) +public class AdvancedModelLoader { + private static Map instances = Maps.newHashMap(); + + /** + * Register a new model handler + * @param modelHandler The model handler to register + */ + public static void registerModelHandler(IModelCustomLoader modelHandler) + { + for (String suffix : modelHandler.getSuffixes()) + { + instances.put(suffix, modelHandler); + } + } + + /** + * Load the model from the supplied classpath resolvable resource name + * @param resourceName The resource name + * @return A model + * @throws IllegalArgumentException if the resource name cannot be understood + * @throws ModelFormatException if the underlying model handler cannot parse the model format + */ + public static IModelCustom loadModel(String resourceName) throws IllegalArgumentException, ModelFormatException + { + int i = resourceName.lastIndexOf('.'); + if (i == -1) + { + FMLLog.severe("The resource name %s is not valid", resourceName); + throw new IllegalArgumentException("The resource name is not valid"); + } + String suffix = resourceName.substring(i); + IModelCustomLoader loader = instances.get(suffix); + if (loader == null) + { + FMLLog.severe("The resource name %s is not supported", resourceName); + throw new IllegalArgumentException("The resource name is not supported"); + } + + URL resource = AdvancedModelLoader.class.getResource(resourceName); + if (resource == null) + { + FMLLog.severe("The resource name %s could not be found", resourceName); + throw new IllegalArgumentException("The resource name could not be found"); + } + return loader.loadInstance(resourceName, resource); + } + + public static Collection getSupportedSuffixes() + { + return instances.keySet(); + } + + + static + { + registerModelHandler(new ObjModelLoader()); + } +} diff --git a/client/net/minecraftforge/client/model/IModelCustom.java b/client/net/minecraftforge/client/model/IModelCustom.java index bd1c04f98..35b4c01ea 100644 --- a/client/net/minecraftforge/client/model/IModelCustom.java +++ b/client/net/minecraftforge/client/model/IModelCustom.java @@ -3,11 +3,7 @@ package net.minecraftforge.client.model; public interface IModelCustom { - - public abstract IModelCustom load(String fileName); - - public abstract void renderAll(); - - public abstract void renderPart(String partName); - + String getType(); + void renderAll(); + void renderPart(String partName); } diff --git a/client/net/minecraftforge/client/model/IModelCustomLoader.java b/client/net/minecraftforge/client/model/IModelCustomLoader.java new file mode 100644 index 000000000..8c3e962aa --- /dev/null +++ b/client/net/minecraftforge/client/model/IModelCustomLoader.java @@ -0,0 +1,31 @@ +package net.minecraftforge.client.model; + +import java.io.InputStream; +import java.net.URL; + +/** + * Instances of this class act as factories for their model type + * + * @author cpw + * + */ +public interface IModelCustomLoader { + /** + * Get the main type name for this loader + * @return the type name + */ + String getType(); + /** + * Get resource suffixes this model loader recognizes + * @return a list of suffixes + */ + String[] getSuffixes(); + /** + * Load a model instance from the supplied path + * @param resourceName The resource name to load + * @param resource The URL associated with the classloader resource + * @return A model instance + * @throws ModelFormatException if the model format is not correct + */ + IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException; +} diff --git a/client/net/minecraftforge/client/model/ModelFormatException.java b/client/net/minecraftforge/client/model/ModelFormatException.java new file mode 100644 index 000000000..c7e0ce768 --- /dev/null +++ b/client/net/minecraftforge/client/model/ModelFormatException.java @@ -0,0 +1,31 @@ +package net.minecraftforge.client.model; + +/** + * Thrown if there is a problem parsing the model + * + * @author cpw + * + */ +public class ModelFormatException extends RuntimeException { + + public ModelFormatException() + { + super(); + } + + public ModelFormatException(String message, Throwable cause) + { + super(message, cause); + } + + public ModelFormatException(String message) + { + super(message); + } + + public ModelFormatException(Throwable cause) + { + super(cause); + } + +} diff --git a/client/net/minecraftforge/client/model/obj/ObjModelLoader.java b/client/net/minecraftforge/client/model/obj/ObjModelLoader.java new file mode 100644 index 000000000..f1ea05ed7 --- /dev/null +++ b/client/net/minecraftforge/client/model/obj/ObjModelLoader.java @@ -0,0 +1,31 @@ +package net.minecraftforge.client.model.obj; + +import java.io.InputStream; +import java.net.URL; + +import net.minecraftforge.client.model.IModelCustom; +import net.minecraftforge.client.model.IModelCustomLoader; +import net.minecraftforge.client.model.ModelFormatException; + +public class ObjModelLoader implements IModelCustomLoader { + + @Override + public String getType() + { + return "OBJ model"; + } + + private static final String[] types = { "obj" }; + @Override + public String[] getSuffixes() + { + return types; + } + + @Override + public IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException + { + return new WavefrontObject(resourceName, resource); + } + +} diff --git a/client/net/minecraftforge/client/model/obj/WavefrontObject.java b/client/net/minecraftforge/client/model/obj/WavefrontObject.java index 2ab2233bb..37291745e 100644 --- a/client/net/minecraftforge/client/model/obj/WavefrontObject.java +++ b/client/net/minecraftforge/client/model/obj/WavefrontObject.java @@ -8,10 +8,10 @@ import java.net.URL; 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; import net.minecraftforge.client.model.IModelCustom; +import net.minecraftforge.client.model.ModelFormatException; import org.lwjgl.opengl.GL11; @@ -34,7 +34,7 @@ public class WavefrontObject implements IModelCustom 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; @@ -46,42 +46,17 @@ public class WavefrontObject implements IModelCustom private GroupObject currentGroupObject; private String fileName; - public WavefrontObject(String fileName) + public WavefrontObject(String fileName, URL resource) throws ModelFormatException { this.fileName = fileName; - - try - { - loadObjModel(this.getClass().getResource(fileName)); - } - catch (DataFormatException e) - { - e.printStackTrace(); - } - } - - public IModelCustom load(String fileName) - { - return new WavefrontObject(fileName); - } - - public void load() - { - try - { - loadObjModel(this.getClass().getResource(fileName)); - } - catch (DataFormatException e) - { - e.printStackTrace(); - } + loadObjModel(resource); } - private void loadObjModel(URL fileURL) throws DataFormatException + private void loadObjModel(URL fileURL) throws ModelFormatException { BufferedReader reader = null; InputStream inputStream = null; - + String currentLine = null; int lineCount = 0; @@ -158,7 +133,7 @@ public class WavefrontObject implements IModelCustom } catch (IOException e) { - e.printStackTrace(); + throw new ModelFormatException("IO Exception reading model format", e); } finally { @@ -168,16 +143,16 @@ public class WavefrontObject implements IModelCustom } catch (IOException e) { - e.printStackTrace(); + // hush } - + try { inputStream.close(); } catch (IOException e) { - e.printStackTrace(); + // hush } } } @@ -216,7 +191,7 @@ public class WavefrontObject implements IModelCustom } } } - + public void renderPart(String partName) { for (GroupObject groupObject : groupObjects) @@ -242,7 +217,7 @@ public class WavefrontObject implements IModelCustom } } - private Vertex parseVertex(String line, int lineCount) throws DataFormatException + private Vertex parseVertex(String line, int lineCount) throws ModelFormatException { Vertex vertex = null; @@ -253,29 +228,29 @@ public class WavefrontObject implements IModelCustom try { - if (tokens.length == 3) + if (tokens.length == 2) + { + return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1])); + } + else 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(); + throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e); } } else { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); } return vertex; } - private Vertex parseVertexNormal(String line, int lineCount) throws DataFormatException + private Vertex parseVertexNormal(String line, int lineCount) throws ModelFormatException { Vertex vertexNormal = null; @@ -291,18 +266,18 @@ public class WavefrontObject implements IModelCustom } catch (NumberFormatException e) { - e.printStackTrace(); + throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e); } } else { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); } return vertexNormal; } - private TextureCoordinate parseTextureCoordinate(String line, int lineCount) throws DataFormatException + private TextureCoordinate parseTextureCoordinate(String line, int lineCount) throws ModelFormatException { TextureCoordinate textureCoordinate = null; @@ -320,18 +295,18 @@ public class WavefrontObject implements IModelCustom } catch (NumberFormatException e) { - e.printStackTrace(); + throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e); } } else { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); } return textureCoordinate; } - private Face parseFace(String line, int lineCount) throws DataFormatException + private Face parseFace(String line, int lineCount) throws ModelFormatException { Face face = null; @@ -351,7 +326,7 @@ public class WavefrontObject implements IModelCustom } else if (currentGroupObject.glDrawingMode != GL11.GL_TRIANGLES) { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 4, found " + tokens.length + ")"); + throw new ModelFormatException("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) @@ -362,7 +337,7 @@ public class WavefrontObject implements IModelCustom } else if (currentGroupObject.glDrawingMode != GL11.GL_QUADS) { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 3, found " + tokens.length + ")"); + throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 3, found " + tokens.length + ")"); } } @@ -422,18 +397,18 @@ public class WavefrontObject implements IModelCustom } else { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + throw new ModelFormatException("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"); + throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); } return face; } - private GroupObject parseGroupObject(String line, int lineCount) throws DataFormatException + private GroupObject parseGroupObject(String line, int lineCount) throws ModelFormatException { GroupObject group = null; @@ -448,7 +423,7 @@ public class WavefrontObject implements IModelCustom } else { - throw new DataFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); + throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format"); } return group; @@ -465,7 +440,7 @@ public class WavefrontObject implements IModelCustom { vertexMatcher.reset(); } - + vertexMatcher = vertexPattern.matcher(line); return vertexMatcher.matches(); } @@ -501,11 +476,11 @@ public class WavefrontObject implements IModelCustom textureCoordinateMatcher = textureCoordinatePattern.matcher(line); return textureCoordinateMatcher.matches(); } - + /*** * 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 + * @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) { @@ -517,11 +492,11 @@ public class WavefrontObject implements IModelCustom 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 + * @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) { @@ -533,11 +508,11 @@ public class WavefrontObject implements IModelCustom 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 + * @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) { @@ -549,11 +524,11 @@ public class WavefrontObject implements IModelCustom 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 + * @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) { @@ -569,17 +544,17 @@ public class WavefrontObject implements IModelCustom /*** * 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 + * @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 + * @return true if the line is a valid group (or object), false otherwise */ private static boolean isValidGroupObjectLine(String line) { @@ -591,4 +566,10 @@ public class WavefrontObject implements IModelCustom groupObjectMatcher = groupObjectPattern.matcher(line); return groupObjectMatcher.matches(); } + + @Override + public String getType() + { + return "obj"; + } }