package tk.amberide.engine.gl.model.obj;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Random;
import javax.imageio.ImageIO;
import org.lwjgl.util.vector.Vector3f;
public class WavefrontObject {
private ArrayList<Vector3f> vertices = new ArrayList<Vector3f>();
private ArrayList<Vector3f> normals = new ArrayList<Vector3f>();
private ArrayList<Vector3f> textures = new ArrayList<Vector3f>();
//private ArrayList<Face> faces = new ArrayList<Face>();
private ArrayList<Group> groups = new ArrayList<Group>();
private HashMap<String, Group> groupsDirectAccess = new HashMap<String, Group>();
HashMap<String, Material> materials = new HashMap<String, Material>();
private File context;
private File mtllib;
private Material currentMaterial;
private Group currentGroup;
public WavefrontObject(File fileName) throws FileNotFoundException {
this.context = fileName;
parse(fileName);
}
protected void parse(File context) throws FileNotFoundException {
InputStream fileInput = new FileInputStream(context);
BufferedReader in;
try {
in = new BufferedReader(new InputStreamReader(fileInput));
String currentLine;
while ((currentLine = in.readLine()) != null) {
currentLine = currentLine.trim();
if (currentLine.isEmpty() || currentLine.startsWith("#")) {
continue;
}
String[] words = currentLine.split(" ");
String type = words[0];
if ("v".equals(type)) {
vertices.add(new Vector3f(
(Float.parseFloat(words[1])),
(Float.parseFloat(words[2])),
(Float.parseFloat(words[3]))));
} else if ("vn".equals(type)) {
normals.add(new Vector3f(Float.parseFloat(words[1]),
Float.parseFloat(words[2]), Float.parseFloat(words[3])));
} else if ("vt".equals(type)) {
Vector3f coordinate = new Vector3f();
if (words.length >= 2) {
coordinate.x = Float.parseFloat(words[1]);
}
if (words.length >= 3) {
coordinate.y = 1 - Float.parseFloat(words[2]); // OBJ origin is at upper left, OpenGL origin is at lower left.
}
if (words.length >= 4) {
coordinate.z = Float.parseFloat(words[3]);
}
textures.add(coordinate);
} else if ("f".equals(type)) {
Face face = new Face();
Vector3f[] normals = new Vector3f[3];
Vector3f[] textures = new Vector3f[3];
int vertexCount = -1;
if (words.length == 4) {
face.setType(Face.GL_TRIANGLES);
vertexCount = 3;
} else if (words.length == 4) {
face.setType(Face.GL_QUADS);
vertexCount = 4;
}
String[] rawFaces;
int currentValue;
int[] vindices = new int[vertexCount];
int[] nindices = new int[vertexCount];
int[] tindices = new int[vertexCount];
Vector3f[] vertices = new Vector3f[vertexCount];
for (int i = 1; i <= vertexCount; i++) {
rawFaces = words[i].split("/");
// v
currentValue = Integer.parseInt(rawFaces[0]);
vindices[i - 1] = currentValue - 1;
// save vertex
vertices[i - 1] = getVertices().get(currentValue - 1); // -1 because references starts at 1
if (rawFaces.length == 1) {
continue;
}
if (!rawFaces[1].isEmpty()) {
currentValue = Integer.parseInt(rawFaces[1]);
if (currentValue <= getTextures().size()) // This is to compensate the fact that if no texture is in the obj file, sometimes '1' is put instead of 'blank' (we find coord1/1/coord3 instead of coord1//coord3 or coord1/coord3)
{
tindices[i - 1] = currentValue - 1;
textures[i - 1] = getTextures().get(currentValue - 1); // -1 because references starts at 1
}
}
// save normal
currentValue = Integer.parseInt(rawFaces[2]);
nindices[i - 1] = currentValue - 1;
normals[i - 1] = getNormals().get(currentValue - 1); // -1 because references starts at 1
}
if (currentGroup == null) {
currentGroup = new Group(String.valueOf(System.nanoTime() + new Random().nextInt()));
getGroups().add(currentGroup);
getGroupsDirectAccess().put(currentGroup.getName(), currentGroup);
setCurrentGroup(currentGroup);
}
// Add list of vertex/normal/texcoord to current group
// Each object keeps a list of its own data, apart from the global list
currentGroup.vertices.add(vertices[0]);
currentGroup.vertices.add(vertices[1]);
currentGroup.vertices.add(vertices[2]);
currentGroup.normals.add(normals[0]);
currentGroup.normals.add(normals[1]);
currentGroup.normals.add(normals[2]);
currentGroup.texcoords.add(textures[0]);
currentGroup.texcoords.add(textures[1]);
currentGroup.texcoords.add(textures[2]);
currentGroup.indices.add(currentGroup.indexCount++);
currentGroup.indices.add(currentGroup.indexCount++);
currentGroup.indices.add(currentGroup.indexCount++); // create index list for current object
face.vertIndices = vindices;
face.normIndices = nindices;
face.texIndices = tindices;
face.setNormals(normals);
face.setNormals(normals);
face.setVertices(vertices);
face.setTextures(textures);
getCurrentGroup().addFace(face);
} else if ("mtllib".equals(type)) {
mtllib = new File(context.getParentFile(), words[1]);
BufferedReader mtlin = new BufferedReader(new InputStreamReader(new FileInputStream(mtllib)));
String mtlline;
while ((mtlline = mtlin.readLine()) != null) {
mtlline = mtlline.trim();
if (mtlline.isEmpty() || currentLine.startsWith("#")) {
continue;
}
String[] mwords = mtlline.split(" ");
String mtltype = mwords[0];
if ("newmtl".equals(mtltype)) {
Material newMaterial = new Material(mwords[1]);
materials.put(mwords[1], newMaterial);
currentMaterial = newMaterial;
} else if ("Ka".equals(mtltype)) {
currentMaterial.setKa(new Vector3f(Float.parseFloat(mwords[1]),
Float.parseFloat(mwords[2]), Float.parseFloat(mwords[3])));
} else if ("Kd".equals(mtltype)) {
currentMaterial.setKd(new Vector3f(Float.parseFloat(mwords[1]),
Float.parseFloat(mwords[2]), Float.parseFloat(mwords[3])));
} else if ("Ks".equals(mtltype)) {
currentMaterial.setKs(new Vector3f(Float.parseFloat(mwords[1]),
Float.parseFloat(mwords[2]), Float.parseFloat(mwords[3])));
} else if ("Ns".equals(mtltype)) {
currentMaterial.setShininess(Float.parseFloat(mwords[1]));
} else if ("map_Kd".equals(mtltype)) {
String texName = mwords[mwords.length - 1];
currentMaterial.texName = texName;
currentMaterial.setTexture(ImageIO.read(new File(context.getParentFile(), texName)));
}
}
mtlin.close();
} else if ("usemtl".equals(type)) {
currentGroup.setMaterial(materials.get(words[1]));
} else if ("g".equals(type)) {
String groupName = words[1];
Group newGroup = new Group(groupName);
if (currentGroup != null) {
currentGroup.pack();
}
groups.add(newGroup);
groupsDirectAccess.put(newGroup.getName(), newGroup);
currentGroup = newGroup;
}
}
in.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("error reading file :'" + context + "'");
}
}
public File getMaterialsFile() {
return mtllib;
}
public File getObjectFile() {
return context;
}
public void setMaterials(HashMap<String, Material> materials) {
this.materials = materials;
}
public void setTextures(ArrayList<Vector3f> textures) {
this.textures = textures;
}
public ArrayList<Vector3f> getTextures() {
return textures != null ? textures : new ArrayList<Vector3f>();
}
public void setVertices(ArrayList<Vector3f> vertices) {
this.vertices = vertices;
}
public ArrayList<Vector3f> getVertices() {
return vertices != null ? vertices : new ArrayList<Vector3f>();
}
public void setNormals(ArrayList<Vector3f> normals) {
this.normals = normals;
}
public ArrayList<Vector3f> getNormals() {
return normals != null ? normals : new ArrayList<Vector3f>();
}
public HashMap<String, Material> getMaterials() {
return materials != null ? materials : new HashMap<String, Material>();
}
public Material getCurrentMaterial() {
return currentMaterial;
}
public void setCurrentMaterial(Material currentMaterial) {
this.currentMaterial = currentMaterial;
}
public ArrayList<Group> getGroups() {
return groups != null ? groups : new ArrayList<Group>();
}
public HashMap<String, Group> getGroupsDirectAccess() {
return groupsDirectAccess;
}
public Group getCurrentGroup() {
return currentGroup;
}
public void setCurrentGroup(Group currentGroup) {
this.currentGroup = currentGroup;
}
}