diff --git a/client/net/minecraftforge/client/model/obj/Face.java b/client/net/minecraftforge/client/model/obj/Face.java new file mode 100644 index 000000000..f47951b1d --- /dev/null +++ b/client/net/minecraftforge/client/model/obj/Face.java @@ -0,0 +1,84 @@ +package net.minecraftforge.client.model.obj; + +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.Vec3; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class Face +{ + + public Vertex[] vertices; + public Vertex[] vertexNormals; + public Vertex faceNormal; + public TextureCoordinate[] textureCoordinates; + + public void addFaceForRender(Tessellator tessellator) + { + addFaceForRender(tessellator, 0.0005F); + } + + public void addFaceForRender(Tessellator tessellator, float textureOffset) + { + if (faceNormal == null) + { + faceNormal = this.calculateFaceNormal(); + } + + tessellator.setNormal(faceNormal.x, faceNormal.y, faceNormal.z); + + float averageU = 0F; + float averageV = 0F; + + if (textureCoordinates.length != 0) + { + for (int i = 0; i < textureCoordinates.length; ++i) + { + averageU += textureCoordinates[i].u; + averageV += textureCoordinates[i].v; + } + + averageU = averageU / textureCoordinates.length; + averageV = averageV / textureCoordinates.length; + } + + float offsetU, offsetV; + + for (int i = 0; i < vertices.length; ++i) + { + + if (textureCoordinates.length != 0) + { + offsetU = textureOffset; + offsetV = textureOffset; + + if (textureCoordinates[i].u > averageU) + { + offsetU = -offsetU; + } + if (textureCoordinates[i].v > averageV) + { + offsetV = -offsetV; + } + + tessellator.addVertexWithUV(vertices[i].x, vertices[i].y, vertices[i].z, textureCoordinates[i].u + offsetU, textureCoordinates[i].v + offsetV); + } + else + { + tessellator.addVertex(vertices[i].x, vertices[i].y, vertices[i].z); + } + } + } + + public Vertex calculateFaceNormal() + { + Vec3 v1 = Vec3.createVectorHelper(vertices[1].x - vertices[0].x, vertices[1].y - vertices[0].y, vertices[1].z - vertices[0].z); + Vec3 v2 = Vec3.createVectorHelper(vertices[2].x - vertices[0].x, vertices[2].y - vertices[0].y, vertices[2].z - vertices[0].z); + Vec3 normalVector = null; + + normalVector = v1.crossProduct(v2).normalize(); + + return new Vertex((float) normalVector.xCoord, (float) normalVector.yCoord, (float) normalVector.zCoord); + } +} diff --git a/client/net/minecraftforge/client/model/obj/GroupObject.java b/client/net/minecraftforge/client/model/obj/GroupObject.java new file mode 100644 index 000000000..0bf36af46 --- /dev/null +++ b/client/net/minecraftforge/client/model/obj/GroupObject.java @@ -0,0 +1,54 @@ +package net.minecraftforge.client.model.obj; + +import java.util.ArrayList; + +import net.minecraft.client.renderer.Tessellator; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class GroupObject +{ + + public String name; + public ArrayList faces = new ArrayList(); + public int glDrawingMode; + + public GroupObject() + { + this(""); + } + + public GroupObject(String name) + { + this(name, -1); + } + + public GroupObject(String name, int glDrawingMode) + { + this.name = name; + this.glDrawingMode = glDrawingMode; + } + + public void render() + { + if (faces.size() > 0) + { + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawing(glDrawingMode); + render(tessellator); + tessellator.draw(); + } + } + + public void render(Tessellator tessellator) + { + if (faces.size() > 0) + { + for (Face face : faces) + { + face.addFaceForRender(tessellator); + } + } + } +} diff --git a/client/net/minecraftforge/client/model/obj/TextureCoordinate.java b/client/net/minecraftforge/client/model/obj/TextureCoordinate.java new file mode 100644 index 000000000..85839055e --- /dev/null +++ b/client/net/minecraftforge/client/model/obj/TextureCoordinate.java @@ -0,0 +1,23 @@ +package net.minecraftforge.client.model.obj; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class TextureCoordinate +{ + + public float u, v, w; + + public TextureCoordinate(float u, float v) + { + this(u, v, 0F); + } + + public TextureCoordinate(float u, float v, float w) + { + this.u = u; + this.v = v; + this.w = w; + } +} diff --git a/client/net/minecraftforge/client/model/obj/Vertex.java b/client/net/minecraftforge/client/model/obj/Vertex.java new file mode 100644 index 000000000..c4ecf35e9 --- /dev/null +++ b/client/net/minecraftforge/client/model/obj/Vertex.java @@ -0,0 +1,23 @@ +package net.minecraftforge.client.model.obj; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class Vertex +{ + + public float x, y, z; + + public Vertex(float x, float y) + { + this(x, y, 0F); + } + + public Vertex(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } +} diff --git a/client/net/minecraftforge/client/model/obj/WavefrontObject.java b/client/net/minecraftforge/client/model/obj/WavefrontObject.java new file mode 100644 index 000000000..42934f8c7 --- /dev/null +++ b/client/net/minecraftforge/client/model/obj/WavefrontObject.java @@ -0,0 +1,447 @@ +package net.minecraftforge.client.model.obj; + +import java.io.BufferedReader; +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 net.minecraft.client.renderer.Tessellator; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@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]+) *$)"; + + public ArrayList vertices = new ArrayList(); + public ArrayList vertexNormals = new ArrayList(); + public ArrayList textureCoordinates = new ArrayList(); + public ArrayList groupObjects = new ArrayList(); + private GroupObject currentGroupObject; + + public WavefrontObject(String fileName) + { + try + { + parseObjModel(this.getClass().getResource(fileName)); + } + catch (ParseException e) + { + e.printStackTrace(); + } + } + + public WavefrontObject(URL fileURL) + { + try + { + parseObjModel(fileURL); + } + catch (ParseException e) + { + e.printStackTrace(); + } + } + + private void parseObjModel(URL fileURL) throws ParseException + { + BufferedReader reader = null; + InputStream inputStream = null; + + String currentLine = null; + int lineCount = 0; + + try + { + inputStream = fileURL.openStream(); + reader = new BufferedReader(new InputStreamReader(inputStream)); + + while ((currentLine = reader.readLine()) != null) + { + lineCount++; + currentLine = currentLine.replaceAll("\\s+", " ").trim(); + + if (currentLine.startsWith("#") || currentLine.length() == 0) + { + continue; + } + else if (currentLine.startsWith("v ")) + { + Vertex vertex = parseVertex(currentLine); + 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); + 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); + 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 ")) + { + + if (currentGroupObject == null) + { + currentGroupObject = new GroupObject("Default"); + } + + Face face = parseFace(currentLine); + + 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); + + if (group != null) + { + if (currentGroupObject != null) + { + groupObjects.add(currentGroupObject); + } + } + else + { + throw new ParseException("Error parsing entry ('" + currentLine + "'" + ", line " + lineCount + ") in file '" + fileURL.getFile() + "'", lineCount); + } + + currentGroupObject = group; + } + } + + groupObjects.add(currentGroupObject); + } + catch (IOException e) + { + e.printStackTrace(); + } + finally + { + try + { + reader.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + try + { + inputStream.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + public void renderAll() + { + Tessellator tessellator = Tessellator.instance; + + if (currentGroupObject != null) + { + tessellator.startDrawing(currentGroupObject.glDrawingMode); + } + else + { + tessellator.startDrawing(GL11.GL_TRIANGLES); + } + + for (GroupObject groupObject : groupObjects) + { + groupObject.render(tessellator); + } + + tessellator.draw(); + } + + public void renderOnly(String... groupNames) + { + for (GroupObject groupObject : groupObjects) + { + for (String groupName : groupNames) + { + if (groupName.equalsIgnoreCase(groupObject.name)) + { + groupObject.render(); + } + } + } + } + + public void renderAllExcept(String... excludedGroupNames) + { + for (GroupObject groupObject : groupObjects) + { + for (String excludedGroupName : excludedGroupNames) + { + if (!excludedGroupName.equalsIgnoreCase(groupObject.name)) + { + groupObject.render(); + } + } + } + } + + private Vertex parseVertex(String line) + { + Vertex vertex = null; + + if (isValidVertexLine(line)) + { + line = line.substring(line.indexOf(" ") + 1); + String[] tokens = line.split(" "); + + try + { + if (tokens.length == 3) + return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2])); + } + catch (NumberFormatException e) + { + e.printStackTrace(); + } + } + + return vertex; + } + + private Vertex parseVertexNormal(String line) + { + Vertex vertexNormal = null; + + if (isValidVertexNormalLine(line)) + { + line = line.substring(line.indexOf(" ") + 1); + String[] tokens = line.split(" "); + + try + { + if (tokens.length == 3) + return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2])); + } + catch (NumberFormatException e) + { + e.printStackTrace(); + } + } + + return vertexNormal; + } + + private TextureCoordinate parseTextureCoordinate(String line) + { + TextureCoordinate textureCoordinate = null; + + if (isValidTextureCoordinateLine(line)) + { + line = line.substring(line.indexOf(" ") + 1); + String[] tokens = line.split(" "); + + try + { + if (tokens.length == 2) + return new TextureCoordinate(Float.parseFloat(tokens[0]), 1 - Float.parseFloat(tokens[1])); + else if (tokens.length == 3) + return new TextureCoordinate(Float.parseFloat(tokens[0]), 1 - Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2])); + } + catch (NumberFormatException e) + { + e.printStackTrace(); + } + } + + return textureCoordinate; + } + + private Face parseFace(String line) + { + Face face = null; + + if (isValidFaceLine(line)) + { + face = new Face(); + + String trimmedLine = line.substring(line.indexOf(" ") + 1); + String[] tokens = trimmedLine.split(" "); + String[] subTokens = null; + + if (tokens.length == 3) + { + if (currentGroupObject.glDrawingMode == -1) + { + currentGroupObject.glDrawingMode = GL11.GL_TRIANGLES; + } + else if (currentGroupObject.glDrawingMode != GL11.GL_TRIANGLES) + { + return null; + } + } + else if (tokens.length == 4) + { + if (currentGroupObject.glDrawingMode == -1) + { + currentGroupObject.glDrawingMode = GL11.GL_QUADS; + } + else if (currentGroupObject.glDrawingMode != GL11.GL_QUADS) + { + return null; + } + } + + face.vertices = new Vertex[tokens.length]; + face.textureCoordinates = new TextureCoordinate[tokens.length]; + face.vertexNormals = new Vertex[tokens.length]; + + // f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ... + if (line.matches(REGEX_FACE_VERTEX_TEXTURECOORD_VERTEXNORMAL)) + { + for (int i = 0; i < tokens.length; ++i) + { + subTokens = tokens[i].split("/"); + + face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1); + face.textureCoordinates[i] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1); + face.vertexNormals[i] = vertexNormals.get(Integer.parseInt(subTokens[2]) - 1); + } + + face.faceNormal = face.calculateFaceNormal(); + } + // f v1/vt1 v2/vt2 v3/vt3 ... + else if (line.matches(REGEX_FACE_VERTEX_TEXTURECOORD)) + { + for (int i = 0; i < tokens.length; ++i) + { + subTokens = tokens[i].split("/"); + + face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1); + face.textureCoordinates[i] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1); + } + + face.faceNormal = face.calculateFaceNormal(); + } + // f v1//vn1 v2//vn2 v3//vn3 ... + else if (line.matches(REGEX_FACE_VERTEX_VERTEXNORMAL)) + { + for (int i = 0; i < tokens.length; ++i) + { + subTokens = tokens[i].split("//"); + + face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1); + face.vertexNormals[i] = vertexNormals.get(Integer.parseInt(subTokens[1]) - 1); + } + + face.faceNormal = face.calculateFaceNormal(); + } + // f v1 v2 v3 ... + else if (line.matches(REGEX_FACE_VERTEX)) + { + for (int i = 0; i < tokens.length; ++i) + { + face.vertices[i] = vertices.get(Integer.parseInt(tokens[i]) - 1); + } + + face.faceNormal = face.calculateFaceNormal(); + } + else + throw new IllegalArgumentException(); + } + + return face; + } + + private GroupObject parseGroupObject(String line) + { + GroupObject group = null; + + if (isValidGroupObjectLine(line)) + { + String trimmedLine = line.substring(line.indexOf(" ") + 1); + + if (trimmedLine.length() > 0) + { + group = new GroupObject(trimmedLine); + } + } + + return group; + } + + private static boolean isValidVertexLine(String line) + { + + return line.matches(REGEX_VERTEX); + } + + private static boolean isValidVertexNormalLine(String line) + { + + return line.matches(REGEX_VERTEX_NORMAL); + } + + private static boolean isValidTextureCoordinateLine(String line) + { + + return line.matches(REGEX_TEXTURE_COORDINATE); + } + + private static boolean isValidFaceLine(String line) + { + + 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); + } + + private static boolean isValidGroupObjectLine(String line) + { + + return line.matches(REGEX_GROUP_OBJECT); + } +}