OpenGL - OBJ File laden: Textur Koordinaten falsch

Hi, mal wieder habe ich ein Problem mit OpenGl :wink:

Und zwar will ich *.obj Dateien laden. Die Vertecies werden ohne Probleme geladen und soweit ich dass erkennen kann auch die Normals. Nur mit den Texture Koordinaten stimmt irgendetwas nicht.

Hier ein Screenshot :

Hier das Obj-File:

Datei-Inhalt
[spoiler]


# Blender v2.68 (sub 0) OBJ File: ''
# www.blender.org
mtllib cube.mtl
o Cube_Cube.001
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 1.000000 1.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vn -1.000000 0.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 -0.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn -0.000000 -1.000000 0.000000
vn -0.000000 1.000000 0.000000
usemtl None
s off
f 5/1/1 6/2/1 1/3/1
f 6/1/2 7/2/2 2/3/2
f 7/1/3 8/2/3 3/3/3
f 8/1/4 5/2/4 4/3/4
f 1/1/5 2/2/5 4/3/5
f 8/1/6 7/2/6 5/3/6
f 6/2/1 2/4/1 1/3/1
f 7/2/2 3/4/2 2/3/2
f 8/2/3 4/4/3 3/3/3
f 5/2/4 1/4/4 4/3/4
f 2/2/5 3/4/5 4/3/5
f 7/2/6 6/4/6 5/3/6

[/spoiler]

Hier der Code zum Laden der Datei:

OBJ_Loader.java
[spoiler]```

package oneric.obj;

import static org.lwjgl.opengl.GL11.GL_COMPILE;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL15.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import oneric.gl.buffers.Attribute;
import oneric.gl.buffers.VBO;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

public class OBJLoader {

/**
 * Creates an Model from the given file
 * 
 * @param f The OBJ-file to load the Model from 
 * */
public static Model loadModel(File f) throws FileNotFoundException, IOException
{
	BufferedReader reader = new BufferedReader(new FileReader(f));
	
	Model mod = new Model();
	
	String line;
	while((line = reader.readLine()) != null)
	{
		if(line.startsWith("v "))
		{
			float x = Float.valueOf(line.split(" ")[1]);
			float y = Float.valueOf(line.split(" ")[2]);
			float z = Float.valueOf(line.split(" ")[3]);
			
			mod.vertecies.add(new Vector3f(x, y, z));
		}
		else if(line.startsWith("vn "))
		{
			float x = Float.valueOf(line.split(" ")[1]);
			float y = Float.valueOf(line.split(" ")[2]);
			float z = Float.valueOf(line.split(" ")[3]);
			
			mod.normals.add(new Vector3f(x, y, z));
		}
		else if(line.startsWith("vt "))
		{
			float x = Float.valueOf(line.split(" ")[1]);
			float y = Float.valueOf(line.split(" ")[2]);
			
			mod.texture.add(new Vector2f(x, y));
		}
		else if(line.startsWith("f "))
		{
			//System.out.println(line);
			Vector3f vetexIndicies = new Vector3f(Float.valueOf( line.split(" ")[1].split("/")[0] ), Float.valueOf( line.split(" ")[2].split("/")[0] ), Float.valueOf( line.split(" ")[3].split("/")[0] ) );
			
			Vector3f normalIndicies = new Vector3f(Float.valueOf( line.split(" ")[1].split("/")[2] ), Float.valueOf( line.split(" ")[2].split("/")[2] ), Float.valueOf( line.split(" ")[3].split("/")[2] ) );
			
			/*TODO*/Vector3f textureIndicies = new Vector3f(Float.valueOf( line.split(" ")[1].split("/")[1] ), Float.valueOf( line.split(" ")[2].split("/")[1] ), Float.valueOf( line.split(" ")[3].split("/")[1] ) );
			
			mod.faces.add(new Face(vetexIndicies, normalIndicies, textureIndicies));
		}
	}
	
	reader.close();
	
	
	
	return mod;
}

/**
* Vertex Atrributes:
*
* 0 = vertex;
* 1 = texture;
* 2 = normals;
* */
public static VBO createVBO(Model mod)
{
IntBuffer vboIDs = BufferUtils.createIntBuffer(3);
glGenBuffers(vboIDs);

	List<Float> vertecies = new ArrayList<Float>();
	List<Float> normals = new ArrayList<Float>();
	/*TODO*/ List<Float> texture = new ArrayList<Float>();
	
	for(Face face : mod.faces)
	{
		vertecies.addAll(asFloatList(mod.vertecies.get((int)face.vertex.x - 1)) );
		vertecies.addAll(asFloatList(mod.vertecies.get((int)face.vertex.y - 1)) );
		vertecies.addAll(asFloatList(mod.vertecies.get((int)face.vertex.z - 1)) );
		
		normals.addAll(asFloatList(mod.normals.get((int)face.normal.x - 1)) );
		normals.addAll(asFloatList(mod.normals.get((int)face.normal.y - 1)) );
		normals.addAll(asFloatList(mod.normals.get((int)face.normal.z - 1)) );
		
		/*TODO*/
		texture.addAll(asFloatList(mod.texture.get((int)face.texture.x - 1)));
		texture.addAll(asFloatList(mod.texture.get((int)face.texture.y - 1)));
		/*TODO*/
	}
	
	
	int fSize = Float.SIZE/8;
	
	VBO vbo = new VBO(GL_ARRAY_BUFFER);
	vbo.init();
	vbo.bind();
	
	vbo.addData(vertecies);
	vbo.addAtribute(new Attribute(0, 3, fSize*3, 0));
	
	vbo.addData(texture);
	vbo.addAtribute(new Attribute(1, 2, fSize*2, fSize * vertecies.size()));
	
	vbo.addData(normals);
	vbo.addAtribute(new Attribute(2, 3, fSize*3, (fSize * vertecies.size()) + (fSize * texture.size()) ));
	
	vbo.unbind();
	
	vbo.setSize(vertecies.size()/3);
	
	return vbo;
	
}

/**
 * Little Helper Method. Turns an Vector into an float-Array
 * */
private static float[] asFloats(Vector3f vec)
{
	return new float[]{vec.x, vec.y ,vec.z};
}

/**
 * Little Helper Method. Turns an Vector into an float-Array
 * */
private static float[] asFloats(Vector2f vec)
{
	return new float[]{vec.x, vec.y};
}


private static List<Float> asFloatList(Vector2f vec)
{
	List<Float> list = new ArrayList<Float>();
	list.add(vec.x);
	list.add(vec.y);
	return list;
}

private static List<Float> asFloatList(Vector3f vec)
{
	List<Float> list = new ArrayList<Float>();
	list.add(vec.x);
	list.add(vec.y);
	list.add(vec.z);
	return list;
}

}





Model.java
[spoiler]```

package oneric.obj;


import java.util.ArrayList;
import java.util.List;

import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

public class Model {

	
	public List<Vector3f> vertecies = new ArrayList<Vector3f>();
	public List<Vector3f> normals = new ArrayList<Vector3f>();
	public List<Face> faces = new ArrayList<Face>();
	/*TODO*/public List<Vector2f> texture = new ArrayList<Vector2f>();
	
	public Model()
	{
		
	}
	

}


```[/spoiler]




Face.java
[spoiler]```

package oneric.obj;

import org.lwjgl.util.vector.Vector3f;

public class Face {

	public Vector3f vertex = new Vector3f();  // threeIndecies(Index), not a vertex
	public Vector3f normal = new Vector3f();  // threeIndecies(Index), not a vertex
	/*TODO*/public Vector3f texture = new Vector3f(); // threeIndecies(Index), not Tex Coord

	@Deprecated
	public Face(Vector3f vert, Vector3f norm)
	{
		this.normal = norm;
		this.vertex = vert;
	}
	
	/*TODO*/
	public Face(Vector3f vert, Vector3f norm, Vector3f tex)
	{
		this.normal = norm;
		this.vertex = vert;
		this.texture = tex;
	}
	

}


```[/spoiler]




Main.java
[spoiler]```

// im Init Code
VBO vboTerrain = null;
				
try {
	vboTerrain = OBJLoader.createVBO(OBJLoader.loadModel(new File("res/models/cube.obj")));
} catch (FileNotFoundException e2) {
	e2.printStackTrace();
} catch (IOException e2) {
	e2.printStackTrace();
}

vao = new VAO(GL_TRIANGLES, /*4 +*/ vboTerrain.getSize());		
vao.addVBO(vboTerrain, true);



//Und später beim rendern

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);
vao.render();


```[/spoiler]


Die Klassen VAO und VBO sollten zwar eigentlich vollkommen in Ordnung sein, da alle Objekte was ich per Hand im Code erstellt hatte problemlos funktionierten, aber wer sie trotzdem sehen will findet sie hier: 

VAO.java
[spoiler]```
package oneric.gl.buffers;

import java.util.Collection;
import java.util.List;

import oneric.util.Disposable;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glDeleteVertexArrays;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.opengl.GL11.GL_FLOAT;

public class VAO implements Disposable {

	private int mode;
	private int size;
	
	private int vaoID;
	
	public VAO(int mode_a, int size)
	{
		this.mode = mode_a;
		this.size = size;
		this.init();
	}
	
	public VAO(int mode_a, int size, VBO... vbos)
	{
		this(mode_a, size);
		
		glBindVertexArray(vaoID);
		for(VBO v : vbos)
		{
			
			this.addVBOinConstructor(v);
		}
		glBindVertexArray(0);
			
	}
	
	public VAO(int mode_a, List<VBO> vbos, int size)
	{
		this(mode_a, size);
		
		//this.size = 0;
		
		glBindVertexArray(vaoID);
		for(VBO v : vbos)
		{
			this.addVBOinConstructor(v);
		}
		
		
		glBindVertexArray(0);
	}
	
	
	/**
	 * Creates an ID for the VAO
	 * */
	private void init()
	{
		this.vaoID = glGenVertexArrays();
	}
	
	/** 
	 * A little bit decreased addVBO Method for the Constructor.
	 * For more Performance
	 * */
	private void addVBOinConstructor(VBO vbo)
	{
		vbo.bind();
		vbo.uploadToGPU(false);
		
		for(Attribute a : vbo.getAttributes())
		{
			glEnableVertexAttribArray(a.attribute);
			glVertexAttribPointer(a.attribute, a.sizeOfAnElement, GL_FLOAT, a.normalized, a.offset, a.firstElement);
		}
		
		vbo.clearAllDatas();
		vbo.unbind();	
	}
	
	/***
	 * Adds a VBO to the VAO. Uses the Attributes set in the VBO-Object
	 * 
	 * @param vbo The VBO to add
	 * @param clear Tells if the client data and attributes of this VBO should be cleared
	 *  */
	public void addVBO(VBO vbo, boolean clear)
	{
		glBindVertexArray(vaoID);
		
		vbo.bind();
		vbo.uploadToGPU(false);
		
		for(Attribute a : vbo.getAttributes())
		{
			glEnableVertexAttribArray(a.attribute);
			glVertexAttribPointer(a.attribute, a.sizeOfAnElement, GL_FLOAT, a.normalized, a.offset, a.firstElement);
		}
		
		if(clear)vbo.clearAllDatas();
		
		vbo.unbind();
		glBindVertexArray(0);
	}
	
	/***
	 * Adds a VBO to the VAO. Uses the Attributes given as Parameters and ignore the VBOs Attributes.
	 * 
	 * @param vbo The VBO to add
	 * @param attribs The Attributes to be used
	 * @param clear Tells if the client data of this VBO should be cleared
	 *  */
	public void addVBO(VBO vbo, Collection<Attribute> attribs, boolean clear)
	{
		glBindVertexArray(vaoID);
		
		vbo.bind();
		vbo.uploadToGPU(false);
		
		for(Attribute a : attribs)
		{
			glEnableVertexAttribArray(a.attribute);
			glVertexAttribPointer(a.attribute, a.sizeOfAnElement, GL_FLOAT, a.normalized, a.offset, a.firstElement);
		}
		
		if(clear)vbo.clearData();
		
		vbo.unbind();
		glBindVertexArray(0);
	}
	
	
	/** 
	 * Render the VAO
	 * */
	public void render()
	{
		glBindVertexArray(vaoID);
		glDrawArrays(mode, 0, size);
	}
	
	/** 
	 * Render the VAO without binding
	 * */
	public void drawArrays()
	{
		glDrawArrays(mode, 0, size);
	}
	
	/** 
	 * Draws a part of the VAO (without binding)
	 * */
	public void drawArrays(int first, int count)
	{
		glDrawArrays(mode, first, count);
	}
	
	
	/** */
	public void bindVAO()
	{
		glBindVertexArray(vaoID);
	}
	
	/** */
	public void unbindVAO()
	{
		glBindVertexArray(0);
	}
	
	
	/** 
	 * Deletes the VAO from OpenGL
	 * */
	@Override
	public void dispose()
	{
		glDeleteVertexArrays(vaoID);
	}
	
	/** 
	 * @return The ID of The VAO
	 * */
	public int getID()
	{
		return vaoID;
	}
	
	
}
```[/spoiler]



VBO.java
[spoiler]```
package oneric.gl.buffers;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

import oneric.util.Disposable;

import org.lwjgl.BufferUtils;

import static org.lwjgl.opengl.GL15.*;

public class VBO implements Disposable{
	
	/**
	 * Contains the vertex and optionally the texture and normal data
	 * */
	private List<Float> data = new ArrayList<Float>();
	/** The size of the vbo */
	private int size;
	
	/** The ID of the VBO */
	private int vboID;
	
	/**
	 * Defines the VBO Mode
	 * */
	private int type;
	
	
	/** The Vertex Attributes*/
	private HashMap<Integer, Attribute> attributes = new HashMap<Integer, Attribute>(); 
	
	/**
	 * Creates an new VBO Object
	 * */
	public VBO() 
	{
		
	}
	
	/**
	 * Creates a new VBO Object by the given arguments
	 * 
	 * @param values The Data of the VBO (Vertex, TextureCoord, Colour, Normals)
	 * @param vboType The mode of the VBO
	 * */
	public VBO(List<Float> values, int vboType)
	{
		
		data = values;
		this.type = vboType;
		this.size = values.size();
		
	}
	
	/**
	 * Creates a new VBO Object by the given arguments
	 * 
	 * @param vboType The mode of the VBO
	 * */
	public VBO(int vboType) 
	{
		
		this.type = vboType;
	}
	
	/**
	 * Creates a new VBO Object by the given arguments
	 * 
	 * @param values The Data of the VBO (Vertex, TextureCoord, Colour, Normals)
	 * */
	public VBO(List<Float> values)
	{
		
		data = values;
		this.size = values.size();
		
		this.type = GL_ARRAY_BUFFER;
	}
	
	/**
	 * Creates a new VBO Object by the given arguments
	 * 
	 * @param values The Data of the VBO (Vertex, TextureCoord, Colour, Normals)
	 * @param vboType The mode of the VBO
	 * @param attribs The attributes of this VBO
	 * */
	public VBO(List<Float> values, int vboType, List<Attribute> attribs)
	{
		
		data = values;
		this.type = vboType;
		this.size = values.size();
		
		for(Attribute a : attribs)
			this.attributes.put(a.attribute, a);
		
	}
	
	
	/**
	 * Creates a new VBO Object by the given arguments
	 * 
	 * @param values The Data of the VBO (Vertex, TextureCoord, Colour, Normals)
	 * @param attribs The attributes of this VBO
	 * */
	public VBO(List<Float> values, List<Attribute> attribs)
	{
		
		data = values;
		this.size = values.size();
		
		this.type = GL_ARRAY_BUFFER;
		
		for(Attribute a : attribs)
			this.attributes.put(a.attribute, a);
		
	}
	
	/**
	 * Create an OpenGL-ID for the VBO
	 * */
	public void init()
	{
		vboID = glGenBuffers();
	}
	
	/** 
	 * Add an Attribute(or better Description of an Attribute) to the VBO
	 * 
	 * @param a The Attribute to add
	 * */
	public void addAtribute(Attribute a)
	{
		this.attributes.put(a.attribute, a);
	}
	
	/** 
	 * sets the Attributes(or better Descriptions of the Attributes) to the VBO
	 * 
	 * @param attribs The Attributes to add
	 * */
	public void setAttributes(List<Attribute> attribs)
	{
		for(Attribute a : attribs)
			this.attributes.put(a.attribute, a);
	}
	
	
	/**
	 * Add data to the VBO
	 * 
	 * @param values The data to add
	 * */
	public void addData(List<Float> values)
	{
		this.data.addAll(values);
		this.size = this.data.size();
	}
	
	/**
	 * Set the Data of this VBO and overwrites any existing Data
	 * 
	 * @param values The (new) VBO data
	 * */
	public void setData(List<Float> values)
	{
		this.data = values;
		this.size = data.size();
	}
	
	
	/**
	 * Sets the Type of this VBO
	 * 
	 * @param vboType The type to set.
	 * */
	public void setVBOType(int vboType)
	{
		this.type = vboType;
	}
	
	/**
	 * Uploads the data to OpenGL / GPU
	 * 
	 * @param clear Tells if the local Object Data should be cleared after loading to OpenGL
	 * */
	public void uploadToGPU(boolean clear)
	{
		FloatBuffer buffer = BufferUtils.createFloatBuffer(data.size());
		for(float f : data)
			buffer.put(f);
		buffer.flip();
		
		glBindBuffer(type, vboID);
		glBufferData(type, buffer, GL_STATIC_DRAW);
		//glBindBuffer(type, vboID);
		
		this.size = this.data.size();
		
		if(clear) this.clearAllDatas();
	}
	
	/**
	 * Uploads the data to OpenGL / GPU and clears the local Data
	 * */
	public void uploadToGPU()
	{
		this.uploadToGPU(true);
	}
	
	/**
	 * Binds this VBO
	 * */
	public void bind()
	{
		glBindBuffer(type, vboID);
	}
	
	/**
	 * Unbinds any VBOs
	 * */
	public void unbind()
	{
		glBindBuffer(type, 0);
	}
	
	/**
	 * Delete the VBO from OpenGL
	 * */
	public void release()
	{
		glDeleteBuffers(vboID);
	}
	
	/** 
	 * Deletes all local data and Deletes the VBO from OpenGL
	 * */
	@Override
	public void dispose()
	{
		this.clearAllDatas();
		this.release();
	}
	
	/**
	 * Clears all datas (Data and Attributes) of this VBO-Object. Does not affect the Data uploaded to OpenGL.
	 * */
	public void clearAllDatas(){
		this.data.clear();
		this.attributes.clear();
	}
	
	/** 
	 * Clears only the Data of the VBO and ignore the Attributes
	 * */
	public void clearData() {
		this.data.clear();
	}
	
	/** 
	 * Clears only the Attributes and ignore the Data
	 * */
	public void clearAttribs() {
		this.attributes.clear();
	}
	
	/**
	 * @return The data of this VBO-Object
	 * */
	public List<Float> getData(){
		return this.data;
	}
	
	/**
	 * @return The Type of this VBO
	 * */
	public int getType(){
		return this.type;
	}
	
	/**
	 * @return The size of the VBO
	 * */
	public int getSize()
	{
		return size;
	}
	
	/**
	 * Returns the Attributes of this VBO Object
	 *  */
	public Collection<Attribute> getAttributes()
	{
		return this.attributes.values();
	}
	
	/** 
	 * @return The ID of The VBO
	 * */
	public int getID()
	{
		return vboID;
	}
	
	
	/** 
	 * Sets The Size
	 * */
	public void setSize(int size_a)
	{
		this.size = size_a;
	}

}
```[/spoiler]



Attribute.java
[spoiler]```

package oneric.gl.buffers;

/**
 * A Data Class for VBOs. For Use with VAOs
 * */
public class Attribute {
	
	/** The number of the Attribute*/
	public final int attribute;
	/** How many Floats one element has*/
	public final int sizeOfAnElement;
	/** The Offset between 2 Elements in byte*/
	public final int offset;
	/** The Index of the first Element*/
	public final int firstElement;
	/** Tells if it should be normalized*/
	public final boolean normalized;
	
	
	/**
	 * Create a new Attribute Object by the given Arguments
	 * 
	 * @param attrib The Attribute Number
	 * @param size How many Floats have one Element (Position: 3, Texture: 2, Normal: 3, etc)
	 * @param offset The offset between the elements in byte
	 * @param first The Index of the first Element in byte
	 * */
	public Attribute(int attrib, int size, int offset, int first)
	{
		this.attribute = attrib;
		this.sizeOfAnElement = size;
		this.offset = offset;
		this.firstElement = first;
		
		this.normalized = false;
	}
	
	
	/**
	 * Create a new Attribute Object by the given Arguments
	 * 
	 * @param attrib The Attribute Number
	 * @param size How many Floats have one Element (Position: 3, Texture: 2, Normal: 3, etc)
	 * @param normalized Tells if it should be normalized
	 * @param offset The offset between the elements in byte
	 * @param first The Index of the first Element in byte
	 * */
	public Attribute(int attrib, int size, boolean normalized, int offset, int first)
	{
		this.attribute = attrib;
		this.sizeOfAnElement = size;
		this.offset = offset;
		this.firstElement = first;
		
		this.normalized = normalized;
	}

}
```[/spoiler]



Schonmal danke im Voraus :D


Mfg,
~Oneric

*** Edit ***

Ach ja die Shader und die Original Textur könnten vielleicht auch noch ganz hilfreich sein.

Vertex-Shader
[spoiler]

#version 330

uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inCoord;
layout (location = 2) in vec3 inNormals;

out vec2 texCoord;

void main()
{
gl_Position = projectionMatrixmodelViewMatrixvec4(inPosition, 1.0);
texCoord = inCoord;
}

[/spoiler]



Fragment-Shader
[spoiler]

#version 330

in vec2 texCoord;
smooth in vec3 vNormal;
out vec4 outputColor;

uniform sampler2D gSampler;

void main()
{
outputColor = texture2D(gSampler, texCoord);
}

[/spoiler]


Original-Textur:


Mfg,
~Oneric

Nachdem ich mir das ganze irgendenwann wieder angeschaut hatte fiel mir auf das ich einfach vergessen hatte auch noch für den dritten Vertex die Textur Position hinuzufügen. So waren einige werte natürlich falsch.

Mfg,
~Oneric

Bei

texture.addAll(asFloatList(mod.texture.get((int)face.texture.x - 1)));
texture.addAll(asFloatList(mod.texture.get((int)face.texture.y - 1)));
// (z fehlt)

?! Vielleicht wären sprechende Namen und ein „Int3“ oder so der Übersichtlichkeit schon zuträglich. Ich hatte es mir angesehen, und dort gedacht: „Joa, textur ist nur x,y … weiterles …“ :wink: