Indexberechnung bei einer "Schale"

Aha, ich habe gerade im Internet gelesen man muss vorher “Unwrappen”, das hab ich gemacht und die Textur Koordinaten werden jetzt reingeschrieben. Ich muss nur
noch machen, dass sie auch importiert werden und dann sehen wir mal, wies dann aussieht.

Ok, ich habe es jetzt fertig, nur stimmt etwas mit der Zuweisung der Normalen(falsche Beleuchtung) und Textur Koordinaten(nur bunte Striche->falsches Mappen) zu den Indexen nicht. Hier der Code, mit dem ich die .obj Datei lade:


public static Mesh loadFromFile(String path, Main main) throws IOException{
		Mesh m = new Mesh(main);
		ArrayList<Vector3f> vertics = new ArrayList<Vector3f>();
		ArrayList<Vector3f> normals = new ArrayList<Vector3f>();
		ArrayList<Integer> normalIndecies = new ArrayList<Integer>(); 
		ArrayList<Integer> indecies = new ArrayList<Integer>();
		ArrayList<Float[]> texCoords = new ArrayList<Float[]>();
		ArrayList<Integer> texCoordIndecies = new ArrayList<Integer>();
		//Hier wird erstmal der kram eingelesen
		BufferedReader reader = new BufferedReader(new InputStreamReader(Mesh.class.getClassLoader().getResourceAsStream("models/"+path)));
		String line;
		while((line = reader.readLine()) != null){
			if(line.startsWith("v ")){
				float x = Float.parseFloat(line.split(" ")[1]);
				float y = Float.parseFloat(line.split(" ")[2]);
				float z = Float.parseFloat(line.split(" ")[3]);
				vertics.add(new Vector3f(x, y, z));
			} else if(line.startsWith("vn ")){
				float x = Float.parseFloat(line.split(" ")[1]);
				float y = Float.parseFloat(line.split(" ")[2]);
				float z = Float.parseFloat(line.split(" ")[3]);
				normals.add(new Vector3f(x, y, z));
			} else if(line.startsWith("vt ")){
				texCoords.add(new Float[]{Float.parseFloat(line.split(" ")[1]), Float.parseFloat(line.split(" ")[2])});
			} else if(line.startsWith("f ")){
				Vector3f vertexIndecies = new Vector3f(Float.parseFloat(line.split(" ")[1].split("/")[0]), 
						Float.parseFloat(line.split(" ")[2].split("/")[0]), 
						Float.parseFloat(line.split(" ")[3].split("/")[0]));
				indecies.add((int) vertexIndecies.x-1);
				indecies.add((int) vertexIndecies.y-1);
				indecies.add((int) vertexIndecies.z-1);
				if(line.split(" ")[1].split("/")[1].length() != 0){
					texCoordIndecies.add(Integer.parseInt(line.split(" ")[1].split("/")[1])-1);
					texCoordIndecies.add(Integer.parseInt(line.split(" ")[2].split("/")[1])-1);
					texCoordIndecies.add(Integer.parseInt(line.split(" ")[3].split("/")[1])-1);
				}
				normalIndecies.add(Integer.parseInt(line.split(" ")[1].split("/")[2])-1);
				normalIndecies.add(Integer.parseInt(line.split(" ")[2].split("/")[2])-1);
				normalIndecies.add(Integer.parseInt(line.split(" ")[3].split("/")[2])-1);
			}
		}
		reader.close();
		reader = null;
		line = null;
		//Hier werden die Punkte in den VertexBuffer übertragen
		float[] v = new float[vertics.size()*3];
		int z = 0;
		for(int i = 0; i < v.length && z < vertics.size(); i+=3){
			v**   = vertics.get(z).x;
			v[i+1] = vertics.get(z).y;
			v[i+2] = vertics.get(z).z;
			z++;
		}
		m.vertexBuffer = Main.putFloatCoords(v);
		m.vboID = Main.createVBO(m.vertexBuffer, m.vboID);
		//Hier werden die Normalen, nach den normalIndexen geordnet(da ist ein Fehler, weils falsch beleuchtet ist)
		float[] n = new float[normals.size()*3];
		int t = 0;
		for(int i = 0; i < n.length; i += 3){
			n**   = normals.get(normalIndecies.get(t)).x;
			n[i+1] = normals.get(normalIndecies.get(t)).y;
			n[i+2] = normals.get(normalIndecies.get(t)).z;
			t++;
		}
		//Hier werden die Indexe in den IndexBuffer gepackt
		int[] i = new int[indecies.size()];
		for(int a = 0; a < indecies.size(); a++){
			i[a] = indecies.get(a);
		}
		m.indexBuffer = Main.putIntCoords(i);
		//Hier werden die Textur-Koodinaten geordnet
		//Da muss was falsch sein, denn die textur wird völlig falsch gemappt
		float[] tc = new float[texCoords.size()*2];
		int l = 0;
		for(int g = 0; g < tc.length; g+=2){
			tc[g] = texCoords.get(texCoordIndecies.get(l))[0];
			tc[g+1] = texCoords.get(texCoordIndecies.get(l))[1];
		}
		m.texCoordBuffer = Main.putFloatCoords(tc);
		m.tboID = Main.createVBO(m.texCoordBuffer, m.tboID);
		m.normalBuffer = Main.putFloatCoords(n);//m.calcNormals();
		m.nboID = Main.createVBO(m.normalBuffer, m.nboID);
		m.initVAO();
		//Hier lösche ich alles(Vorsichtshalber so viel, weil ich RAM sparen will)
		vertics.clear();
		vertics = null;
		indecies.clear();
		indecies = null;
		texCoords.clear();
		texCoords = null;
		normals.clear();
		normals = null;
		normalIndecies.clear();
		normalIndecies = null;
		texCoordIndecies.clear();
		texCoordIndecies = null;
		v = null;
		n = null;
		i = null;
		tc = null;
		m.mode = GL_TRIANGLES;
		return m;
	}

nur um sicher zu gehen, poste mal eine beispiel obj… mit der es nicht funktioniert
dann kann ich es damit testen, ist gerade einfacher

[ot]
Nochmal…bei sowas wie


    texCoordIndecies.add(Integer.parseInt(line.split(" ")[1].split("/")[1])-1);
    texCoordIndecies.add(Integer.parseInt(line.split(" ")[2].split("/")[1])-1);
    texCoordIndecies.add(Integer.parseInt(line.split(" ")[3].split("/")[1])-1);

spritzt mir so viel Blut aus den Augen, dass ich mich schlicht weigere, das in [ java <3 ]-Tags zu setzen…
[/ot]

Ganz davon abgesehen das es in Java so was wie Methoden und Klassen gibt. Man muss das nicht alles in einem Krautsalat von Zeilen hintereinander schreiben.

Wenn Dir der OBJ Reader von Marco zu objektorientiert ist, kannst Du dir auch eine meiner Varianten ansehen. Die sollte einfach zu lesen sein.

Gruß
Fancy

[OT]Ja, also ich sehe nicht wirklich was gegen diesen Codeschnipsel zu sagen ist, höchstens, dass da alles in drei Zeilen gepackt ist, aber ich denke es ist sinnfrei das in der Zeile ausgelesene erst in ein eigenes Objekt zu packen, oder so etwas.[/OT]
@Fancy Ich würde nur ungern eine vorgefertigte Klasse zum laden benutzen und das lieber selbst schreiben, ist ja auch nicht soo schwer und ich sehe keinen Sinn darin alles in verschiedene Klassen outzusoucen, wen ich es auch schön übersichtlich in eine Methode schreiben kann.

@mymaksimus Diese .obj Datei funktioniert nicht:


# Blender v2.74 (sub 0) OBJ File: ''
# www.blender.org
mtllib test.mtl
o Cube
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 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.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
usemtl Material.001
s off
f 2/1/1 3/2/1 4/3/1
f 8/1/2 7/2/2 6/3/2
f 5/1/3 6/2/3 2/3/3
f 6/1/4 7/2/4 3/3/4
f 3/4/5 7/1/5 8/2/5
f 1/1/6 4/2/6 8/3/6
f 1/4/1 2/1/1 4/3/1
f 5/4/2 8/1/2 6/3/2
f 1/4/3 5/1/3 2/3/3
f 2/4/4 6/1/4 3/3/4
f 4/3/5 3/4/5 8/2/5
f 5/4/6 1/1/6 8/3/6

Quellcode ist dazu da um gelesen zu werden. Entsprechend sollst Du meine Klasse nicht verwenden, sondern hauptsächlich lesen und verstehen. Je offensichtlicher das ist was im Quellcode passiert, desto besser ist der Quellcode.

Überlege mal ob Du in einem halben Jahr noch weißt, was die 3 Zeilen die Marco zitiert hat bedeuten. Und ob Du die Bedeutung der einzelnen Indices dann noch als offensichtlich empfindest. Oder ob ein Außenstehender wohl Lust hat Quellcode zu lesen den er nicht sofort versteht?

Hier gibt es eine ganze Reihe von Nutzern, die es als erstrebenswert ansehen, wenn eine Methode genau eine Sache erledigt und aus maximal 3 - 7 Anweisungen besteht. Alleine das Du Inline- Kommentare eingefügt hast, ist ein sicheres Zeichen dafür, das Deine Methode zu viel macht.

Was ich in deinem Code z.B. gar nicht sehe ist wo die Abbildung der 3 OBJ Indices auf den einen OpenGL Index passiert, also z.B. 2/1/1 -> 0. Bei mir im Beispiel ist das in der Map<String, Integer> f gekapselt. Bei Dir sehe ich weder eine Map noch etwas vergleichbares. Entweder hast Du das nicht berücksichtigt (schlecht) oder es ist nicht offensichtlich wo das passiert (schlecht).

(Auch die Variante die ich gepostet habe kann in der Lesbarkeit noch verbessert werden.)

Gruß
Fancy

Ich weiß jetzt nicht genau, was du meinst, dass die obj Indices auf den OpenGL Index abgebildet werden, die Vertex-Indexe bau ich in einen Indexbuffer direkt(mit -1 natürlich) ein, und die anderen beiden jeweils in eine ArrayList. Das passiert hier:


else if(line.startsWith("f ")){
				Vector3f vertexIndecies = new Vector3f(Float.parseFloat(line.split(" ")[1].split("/")[0]), 
						Float.parseFloat(line.split(" ")[2].split("/")[0]), 
						Float.parseFloat(line.split(" ")[3].split("/")[0]));
				indecies.add((int) vertexIndecies.x-1);
				indecies.add((int) vertexIndecies.y-1);
				indecies.add((int) vertexIndecies.z-1);
				if(line.split(" ")[1].split("/")[1].length() != 0){
					texCoordIndecies.add(Integer.parseInt(line.split(" ")[1].split("/")[1])-1);
					texCoordIndecies.add(Integer.parseInt(line.split(" ")[2].split("/")[1])-1);
					texCoordIndecies.add(Integer.parseInt(line.split(" ")[3].split("/")[1])-1);
				}
				normalIndecies.add(Integer.parseInt(line.split(" ")[1].split("/")[2])-1);
				normalIndecies.add(Integer.parseInt(line.split(" ")[2].split("/")[2])-1);
				normalIndecies.add(Integer.parseInt(line.split(" ")[3].split("/")[2])-1);
			}

PS: Ich habe mir diesen Code, den Fancy verlinkt hat mal angesehen und probiert, aber die Textur wird zwar besser, aber immer noch nicht wie in Blender draufgemappt.

[ot]
Auch wenn das als “premature” angesehen werden kann, dachte ich bei den Zeilen nicht zuletzt daran, dass Java ja schrecklich langsam ist. Wie viele Millionen mal da (überflüssigerweise!) “split” aufgerufen wird, will ich gar nicht wissen. Ansonsten, zum Stil, hatte ich ja schon in http://forum.byte-welt.net/java-forum-das-java-welt-kompetenz-zentrum-wir-wissen-und-helfen-/spiele-und-multimedia-programmierung/16323-licht-opengl-3.html#post119101 was gesagt…
[/ot]

In OpenGL hast Du für jeden Vertex die Vertexattribute (z.B.: Koordinaten, Texturkoordinaten, Normale) und einen (gemeinsamen) Index auf diese Daten. Ein Dreieck besteht also aus 3 Indices.

In einer Obj Datei hast Du für die einzelnen Attribute (V, VT, VN) einzelne Indices. Ein Dreieck besteht also aus 9 Indices.

Wenn Du dir dein OBJ Beispiel ansiehst, fällt doch auch sofort auf, dass das ein Würfel ist. Da sind 8 Vertexkoordinaten, 4 Texturkoordinaten und 6 Normale. Ein Würfel (texturiert und beleuchtet, aus Dreiecken) benötigt in OpenGL aber eine Folge von 36 Indices (6 Seiten mit jeweils 6 Indices.) und einer Folge von 192 Attributen (6 Seiten mit 4 Ecken und jeweils V, VT, VN = 192).

Du musst also jeweils 3 Obj Indices auf einen OpenGL Index abbilden. Dabei musst Du dann die Vertexattribute entsprechend vervielfältigen.

In Deinem Beispiel besteht der erste Vertex (und damit der OpenGL Index 0) aus den Obj Indices 2/1/1. Der zweite Vertex (und damit der OpenGL Index 1) aus den Obj Indices 3/2/1 usw.

Bei mir funktioniert der genauso wie er soll: Gist (lol, das KSKB ist zu lang fürs Forum :wink: )

@Marco13 : Ja, das split fand ich auch schräg. :wink:

Gruß
Fancy

Die Normalen sind mit dieser Klasse auch richtig und der Würfel funktioniert auch. Nur bei dem etwas komplizierteren Mesh (leider ist die .obj Datei zu groß zum hier zeigen) wird die Textur nicht so, wie in Blender gezeigt, das kann entweder daran liegen, dass Blender sie falsch exportiert, oder dass die Textur Koordinaten falsch geladen werden.
In Blender:

und so in meinem Programm:

Das sieht schon gar nicht sooo falsch aus - als würde nur irgendwo ein Skalierungsfaktor nicht stimmen. Fancy wird da ggf. was genaueres dazu sagen können. Ggf. kannst du die OBJ als ZIP als Anhang hier einfügen (soooo riesig sollte sie ja nicht sein)

Hier ist die .obj Datei in einer Zip(nicht wundern, es sind 2 .obj Dateien, einfach beide zeichnen)

Für mich sieht das aus als ob mindestens einer der Texturindexe im Shader entweder außerhalb von (0 <= x <= 1) liegt (weil sie sich immer wieder wiederholt, wurde texture repeat beim texture laden eingestellt? Oder clamp?) oder als wie wenn jedes Face die selben Texturkoordinaten bekommen hätte.
Das Holz sieht doch spitze aus.

Ja, so ziemlich jedes Face scheint dieselben Koordinaten zu haben. Außerdem ist die Normale für den oberen Rand der Schüssel falsch.

(Die Textur ist 4x4 Pixel mit GL_CLAMP_TO_EDGE und GL_NEAREST)

Gruß
Fancy

Beim Texturladen ist GL_REPEAT eingestellt, das habe ich aber in Blender auch gesagt. Und wenn ich GL_CLAMP_TO_EDGE und GL_NEAREST einstelle sieht’s genauso aus. Das Holz sieht so schon gut aus, nur sollen diese Stamm-Enden auf den Endflächen der Zylinder sein.Und was ist dieser “Schlüssel”? Meine Textur ist üprigens 500x500, und es gibt keine Möglichkeit sie so drastisch runterzuskalieren, wie Fancy.

Die Texturgröße und Parameter hab ich angegeben, damit man auf dem Bild abschätzen kann wie die Texturkoordinaten liegen.

Irgendwas machst Du halt “anders” wenn Du deine Schüssel in Blender erzeugst. Wenn ich auf die Schnelle eine Schüssel dengel und einfach smart UV unwarp machen lasse, liegt die Textur über die ganze Schüssel und nicht separat über jedes einzelne Face. Aber wie Marco schon schrieb, es gibt unzählige Freiheitsgrade wie die Textur auf die Geometrie gelegt werden kann.

Gruß
Fancy

Aber in Blender wird die Textur doch richtig angezeigt und Blender exportiert doch dann genau diese Textur Koordinaten, die es beim Rendern verwendet. dann verstehe ich nicht, wo der Fehler liegen kann.

Wie sieht das den bei Dir im UV/Image Editor aus? Siehst Du dort wie die einzelnen Faces auf der Textur liegen?

Gruß
Fancy

Diesen Editor verstehe ich nicht so ganz. Wenn ich ihn Aufrufe erscheint nur ein leeres Gitter: