Nagłówek: Math.hpp
Elementy modułu: Math Module
To jest zaanwasowana biblioteka matematyczna. Definiuje typu takie jak wektor czy macierz. Elementy matematyczne nie wymagające tych typów, a jedynie operujące na zwykłych liczbach, znajdziesz w module Base. Math to największy z modułów CommonLib. Zawarte w nim elementy przeznaczone są głównie do programowania gier. Math dubluje większość funkcjonalności biblioteki D3DX z DirectX w zakresie matematyki, a także implementuje dużo innych funkcji wyższego poziomu, jak kolizje między różnymi obiektami geometrycznymi.
dot3(v1, v2) 2. VEC3::dot(v1, v2) 3. v1.dot3(v2) DECYZJA: 1 BO:PLANE plane1(v1, v2, v3); 2. PLANE plane1; CreatePlane(&plane1, v1, v2, v3); 3. PLANE plane1; plane1.Fill(v1, v2, v3); DECYZJA: 2, konstruktor tylko do bezpośredniej inicjalizacji pól bez konwersji. BO:Podstawą modułu matematycznego są różne obiekty geometryczne, z których większość posiada swoją reprezentację jako osobne struktury. Dla każdej z tych struktur zdefiniowany jest szereg przeładowanych operatorów, metod oraz funkcji globalnych, które nie są tutaj szczegółowo opisane. Każdy z nich daje się również skonwertować do/z łańcucha znaków za pomocą ujednoliconego mechanizmu SthToStr i StrToSth.
int x, y.float x, y oraz dodatkowo z i w. Jeśli zdefiniowane jest makro USE_DIRECTX, są to aliasy do typów z D3DX: D3DXVECTOR2, D3DXVECTOR3 i D3DXVECTOR4. Jeśli nie, moduł dostarcza własnej implementacji o zbliżonej funkcjonalności. Zdefiniowane jest wiele funkcji do operacji na tych wektorach, w tym iloczyn skalarny i wektorowy, obliczanie długości i odległości, normalizacja, interpolacja liniowak, rzutowanie, ortogonalizacja i inne.int left, top, right, bottom. Odpowiada strukturze RECT z WinAPI.float left, top, right, bottom. Istnieją funkcje testujące czy punkt lub inny prostokąt zawiera się w danym prostokącie, jak również liczące sumę i część wspólną dwóch prostokątów.VEC3 p1, p2.float R, G, B, A reprezentujące poszczególne składowe jako liczby zmiennoprzecinkowe w zakresie 0.0..1.0. Funkcje operujące na kolorach obejmują m.in. interpolację i konwersję do/z przestrzeni kolorystycznej HSB.A x + B y + C z + D = 0. Jej współczynniki float a, b, c, d potrafi przechowywać struktura common::PLANE. Jeśli zdefiniowane jest makro USE_DIRECTX, jest on aliasem do typu D3DXPLANE. W przeciwnym razie moduł dostarcza własnej struktury o podobnych możliwościach. Tworzenie płaszczyzny polega zazwyczaj na podaniu wektora normalnego i przykładowego punktu który do niej należy bądź podaniu trzech należących do niej punktów.A x + B y + C = 0. Do reprezentacji jej współczynników float a, b, c zdefiniowana została struktura common::LINE2D.float x, y, z, w. Nie powinien być mylony z punktem we współrzędnych jednorodnych, gdyż zachowuje się zupełnie inaczej. Dlatego reprezentowania kwaternionów utworzona została osobna struktura - common::QUATERNION. Jeśli zdefiniowane jest makro USE_DIRECTX, ten typ jest aliasem do D3DXQUATERNION. Jeśli nie, moduł dostarcza własnej implementacji o podobnej funkcjonalności. Kwaternion wykorzystywany jest w grafice 3D do opisywania dowolnej rotacji lub orientacji i jest w tym zastosowaniu lepszy niż kąty Eulera czy macierz, ponieważ nie posiada zjawiska "Gimbal Lock" oraz daje się łatwo interpolować (SLERP - ang. Spherical Linear Interpolation). Kwaternion powstaje przez podanie wektora osi obrotu i kąta obrotu wokół tej osi. Możliwe jest jednak dowolne przechodzenie między opisem rotacji przez kąty Eulera, macierz rotacji lub kwaternion. Do wszystkich tych przekształceń dostarczone są odpowiednie funkcje.VEC3 SphereCenter; float SphereRadius;.To tak naprawdę szablon funkcji, który w ogólnym przypadku nie działa (rzuca assertion failed), ale ma zdefiniowane specjalizacje. Służy do konwersji między typami tego modułu math a typami innymi matematycznymi, np. z Win32API. Ma zwykle zerowy narzut, bo dokonuje reinterpretacji, a nie żadnej konwersji. Używa się go intuicyjnie, tak samo jak normalnego rzutowania, np.:
common::POINT_ Pt(1, 2); POINT WinPt = common::math_cast<POINT>(Pt);
Zdefiniowane są następujące konwersje:
Zbiór kilkudziesięciu funkcji oznaczonych jako liczące kolizje pozwala stwierdzać o zachodzeniu na siebie brył różnego rodzaju. Praktycznie każda z tych funkcji zawiera w swojej implementacji pewien sprytny algorytm pochodzący z jakiejś książki lub publikacji internetowej, a skompletowanie biblioteki tych funkcji kosztowało wiele czasu i pracy. Wiele z nich zostalo opracowanych na podstawie Fletcher Dunn, Ian Parberry, "3D Math Primer for Graphics and Game Development", Wordware Publishing, 2002. Dokładne informacje o źródłach poszczególnych algorytmów znaleźć można w komentarzach w kodzie.
Przykładowo, funkcja common::SweptBoxToBox sprawdza kolizję poruszającego się prostopadłościanu z innym prostopadłościanem. Wykorzystuje w tym celu sumę Minkowskiego. Funkcja common::TriangleToBox sprawdza, czy trójkąt w przestrzeni 3D przecina prostopadłościan. Wykorzystuje w tym celu twierdzenie o płaszczyznach rozdzielających (ang. Separating Axis Theorem).
Punkt:
Promień, prosta, odcinek:
Prostopadłościan AABB:
Sfera:
Płaszczyzna:
Trójkąt:
Frustum:
Kolizję odcinka z dowolnym obiektem można zrobić tak: Dany jest odcinek o początku w punkcie SegOrig, kierunku SegDir i parametrze końca SegEndT oraz obiekt Coś, z którym testujemy kolizję. Użyjemy funkcji dla prostej i niezależnie czy w przypadku kolizji z tyłu punktu RayOrig (zwrócone T ujemne) zwróci miejsce kolizji bliższe czy dalsze, odcinek koliduje z obiektem, kiedy:
float T; if (PointInCoś(SegOrig, Coś)) return true; if (!LineToCoś(SegOrig, SegDir, Coś, &T)) return false; if (T < 0 || T > SegEndT) return false; return true;
Dysk Poissona (ang. Poisson Disc) to takie rozmieszczenie punktów na płaszczyźnie lub w przestrzeni, w którym pozycje tych punktów są losowe, ale żadna para punktów nie jest od siebie odległa o mniej niż określona stała granica. Taki rozkład punktów wykorzystywany bywa w różnych zastosowaniach, np. podczas próbkowania przy śledzeniu promieni (Supersampling). Ma tę zaletę ponad regularną siatką punktów, że ich losowe rozmieszczenie zapobiega zjawisku aliasingu, natomiast jego zaleta w porównaniu z rozmieszczeniem zupełnie losowym polega na nieskupianiu się punktów w miejscach o większej lub mniejszej gęstości.
Problem z zastosowaniem dysku Poissona polega na dużej złożoności obliczeniowej generowania zbioru takich punktów. Dlatego dobrze jest przygotować wcześniej tablicę wypełnioną przykładowymi punktami o tym rozkładzie. Problemem jest jednak dostosowanie ich liczby do wymagań konkretnego zastosowania.
Aby temu zaradzić, postanowiłem wygenerować zbiór punktów dysku Poissona wg następującego algorytmu: Losowane są punkty odległe od siebie nie mniej, niż o pewną dużą wartość. Po wielu nieudanych próbach dodania następnego takiego punktu wartość ta jest zmniejszana pozwalając na obecność punktów nieco bliżej siebie położonych. Proces jest powtarzany aż do wygenerowania pożadanej liczby punktów.
Takie podejście pozwala otrzymać tablicę punktów, z których można wziąć N pierwszych elementów i zawsze stanowić one będą poprawny dysk Poissona o wartości granicznej odległości tym większej, im mniejsze jest N. Dzięki temu te same tablice punktów zastosowne mogą być w różnych sytuacjach. Napisałem specjalny program generujący takie tablice za pomocą kosztownych czasowo obliczeń, a następnie zapisałem je bezpośrednio w kodzie jako zbiory 100 1-, 2- i 3-wymiarowych punktów w zmiennych odpowiednio: common::POISSON_DISC_1D, common::POISSON_DISC_2D i common::POISSON_DISC_3D. Proponuję nazwę dla takiego rozwiązania: "progresywny dysk Poissona" (ang. Progressive Poisson Disc).
1.6.1