Tablice trójwymiarowe

Drugie podejście do implementacji generatora świata, tym razem z wykorzystaniem tablic trójwymiarowych jako struktury przechowującej informacje o świecie.

24.11.2017 12:00 WorldGen

Od czasu kiedy opublikowałem poprzedni artykuł niestety minęło dosyć sporo. Liczyłem, że szybciej uda mi się zebrać i napisać kolejny artykuł jako, że podejście z tablicami miałem już przygotowane i czekało tylko na opisanie go, ale niestety poprzez inne obowiązki, a także brak weny odkładałem to coraz dłużej, a teraz w rzeczywistości prace posunęły się już całkiem sporo do przodu :\

Po pierwszym nieudanym podejściu nie poddałem się i od razu zabrałem się do pracy nad kolejnym pomysłem. W rzeczywistości pomysł z tablicami trójwymiarowymi był moim pierwszym pomysłem, ale ponieważ drzewa ósemkowe wydawały się być o wiele wydajniejsze i pozwalały zaoszczędzić dużo pamięci tamten pomysł został najpierw przetestowany. Jak się niestety okazało nie wszystko poszło do końca tak jak powinno i w efekcie musiałem zmienić założenie. Dokładny opis i przyczyny zmiany decyzji możesz znaleźć w poprzednim artykule.

W przeciwieństwie do drzew ósemkowych tablice są bardzo prostą strukturą, która dane przechowuje po prostu jako ciąg kolejnych wartości zapisanych jedna obok drugiej. Implementacja takiej struktury w ogóle nie była potrzebna (ponieważ język C++ już pozwala tworzyć tablice XD), natomiast zastosowanie tablic wymagało napisania całego generatora od podstaw. Być może nie byłoby to konieczne gdyby stary generator był od razu napisany z dobrą obsługą wielowątkowości. Niestety nie był, a z moich poprzednich doświadczeń wynikało, że bez wielu wątków się nie obejdzie. Dlatego właśnie postanowiłem napisać go od nowa, ale tym razem mając na uwadze to, że generowanie warto byłoby przyspieszyć poprzez wykorzystanie obliczeń równoległych (szczególnie, że każdy kawałek świata może liczyć się niezależnie, wprawdzie do czasu niestety, ale o tym napiszę w innym artykule).

Wykorzystując tablice jako struktura do przechowywania danych zyskałem bardzo dużo jeśli chodzi o szybkość dostępu do nich. Niestety każde podejście ma swoje mocne i słabe strony. Słabą stroną tego podejścia jest fakt iż wykorzystuje niestety o wiele więcej pamięci. Ponieważ każdy woksel reprezentowany jest jako liczba (identyfikator typu danego woksela) która zajmuje 2 bajty pamięci, a takich wokseli w jednym kawałku świata jest np. 1 048 576 (przy aktualnym założeniu, że kawałek świata ma boki długości 64 na 64 woksele i wysokość 256 wokseli). Z tego łatwo można policzyć, że pamięć potrzebna na przechowywanie jednego takiego kawałka świata to dokładnie 2 MB. Nie jest to niby zatrważająca ilość pamięci szczególnie, że w dzisiejszych czasach komputery mają jej naprawdę sporo, a nawet zwykły użytkownik może pozwolić sobie na duże jej ilości. Jest to jednak spore marnowanie pamięci kiedy np. cały kawałek świata wypełniony jest tylko powietrzem.

Tak jak w poprzednim przypadku po implementacji przyszła także pora na testy. Wprawdzie z tablicami nie musiałem testować za bardzo tego czy odpowiednio działają, ponieważ są bardzo prostą strukturą, ale także musiałem stworzyć coś co wizualizowałoby ich zawartość. Było to niezbędne, aby zobaczyć efekt pracy i zbadać czy ich szybkość jest wystarczająca.

W tym właśnie celu napisałem generator, który miał za zadanie generować jakiś teren (na początek nie martwiłem się za bardzo o jego realizm), a następnie wizualizował go. Tym razem od początku uważałem na wielowątkowość i została ona zaimplementowana w o wiele lepszy sposób co poprawiło stabilność działania całego programu. Generator nie tylko zapewnia generowanie terenu, ale także możliwość przemieszczania się po wygenerowanym świecie oraz zajmuje się wczytywaniem kolejnych kawałków świata. Pisanie tej części było sporym wyzwaniem, ponieważ musi to być zrobione bardzo efektywnie, aby całość działała płynnie (w miarę, oczywiście pełnej płynności nie da się uzyskać, szczególnie na słabszych konfiguracjach sprzętowych). Efektem mojej pracy był gotowy program, który pozwolił mi mniej więcej ocenić wydajność tablic trójwymiarowych.

Dla małej zachęty poniżej przedstawiam zrzut ekranu z działającego już generatora w początkowym etapie. Wprawdzie generowany teren nie jest idealny, ale nie wygląda najgorzej:

Jak się okazało testy wypadły naprawdę dobrze. Po małej zabawie z priorytetami wątków udało się uzyskać efekt całkiem płynnego wczytywania kolejnych kawałków świata. Co lepsze wczytują się one bardzo szybko i nie ma już problemu ze zbyt małą szybkością struktury w której przechowywane są dane. Ja sam jestem bardzo zadowolony z wyników testów :)

Jako podsumowanie tego artykułu mogę napisać, że tablice trójwymiarowe świetnie sprawdziły się do przechowywania informacji. Zapewniają bardzo dużą szybkość odczytu i zapisu dowolnego woksela, a do tego wcale nie zajmują ogromnych ilości pamięci. Oczywiście pamięć jaką zajmują jest polem do optymalizacji i jeśli użyć by bardziej wymyślnej struktury, która nie tylko zajmuje mniej pamięci, ale jest też szybka, mógłby być to strzał w dziesiątkę :D

Na moje potrzeby jednak ograniczę się do tablic, które są dostatecznie szybką strukturą, a dodatkowo bardzo ułatwiają pracę kiedy generowany świat ma być dynamicznie modyfikowany. W następnym artykule postaram się krótko opisać inne zagadnienia, które musiałem rozważyć podczas pisania mojego generatora. Dziękuję za Twoją uwagę i zapraszam Cię do dalszego śledzenia losów tego projektu ;)