sobota, 10 lipca 2010

Materiały osadzone na meshach

scav1 Wspominałem parę dni temu o tym, że stworzenie systemu materiałów i światła będzie dla mnie czymś nowym – nigdy przedtem nie projektowałem takiego mechanizmu (zawsze wystarczało mi to, co standardowo było oferowane) i tak naprawdę (tak, wiem, wstyd się przyznawać) nigdy nie zastanawiałem się “po co jest taki parametr i dlaczego”. A teraz już wiem.

Tak poważnie, to ważnym problemem było po prostu zdefiniowanie parametrów, jakie wchodzą w skład materiału. To akurat nie było specjalnie skomplikowane – otworzyłem przykłady dotyczące Cg, zobaczyłem, co jest przydatne, zebrałem, pogrupowałem, pomyślałem (tak, nowość) i uznałem, że i tak pewnie potem będę dodawał nowe parametry (co ma być w miarę elastyczne, zgodnie z ideą obiektowości [co i tak do końca nie wyszło]), więc nic nie szkodzi.

Jakie założenia przyjąłem odnośnie materiałoznawstwa w Scavie?8f_011 Postudiowałem trochę książkę o Cg, pracę magisterską Regedita (pozdrawiam), tutoriale Ogre’a (choć tutaj bardziej już o światło zahaczałem), powypytywałem mojego glonojada (gdzieś tu obok się kręci – nie był zbyt zainteresowany rozmową) i o to, co mi wyszło.

  • Jeden obiekt w grze (mesh) może mieć przypisanych więcej niż jeden materiał
  • Jeden materiał oznacza jedną teksturę wraz z przyległościami (reakcja na odpowiednie światło, zdolność do odbijania itd.)
  • Programista korzystający z silnika nie może aż tak bardzo przejmować się tym, jak zarządzane są materiały wewnątrz mesha (choć oczywiście powinien mieć możliwość przypisania “tak, chcę aby ta część miała taki materiał”)
  • Obsługa materiałów powinna być prosta – jako wzór powinna służyć struktura pliku OBJ (po nazwie materiału wszystkie dane korzystają właśnie z tego materiału)

W sumie to tyle. Jak widać, idea szczytna (i dosyć standardowa), ale w trzeba było to jakoś sprytnie zaimplementować. Cóż, sprytnie nie wyszło, ale za to skutecznie.

Otóż, dla każdego mesha generowane są bufory VBO, żeby rendering przebiegał szybciej, a wszystko wygląda bardzo profesjonalnie. Dlatego trzeba było coś zrobić z tymi buforami, aby można wyświetlać obiekt partiami. Czy najprostsze rozwiązanie już Wam przyszło do głowy? Owszem, stwórzmy więcej buforów.

W tej chwili informacje o wierzchołkach, zamiast w wektorach, są zapisywanie w mapach wektorów.

   1: std::map<std::string, std::vector<CVertex*> > dane;

Gdzie std::string określa nazwę materiału, dla którego mamy dane informacje. Co ciekawe, przy wydobywaniu informacji o współrzędnych z wektorów użytkownik tak naprawdę nie wie, do którego materiału należy dany punkt – według mojego toku myślenia nie powinno go to interesować, gdyż raczej nie będzie chciał zmieniać umiejscowienia materiałów w locie.

Owszem, rozwiązanie to jest nieco mniej efektywne niż można by było przypuszczać, ale wada nie jest potworna, a elastyczność i wygoda korzystania – znaczna. Spójrzmy bowiem na dodawanie informacji o danym meshu do węzła sceny o nazwie scene_node.

1: float vert[] = { 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0 };
   2: float tex[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
   3: float norm[] = { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
   4:  
   5: CStaticMesh* mesh = scene_node->AddMesh("kwadrat");
   6: CMaterial* mat = mesh->AddMaterial("drewno");
   7: mat->SetTexture(texture); // gdzie texture to wskaźnik do obiektu tekstury
   8: // te informacje, które teraz podajemy, odnoszą się do aktualnie
   9: // ustanowionego materiału o nazwie "drewno"
  10: mesh->AddVertex(vert, 4);
  11: mesh->AddTexCoord(tex, 4);
  12: mesh->AddNormal(norm, 4);
  13: mesh->SetMeshType(MT_QUADS);

A teraz zróbmy to samo wczytując mesha z pliku tekstowego (taką formę reprezentacji wybrałem na razie, pewnie potem ulegnie to modyfikacji).

   1: CStaticMesh* mesh = scene_node->AddMesh("kwadrat");
   2: mesh->Import("kwadrat.mesh"); // gdzie kwadrat.mesh to wcześniej wyeksportowany
   3: // plik z danymi, które wcześniej podaliśmy w sposób jawny w kodzie

Jak widać, przypomina to nieco inne silniki, a jednocześnie jest w miarę intuicyjne – pobieramy sobie wskaźnik na nowo utworzony obiekt i modyfikujemy go według potrzeb. W drugim przypadku nie musimy nawet zajmować się materiałem, gdyż informacja o nim znajduje się w pliku .mesh, a importer sam próbuje znaleźć plik o nazwie [nazwa_materiału].material w bieżącym katalogu i go wczytać. Jeżeli mu się nie uda – musimy sami zadbać o dostarczenie danych, ale przy dobrej organizacji nie powinno być problemu.

A co, jeżeli nie chce nam się dla mesha definiować osobno materiału? Wtedy zawsze pozostanie utworzony materiał o nazwie “default_material” (zdefiniowany pod makrem DEFAULT_MATERIAL), do którego również możemy się normalnie odwoływać.

Podsumowując, wprowadzenie materiałów troszeczkę zagmatwało sytuację z meshami, ale szczęśliwie, po wielu krzykach, nawoływaniach i różnego rodzaju motywowaniu się (i męczeniu kolegów) udało się wszystko pogodzić, żeby działało.

Teraz pozostaje implementacja światła. Czy również będzie tak “prosto” jak w przypadku materiałów? Zobaczymy.

Pozdrawiam i dziękuję - SceNtriC

0 komentarze: