Add in a generic factory interface, allowing for additional model support

to be added at runtime.
This commit is contained in:
Christian 2013-04-10 18:24:34 -04:00
parent d4b71ef964
commit 1ae7fa0080
6 changed files with 228 additions and 76 deletions

View File

@ -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<String, IModelCustomLoader> 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<String> getSupportedSuffixes()
{
return instances.keySet();
}
static
{
registerModelHandler(new ObjModelLoader());
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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";
}
}