So, I'm trying to load (Wavefront) OBJ models in Java. Currently it loads vertex positions properly but texture coords are messed up:
This is what I see in the engine:
This is what I see in blender:
My current loading code is here:
private void loadOBJ(String filename)
{
ArrayList<Vector3f> verts = new ArrayList<Vector3f>();
ArrayList<Vector3f> norms = new ArrayList<Vector3f>();
ArrayList<Vector2f> uvs = new ArrayList<Vector2f>();
ArrayList<Integer> ints = new ArrayList<Integer>(); //Indices
ArrayList<Integer> nints = new ArrayList<Integer>(); //Normal indices
ArrayList<Integer> tints = new ArrayList<Integer>(); //Texture coord indices
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split(" ");
if(tokens[0].startsWith("vn"))
{
norms.add(new Vector3f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
}
else if(tokens[0].startsWith("vt"))
{
uvs.add(new Vector2f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2])));
}
else if(tokens[0].startsWith("v"))
{
verts.add(new Vector3f(Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
}
else if(tokens[0].startsWith("f"))
{
ints.add(Integer.parseInt(tokens[1].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[2].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[3].split("/")[0]) - 1);
tints.add(Integer.parseInt(tokens[1].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[2].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[3].split("/")[1]) - 1);
nints.add(Integer.parseInt(tokens[1].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[2].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[3].split("/")[2]) - 1);
if(tokens.length > 4) //For quads
{
ints.add(Integer.parseInt(tokens[1].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[4].split("/")[0]) - 1);
ints.add(Integer.parseInt(tokens[2].split("/")[0]) - 1);
tints.add(Integer.parseInt(tokens[1].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[4].split("/")[1]) - 1);
tints.add(Integer.parseInt(tokens[2].split("/")[1]) - 1);
nints.add(Integer.parseInt(tokens[1].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[4].split("/")[2]) - 1);
nints.add(Integer.parseInt(tokens[2].split("/")[2]) - 1);
}
}
}
reader.close();
} catch (IOException e) {
System.err.println("Could not read file.");
e.printStackTrace();
System.exit(-1);
}
//Now convert the loaded data to internal format: VertexData[] that con tains positions, uvs, and normals, and int[] that has indices
vertices = new VertexData[verts.size()];
indices = new int[ints.size()];
for(int i = 0; i < ints.size(); i++)
{
indices[i] = ints.get(i);
int j = ints.get(i);
int k = tints.get(i);
int q = nints.get(i);
vertices[j] = new VertexData();
vertices[j].setXYZ(verts.get(j).x, verts.get(j).y, verts.get(j).z);
vertices[j].setST(uvs.get(k).x, uvs.get(k).y);
vertices[j].setNormal(norms.get(q).x, norms.get(q).y, norms.get(q).z);
}
}
As you can see from the pictures current code failes to properly load uvs, but I can't figure out whats wrong. Help?
Your code does not correctly generate the vertex data suitable for the GL. As you know, in the obj format, there is an separate array for vertex position, normals, texcoords and so on. And faces are formed by independetly indexing into them. In the GL, a vertex is the set of all of its attributes, and you can't use separate indices per attribute (at least not directly; modern GL is flexible enough to allow you to implement that level of indirection in the vertex shader, but that would be another story).
Your code maybe attempts at fixing this, but the solution is wrong. You simply use the vertex index from the OBJ file as the global index, and write your vertex containing all of the attributes using the other indices no matter, if the same vertex index might be used with a different combination of normals or texcoords - you simply overwrite it with the last such combination.
THe coorect approach would creating a separate vertex for each unique (vertex,normal,texcoord) combination that occurs. As a result, the number of vertices will of course increase, and the index value can't be directly reused for the element array, either.