Próbuję wczytać plik .obj i narysować go przy pomocy glDrawElements
.Mapowanie/Składanie wielu buforów .OBJ-index do bufora indeksu OpenGL 1
Teraz z glDrawArrays
wszystko działa idealnie, ale jest - oczywiście - nieefektywne.
Problem, który mam teraz, polega na tym, że plik .obj używa wielu buforów indeksowych (dla każdego atrybutu), podczas gdy OpenGL może używać tylko jednego. Muszę więc odpowiednio je odwzorować.
Istnieje wiele pseudo algorytmów i znalazłem nawet implementację C++. Znam sporo C++, ale o dziwo, nie pomogło mi to w implementacji w Scali.
Zobaczmy:
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)
}
val indices: List[Int]
było moje pierwsze naiwne podejście i oczywiście jest błędne. Ale zacznijmy od początku:
Załaduję plik i przechodzę przez niego. (Zakładam, że wiesz, jak powstaje plik .obj)
Czytam w wierzchołkach, współrzędnych tekstur i normalnych. Potem podchodzę do twarzy.
Teraz każda twarz w moim przykładzie ma 3 wartości v_x, t_y, n_z
określające vertexAtIndexX, textureCoordAtIndexY, normalAtIndexZ
. Zatem każdy z nich definiuje jeden wierzchołek, podczas gdy potrójny z nich (lub jedna linia w pliku) definiuje twarz/wielokąt/trójkąt.
w val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
I rzeczywiście starają się tworzyć wierzchołki (w indywidualnych przypadkach, klasę, która obecnie tylko utrzymuje pozycje i tekstury współrzędne i zaniedbuje normalne teraz)
prawdziwym problemem jest to linia:
val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
aby uzyskać prawdziwe indeksy I w zasadzie trzeba to zrobić zamiast z
val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
i 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)
Jednak jestem całkowicie zatrzymany. Próbowałem różnych rzeczy, ale nie mogę wymyślić ładnego i czystego rozwiązania, które faktycznie działa.
miejsca jak:
val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
i próbuje f.distinct
nie pracuje, bo nie ma nic do odrębnych, wszystkie wpisy nie są wyjątkowe, które całkowicie sens, gdy patrzę na plik i jeszcze to, co pseudo-kod nakazuje mi sprawdzenie.
Oczywiście wtedy muszę wypełnić indeksy odpowiednio (najlepiej w jednej liniowej i z dużą ilością funkcjonalnego piękna)
Ale powinienem spróbować znaleźć duplikaty, więc ... Jestem trochę oszołomiony. Chyba mieszam różne "wierzchołki" i "pozycje" za dużo, z wszystkimi odniesieniami.
Więc, myślę źle, czy algorytm/myślenie jest właściwe, a ja po prostu muszę to zaimplementować w ładnym, czystym (i faktycznie działającym) kodzie Scala?
Proszę, oświeć mnie!
Zgodnie komentarzach, zrobiłem małą aktualizację:
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
}
gdzie stoi nadal jest:
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)))
Ciekawostka ja wciąż nie rozumiejąc go oczywiście, bo to sposób, w jaki nigdy nie dostaję duplikatu!
znaczy, że combinedIndices
na końcu po prostu trzyma liczby naturalne takie jak:
ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...)
Nie znam Scala w ogóle, więc nie mogę powiedzieć, czy to już robisz. Ale kluczowa struktura danych, której potrzebujesz, to mapa, która używa krotki z ** indeksami ** pozycji, współrzędnych tekstury i normalnej z rekordu twarzy jako klucza, a indeks wierzchołka OpenGL jako wartość. Oto pseudo-kod w starszej odpowiedzi na moje pytanie, na wypadek gdyby nie było to jedno z postów, które już znalazłeś w swoich poszukiwaniach: http://stackoverflow.com/questions/23349080/opengl-index-buffers-difficult/23356738#23356738 . –
@RetoKoradi: Tak więc dla każdej twarzy muszę utworzyć Map-Entry za pomocą klawisza [position (face._1-1), textureCoordinates (face._2-1), normal (face._2-1)]). następnie obliczyć indeks wierzchołka OpenGL dla wartości? – Sorona
Zaczynasz od 0 i zwiększasz go za każdym razem, gdy potrzebujesz nowego wierzchołka (tzn. Klucz nie był już na mapie). Z pseudo kodu, który łączyłem, powinno być całkiem jasne. –