Ich versuche, in eine .obj-Datei zu laden und zeichne es mit Hilfe von glDrawElements
.Map/Falte die multiplen .OBJ-Index-Puffer zu OpenGLs 1 Index Puffer
Jetzt, mit glDrawArrays
funktioniert alles perfekt, aber es ist - natürlich - ineffizient.
Das Problem, das ich gerade habe ist, dass eine .obj-Datei mehrere Index-Puffer (für jedes Attribut) verwendet, während OpenGL nur eine verwenden kann. Also muss ich sie entsprechend abbilden.
Es gibt viele Pseudo-Algorithmen und ich habe sogar eine C++ - Implementierung gefunden. Ich kenne ziemlich viel C++, aber seltsamerweise hat mir keiner mit meiner Implementierung in Scala geholfen.
Mal sehen:
private def parseObj(path: String): Model =
{
val objSource: List[String] = Source.fromFile(path).getLines.toList
val positions: List[Vector3] = objSource.filter(_.startsWith("v ")).map(_.split(" ")).map(v => new Vector3(v(1).toFloat,v(2).toFloat,v(3).toFloat))//, 1.0f))
val normals: List[Vector4] = objSource.filter(_.startsWith("vn ")).map(_.split(" ")).map(v => new Vector4(v(1)toFloat,v(2).toFloat, v(3).toFloat, 0.0f))
val textureCoordinates: List[Vector2] = objSource.filter(_.startsWith("vt ")).map(_.split(" ")).map(v => new Vector2(v(1).toFloat, 1-v(2).toFloat)) // TODO 1-y because of blender
val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))
val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
println(f.mkString("\n"))
val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
new Model(vertices.toArray, indices.toArray)
}
Die val indices: List[Int]
mein erster naiver Ansatz war und ist natürlich falsch. Aber beginnen wir oben:
Ich lade die Datei und gehe durch sie. (Ich nehme an, Sie wissen, wie eine .obj-Datei besteht)
Ich lese in die Vertices, Textur-Koordinaten und Normalen. Dann komme ich zu den Gesichtern.
Jetzt hat jedes Gesicht in meinem Beispiel 3 Werte v_x, t_y, n_z
, die vertexAtIndexX, textureCoordAtIndexY, normalAtIndexZ
definieren. So definieren diese jeweils einen Vertex, während ein Tripel von ihnen (oder eine Zeile in der Datei) ein Face/Polygon/Triangle definiert.
in val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
Ich versuche eigentlich Vertices (eine Fall-Klasse, die derzeit nur hält Positionen und Textur-Koordinaten und vernachlässigt Normale vorerst) zu erstellen
Das eigentliche Problem ist, diese Zeile:
val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
um die realen Indizes bekommen muss ich im Grunde diese statt von tun
val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
und val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
Pseudo-Code:
Iterate over all faces
Iterate over all vertices in a face
Check if we already have that combination of(position, texturecoordinate, normal) in our newVertices
if(true)
indices.put(indexOfCurrentVertex)
else
create a new Vertex from the face
store the new vertex in the vertex list
indices.put(indexOfNewVertex)
Doch bin ich total fest. Ich habe verschiedene Dinge ausprobiert, kann aber keine schöne und saubere Lösung finden, die tatsächlich funktioniert.
Dinge wie:
val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
und zu versuchen, f.distinct
arbeitet nicht, weil es einzigartig nichts zu verschieden, alle Einträge gibt es, das Gefühl völlig macht, wenn ich in der Datei aussehen und doch das ist, was Der Pseudo-Code sagt mir, dass ich das überprüfen soll.
Natürlich würde ich brauchen, um die Indizes entsprechend (in einem Einzeiler bevorzugt und mit vielen funktionalen Schönheit) zu füllen
Aber ich versuchen soll, um Duplikate zu finden, so ... Ich bin ein bisschen verblüfft. Ich schätze, ich verwechsle die verschiedenen "Ecken" und "Positionen" zu sehr, mit allen Referenzen.
Also, ich denke falsch, oder ist der Algorithmus/Denken richtig und ich muss nur dies in schönen, sauberen (und tatsächlich funktioniert) Scala-Code zu implementieren?
Bitte, erleuchte mich!
Per Kommentare habe ich ein kleines Update:
var index: Int = 0
val map: mutable.HashMap[(Int, Int, Int), Int] = new mutable.HashMap[(Int, Int, Int), Int].empty
val combinedIndices: ListBuffer[Int] = new ListBuffer[Int]
for(face <- faces)
{
val vID: Int = face._1-1
val nID: Int = face._2-1
val tID: Int = face._3-1
var combinedIndex: Int = -1
if(map.contains((vID, nID, tID)))
{
println("We have a duplicate, wow!")
combinedIndex = map.get((vID, nID, tID)).get
}
else
{
combinedIndex = index
map.put((vID, nID, tID), combinedIndex)
index += 1
}
combinedIndices += combinedIndex
}
wo noch steht, ist:
val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))
Fun Tatsache verstehe ich immer noch nicht offensichtlich, denn das So bekomme ich nie ein Duplikat!
Was bedeutet, dass combinedIndices
am Ende hält nur die natürlichen Zahlen wie:
ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...)
Ich weiß nicht, Scala überhaupt, also kann ich nicht sagen, ob das ist, was Sie bereits tun. Aber die Schlüsseldatenstruktur, die Sie benötigen, ist eine Map, die ein Tupel mit den ** Indizes ** der Position, Texturkoordinaten und Normal aus dem Gesichtssatz als Schlüssel und dem Index des OpenGL-Eckpunkts als Wert verwendet. Hier ist Pseudo-Code in einer älteren Antwort von mir, falls dies nicht einer der Beiträge ist, die Sie bereits in Ihren Suchen gefunden haben: http://stackoverflow.com/questions/23349080/open-l-in-dex-buffers-difficulties/23356738#23356738 . –
@RetoKoradi: Also muss ich für jedes Gesicht einen Map-Entry mit Key erstellen [positions (face._1-1), textureCoordinates (face._2-1), normals (face._2-1)]) Wie mache ich das Berechne dann den Index des OpenGL-Eckpunkts für den Wert? – Sorona
Sie beginnen bei 0 und erhöhen sie jedes Mal, wenn Sie einen neuen Scheitelpunkt benötigen (d. H. Der Schlüssel war nicht bereits in der Karte). Es sollte ziemlich klar aus dem Pseudo-Code sein, den ich verlinkt habe. –