Rok akademicki 2012/2013 Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych Instytut Informatyki

PRACA DYPLOMOWA INŻYNIERSKA

Piotr Bałut

DuctilePhysics - silnik fizyczny 2D wspierający deformację obiektów

Opiekun pracy dr hab. inż. Tomasz Martyn

Ocena: ...... Podpis Przewodniczącego Komisji Egzaminu Dyplomowego Specjalność: Inżynieria Systemów Informatycznych

Data urodzenia: 1988.02.16

Data rozpoczęcia studiów: 2008.10.01

Życiorys

Urodziłem się 16.02.1988 roku w Białej Podlaskiej. W latach 2004-2007 uczęszczałem do Katolickiego Liceum Ogólnokształcącego im. Cypriana Norwida w Białej Podlaskiej. W lutym 2008 roku rozpocząłem studia na Wydziale Elektroniki i Technik Informacyjnych Politechniki Warszawskiej, w toku studiów obierając specjalizację Inżynieria Systemów Informatycznych. W październiku 2011 roku rozpocząłem pracę jako programista w firmie Comarch S.A. Interesuję się komputerami, modelarstwem, gry planszowymi i bitewnymi, a także literaturą – w szczególności fantastyką i fantastyką naukowa.

...... Podpis studenta

EGZAMIN DYPLOMOWY

Złożył egzamin dyplomowy w dniu ...... 20__ r z wynikiem ......

Ogólny wynik studiów: ......

Dodatkowe wnioski i uwagi Komisji: ......

......

......

2

STRESZCZENIE

Streszczenie pracy w języku polskim.

Praca przedstawia sposób implementacji dwuwymiarowego silnika fizycznego przeznaczonego do przeprowadzania symulacji w czasie rzeczywistym, posiadającego wsparcie zarówno dla brył sztywnych, jak i ulegających deformacjom ciał miękkich. Praca przedstawia ogólną budowę silnika fizycznego oraz przedstawia różne podejścia i algorytmy stosowane podczas budowy silników fizycznych. M.in. przedstawia podejścia do symulacji ciał miękkich oraz wyzwania związane z ich implementacją w porównaniu do implementacji silnika obsługującego tylko bryły sztywne. Zaprezentowano opis implementacji poszczególnych modułów silnika fizycznego, takich jak całkowanie równań ruchu, detekcja i odpowiedź na kolizje, a także schematyczny projekt implementacji silnika. Szczególny nacisk położony jest na opis integracji symulacji brył sztywnych z symulacją ciał miękkich.

Słowa kluczowe: silnik fizyczny, 2d, masy i sprężyny, ciała miękkie, bryły sztywne

DuctilePhysics - 2D with body deformation support

Summary in English.

The thesis describes implementation process of 2D physics engine, dedicated to performing real-time with support for both rigid and soft bodies. Methods and algorithms useful during physics engine implementation and soft body dynamics implementation are presented. Differences between pure rigid body simulation and simulation consisting of both soft and rigid bodies are demonstrated. Moreover the document includes a description of implementation of modules that physics engine is built of, namely equation of motion integrator, subsystem and collision response subsystem. The emphasis is put on describing how the soft and rigid body are integrated with each other.

Keywords: physics engine, 2d, masses and springs, soft body, rigid body

3

Spis treści

1. Wstęp ...... 7

1.1. Wprowadzenie ...... 7

1.2. Cel pracy ...... 8

1.3. Istniejące rozwiązania ...... 9

2. Budowa silnika fizycznego ...... 11

2.1. Modelowanie ciał fizycznych ...... 11

2.2. Całkowanie równań ruchu ...... 13

2.3. Wykrywanie kolizji ...... 13

2.3.1. Założenia odnośnie działania systemu wykrywania kolizji ...... 13

2.3.2. Reprezentacja geometryczna ciał ...... 15

2.3.3. Działanie systemu wykrywania kolizji ...... 15

2.4. Odpowiedź na kolizje ...... 19

3. Opis implementacji ...... 22

3.1. Założenia projektowe ...... 22

3.2. Ogólny rys implementacji silnika ...... 23

3.2.1. Organizacja plików źródłowych i klas ...... 23

3.2.2. Kolejność przetwarzania danych ...... 24

3.2.3. Schemat modułów silnika ...... 25

3.3. Reprezentacja ciał ...... 28

4

3.3.1. Geometria ciała ...... 29

3.3.2. Fizyczny model ciała ...... 31

3.4. Implementacja algorytmu SAP ...... 33

3.5. Implementacja wykrywania kolizji na bazie SAT ...... 34

3.6. Impulsowa odpowiedź na kolizje ...... 36

3.7. Grupowanie punktów kontaktowych ...... 37

3.7.1. Model zderzeń ciał ...... 37

3.8. Korekcja położenia ciał ...... 39

3.9. Interfejs biblioteki ...... 40

3.9.1. Klasa DuctilePhysics ...... 40

3.9.2. Ciała ...... 42

3.9.3. Generowania szablonów ciał ...... 44

3.10. Program testowy ...... 46

4. Testowanie ...... 48

4.1. Funkcje programu testowego ...... 48

4.2. Interfejs programu testowego ...... 49

4.3. Sceny testowe ...... 52

5. Podsumowanie i wnioski ...... 53

5.1. Ocena implementacji ...... 53

5.2. Perspektywy rozwoju ...... 54

5

6. Bibliografia ...... 56

7. Spis rysunków ...... 57

8. Spis załączników ...... 57

6

1. Wstęp

1.1. Wprowadzenie

Symulowanie fizyki jest niezbędne w wielu gałęziach współczesnego przemysłu, badań naukowych oraz elektronicznej rozrywki. W dziedzinach tych stosowane są różnorakie silniki fizyczne – systemy, których rolą jest odzwierciedlenie zdarzeń znanych ze świata rzeczywistej fizyki w komputerowej symulacji.

W zależności od dziedziny konstruowane są silniki przeznaczone do wykonywania symulacji różniących się szczegółowością, rodzajem symulowanych obiektów oraz działające w warunkach nakładających odmienne ograniczenia czasu przeznaczonego na obliczenia fizyczne niezbędne do uzyskania wyniku. Najbardziej ogólny podział silników fizycznych jest związany ściśle z dokładnością rezultatów symulacji i czasem; możemy wyróżnić silniki wysokiej precyzji (naukowe) oraz silniki interaktywne (czasu rzeczywistego).

Silniki fizyczne wysokiej precyzji charakteryzują się wymogiem dużej dokładności symulacji. Czas odgrywa tu drugorzędną rolę – ważne jest to, aby rezultat symulacji był możliwie najbardziej zbliżony do tego, co można zaobserwować i zmierzyć w świecie rzeczywistym. Za przykład podać tu można np. silniki fizyczne przeznaczone do symulowania ruchu cząsteczek, silniki przeznaczone do analizy materiałów w przemyśle, prognozowania pogody lub tworzenia realistycznej animacji bazującej na fizyce w filmach.

Drugą wyróżnioną kategorią są silniki interaktywne. W nich precyzja odgrywa drugorzędną rolę, ponieważ istotny jest czas generowania wyniku, a nie jego dokładność. Do tej kategorii należą przede wszystkim silniki wykorzystywane w grach komputerowych oraz animacji komputerowej generowanej w czasie rzeczywistym. Czas przeznaczony na wygenerowanie pojedynczej klatki w tej kategorii silników bywa bardzo krótki, rzędu kilku(nastu) milisekund. Biorąc pod uwagę czasochłonność dokładnych obliczeń fizycznych wymogiem w takim przypadku jest zbliżenie się do zachowania rzeczywistych obiektów na tyle, aby potencjalny odbiorca uznał wynik za przekonywujący.

Niezależnie od wyboru powyższej kategorii każdy silnik przeznaczony jest do symulacji określonego typu obiektów fizycznych. Silniki wysokiej precyzji mogą modelować

7

wspomniane wcześniej zachowanie cząsteczek elementarnych lub dokładne zachowanie materiałów lub całych ciał pod wpływem obciążeń, temperatury i innych parametrów. Silniki interaktywne są z natury mniej dokładne – zazwyczaj modelują zachowanie punktów materialnych (np. efekty cząsteczkowe) lub ciał stałych odwzorowanych pod postacią brył sztywnych. W związku ze stale rosnącą wydajnością komputerów w ostatnich latach silniki interaktywne coraz częściej symulują zachowanie ciał miękkich, to znaczy ciał, które podlegają różnym odkształceniom.

1.2. Cel pracy

W dziedzinie interaktywnych silników fizycznych na dzień dzisiejszy dostępnych jest wiele rozwiązań, oferujących różne możliwości i podchodzących do zagadnienia symulacji w różny sposób. Mimo to większość silników przeznaczonych do wykorzystania w grach komputerowych koncentruje się na symulowaniu dynamiki brył sztywnych. Istnieją wprawdzie silniki wspierające symulowanie ciał miękkich, jednak przeznaczone są one zazwyczaj do przeprowadzania symulacji trójwymiarowych – istnieje deficyt tego typu rozwiązań działających w dwóch wymiarach. Co więcej nawet w przypadku silników trójwymiarowych znaczną część z nich stanowią silniki komercyjne, o zamkniętym kodzie źródłowym.

Ciała miękkie, mogące ulegać różnym odkształceniom, w naturalny sposób mogą wzbogacić symulację fizyczną. Rozszerzenie „standardowego” repertuaru brył sztywnych popularnych w silnikach fizycznych przeznaczonych do wykorzystania w grach komputerowych o ciała ulegające deformacjom może dać okazję do urozmaicenia rozgrywki poprzez wprowadzenie nowych typów interaktywnych obiektów, czy też wzbogacenie animacji zniszczeń obiektów w grze.

Celem mojej pracy jest stworzenie silnika fizycznego odpowiedniego do wykorzystania w dwuwymiarowej grze komputerowej i charakteryzującego się wsparciem zarówno brył sztywnych, jak i ciał miękkich. Ciała miękkie powinny być w pełni zintegrowane z silnikiem, zaś ich wykorzystanie powinno być wygodne.

8

1.3. Istniejące rozwiązania

Jednym z najpopularniejszych interaktywnych dwuwymiarowych silników fizycznych posiadającym licencję open source i świetnie nadającym się do wykorzystania w grze komputerowej jest . Oferuje on dużą szybkość działania, zaś zaproponowane przez jego autora rozwiązania wykorzystywane są w wielu innych silnikach fizycznych (np. Physics Engine). Nie obsługuje on jednak ciał miękkich – operuje tylko na bryłach sztywnych. Jednak w tej dziedzinie dzięki swojej otwartości stanowi bardzo dobry materiał referencyjny, którym częściowo inspirowałem swoje rozwiązania.

Innym ciekawym dwuwymiarowym silnikiem, tym razem wspierającym ciała miękkie, jest Dax Phyz. Symulacja przebiega w nim na zasadzie modelowania ciał jako zbioru bardzo wielu oddziałujących ze sobą mikrocząsteczek. Jest to nietypowe podejście dające ciekawe efekty, jednak brak wsparcia dla brył sztywnych sprawia, że silnik nie nadaje się do wykorzystania w większości gier, wymagających także obecności ciał nie zmieniających swojego kształtu.

W pakiecie aplikacji KDE dostępny jest również otwarty symulator fizyczny Step, którego jedną z funkcji jest symulowanie ciał miękkich za pomocą systemu mas i sprężyn. Częścią projektu jest również samodzielny silnik fizyczny StepCore, jednak całość zorientowana jest bardziej na obrazowanie procesów fizycznych niż działanie jako silnik fizyki w grze komputerowej. Wiąże się to z koncentracją na możliwości mierzenia różnych parametrów symulacji i modularności wszystkich elementów silnika kosztem jego wydajności.

Większość interaktywnych silników wspierających ciała miękkie przeznaczona jest do przeprowadzania symulacji środowisk trójwymiarowych. Prym wiodą tu silniki komercyjne – praktycznie każdy liczący się silnik wspiera obecnie deformacje ciał. Za przykład podać można tu znane w świecie gier silniki PhysX i , a także te znane trochę mniej, jak . Istnieją również otwarte silniki wspierające fizykę ciał miękkich, jak na przykład . Różnią się one podejściem do modelowania ciał miękkich – Bullet używa systemu punktów materialnych połączonych sprężynami, DMM używa algorytmów opartych na MES, zaś PhysX autorskiego algorytmu modelującego deformacje ciał, który twórcy opisują jako pośredni między dwoma wspomnianymi rozwiązaniami.

9

Opis budowy silników fizycznych oraz ich elementów znaleźć można również w literaturze. Książki skupiają się jednak głównie na aspekcie tworzenia silnika symulującego bryły sztywne lub symulującego jedynie zachowanie punktów materialnych (z ewentualnymi ograniczeniami wzajemnego położenia). Techniki łączenia tych metod nie są dobrze opisane. Mimo to bardzo przydatną pozycją okazał się [1].

10

2. Budowa silnika fizycznego

Symulowanie fizyki przez program komputerowy może być zrealizowane na różne sposoby, w zależności od przyjętych założeń i celu symulacji. Celem pracy jest implementacja silnika obsługującego bryły sztywne i ciała miękkie, tak więc silnik musi obsługiwać dynamikę tych brył oraz ich wzajemne interakcje. Wygodnym i szeroko stosowanym rozwiązaniem jest wydzielenie kilku faz w przeprowadzanej symulacji:

 całkowania równań ruchu, w której symulowane są przesunięcia i obroty ciał;  wykrywania kolizji między ciałami, w której wykrywane są nałożenia brył opisujących ciała;  fazę odpowiedzi (reakcji) ciał na kolizję, w której zachodzi interakcja między kolidującymi ciałami.

2.1. Modelowanie ciał fizycznych

Założenia pracy wymagają reprezentacji w symulacji dwóch typów ciał – stałych (brył sztywnych) i deformowalnych (ciała miękkie). Matematyczny model dynamiki bryły sztywnej jest prosty i sprowadza się zasadniczo do rozwiązania równań ruchu liniowego i obrotowego tej bryły [1]. Ruch liniowy bryły sztywnej przekłada się bezpośrednio na ruch punktu będącego jej środkiem ciężkości. Również ruch obrotowy odbywa się w takiej bryle wokół osi wyznaczanej przez środek ciężkości.

W kategorii ciał miękkich stosowane są bardzo różne rozwiązania, różniące się precyzją, łatwością implementacji oraz szybkością działania. Wyróżnić można między innymi następujące metody [2]:

 Wykorzystanie układu punktów materialnych połączonych sprężynami i nakładających tym samym ograniczenia na wzajemne położenie tych sprężyn. Znaczną zaletą tego rozwiązania jest szybkość i prostota implementacji. Równania ruchu rozwiązywane są dla każdego punktu materialnego z osobna, z uwzględnieniem sił pochodzących z łączących je sprężyn. Wadą rozwiązania jest natomiast trudność strojenia parametrów sprężyn oraz mały realizm ciał przybliżonych takim układem. Metoda ta jest stosowana już od pewnego czasu, z dużymi sukcesami zwłaszcza na polu symulacji zachowania

11

tkanin. Ze względu na swoje parametry dobrze nadaje się do implementacji w silniku czasu rzeczywistego [1][9].  Wykorzystanie metody elementów skończonych do symulowania ciał. Ta metoda posiada wady i zalety odmienne niż te charakteryzujące metodę wymienioną powyżej: z jednej strony pozwala wiernie oddać rzeczywistość, z drugiej zaś jest bardzo wymagająca obliczeniowo i trudniejsza w implementacji. Duże wymagania odnośnie mocy obliczeniowej przez długi czas dyskwalifikowały tę metodę w silnikach czasu rzeczywistego. Jednak mająca miejsce ostatnio popularyzacja delegacji obliczeń z procesora głównego do procesora graficznego umożliwiła wykorzystanie tej metody w silnikach fizycznych na których bazują gry komputerowe [3].  Kolejnym, rzadziej spotykanym podejściem może być oparcie deformacji ciał na metodzie bazującej na minimalizacji energii. Polega ona na obliczaniu energii powierzchni ciała wynikającej ze zmiany powierzchni względem stanu pierwotnego, a następnie obliczaniu sił wewnętrznych działającej na ciało w wyniku energii wynikającej z odkształceń. Również ta metoda jest skomplikowana i stosunkowo wymagająca obliczeniowo [4].  Innym rozwiązaniem jest metoda polegająca na badaniu odkształcenia ciała od ciała wzorcowego i jego modyfikacja tak, aby powrócił do kształtu pierwotnego. Rozwiązanie to może dawać dobre wyniki, jest umiarkowanie wymagające obliczeniowo i implementacyjnie, jednak strojenie układów zbudowanych na podstawie tej metody jest trudne [5].

Po rozważeniu wszystkich metod zdecydowałem się na wybór pierwszej z nich, to jest implementację ciał miękkich bazowanych na układzie punktów materialnych połączonych sprężynami. Jej największą zaletą jest prędkość, zaś odpowiednia implementacja może dać wyniki przekonywujące wizualnie. Ewentualny zysk w precyzji symulacji nie rekompensuje większych wymogów odnośnie mocy obliczeniowej i zwiększonej trudności implementacji w przypadku użycia drugiej, trzeciej lub czwartej z wymienionych metod. W porównaniu do czwartej z omawianych metod nie daje też zysku w postaci łatwiejszego strojenia układu – wręcz przeciwnie.

12

2.2. Całkowanie równań ruchu

Na tym etapie symulacji dokonywane są przesunięcia i obroty wszystkich ciał zgodnie z posiadanymi przez nie prędkościami, rozpatrywane są ich przyspieszenia i oddziałujące na nie siły. Ciała miękkie uginają i deformują się właśnie na tym etapie. Każde ciało rozpatrywane jest niezależnie od pozostałych, wszystkie interakcje między symulowanymi obiektami fizycznymi rozpatrywane są na pozostałych etapach symulacji.

Na tym etapie kluczowy jest wybór schematu numerycznego całkowania równań ruchu. Wybierając metodę niższego rzędu zwiększamy prędkość rozwiązania, za to zmniejszamy jego precyzję i zwiększamy błąd kumulujący się wraz z upływem czasu. Wyszczególnić można następujące opisywane w literaturze metody [6] [7]:

 podstawowa metoda Eulera (pierwszego rzędu);  algorytm prędkościowy Verleta (drugiego rzędu);  algorytm Rungego-Kutty (wariant RK4 – czwartego rzędu).

Zdecydowałem się na implementację algorytmu prędkościowego Verleta. Nie jest on tak dokładny jak algorytmy czwartego rzędu, w tym popularny RK4, jednak wystarczająco dokładny dla potrzeb symulacji odpowiadającej grze komputerowej. Błąd przy zmiennym przyspieszeniu jest w nim akceptowalny. Poza tym w długotrwałych symulacjach nie zachowuje wprawdzie energii w sposób idealny, ale nie zmienia jej również w sposób znaczny – ogólna energia układu waha się wokół idealnej wartości.

2.3. Wykrywanie kolizji

2.3.1. Założenia odnośnie działania systemu wykrywania kolizji

Symulacja komputerowa fizyki ma z natury postać dyskretną – kolejne kroki symulacji wykonujemy z pewnym krokiem czasowym. Mimo istnienia kroku czasowego, w zależności od przyjętych założeń, silnik fizyczny możne zrezygnować z dyskretnego podejścia do symulacji i symulować ciągły upływ czasu. Różnica widoczna jest przede wszystkim podczas określania kolizji między ciałami.

13

Tak więc wykrywanie kolizji możemy podzielić właśnie na dyskretne i ciągłe. Podejście dyskretne (a posteriori) cechuje się wykrywaniem kolizji po fakcie ich wystąpienia. Podejście ciągłe (a priori) charakteryzuje się natomiast wykrywaniem kolizji dokładnie w momencie zderzenia dwóch ciał. Dostępne silniki wybierają różne z tych dwóch dróg, jednak wybór którejkolwiek ma duży wpływ na budowę i możliwości silnika [8].

Zasadniczą konsekwencją wyboru podejścia a posteriori jest to, że symulowane ciała mogą na siebie nachodzić. Nie jest badana trajektoria poruszania się ciał i nie jest istotny dokładny czas zderzenia dwóch ciał. Co krok czasowy wykrywany jest tylko stan - czy dane ciała kolidują ze sobą. Dodatkowo w celu zapewnienia wizualnej wiarygodności wszystkie kolizje muszą zostać skorygowane tak, aby penetracja ciał nie zachodziła. Podejście to niesie za sobą jeszcze jedno zagrożenie – system detekcji kolizji może „przegapić” kolizję między dwoma ciałami, jeżeli jedno z nich porusza się bardzo szybko. W takim wypadku ciało A, które podczas pierwszej aktualizacji stanu ciał znajdowało się po jednej stronie ciała B, może poruszając się z bardzo dużą prędkością znaleźć się po drugiej stronie ciała B, niejako je przeskakując.

Podejście a priori pozbawione jest powyższych zagrożeń. Przenikanie i nakładanie się ciał nie występuje przy założeniu wykrywania zderzenia ciał w momencie zaistnienia kolizji. Pozytywnie rzutuje to na wizualną jakość symulacji z ciągłym podejściem do wykrywania kolizji – ewentualne korekcje położeń ciał przy użyciu detekcji dyskretnej zmniejszają fizyczną dokładność silnika.

Pomimo zalet podejścia ciągłego jego wadą jest trudna implementacja i znaczne zwiększenie niezbędnych do wykonania obliczeń w stosunku do podejścia dyskretnego. Zastosowanie ciał miękkich dodatkowo komplikuje implementację algorytmu a priori – śledzenie trajektorii ciał zmieniających kształt jest o wiele trudniejsze niż śledzenie samych brył sztywnych.

W związku z tym ostatecznie wybrałem wariant wykrywania kolizji a posteriori. W praktyce zastosowanie niewielkich kroków czasowych i ograniczonych prędkości występujących w świecie makroskopowym powinno sprawić, że penetracje nie będą duże, zaś korekcje nie powinny psuć wrażeń wizualnych wynikających z oglądania symulowanej sceny.

14

2.3.2. Reprezentacja geometryczna ciał

Sam proces wykrywania kolizji może różnić się w zależności od reprezentacji ciał w silniku fizycznym. Popularnym w silnikach fizycznych sposobem reprezentacji geometrycznej ciał jest zastosowanie wielokątów wypukłych dla brył sztywnych oraz reprezentacja za pomocą pojedynczych punktów materialnych dla ciał modelowanych za pomocą mas i sprężyn [9].

W takim przypadku dla kolizji brył sztywnych znajdują zastosowanie algorytmy wykrywające przecięcia dwóch wielokątów wypukłych. Kolizję ciał miękkich z otoczeniem bada się za pomocą testów na zawieranie punktu w wielokącie dla każdego punktu materialnego należącego do ciała miękkiego. Wymusza to implementację osobnych fragmentów programu wykrywających kolizję dla par dwóch brył sztywnych, dwóch ciał miękkich i kombinacji tych dwóch. Zaletą takiego rozwiązania jest brak konieczności implementacji obsługi wielokątów niewypukłych, dla których wykrywanie kolizji nie jest zadaniem łatwym.

W implementowanym przeze mnie silniku postanowiłem ujednolicić sposób traktowania brył sztywnych i ciał miękkich. Obydwie kategorie reprezentowane są przez wielokąty wypukłe, co pozwala uczynić kod bardziej spójnym i dla każdego typu ciał stosować te same algorytmy na poziomie wykrywania kolizji, a także traktować obydwie kategorie w ten sam sposób w większości pozostałych podsystemów silnika, bez popularnych w innych rozwiązaniach rozróżnień. W pewnych obszarach takie podejście może również ułatwić optymalizację pracy kodu.

2.3.3. Działanie systemu wykrywania kolizji

Wykrywanie kolizji między dowolnymi dwoma wielokątami nie zajmuje dużo czasu, jednak scena symulacji fizycznej może zawierać bardzo wiele obiektów. Testowanie każdej pary ciał szczegółowym algorytmem może mieć katastrofalny wpływ na wydajność silnika. Z tego powodu konieczne jest zastosowanie dwuetapowego wykrywania kolizji [1]:

 W pierwszej fazie wykrywane są przecięcia między prostokątami ograniczającymi wielokąty reprezentujące ciała. Etap ten określany jest w anglojęzycznej literaturze mianem broad phase.

15

 W drugiej fazie, dla wszystkich przecinających się brył ograniczających, wykonywane są szczegółowe testy przecinania ograniczanych wielokątów. Ten etap określany jest jako narrow phase.

Dzięki zastosowaniu wyspecjalizowanych algorytmów w obydwu fazach ogólna wydajność wykrywania kolizji może być bardzo duża.

Broad Phase

Pierwszy etap wykrywania kolizji ma na celu wstępny przesiew ciał i określenie, które z nich mają szansę przecinać się na podstawie opisujących ich reprezentacje geometryczne brył ograniczających. W przypadku przestrzeni dwuwymiarowej mamy ponadto do czynienia z wielokątem ograniczającym. Kształt wielokąta ograniczającego może się różnić – w mojej implementacji przyjąłem prostokąt AABB (ang. Axis Aligned Bounding Box) – prostokąt, którego każdy bok jest zawsze jednocześnie prostopadły do jednej z osi układu współrzędnych i równoległy do drugiej [10].

Stosowanych jest wiele algorytmów nadających się do wyznaczonego powyżej celu. Wymienić można między innymi:

 Algorytmy oparte na podziale przestrzeni, np. na drzewach czwórkowych lub drzewach BSP (ang. Binary Space Partitioning). Organizują one przestrzeń w taki sposób, aby szybko można było odnaleźć ciała znajdujące się blisko siebie. W zależności od doboru konkretnego algorytmu cechują się różną wydajnością w dodawaniu i aktualizacji pozycji ciał [10].  Algorytm SAP (ang. Sweep And Prune), który działa na zasadzie rzutowania brył ograniczających na osie współrzędnych, a następnie analizowanie, które z ciał przecinają się na podstawie zachodzenia przecięć rzutów na wszystkich osiach. Algorytm ten cechuje się bardzo dobrą wydajnością w przypadku niewielkich przesunięć ograniczonej liczby ciał. Jego wadą jest natomiast spadek wydajności w przypadku dużej liczby poruszających się ciał znajdujących się na dużej scenie oraz stosunkowo wolne wstawianie nowych ciał [11].

16

Zdecydowałem się na implementację algorytmu SAP. W przypadku dwuwymiarowym do rozpatrzenia są tylko dwie osie współrzędnych, co zwiększa wydajność algorytmu. Mimo że podczas symulacji dużych dynamicznych scen algorytm może działać wolno, to jego szybkość będzie akceptowalna nawet w przypadku dużych, zawierających znaczną liczbę nieruchomych ciał scen.

Narrow Phase

Po wstępnym wyborze wielokątów należących do ciał wykonywane są szczegółowe testy pomiędzy tymi wielokątami i następuje określenie, czy kolizja ciał rzeczywiście zachodzi. Algorytm testujący kolizje pomiędzy wielokątami reprezentującymi ciała musi dodatkowo wygenerować zestaw danych niezbędnych do przeprowadzenia odpowiedzi na kolizje: określić głębokość wzajemnej penetracji ciał (niezbędne do korekcji położenia ciał przy wykrywaniu kolizji a posteriori) oraz znaleźć punkty, w których wielokąty stykają się (w wariancie dyskretnego wykrywania kolizji jest to punkt największej penetracji, jednak wygenerowanie dodatkowych punktów zwiększa dokładność odpowiedzi).

W swojej pracy założyłem, że każde ciało może być reprezentowane przez dowolny wielokąt, w tym wielokąt wklęsły. Algorytmy wykrywające przecięcia wielokątów wklęsłych naogół są skomplikowane i zazwyczaj wymagają dużej ilości obliczeń. Biorąc pod uwagę potencjalnie dużą liczbę testów przeprowadzanych w silniku, algorytm stwierdzający zaistnienie kolizji powinien być jak najszybszy. Ostatecznie wytypowałem dwa rozwiązania obiecujące w kontekście rozwiązywanego problemu:

 Postępując zgodnie z zasadą „dziel i rządź” problem badania przecięć pomiędzy wielokątami wklęsłymi można sprowadzić do badania przecięć pomiędzy wielokątami wypukłymi składającymi się na wielokąty wklęsłe. Zaletą tego rozwiązania jest sprowadzenie testów do prostego i szybkiego w rozwiązaniu problemu przecięć dwóch wielokątów wypukłych. Wadą rozwiązania jest konieczność opracowania sposobu na podział wielokątów wklęsłych na wypukłe oraz, już po takim podziale, wprowadzenie dodatkowych wielokątów do pierwszej, wstępnej fazy wykrywania kolizji, co w przypadku podziału złożonych wielokątów może negatywnie rzutować na wydajność.

17

 Każdy wielokąt wklęsły można przybliżyć stosując ułożone odpowiednio okręgi. Takie okręgi można następnie zorganizować w drzewiastą hierarchię budując drzewo, w którym głębsze poziomy będą odpowiadały lepszemu przybliżeniu. Drzewo takie może zostać następnie wykorzystane do badania kolizji pomiędzy wielokątami. Plusem tego rozwiązania jest szybkość i możliwość sterowania przybliżeniem w zależności od obciążenia silnika, jednak z drugiej strony wymaga on opracowania algorytmu przybliżania wielokątów wklęsłych za pomocą okręgów i aktualizacji drzewa, kiedy wielokąt będzie zmieniał swój kształt. Pewne obawy budzi także jakość przybliżenia – w środowisku dwuwymiarowym wszystkie błędy będą bardziej widoczne niż w trójwymiarowym, gdzie zmienna perspektywa może maskować ewentualne niedokładności wykrywania kolizji [12].

Mając na uwadze powyższe zalety i wady stwierdziłem, że w swojej implementacji zastosuję pierwsze rozwiązanie. Wprowadzanie dodatkowych struktur danych opisujących wielokąty przybliżane przez drzewa przyczyniło by się tylko do zwiększenia stopnia złożoności silnika, niosąc jednocześnie ryzyko zmniejszenia dokładności wykrywania kolizji.

Decydując się na podział wielokątów wklęsłych na wypukłe i sprowadzając problem do testowania kolizji pomiędzy dwoma wypukłymi wielokątami do rozważanie pozostała kwestia wyboru algorytmu przeprowadzającego tego typu testy. Rozważyłem dwa z nich:

 Algorytm bazujący na SAT (ang. Separating Axis Theorem), którego zaletą jest szybkość działania i szansa na wczesne przerwanie działania algorytmu, jeżeli przecięcie nie zachodzi. Algorytm daje poprawne wyniki tylko w przypadku wypukłych wielkotątów [10].  Algorytm GJK (od nazwisk autorów Gilbert, Johnson i Keerthi) służący do mierzenia minimalnej odległości między dwoma zbiorami wypukłymi. Również ten algorytm cechuje się dużą szybkością działania, w sprzyjających okolicznościach (małe zmiany położenia badanych wielokątów), dzięki wykorzystaniu danych zebranych podczas poprzedniego badania jego złożoność może zbliżyć się do O(n) [13].

18

Żaden algorytm w wariancie podstawowym nie generuje wielu punktów kontaktu wielokątów. Ostatecznie w swojej pracy zdecydowałem się na implementację algorytmu wykrywania kolizji par wielokątów wypukłych bazującego na SAT.

2.4. Odpowiedź na kolizje

Ostatnim z trzech głównych etapów działania silnika fizycznego jest odpowiedź na kolizje. Przekonywująca wzajemna interakcja ciał fizycznych jest niezbędna do pozytywnego odbioru symulacji przez człowieka, dlatego dobrze zrealizowana odpowiedź na kolizje jest bardzo istotna dla jakości silnika fizycznego.

Podczas gdy system wykrywania kolizji dba o wykrywanie zachodzących zderzeń, esencją symulacji reakcji między ciałami jest takie modyfikowanie ich parametrów fizycznych, aby ciała w możliwie realistyczny sposób odbijały się od siebie, spoczywały jedne na drugich, ciała miękkie otrzymywały bodźce do deformacji, itp. Poza modelowaniem przepływu energii kinetycznej między ciałami, skutkującej ich odbijaniem się od siebie, każda metoda musi symulować zachodzące między ciałami tarcie. Istnieją trzy zasadnicze paradygmaty według których silniki fizyczne realizują odpowiedź na kolizje:

 Metody kary (ang. Penalty Methods) opierające się na modelowaniu reakcji pomiędzy ciałami za pomocą tworzenia tymczasowych sprężyn łączących ciała i aplikujących siły odpychające je od siebie. Metoda ta pozwala na osiągnięcie stosunkowo szybkiej implementacji, jednak otrzymany model jest trudny w strojeniu i obarczony ryzykiem małej dokładności, zwłaszcza w przypadku kolizji poruszających się ciał. Problemem jest także to, że wpływ aplikowanych na ciała sił jest zależny od kroku czasowego symulacji – wysoka dokładność wymaga małych odstępów czasowych pomiędzy kolejnymi aktualizacjami [14].  Metoda analityczna polega na rozwiązaniu problemu programowania liniowego uwzględniającego parametry fizyczne, które ulegają zmianie podczas zderzenia. Model ten jest dokładny, jednak – zwłaszcza w przypadku dużych grup zderzających się ciał – wymagający obliczeniowo i potencjalnie wolny w działaniu [15].  Metoda impulsowa zakłada użycie impulsów sił generowanych w miejscach zderzeń ciał i zmieniających ich parametry fizyczne. Metoda ta ma dobrą podstawę fizyczną

19

(modelowanie zderzeń ciał opiera się na zmianach pędu) i, co za tym idzie, praktycznie nie wymaga strojenia. Implementacja metody może być szybka, jednak potencjalnie także niestabilna – podejście oparte o impulsy jest bardzo dobre podczas modelowania zderzeń poruszających się ciał, gorzej sprawdza w wypadku modelowania kontaktów ciał pozostających we względnym spoczynku [16].  Poza trzema powyższymi metodami podejmowane były także próby tworzenia rozwiązań hybrydowych, stosujących różne metody w zależności od typu i parametrów ciał biorących udział w kolizji [14].

W rozdziale 2.3. Wykrywanie kolizji wspomniana została korekcja położenia ciał w przypadku penetracji dwóch reprezentujących je wielokątów. Penetracja wielokątów reprezentujących ciała ma i musi mieć miejsce w przypadku detekcji kolizji a posteriori, zaś sposób przeprowadzania korekcji jest zależny od wyboru metody odpowiedzi na kolizję.

W przypadku wyboru metody impulsowej prędkość ciał modyfikowana jest poprzez zastosowanie impulsów siły. Dwa spoczywające i „przenikające” się ciała ciężko rozdzielić stosując impulsy – wyliczenie impulsu, który sprawi, że ich prędkości zostaną wyzerowane (np. negując siły grawitacji tak, aby nie zachodziło dalsze wnikanie) nie stanowi problemu, jednak zastosowanie nawet małego impulsu rozdzielającego wprowadza dodatkową energię do układu. Impulsy natychmiastowo modyfikują prędkość ciała i nie zależą od czasu, więc określenie ich poprawnej wartości w zasadzie nie jest możliwe. Dlatego w przypadku metody impulsowej korekcja odbywa się poprzez przesunięcie ciał z pominięciem obliczeń fizycznych tak, aby penetracja nie zachodziła.

W przypadku wyboru metody kary korekcja może odbywać się z wykorzystaniem modelu kontaktu ciał. Tymczasowa sprężyna łącząca ciała w miejscu penetracji działa z tym większą siłą, im większa penetracja, zmniejszając penetrację, aż siła grawitacji (bądź inne siły które powodują nachodzenie ciał na siebie) zostanie zrównoważona przez siłę sprężyny odpychającej ciała. Metoda ta może zostać dodatkowo wsparta przez modyfikację pozycji ciał tak jak w rozwiązaniu impulsowym.

W przypadku zastosowania podejścia analitycznego problem korekcji może zostać rozwiązany poprzez włączenie równań definiujących pozycje ciał i ich wzajemne nałożenia do

20

rozwiązywanego problemu programowania liniowego. W ten sposób rozwiązanie problemu określa także zmiany pozycji ciał, które zapewnią brak penetracji wielokątów opisujących te ciała.

Ostatecznie w swoim silniku zdecydowałem się na implementację metody bazującej na impulsach, opierając się na metodzie zaproponowanej przez E. Catto [16]. Jej dużą zaletą jest klarowność fizyczna i potencjalna dobra wydajność implementacji.

21

3. Opis implementacji

3.1. Założenia projektowe

W ramach swojej pracy inżynierskiej założyłem implementację dwóch elementów: silnika fizycznego realizującego symulację brył sztywnych i ciał miękkich w przestrzeni dwuwymiarowej oraz programu testowego wykorzystującego zaimplementowany silnik.

Podczas implementacji silnika przyjąłem następujące założenia:

 Silnik będzie należał do kategorii silników interaktywnych i będzie działał w czasie rzeczywistym; musi pozwalać na symulowanie ciał z wydajnością wystarczającą do zastosowania w grze komputerowej.  Silnik musi wspierać zarówno bryły sztywne, jak i ciała miękkie.  Całość nie musi być precyzyjna w sensie fizycznym – rezultat symulacji powinien być przekonywujący dla odbiorcy, zaś sama symulacja powinna być stabilna.  Silnik zostanie zrealizowany jako biblioteka, tak aby możliwe było jego użycie w wielu programach. API biblioteki powinno być przejrzyste i proste w użyciu.  W ramach silnika zostaną zaimplementowane algorytmy wybrane w rozdziale „Budowa silnika fizycznego”, jednak sam silnik powinien być zbudowany w sposób modularny, tak, aby możliwa była łatwa zmiana algorytmów odpowiedzialnych za dane etapy procesu symulacji.  Aktualizacja stanu symulacji fizycznej będzie odbywać się poprzez wywołanie funkcji biblioteki silnika z podaniem czasu, który upłynął od ostatniej aktualizacji.  Silnik zostanie zaimplementowany w języku ++, oferującym dużą wydajność i przenośność, a także stosowanym często podczas implementacji gier komputerowych.

Silnik fizyczny jako całość ciężko przetestować w sposób formalny. Ze względu na przyjęte uproszczenia wymyka się on ramom, które mogłaby nakreślić wynikom symulacji tradycyjna fizyka. Złożoność problemu jest dodatkowo zwiększona przez wielość możliwych symulowanych scen zawierających odmienne konfiguracje ciał. O ile jednak ciężko jest sformalizować takie testy, to wyniki działania symulacji mogą zostać łatwo zweryfikowane

22

przez człowieka, ponieważ intuicyjnie rozumiemy, w jaki sposób powinna zmieniać się scena symulacji.

Mając na względzie powyższe przyjąłem następujące założenia odnośnie programu testowego:

 program testowy zapewni wizualizację symulacji fizycznej przeprowadzanej przez silnik tak, aby użytkownik mógł empirycznie zweryfikować jej poprawność;  program testowy musi być zbudowany w oparciu o bibliotekę silnika, jednocześnie weryfikując użyteczność jej interfejsu;  program testowy musi umożliwiać badanie aktualnych parametrów symulowanych ciał i ich modyfikację;  program testowy musi zawierać kilka przygotowanych scen gotowych do symulacji oraz umożliwiać dodawanie nowych ciał użytkownikowi;  program testowy zostanie zaimplementowany w języku C++ z wykorzystaniem bibliotek Qt, znacznie upraszczającymi realizację interaktywnych aplikacji z graficznym interfejsem użytkownika.

3.2. Ogólny rys implementacji silnika

3.2.1. Organizacja plików źródłowych i klas

Klasy tworzące silnik zostały uporządkowane w przestrzenie nazw odpowiadające poszczególnym modułom z których składa się silnik. Pliki źródłowe i nagłówkowe zostały uporządkowane w foldery odpowiadające nazwami i zagnieżdżeniem przestrzeniom nazw, zaś nazwy poszczególnych plików zawierających źródła w ogólności odpowiadają nazwom definiowanym lub deklarowanym w nich klas. Dodatkowo pliki źródłowe i nagłówkowe zostały rozdzielone: te pierwsze znajdują się w katalogu src, zaś te drugie w katalogu Ductile.

Przestrzenie nazw zorganizowane są w następujący sposób:

 Ductile – główna przestrzeń nazw projektu  Physics – przestrzeń klas stanowiących rdzeń silnika . BroadPhase – zawiera implementacje algorytmów broad phase  SAP – zawiera implementację algorytmu Sweep And Prune

23

. Constraint – zawiera klasy wpływające na wzajemne ograniczanie ruchu ciał  Solver – zawiera implementację odpowiedzi na kolizje . Contact – przestrzeń nazw klas pomocniczych definiujących i przetwarzających dane o kontaktach między ciałami . NarrowPhase – zawiera implementacje algorytmów narrow phase . Shape – przestrzeń nazw klas reprezentujących kształty ciał fizycznych  Tools – zawiera dodatkowe klasy pomocnicze dla użytkownika biblioteki

3.2.2. Kolejność przetwarzania danych

Trzy fazy przeprowadzania symulacji fizycznej wymienione w rozdziale 2. Budowa silnika fizycznego stanowią trzy zasadnicze etapy przetwarzania danych przez silnik. Etap odpowiedzi na kolizje w naturalny sposób występuje po detekcji kolizji. Parę tę możemy określić mianem obsługi kolizji. Etap całkowania równań ruchu może natomiast wystąpić zarówno przed jak i po obsłudze kolizji, przy czym każda kombinacja ma swoje uzasadnienie.

Każda aktualizacja stanu silnika następuje w pewnym punkcie czasu, odległym o pewien krok czasowy od poprzedniej aktualizacji. Wybór kolejności obsługi kolizji i całkowania równań ruchu sprowadza się więc do określenia, w jakim stanie symulacja zostanie pozostawiona pomiędzy aktualizacjami. W przypadku kolejności w której najpierw następuje całkowanie równań ruchu, a potem obsługa kolizji, ciała najpierw zostaną poruszone, a następnie obserwować będziemy konsekwencje kolizji, takie jak zmiana prędkości ruchu liniowego i obrotowego ciał, a także ewentualne korekcje ich pozycji. Odwrotna kolejność sprawia, że najpierw zastosowane zostaną zmiany parametrów fizycznych ciał, a następnie zostaną one poruszone zgodnie z nowymi parametrami.

W przypadku dokonanego przeze mnie wyboru algorytmów sens ma pierwszy z wymienionych wariantów. Drugi wariant byłby przydatny w przypadku zastosowania odpowiedzi na kolizje wykorzystującej metodę kary – wtedy najpierw określane byłyby wynikające z kolizji siły działające na ciała, a następnie dokonywana byłaby korekcja penetracji wielokątów reprezentujących ciała, wynikająca z tych sił działających podczas całkowania równań ruchu. W przypadku metody impulsowej korekcja jest ściśle związana z

24

pozycją ciał w momencie przeprowadzania wykrywania kolizji. Tak więc aby pomiędzy aktualizacjami pozostawić scenę bez nakładających się na siebie ciał, należy najpierw przesunąć ciała, a następnie wykryć kolizje i zastosować korekcje.

3.2.3. Schemat modułów silnika

Silnik fizyczny cyklicznie przetwarza dane opisujące symulowane ciała i sprawdza zachodzące między nimi interakcje. Pierwszym etapem jest aktualizacja parametrów fizycznych wszystkich ciał poprzez przeprowadzanie całkowania równań ruchu.

Kolejnym etapem jest przetestowanie wszystkich wielokątów ograniczających ciała na okoliczność zachodzenia przecięcia między nimi (broad phase), a następnie przetestowanie wszystkich par przecięć pod kątem zachodzenia kontaktu między ciałami (narrow phase). W praktyce testowanie wszystkich przecięć jest wysoce nieoptymalne, ponieważ wiele z ciał może nie poruszać się (nie zmienia się wtedy informacja o zachodzących przecięciach), a także sam algorytm broad phase może zostać przyspieszony poprzez wymóg raportowania nie wszystkich przecięć, a jedynie ich zmian (tzn. raportowanie nowych i usuniętych przecięć).

Mając powyższe na uwadze, po całkowaniu równań ruchu silnik kieruje do modułu obsługującego broad phase jedynie te ciała, których parametry zmieniły się od ostatniej aktualizacji. Zwrotna informacja o usunięciu przecięcia pomiędzy wielokątami ograniczającymi oznacza, że ewentualny kontakt między ciałami na pewno został usunięty. Z kolei informacja o każdym nowym przecięciu sugeruje istnienie potencjalnego nowego kontaktu, więc przecinające się wielokąty ograniczające są kierowane do narrow phase, gdzie opisywane przez nie ciała zostaną szczegółowo zbadane na okoliczność zachodzenia kolizji. Dodatkowo wszystkie istniejące kontakty muszą zostać ponownie zbadane przez moduł obsługujący narrow phase – brak zmiany stanu przecięcia dwóch wielokątów ograniczających nie oznacza, że opisywane ciała nie poruszyły się w nieznacznym stopniu – a każde, nawet najmniejsze poruszenie może spowodować zmianę informacji o kontakcie między ciałami.

Po wygenerowaniu dane określające kontakty zachodzące między ciałami zostają następnie skierowane do modułu odpowiedzi na kolizje, który dokonuje aktualizacji parametrów fizycznych ciał oraz korekcji ich położenia.

25

Zbiór ciał

Całkowanie równań ruchu Odpowiedź na kolizje

Lista zaktualizowanych ciał

Lista nowych przecięć pomiędzy wielokątami ograniczającymi Broad Phase

Narrow Phase Lista usuniętych przecięć pomiędzy wielokątami ograniczającymi

Informacja o punktach Weryfikacja istniejących w których zachodzi kontaktów kontakt pomiędzy ciałami

Lista nowych kontaktów

Aktualizacja listy znanych kontaktów

Rys. 1. Przepływ danych w implementowanym silniku fizycznym

Z diagramu przedstawionego na rys. 1. wynika jasny podział przetwarzanych danych oraz modułów przeprowadzających symulacje. Dane o symulowanej scenie fizycznej zawarłem w obiekcie Scene, który przechowuje informacje dane na temat symulowanej sceny fizycznej. Do tych danych należy przede wszystkim informacja o ciałach biorących udział w symulacji, ale także aktualna lista kontaktów, działających globalnie sił (w szczególności będzie siłą taką jest grawitacja).

Całkowanie równań ruchu odbywa się w obiekcie Body, reprezentującym symulowane ciało fizyczne. Ciało takie „zna” swoją charakterystykę (bryła sztywna lub ciało miękkie) i na żądanie wybiera i zmienia w odpowiedni sposób swoje parametry. Geometria ciała

26

reprezentowana jest przez obiekt Shape::Generic (obiekt Generic zdefiniowany w przestrzeni nazw Shape).

Wykrywanie kolizji obsługiwane jest przez dwie klasy – BroadPhase::Generic i NarrowPhase::Generic. Definiują one interfejs pozwalający przeprowadzać opisaną w pracy dwuetapową detekcję kolizji przez różne algorytmy przynależne do kategorii broad phase i narrow phase. BroadPhase::Generic przyjmuje listę ciał, dla których ma zaktualizować dane dotyczące przecięć wielokątów ograniczających i zwraca listę nowych i usuniętych przecięć. NarrowPhase::Generic posiada metodę pozwalającą na testowanie par kształtów pod kątem występowaniu kontaktu między nimi. Wynikiem testu jest obiekt Contact::Manifold opisujący kontakt: zawiera on informacje między innymi o głębokości penetracji (lub jej braku) oraz listę punktów w których kontakt zachodzi.

W etapie końcowym przeprowadzana jest odpowiedź na kolizje, za którą odpowiada Constraint::Solver::Generic (kolizja jest pewnym ograniczeniem wzajemnego położenia dwóch ciał – obecne założenia projektu nie zakładają implementacji innych, narzuconych przez użytkownika biblioteki ograniczeń, jednak jest to jeden z pomysłów na dalszy rozwój biblioteki). W początkowych etapach pracy nad implementacją obiekt zarządzający fazą odpowiedzi na kolizje przyjmował listę obiektów Contact::Manifold, i na jej podstawie dokonywał obliczeń. W późniejszych etapach implementacji stwierdziłem, że korzystne dla wydajności implementacji będzie grupowanie kontaktów wedle grup stykających się ciał. Dlatego w ostatecznej wersji silnika lista obiektów Contact::Manifold podawana jest do obiektu Contact::Cartographer, który grupuje kontakty (reprezentowane ostatecznie przez obiekt Contact::Data) w wyspy. Wyspa stanowi zbiór punktów kontaktowych związanych z grupą ciał między którymi zachodzi kolizja. Lista wysp jest następnie przekazywana do obiektu przeprowadzającego odpowiedź na kolizje.

Centralnym obiektem zarządzającym przebiegiem symulacji jest Core, który zarządza aktualizacją stanu symulacji fizycznej. Uznałem, że dla użytkownika biblioteki wygodna może okazać się możliwość konstruowania nowej sceny fizycznej niezależnie od obecnie symulowanej (np. ładowanie nowych plansz w tle w grze komputerowej), dlatego poza obiektami w jasny sposób przynależącymi do Scene takimi jak ciała, należą do niej obiekty BroadPhase::Generic, NarrowPhase::Generic i Constraint::Solver::Genric. Użytkownik może

27

chcieć stosować różne algorytmy w różnych scenach. Ponadto może chcieć swobodnie wymieniać sceny, zaś przygotowanie każdego z algorytmów do działania na nowej scenie zajmuje pewien czas. Jedyną klasą niezależną od sceny przynależącą – poza nią samą – do klasy Core jest Contact::Cartographer, który analizuje strukturę kontaktów od nowa po każdej aktualizacji. Schemat klas zawierający najistotniejsze dla ogólnego rysu silnika informacje przedstawiono na Rys. 2.

NarrowPhase::Generic 1 BroadPhase::Generic

+test(shapePair) +findIntersections(activeBodies) 1

Constraint::Solver::Generic

+solve(islands) 1

1 1 Scene Shape::Generic 1 +contacts_ 1 +gravityFields_ 1 1 * Body 1 +step(dt)

1

Core 1 Contact::Cartographer 1 +step(dt) +findIslands(contacts) +setScene(scene)

Rys. 2. Ogólny schemat klas w silniku fizycznym 3.3. Reprezentacja ciał

Reprezentację ciał rozpatrywać można dwupłaszczyznowo. Z jednej strony silnik modeluje fizyczne właściwości ciała, a więc nadaje mu parametry fizyczne takie jak masa, bezwładność, itd. Z drugiej strony oprócz samych parametrów fizycznych ciała silnik musi znać geometrię ciała na potrzeby prawidłowej symulacji interakcji między ciałami.

28

3.3.1. Geometria ciała

Geometria ciała, jego kształt, reprezentowana jest przez klasę Shape::Generic. Zgodnie z przyjętym założeniem każde ciało może być reprezentowane przez wielokąt wklęsły bądź wypukły, jednak na potrzeby wszelkich testów przecinania wielokątów wielokąty wklęsłe dzielone są najpierw na wypukłe. Shape::Generic jest klasą abstrakcyjną, po której dziedziczy Shape::Polygon zawierający interfejs operacji wspólny dla wszystkich wielokątów. Ta klasa również jest abstrakcyjna: ciała mogą zawierać dziedziczącą po niej klasę Shape::CovexPoly lub Shape::ConcavePoly. Klasa Shape::ConcavePoly udostępnia metodę pozwalającą pobrać listę obiektów Shape::InternalPoly reprezentujących wielokąty wypukłe składające się na reprezentowany przez nią wielokąt niewypukły.

Wewnętrznie wielokąt reprezentowany jest jako tablica wierzchołków. Po utworzeniu wierzchołki reprezentowane są we współrzędnych lokalnych ciała i wielokąta – aktualizacja stanu ciała nie wpływa bezpośrednio na zmianę lokalnych wierzchołków wielokąta (chyba że zmieni się jego kształt). Oprócz tego dostępna jest tablica wierzchołków we współrzędnych świata symulacji, wypełniana na podstawie przekształcenia lokalnych wierzchołków przez macierz przekształcenia ciała utworzoną na podstawie współrzędnych i kąta obrotu ciała. Shape::ConvexPoly i Shape::ConcavePoly podczas konstrukcji sterują wielkością tych tablic tak, aby pomieścić w niej wszystkie swoje wierzchołki. Wierzchołki w tablicy uporządkowane są przeciwnie do ruchu wskazówek zegara. W przypadku wielokąta wklęsłego tylko n pierwszych wierzchołków stanowi uporządkowany kontur wielokąta; reszta to punkty wewnętrzne należące do wypukłych wielokątów wewnętrznych. Shape::InternalPoly zamiast tablicy wierzchołków przechowuje tablicę wskazań na wierzchołki swojego rodzica, trzymając się konwencji uporządkowania ich przeciwnie do ruchu wskazówek zegara. Dzięki przyjętej konwencji większość ogólnych operacji dla wielokątów działa na wszystkich jego typach bez konieczności pisania dodatkowego kodu (dla wygody udostępniona została klasa VertexIterator pozwalająca na iterację po konturze wielokąta niezależnie od jego typu). Dodatkowo tłumaczenia współrzędnych wierzchołków lokalnych na wierzchołki we współrzędnych świata odbywa się raz dla Shape::ConcavePoly - wszystkie zawarte w nim Shape::InternalPoly wskazują na aktualne współrzędne.

29

Dodatkowo każdy kształt udostępnia metodę pozwalającą na pobranie swojego wielokąta ograniczającego.

Na rys. 3. Przedstawiono schemat klas odpowiadających za reprezentację geometrii w implementowanym silniku.

ConvexPoly ConcavePoly 1 * InternalPoly +subPolys()

Wskazania na wierzchołki:

Polygon Wierzchołki: +worldVertexIterator() +localVertexIterator() +vertexCount() +isWorldEdge(vertex1,vertex2) +isLocalEdge(vertex1,vertex2)

Generic

+localCenter() +worldCenter() +shapeType() +containsLocalPoint(point) +containsWorldPoint(point) +globalize(transform) +worldBoundingBox()

Rys. 3. Hierarchia klas kształtów w silniku fizycznym

Związek geometrii ciał z broad phase

Wymóg wykrywania kolizji na poziomie wielokątów wypukłych a nie wklęsłych sprawia, że w fazie wstępnego odsiewania kolidujących ciał biorą udział tylko reprezentacje wielokątów, które mogą kolidować – czyli Shape::ConvexPoly i Shape::InternalPoly. Model

30

taki jest wygodny i pozytywnie wpływa na jednolitość kodu - sprawia, że na etapie broad phase nie zachodzi żadne rozróżnianie pomiędzy testowanymi wielokątami.

3.3.2. Fizyczny model ciała

Model bryły sztywnej

W przypadku brył sztywnych model fizyczny sprowadza się do podstawowych fizycznych parametrów opisujących ciała sztywne. Przechowywane są one bezpośrednio w klasie Body.

Model ciała miękkiego

Modelowanie ciała miękkiego odbywa się za pomocą układu odpowiadającemu sieci punktów materialnych połączonych sprężynami, na którą składa się klasa SoftSystem, Mass i Spring. Klasa SoftSystem zarządza procesem deformacji ciała, mianowicie całkuje równania ruchu punktów materialnych reprezentowanych przez klasę Mass, a także oblicza pochodzące od sprężyn (reprezentowane oczywiście przez klasę Spring) siły działające na punkty materialne. Wskazanie na klasę SoftSystem zawarte jest w klasie Body i jest brane pod uwagę, jeżeli ciało jest miękkie.

Każdy obiekt klasy Mass ma określoną pozycję w lokalnych współrzędnych ciała. Pozycja ta jest jednocześnie określona przez wskazanie na konkretny wierzchołek w wielokącie wklęsłym reprezentującym ciało. W ten sposób wszelkie zmiany zachodzące w układzie są natychmiast odzwierciedlane w zmianie kształtu nadrzędnego ciała (por. Rys. 4).

Prędkość punktu materialnego w układzie jest przechowywana w dwóch zmiennych: jednej określającej „zewnętrzną” prędkość całego ciała, oraz drugiej, określającej „wewnętrzną”, indywidualną prędkość danego punktu materialnego. Podział taki wynika z zastosowania sprężyn do łączenia mas. Równanie ruchu masy na sprężynie jest przykładem równania tłumionego oscylatora harmonicznego. Tłumienie zależy od prędkości. Pożądane jest jednak tłumienie jedynie prędkości wynikającej z oddziaływań sprężyn, nie zaś tej wynikającej z oddziaływania sił zewnętrznych, takich jak grawitacja bądź (w pewnym zakresie) zderzenia między ciałami, gdyż wyglądało by to nienaturalnie. Rozdzielenie tych prędkości następuje po całkowaniu równań ruchu ciała (w przypadku tłumienia brana jest pod uwagę tylko prędkość wewnętrzna). Prędkości wewnętrzne są analizowane i wyłączana jest z nich wspólna część

31

prędkości liniowej i obrotowej. O ile istniała część nadająca się do wyłączenia, to zostaje ona przeniesiona do prędkości zewnętrznej.

Na rys. 4. Przedstawiono ogólny schemat klas odpowiedzialnych za reprezentację geometrii ciał miękkich w implementowanym silniku.

Wierzchołki: Polygon Spring

* Położenie Łączone masy masy

1 ConcavePoly Mass * 1 SoftSystem

1 1

1 1 Body

Rys. 4. Ogólny schemat reprezentacji ciał miękkich

Konwersja między typami ciała

Podczas implementacji silnika przedmiotem moich rozważań był sposób odzwierciedlenia w kodzie podziału ciał na sztywne i miękkie. Pozornie dobrym pomysłem (a także moim pierwszym podejściem do problemu) było zdefiniowanie ogólnej klasy Body, a następnie stworzenie dwóch klas potomnych: SoftBody i RigidBody. Rozwiązanie takie działało, jednak w toku implementacji i dalszych przemyśleń okazało się, że możliwość zmiany natury ciała w locie lub ograniczenia właściwości ciała miękkiego do właściwości bryły sztywnej jest cechą pożądaną. Daje ona bowiem możliwość wykonania pewnych optymalizacji: jeżeli ruch poszczególnych punktów materialnych we współrzędnych lokalnych zamiera (brana jest pod uwagę prędkość wewnętrzna punktów materialnych), ciało zmienia swoją naturę na bryłę sztywną (jej prędkości ustalane są na podstawie wspólnej dla wszystkich punktów ciała

32

prędkości zewnętrznej), której analiza i aktualizacja jest znacznie szybsza niż rozpatrywanie osobno wszystkich elementów ciała miękkiego. Dodatkowo zmiana natury ciała jest ciekawą opcją, którą silnik fizyczny może zaproponować swojemu przyszłemu użytkownikowi.

3.4. Implementacja algorytmu SAP

W swojej implementacji jako algorytm broad phase wykorzystałem SAP (ang. Sweep And Prune). Algorytm ten polega na rzutowaniu wielokątów ograniczających ciała na każdą z osi współrzędnych – jeżeli na każdej z osi zachodzi nałożenie rzutów oznacza to, że przecięcie zachodzi.

Implementacja polega na utworzeniu dwóch tablic, z której każda odpowiada osi współrzędnych. Tablice zawierają struktury danych – instancje klasy EndPoint – zawierające informacje o ciele, do którego należą, wartości współrzędnej wierzchołka, którego są rzutem, a także informacje o tym, czy punkt stanowi początek, czy też koniec ciała. Kiedy ciała poruszają się, aktualizowane są informacje w tablicach (w szczególności struktury są zamieniane miejscami, kiedy wierzchołki wielokątów ciał zamienią się miejscami). Zamiana struktur miejscami świadczy o tym, że istnieje szansa nowego przecięcia lub przecięcie przestało zachodzić. Każda taka zamiana raportowana jest do obiektu klasy IntersectionManager, która śledzi na ilu osiach zachodzi nałożenie rzutów i zwraca informację o nowych oraz usuniętych przecięciach pod koniec każdej aktualizacji.

33

Na rys. 5. przedstawiono schemat działania algorytmu SAP.

IntersectionManager

EndPoint +addIntersection(shape1,shape2) EndPoint +addIntersection(shape1,shape2) +getIncrementalResult() EndPoint IntersectionManager śledzi zmiany EndPoint położenia ciał, zgłaszane w wyniku EndPoint zamiany kolejnosci struktur EndPoint. EndPoint W chwili obecnej wiadomo, że zachodzi przecięcie między ciałem zielonym i niebieskim - ich rzuty przecinają się na obu osiach. Rzuty ciała białego przecinają się z rzutami pozostałych ciał, ale z każdym tylko na jednej osi. Oznacza to, że białe ciało nie przecina żadnego innego ciała.

EndPoint EndPoint EndPoint EndPoint EndPoint EndPoint

Rys. 5 Schemat działania algorytmu SAP 3.5. Implementacja wykrywania kolizji na bazie SAT

Testowanie kolizji par wielokątów reprezentujących ciała w symulacji odbywa się przy wykorzystaniu algorytmu opartego na SAT (ang. Seprating Axis Theorem). Podstawą algorytmu jest wyznaczenie prostych normalnych do odcinków stanowiących boki obydwu testowanych wielokątów, a następnie rzutowanie wierzchołków obydwu wielokątów na wyznaczoną prostą. Jeżeli dla przynajmniej jednej prostej normalnej rzuty nie nakładają się, oznacza to, że przecięcie wielokątów nie zachodzi. W przeciwnym wypadku algorytm dostarcza informacji o kolizji: nałożenie dwóch rzutów o najmniejszej spośród wszystkich długości wraz ze swoją prostą normalną określają głębokość penetracji wielokątów oraz informują w jaki sposób rozsunąć wielokąty aby przecięcie nie zachodziło.

W wyniku przyjęcia założeń odnośnie podziału potencjalnie skomplikowanych wielokątów wklęsłych na szereg wypukłych testowane pary figur na ogół są proste i nie posiadają wielu boków. W przypadku testowania dwóch trójkątów wykonywanych jest sześć rzutowań, każde po sześć punktów; nie jest to liczba, która w znaczącym stopniu zwalnia wykonywanie testów. Dodatkowo przyjęty podział na samodzielne wielokąty wypukłe (reprezentowane przez klasę ConvexPoly) oraz należące do wielokątów wklęsłych (reprezentowane przez klasę InternalPoly) pozwala na wykluczanie boków w tym drugim przypadku – wszystkie boki

34

leżące wewnątrz wielokąta wypukłego są ignorowane, ponieważ nie może zachodzić kolizja z wnętrzem ciała.

SAT dostarcza informacji o fakcie zaistnienia przecięcia figur oraz głębokości ich wzajemnej penetracji. Jednak do odpowiedzi na kolizje niezbędny jest również co najmniej jeden punkt określający, w którym miejscu ciała stykają się. Dobrym kandydatem na pierwszy z takich punktów jest punkt najgłębszej penetracji figur: jest to wierzchołek wielokąta, do którego nie należy prosta normalna, leżący po zrzutowaniu na krańcu rzutu zachodzącym na drugi wielokąt.

Wygenerowanie jednego punktu kontaktowego jest wystarczające do przeprowadzenia symulacji fizycznej, w której ciała nie będą na siebie nachodzić i będą odbijać się od siebie w razie potrzeby. Jednak wygenerowanie dodatkowych punktów kontaktowych poprawia stabilność stykających się ciał znajdujących się we względnym spoczynku (np. gdy są one ułożone w stosy). W związku z tym algorytm wyszukuje także dodatkowe punkty kontaktowe – są nimi punkty przecięcia odcinków odpowiadających bokom wielokąta, do którego nie należy prosta normalna z odcinkiem odpowiadającą bokowi i na podstawie którego wyznaczona została prosta normalna. Jeżeli taki punkt nie istnieje dla danego końca tego boku, to punktem kontaktowym jest właśnie punkt końcowy boku, przesunięty wzdłuż prostej normalnej tak, aby nakładał się na najbliższy bok drugiego wielokąta.

Na rys. 6. Przedstawiono schemat wykrywania penetracji wielokątów wypukłych z wykorzystaniem algorytmu opartego o SAT.

35

Prosta normalna, na którą wykonane zostało rzutowanie, z zaznaczonym przesunięciem wymaganym do rozdzielenia figur (głębokość penetracji)

- Punkt największej penetracji - Dodatkowe punkty kontaktowe

Rys. 6. Schemat działania algorytmu narrow phase opartego o SAT 3.6. Impulsowa odpowiedź na kolizje

Kolejnym krokiem po wykryciu par kolidujących ciał – i zarazem ostatnim przed zakończeniem pojedynczej aktualizacji stanu symulowanej sceny – jest zmiana parametrów fizycznych kolidujących ciał, czyli odpowiedź na kolizje. Metoda impulsowa zakłada wyprowadzenie docelowych, względnych prędkości ciał w punktach ich wzajemnego kontaktu podczas kolizji, a następnie sekwencyjne aplikowanie impulsów w kolejnych punktach kontaktowych, do czasu osiągnięcia przez punkty docelowych prędkości. Aplikacja impulsów ma charakter iteracyjny – wszystkie kontakty są przeglądane i rozpatrywane dopóki nie zostanie wykonana określona liczba iteracji, lub względne prędkości ciał w miejscach kontaktów nie zbliżą się do zadanej względnej prędkości docelowej.

36

3.7. Grupowanie punktów kontaktowych

W celu przyspieszenia rozpatrywania odpowiedzi na kolizję kontakty są grupowane przez obiekt klasy Contact::Cartographer. Kontakty między ciałami traktowane są jak graf, którego wierzchołkami są ciała nie będące ciałami statycznymi, natomiast krawędziami są kontakty. Grupy kontaktów wydzielane są z wykorzystaniem przeszukiwania w głąb tak długo, jak długo w grafie istnieją przynajmniej dwa nieprzydzielone do grup ciała. Każda grupa rozpatrywana jest osobno. Zatem małe grupy lub grupy, w których wzajemne względne prędkości ciał zostaną szybko osiągnięte mogą zostać rozpatrzone z wykorzystaniem małej liczby iteracji, co pozytywnie wpływa na wydajność silnika.

3.7.1. Model zderzeń ciał

Model zderzeń symulowanych ciał opiera się na fizycznym modelu zderzeń dwóch ciał, który polega na obliczaniu prędkości punktu należącego do ciała, a następnie stosowaniu impulsu siły w miejscu zderzenia. W przypadku bryły sztywnej obydwa te parametry są łatwe do uzyskania na podstawie wzorów fizycznych.

Podstawowym parametrem fizycznym opisującym zderzenie dwóch ciał jest współczynnik restytucji. Zgodnie z modelem fizycznym zderzenia ciał, wysoki współczynnik restytucji sprawia, że ciała będą „mocniej” odbijać się od siebie (w przypadku współczynnika o wartości 1 następuje zderzenie idealnie sprężyste, bez strat energii przy zderzeniu), zaś w przypadku niskiej wartości współczynnika więcej energii będzie traconej (w przypadku współczynnika o wartości 0 ciała po zderzeniu powinny znajdować się we względnym spoczynku). Aby zwiększyć stabilność symulacji dla niskich prędkości ciał w punktach kontaktowych współczynnik restytucji ciał jest zerowany, aby nie wprowadzać do symulacji drgań wynikających z odbijania ciał z minimalną, jednak ciężką do wygaszenia w kolejnych odbiciach prędkością.

Prędkość punktu ciała miękkiego

W przypadku ciał miękkich modelowanych przy wykorzystaniu układu punktów materialnych połączonych sprężynami prędkość danego punktu należącego do ciała

37

wyprowadzana jest na podstawie trzech najbliższych temu punktowi punktów materialnych, na podstawie średniej ważonej, gdzie wagą jest odwrotność odległości od tych punktów.

Wyszukiwanie trzech najbliższych danemu punktowi na ciele punktów materialnych może być czasochłonne, zwłaszcza w przypadku złożonych ciał miękkich posiadających wiele punktów materialnych. Ze względu na iteracyjną naturę algorytmu odpowiedzi na kolizję prędkości punktów kontaktowych pobierane są wielokrotnie, co może mieć negatywny wpływ na wydajność silnika. Można tu jednak wykonać optymalizację, zauważając, że sprawdzane punkty powtarzają się, zaś podczas jednej aktualizacji silnika – a więc pojedynczego wywołania odpowiedzi na kolizję – ciała nie zmieniają swojego położenia, Zatem dla każdego punktu trzy najbliższe mu punkty materialne oraz wagi związane z odległością pozostają takie same. W związku z tym informacje te są zapamiętywane w tablicy mieszającej, co sprawia, że pierwsze sprawdzenie prędkości punktu ciała miękkiego wykonywane jest w czasie proporcjonalnym do O(n), gdzie n to liczba punktów materialnych w ciele, jednak kolejne sprawdzenia działają w czasie stałym lub bliskim stałemu.

Aplikacja impulsu w ciele miękkim

W fizyce zderzenia dwóch ciał opisywane są przez współczynnik restytucji – parametr określający, jaka część energii dostarczonej do ciał zostaje przekształcona na energię kinetyczną ruchu tych ciał, a jaka zostaje zmieniona na energię wewnętrzną ciał – ceipło, odkształcenie, etc. Do opisu ciała miękkiego i modelowania jego zderzeń z otoczeniem w silniku wykorzystany został współczynnik restytucji, a także niefizyczny, przyjęty arbitralnie parametr deformationRatio, który określa jaką część energii – a więc impulsu – dostarczonego do ciała zamieniana jest w energię przekazywaną do układu punktów materialnych i tym samym wpływa na przeprowadzenie deformacji.

Po obliczeniu wartości pierwotnego impulsu przekazanego do układu punktów materialnych jest on następnie aplikowany poprzez przyłożenie wyprowadzonych z niego impulsów do poszczególnych należących do układu punktów materialnych. Wartość impulsu aplikowanego do punktu materialnego zależna jest od odległości od punktu przyłożenia pierwotnego impulsu. Aby zasymulować ciała o różnym stopniu giętkości przyjąłem dodatkowy niefizczny parametr opisujący ciało miękkie – hardness, wpływający na sposób

38

zmiany impulsu pierwotnego w zależności od odległości. Jeżeli odległość od punktu pierwotnego oznaczymy jako d, impuls pierwotny I, zaś impuls w danym punkcie I’, to I’ obliczane jest na podstawie następującego równania:

Podczas aplikacji impulsu w ciele miękkim cała zewnętrzna prędkość poszczególnych punktów materialnych zostaje przeniesiona do prędkości wewnętrznej. Daje to szansę na stłumienie prędkości zewnętrznej, która narosła w układzie przed zderzeniem ciał, np. w wyniku działania sił grawitacji.

3.8. Korekcja położenia ciał

Trzecim aspektem działania odpowiedzi na kolizje jest korekcja położenia zderzających się ciał. Sama aplikacja impulsów wpływa tylko na prędkości ciał – ich położenie zmieniane jest dopiero podczas następnej aktualizacji, w fazie całkowania równań ruchu. Ponadto aplikacja impulsów w przypadku ciał, które pozostają we względnym spoczynku sprowadza się co najwyżej do zniwelowania prędkości tych ciał tak, żeby nie zachodziła dalsza penetracja. Nie prowadzi jednak w żaden sposób do ich rozsunięcia w przypadku, gdy ciała już na siebie nachodzą. Korekcja położenia przeprowadzana jest na podstawie informacji o głębokości penetracji dostarczonej przez narrow phase i polega na zmianie położenia ciał oraz aktualizacji informacji o penetracji z innymi niż rozpatrywana para ciałami (jeżeli korekcja doprowadziła do takiej sytuacji). Może się to zdarzyć w przypadku stosu ciał – w związku z tym również korekcja ma charakter iteracyjny i przeprowadzana jest do czasu, aż nie minie określona liczba powtórzeń lub stosowane korekcje nie będą pomijalnie małe.

Sposób przeprowadzania korekcji na danej parze ciał zależy od rozpatrywanej osi. Grawitacja na ogół działa pionowo w dół, toteż w przypadku rozpatrywania osi rzędnych całe przesunięcie aplikowane jest do ciała znajdującego się wyżej – pomaga to przy budowie stosów ciał. W przypadku rozpatrywania osi odciętych brana jest pod uwagę bezwładność ciał: przesunięcie poziome dzielone i aplikowane jest odwrotnie proporcjonalnie do bezwładności

39

ciał (tak, aby ciała o większej bezwładności przesunęły się mniej od tych o małej bezwładności).

3.9. Interfejs biblioteki

3.9.1. Klasa DuctilePhysics

Głównym interfejsem służącym do komunikacji użytkownika z biblioteką jest klasa DuctilePhysics. Udostępnia ona metody pozwalające na wykonywanie podstawowych operacji na silniku, bez znajomości jego klas wewnętrznych oraz sposobu ich wzajemnej komunikacji. Instancja klasy pozwala na przeprowadzanie jednej symulacji fizycznej w jednym momencie, a także na równoległe konstruowanie nowych scen oraz późniejszą ich zamianę z aktywną sceną. Ponadto plik nagłówkowy DuctilePhysics.hpp eksportuje do głównej przestrzeni nazw biblioteki typy wybrane zagnieżdżone niżej w hierarchii przestrzeni nazw tak, aby korzystanie z nich było wygodniejsze poza biblioteką (dla wygody również poniżej podawane są typy eksportowane do przestrzeni nazw Ductile).

Klasa udostępnia następujące metody:

 void step(Ductile::real dt) – aktualizuje symulację, modyfikując aktywną scenę przy założeniu, że od ostatniej aktualizacji minęło dt sekund (sugerowana częstotliwość aktualizacji wynosi przynajmniej dziesięć razy na sekundę przy dt niższym niż 0,1).

 Ductile::Scene* scene() – zwraca aktywną scenę fizyczną.

 Ductile::Scene* createScene() – tworzy nową, pustą scenę symulacji fizycznej.

 void setBroadPhaseHandler(Ductile::Scene* scene, Ductile::BroadPhaseHandler broadPhaseHandler[, int expectedObjects]) – przypisuje do sceny wybrany algorytm broad phase i przygotowuje go na przyjęcie zadanej liczby obiektów (wartość może zostać przekroczona, jednak klasa obsługująca algorytm alokuje wewnętrzne struktury danych przygotowując się na przyjęcie zadanej liczby ciał). Obecnie enumeracja Ductile::BroadPhaseHandler może przyjąć tylko jedną wartość – SweepAndPrune.

 void setNarrowPhaseHandler(Ductile::Scene* scene, Ductile::NarrowPhaseHandler narrowPhaseHandler) – przypisuje do sceny wybrany algorytm narrow phase. Obecnie enumeracja Ductile::NarrowPhaseHandler może przyjąć tylko jedna wartość – SeparatingAxisTheorem.

40

 void addGravity(Ductile::Scene* scene, Ductile::ForceGenerator* gravity), void addGravity(Ductile::Scene* scene, const Ductile::Vec2r& gravity), void addGravity(Ductile::Scene* scene, Ductile::real g) – dodaje do zadanej sceny grawitację. W zależności od argumentu jest ona odpowiednio obliczana przez podany obiekt Ductile::ForceGenerator, ma wartość określoną przez zadany wektor, bądź też przyjmowana jest typowa grawitacja o podanej wartości i skierowana w dół osi rzędnych.

 Ductile::Physics::Body* createBody(Ductile::RigidBodyTemplate bodyTemplate, [Ductile::Body::body_flags flags]) oraz Ductile::Physics::Body* createBody(

Ductile::SoftBodyTemplate bodyTemplate, [Ductile::Body::body_flags flags]) – tworzy nowe ciało fizyczne na podstawie zadanego szablonu ciała miękkiego lub sztywnego i ustawia podane znaczniki ciała. Szablony i znaczniki ciała zostaną omówione w rozdziale 3.9.2. Ciała

 void addBody(Ductile::Scene* scene, Ductile::Body* body, Ductile::WorldCoords coords) – dodaje ciało do sceny fizycznej na zadanej pozycji.

 void setPosition(Ductile::Scene* scene, Ductile::Body* body, Ductile:: WorldCoords coords) – ustawia ciało należące do danej sceny fizycznej na zadanej pozycji.

 void setActiveScene(Ductile::Scene* scene) – pozwala ustawić aktywna scene fizyczną.

 Ductile::BodyList probe(Ductile::Scene*, Ductile::WorldCoords worldPoint) – zwraca listę wszystkich ciał należących do danej sceny, których geometryczne reprezentacje zawierają zadany punkt sceny symulacji.

 Ductile::BodyList probeBoundingBox(Ductile::Scene*, Ductile::WorldCoords worldPoint) – zwraca listę wszystkich ciał należących do danej sceny, których prostokąty ograniczające zawierają zadany punkt sceny symulacji.

 Ductile::BodyList probeBoundingBox(Ductile::Scene*, Ductile::BoundingBox boundingBox) – zwraca listę wszystkich ciał należących do danej sceny, których prostokąty ograniczające przynajmniej częściowo zawierają się w zadanym obszarze.

 bool containsPoint(Ductile::Body*, Ductile::WorldCoords worldPoint) – pozwala określić, czy zadany punkt znajduje się w granicach geometrycznej reprezentacji ciała.

41

 void setZeroCorBelow(Ductile::real velocity) – ustawia zerowanie współczynnika restytucji ciał poniżej zadanej względnej prędkości ciał podczas zderzenia. Pozwala to zwiększyć stabilność symulacji poprzez stłumienie ruchu wynikającego z odbić wolno poruszających się ciał (dla współczynnika restytucji o wartości zero docelowa względna prędkość ciał po zderzeniu wynosi zero). Domyślnie zeroCorBelow ma wartość 0.5.

 Ductile::real zeroCorBelow() – pozwala pobrać wartość powyższego parametru.

 const char* version() const – zwraca wersję biblioteki.

3.9.2. Ciała

Materiał

Każde ciało ma przypisany materiał (reprezentowany przez klasę Ductile::Material) który opisuje jego parametry. W szczególności materiał opisuje parametry takie jak współczynnik restytucji, współczynniki tarcia statycznego i dynamicznego, gęstość ciała oraz współczynniki znajdujące zastosowanie tylko w przypadku ciał miękkich, tj. omawiany w dziale Aplikacja impulsu w ciele miękkim współczynnik hardness i deformationRatio, a także parametry sprężyn (współczynnik sprężystości i tłumienie).

Przypisany materiał ma wpływ na aktualne parametry ciała. Można go także zmienić w każdym momencie poprzez wywołanie metody Ductile::Body::setMaterial(const Material*) lub Ductile::Body::applyMaterial(const Material* [, real samplingResolution]). Pierwsza zmienia materiał ciała, nie przelicza jednak zmiennych wynikających z gęstości ciała – a więc jego masa i moment bezwładności pozostaje niezmieniona. Druga wersja przelicza na nowo te parametry.

Próbkowanie ciał

Obliczenie masy i moment bezwładności ciała jest wykonywane przez silnik poprzez dyskretyzację reprezentującej je figury geometrycznej. Na podstawie gęstości ciała i rozdzielczości próbkowania obliczana jest masa pojedynczego fragmentu ciała, następnie figura jest próbkowana i na tej podstawie obliczane są parametry ciała. Podstawą są tu wzory na obliczanie masy i momentu bezwładności układu punktów materialnych.

42

Analogicznie obliczane są masy punktów materialnych należących do ciała – ich masa jest sumą mas fragmentów ciała odpowiadających najbliższym punktowi próbkom. Moment bezwładności ciała miękkiego obliczany jest natomiast na podstawie zbioru należących do ciała punktów materialnych – próbkowanie jest zbędne.

Szablony ciał

Biblioteka udostępnia dwa szablony ciał: Ductile::SoftBodyTemplate oraz Ductile::RigidBodyTemplate. Przechowują one zestaw danych opisujących odpowiednio ciało miękkie bądź bryłę sztywną. Raz utworzony szablon może zostać wykorzystany do wygodnego utworzenia całego szeregu ciał posiadających ten sam zestaw parametrów.

Wielokąty wklęsłe składające się z wielokątów wypukłych mogą zawierać wiele wierzchołków – zarówno zewnętrznych, jak i wewnętrznych, należących do zawartych w figurze wielokątów wypukłych. Każdy wierzchołek składający się na wielokąt wklęsły posiada odpowiadający mu zawarty w szablonie punkty materialne modelujący ciało miękkie.

Znaczniki opisujące ciało

Ciało fizyczne reprezentowane jest przez klasę Ductile::Body. Posiada ono serię znaczników określających jego stan. Wyszczególnić można:

 charakter ciała: Ductile::Body::Static albo Ductile::Body::Dynamic. Określa, czy ciało ma brać aktywny udział w symulacji (wersja Dynamic), bądź też ma być nieporuszalnym ciałem nieruchomym (wersja Static).  typ ciała: Ductile::Body::RigidBody albo Ductile::Body::SoftBody. Określa, czy ciało jest miękkie (zawiera ważną instację klasy SoftSystem), czy też może działać jedynie jako bryła sztywna. Ten znacznik jest automatycznie ustawiany przez bibliotekę podczas tworzenia ciała.  Aktualna natura ciała: Ductile::Body::RigidNature albo Ductile::Body::SoftNature. W przypadku ciał miękkich określa, czy w danym momencie ciało jest traktowane jako bryła sztywna lub też ciało miękkie.

43

Domyślnie ciała tworzone są jako ciała dynamiczne; ciała tworzone na podstawie szablonu ciała miękkiego tworzone są ze znacznikiem Ductile::Body::SoftBody, natomiast te tworzone na podstawie szablonu ciała sztywnego otrzymują znacznik Ductie::Body:: RigidBody. Początkową naturą ciała jest natura ciała sztywnego.

3.9.3. Generowania szablonów ciał

Biblioteka dostarcza także dwie pomocnicze klasy ułatwiające generowanie szablonów ciał na podstawie wielokątów, które będą stanowić geometryczne reprezentacje ciał.

Ductile::ConvexPolyBodyBuilder

Pierwsza z klas umożliwia generowanie szablonów ciał, które podczas symulacji reprezentowane będą przez bryły wypukłe. Udostępnia ona dwie statyczne metody:

 static RigidBodyTemplate Box(real width, real height [, const Physics::Material* material]) – tworzy szablon bryły sztywnej o kształcie prostokąta o zadanych wymiarach.

 static RigidBodyTemplate Triangle(real base, real height, real heightOffset [, const Physics::Material* material]) – tworzy szablon bryły sztywnej o kształcie trójkąta posiadającego zadaną podstawę i wysokość, gdy wysokość przesunięta jest o heightOffset względem środka podstawy.

Ductile::ConcavePolyBodyBuilder

Druga z klas pozwala na tworzenie szablonów ciał reprezentowanych przez wielokąty wklęsłe złożone z trójkątów. Działa ona na zasadzie dodawania do wielokąta kolejnych wierzchołków – każdy nowy wierzchołek łączony jest z dwoma innymi najbliższymi wierzchołkami już zawartymi w wielokącie (tworzony jest w ten sposób swoisty graf zawierający wierzchołki i boki trójkątów). W ten sposób możliwa jest budowa stosunkowo skomplikowanych wielokątów wklęsłych w prosty sposób.

Klasa udostępnia następujące metody:

44

 void putVertex(real x, real y) – dodaje wierzchołek do wielokąta. Tworzone są dwie krawędzie łączące nowy wierzchołek z już istniejącymi wierzchołkami.

 RigidBodyTemplate rigidBodyTemplate([const Physics::Material* material]) – tworzy szablon bryły sztywnej na podstawie zbudowanego wielokąta. Po użyciu tej metody klasa gotowa jest do budowy nowego wielokąta.

 SoftBodyTemplate softBodyTemplate([const Physics::Material* material, int extraSpringLayers]) – tworzy szablon ciała miękkiego na podstawie zbudowanego wielokąta. Punkty materialne tworzone są na podstawie i wiązane z istniejącymi wierzchołkami i łączone sprężynami ze swoimi sąsiadami. Algorytm odnajdywania sąsiadów opisano poniżej; ilością łączonych sąsiadów steruje parametr extraSpringLayers. Po użyciu tej metody klasa gotowa jest do budowy nowego wielokąta.

 static RigidBodyTemplate RigidSphere(Ductile::real radius, int vertices, [const Physics::Material* material]) – tworzy szablon ciała sztywnego o kształcie przybliżającym dwuwymiarową sferę o promieniu radius za pomocą wielokąta o vertices wierzchołkach.

 static SoftBodyTemplate SoftSphere(Ductile::real radius, int vertices, int interconnect, [const Physics::Material* material]) – działa analogicznie do RigidSphere, z tą różnicą, że tworzony jest szablon ciała miękkiego. Układ punktów materialnych i sprężyn reprezentujący ciało miękkie budowany jest w ten sposób, że każdy wierzchołek na boku wielokąta łączony jest z punktem leżącym w środku sfery oraz z 1 + interconnect najbliższymi punktami leżącymi na krawędzi sfery.

Wybór sąsiadujących ze sobą punktów materialnych należących do ciał miękkich opiera się na informacji o krawędziach trójkątów budujących wielokąt. Każda krawędź odpowiada sprężynie, gdyż każde dwa wierzchołki odpowiadające punktom materialnym połączone krawędzią na pewno leżą koło siebie. Jednak połączenie dwóch wierzchołków krawędzią nie jest warunkiem koniecznym uznania dwóch wierzchołków za sąsiadujące. Rozsądnym parametrem pozwalającym zaklasyfikować dwa wierzchołki jako sąsiadów jest odległość między nimi. Kwestie doboru odległości kwalifikującej wierzchołki jako sąsiadów rozstrzygnąłem na podstawie pomiaru odległości krawędzi wychodzących z wierzchołka do innych wierzchołków, które w naturalny sposób uznać można za sąsiadów.

45

Jeżeli dany będzie graf zawierający zbiór wierzchołków V oraz zbiory krawędzi wychodzących z poszczególnych wierzchołków E(v), a także nieujemny, całkowitoliczbowy parametr extraLayers, to algorytm przedstawia się następująco:

1. Usuń wierzchołek v ze zbioru V 2. Przygotuj zmienną określającą maksymalną odległość na jaką oddaleni są sąsiedzi l_max = 0 3. Określ kąt pod jakim sprężyny mogą wychodzić z v. Sprężyna może wychodzić z v pod danym kątem, jeżeli przechodzić będzie wewnątrz wielokąta wypukłego. 4. Dla wierzchołka v’ będącego drugim końcem każdej krawędzi e w E(v): a. Utwórz sprężynę łączącą v i v’ b. Oblicz l = długość e c. Oblicz l_max = max(l_max, e) 5. Dla wierzchołka v’ będącego drugim końcem każdej krawędzi e w E(v): a. Przygotuj zmienną określającą maksymalną odległość na jaką oddaleni są sąsiedzi wierzchołka v’: l_max’ = 0 b. Jeżeli wartość extraLayers jest większa od zera, zmniejsz ją o 1 c. Dla wierzchołka v’’ będącego drugim końcem każdej krawędzi e’ w E(v’): i. Oblicz l’ = odległość v’’ do v ii. Jeżeli l’ jest mniejsza niż l_max lub jeżeli extraLayers ma niezerową wartość, utwórz sprężynę łączącą v’’ z v, o ile taka sprężyna nie istnieje i spełnia wymagania odnośnie kąta pod jakim tworzone mogą być sprężyny. d. Oblicz l_max’ = max(l’, l_max’) e. Jeżeli wartość extraLayers jest różna od zera to dla wierzchołka v’’ będącego drugim końcem każdej krawędzi e’ w E(v’) i nie połączonego sprężyną z v wykonaj rekurencyjnie krok 4 algorytmu, przyjmując l_max = l_max’ 6. Jeżeli V jest zbiorem niepustym przejdź do kroku pierwszego.

3.10. Program testowy

Program testowy został zaimplementowany z wykorzystaniem biblioteki Qt w wersji 4.8.4 oraz biblioteki silnika fizycznego zaimplementowanej w ramach pracy inżynierskiej. Jego zasadniczą funkcją jest wizualizacja przeprowadzanej przez silnik symulacji fizycznej. W tym celu zaimplementowane zostało rozszerzenie klasy QWidget – kontrolka, która wraz z

46

towarzyszącymi klasami symuluje i renderuje scenę fizyczną, a zarazem stanowi rdzeń programu testowego.

Ogólny schemat klas kontrolki przedstawiony został na Rys. 7. Wszystkie klasy umieszczone zostały w przestrzeni nazw DuctileQGL. Jak sugeruje nazwa renderowanie sceny fizycznej przeprowadzane jest przy pomocy technologii OpenGL. Klasa DuctileQGL::Renderer przprowadzająca renderowanie w podstawowym wariancie rysuje schematyczne reprezentacje wielokątów odzwierciedlających ciała fizyczne, jednak może zostać rozszerzona przez użytkownika w celu przeprowadzenia alternatywnej wizualizacji ciał. W podobny sposób zmienić można zachowanie klasy DuctileQGL::PhysicsLoop, przy pomocy której kontrolka przeprowadza aktualizacje symulacji fizycznej. Sama pętla aktualizacji działa w osobnym, tworzonym przez kontrolkę wątku. Podstawowy interfejs kontrolki stanowi klasa DuctileQGL::Widget.

QGLWidget

DuctileQGL::Widget

+Widget(DuctileQGL::Renderer*=nullptr,DuctileQGL::PhysicsLoop*=nullptr, parent:QWidget*=nullptr) +physicsLoop(): DuctileQGL::PhysicsLoop +renderer(): DuctileQGL::Renderer 1 1 DuctileQGL::PhysicsThread

1 1 DuctileQGL::Renderer DuctileQGL::PhysicsLoop

+paintGL() +loop() +resizeGL() +setScene(Ductile::Scene*) QThread +initializeGL() +pause() +resume() +stepOnce()

QObject

Rys. 7. Ogólny schemat klas kontrolki DuctileQGL

47

4. Testowanie

4.1. Funkcje programu testowego

Testowanie biblioteki odbywa się przy pomocy dedykowanego programu testowego. Miarą jakości silnika fizycznego jest jakość przeprowadzanej przez niego symulacji. W przypadku zastosowania w grach jakość mierzona jest nie przez dokładność fizyczną, ale przez subiektywne odczucie odbiorcy, który uzna, że scena symulacji zmienia się w wiarygodny i ciekawy sposób oraz, dla silników czasu rzeczywistego, liczbę obiektów, które mogą być symulowane bez zauważalnych spadków w szybkości aktualizacji stanu sceny.

Poza wizualizacją sceny fizycznej stanowiącą centralną funkcjonalność programu testowego umożliwia on:

 wstrzymywanie i wznawianie symulowania sceny;  przeprowadzanie symulacji w trybie krokowym;  możliwość wyboru kilku przykładowych predefiniowania scen fizycznych;  odglądanie i modyfikowanie parametrów ciał oraz niektórych informacji związanych z przeprowadzaniem symulacji (prostokąty ograniczające, punkty kontaktowe, itp.);  tworzenie nowych ciał przy pomocy interfejsu wykorzystującego klasy Ductile::ConvexPolyBodyBuilder oraz Ductile::ConcavePolyBodyBuilder;  śledzenie informacji o ilości aktualizacji stanu sceny w ciągu sekundy.

48

4.2. Interfejs programu testowego

Rys. 8. Interfejs programu testowego

Na Rys. 8. Interfejs programu testowego. przedstawiono zrzut ekranu programu testowego. Wyróżnić możemy w nim następujące elementy:

1. Menu scen. Pozwala wczytywać predefiniowane sceny symulacji fizycznej. 2. Zakładka Preview, której otwarcie równoznaczne jest z ustawieniem programu testowego w tryb przeprowadzania symulacji. 3. Zakładka New body, przełączająca program testowy w tryb tworzenia nowych ciał. 4. Menu kontekstowe. Podczas przeprowadzania symulacji pozwala na podgląd parametrów fizycznych ciał, a także modyfikację parametrów wybranego ciała (wybieranego przez kliknięcie LPM na ciele).

49

5. Okno symulacji zawierające wizualizację sceny 6. Pasek statusu, zawierający kolejno: a. współrzędne punktu wskazywanego przez kursor; b. liczba aktualizacji stanu fizyki na sekundę, odpowiednio liczba minimalna, maksymalna, średnia oraz aktualna. Trzy pierwsze wartości obliczane są na podstawie danych zebranych od wczytania sceny, zaś ostatnia na podstawie danych pochodzących z ostatnich 500 ms; c. przycisk wstrzymaj/wznów symulację (dostępny w trybie przeprowadzania symulacji); d. przycisk wykonywania krokowego (dostępny tylko gdy odtwarzanie symulacji jest w trzymane, zaś program testowy znajduje się w trybie przeprowadzania symulacji); e. wersja silnika.

Okno symulacji przedstawia wielokąty stanowiące geometryczną reprezentację ciał. Boki wielokątów mają kolor biały; w przypadku wielokątów wklęsłych boki wewnętrzne zawartych w nich wielokątów wypukłych rysowane są w kolorze szarym. Ponadto prezentowane mogą być następujące informacje:

 prędkość punktu na ciele znajdującego się pod kursorem reprezentowana jest przez zieloną strzałkę;  odnalezione punkty kontaktowe reprezentowane są przez czerwone i różowe strzałki na zetknięciach ciał. Długość strzałki odpowiada wzajemnej penetracji wielokątów (a więc także niezbędnej korekcji położenia). Strzałki czerwone reprezentują normalne kontakty, zaś różowe kontakty o niewielkiej ujemnej penetracji – to znaczy punkty nie będące de facto kontaktami, jednak będące na granicy zetknięcia ciał;  prostokąty ograniczające rysowane są kolorem szarym;  sprężyny w ciałach miękkich rysowane są w kolorze żółtym, zaś prędkości poszczególnych punktów materialnych w tych ciałach – kolorem niebieskim.

Na Rys. 9. Zakładki menu programu symulacyjnego przedstawiono opcje i informacje prezentowane w poszczególnych zakładkach menu programu.

50

Rys. 9. Zakładki menu programu symulacyjnego

1. Menu Preview zawiera opcje wizualizacji sceny: a. Render bounding boxes – przełącza rysowanie prostokątów ograniczających; b. Render contact manifolds – przełącza rysowanie punktów kontaktowych (wraz z głębokością penetracji wielokątów); c. Render springs – przełącza rysowanie sprężyn w ciałach miękkich. 2. Menu New body zawiera opcje tworzenia nowych ciał. Oferuje możliwość utworzenia ciała za pomocą generatorów udostępnianych przez bibliotekę. Udostępnione są następujące opcje: a. Box pozwala tworzyć prostokątne ciała; b. Triangle pozwala tworzyć ciała w kształcie trójkątów; c. Sphere pozwala tworzyć ciała kształtem aproksymujące z zadaną dokładnością dwuwymiarową sferę;

51

d. ConcavePoly pozwala utworzyć ciało reprezentowane przez wielokąt niewypukły.

4.3. Sceny testowe

Aby możliwie wszechstronnie przetestować silnik przygotowany został szereg scen testowych, sprawdzających działanie symulacji fizycznej zawierających różne konfiguracje ciał miękkich i sztywnych. Dostępne są następujące sceny:

 Balls – scena składająca się z piłek przedstawionych za pomocą ciał miękkich.  Bridges – scena w której prezentowane są ciała miękkie ustawione w formie mostów, na które spadają bryły sztywne. Ciała miękkie różnią się ustawieniami odnośnie odległości swoich sąsiadów, co pozwala porównać różne konfiguracje sprężyn i ich wpływ na ciała.  The Tree – scena prezentująca drzewo będące ciałem miękkim.  Catapult – scena prezentująca „katapulty”, pokazujące przenoszenie energii między ciałami (ciało spadając na belkę wyrzuca w powietrze inne ciało)  Jelly Shower – scena pokazujące interakcje między ciałami miękkimi  Blank – ładowana domyślnie pusta scena zawiera jedynie jedno duże ciało statyczne („podłogę”). Scenę można zagospodarować samodzielnie tworząc nowe ciała.

52

5. Podsumowanie i wnioski

5.1. Ocena implementacji

W pracy przedstawiono schemat i opis implementacji przeznaczonego do zastosowania w grze dwuwymiarowego silnika fizycznego wspierającego symulowanie fizyki zarówno ciał będących bryłami sztywnymi, jak i ulegających odkształceniom ciał miękkich. Ciała miękkie stanowią integralną część silnika i są w naturalne sposób obsługiwane przez poszczególne komponenty silnika. Sam silnik ma również modularną budowę i jest przygotowany do dalszego rozwoju, w szczególności przez ulepszenie bądź wymianę poszczególnych komponentów odpowiadających za różne etapy symulacji. Użytkowanie silnika jest wygodne, posiada on bowiem wygodny interfejs programistyczny, dający łatwy dostęp do kluczowych dla jego wykorzystania funkcji.

Sama symulacja fizyczna przeprowadzana przez silnik również daje przekonywujące rezultaty i może być użyta w grze komputerowej. Mimo tego podczas implementacji nie udało ustrzec się problemów, których poprawa jeszcze bardziej podwyższyłaby jakość symulacji. W szczególności wymienić można następujące elementy, które mogłyby ulec poprawie:

 O ile bryły sztywne symulowane przez silnik są stabilne, to ciała miękkie nie zawsze zachowują stabilność podczas kontaktu z innymi ciałami. Utrata stabilności jest możliwa, jeżeli ciało miękkie zostanie poddane działaniu dużych sił, np. podczas układaniu wysokich stosów zawierających niewielkie ciała miękkie, bądź też zderzenia dwóch ciał, z których jedno jest miękkie i ma masę znacząco mniejszą niż drugie (jest wtedy poddane oddziaływaniu impulsów o znacznej wielkości). Bezpośrednią przyczyną utraty stabilności w takim wypadku jest rozchwianie systemu – nie dość, że układ może długo wracać do położenia równowagi, to zamiana punktów materialnych miejscami może sprawić, że wielokąt wypukły opisujący ciało stanie się nieprawidłowy – krawędzie zewnętrzne przestaną być krawędziami zewnętrznymi lub wierzchołki wielokątów wewnętrznych nie będą ułożone w wymaganej przez silnik kolejności przeciwnej do ruchu wskazówek zegara. Aby zabezpieczyć się przed ewentualnością uszkodzenia wielokąta wypukłego opisującego ciało miękkie, każdy składający się na niego wielokąt wewnętrzny jest obecnie weryfikowany pod kątem poprawnego ułożenia wierzchołków – w przypadku wykrycia

53

nieprawidłowości przywracany jest ostatni znany poprawny stan wierzchołków. Nie zabezpiecza to jednak przed rozchwianiem systemu.  Pomimo zastosowanych technik optymalizacyjnych wyspy kontaktów zawierające znaczną liczbę punktów kontaktowych (dużo więcej niż sto) są rozstrzygane wolniej niż kilka mniejszych wysp o sumarycznie podobnej ilości kontaktów. Gdy istotną częścią takiej wyspy stanowią kontakty z ciałem albo między ciałami miękkimi efekt ten może się pogłębić, ponieważ w ogólności ciała miękkie mają tendencję do większych niż w przypadku brył sztywnych zmian prędkości poszczególnych punktów należących do tych ciał, co może wydłużyć czas potrzebny na uzyskanie przez system odpowiedzi na kolizje docelowych prędkości punktów kontaktowych.  Przyjęty algorytm korekcji ciał sprawdza się dobrze, jednak korekcja ciał na osi pionowej z przyjęciem przesuwania ich tylko do góry czasem prowadzi do nagłych, niespodziewanych „przeskoków” ciał. Dobrym rozwiązaniem mogło by się okazać opracowanie algorytmu, który dynamicznie rozstrzygałby w którą stronę należy przesuwać ciała na podstawie informacji o znajdujących się na tej samej wyspie kontaktów ciał statycznych i uwzględniając wektor grawitacji.  Pewnym mankamentem jest również fakt, że przyjęty w rozwiązaniu model ciał miękkich najlepiej nadaje się do symulowania elastycznych ciał, takich jak guma lub materia organiczna; nie sprawdza się tak dobrze podczas symulowania obiektów, które w rzeczywistości są mniej elastyczne.

5.2. Perspektywy rozwoju

Poza wymienionymi wyżej poprawkami silnik stanowi również bardzo dobry obiekt do dalszego rozwoju i implementacji nowych funkcjonalności:

 Wymienialność modułów budujących silnik, przede wszystkim podsystemu wykrywania kolizji, daje okazję do implementacji różnych algorytmów z tej dziedziny. W szczególności niektóre algorytmy mogą przedstawiać różną wartość w zależności od symulowanej sceny fizycznej.  Przyjęty model ciał miękkich jest uproszczony i mało precyzyjny fizycznie, chociaż szybki. Dobrym pomysłem na dalszy rozwój pracy, byłoby skupienie się na implementacji bardziej realistycznego systemu symulacji ciał miękkich. Nowa metoda mogłaby być oparta o MES

54

i realizować obliczenia w czasie rzeczywistym z wykorzystaniem wielowątkowości lub delegacji obliczeń do procesora graficznego.  Również poszczególne moduły silnika mogłyby zostać zmodyfikowany tak, aby przeprowadzać obliczenia równolegle w kilku wątkach. Większość wykonywanych przez silnik operacji dobrze nadaje się do takiego podziału, prawdopodobnie oferując bliski liniowemu wzrost wydajności wraz ze wzrostem liczby rdzeni.

55

6. Bibliografia

[1] I. Millington, Engine Development, San Francisco, USA: Elsevier 2007 [2] Wikipedia.org, Soft Body Physics: http://en.wikipedia.org/wiki/Soft_body_physics (aktualne na dzień 9.01.2013) [3] C. Mendoza, M.Garcia, Soft Bodies Using Finite Elements z Game Physics Pearls, A K Peters Ltd. 2010, s. 217 – 218 [4] D. Terzopoulos, J. Platt, A. Barr, K. Fleischer, Elastically Deformable Models, , vol 21, no 4, czerwiec 1987, s. 205 - 214 [5] D. Steinemann, A. Miguel, M. Gross, Fast Adaptive Shape Matching Deformations, SIGGRAPH 2008 [6] D. M. Bourg, Fizyka dla programistów gier, Gliwice: Helion 2003 [7] Wikipedia.org, Verlet Integration: http://en.wikipedia.org/wiki/Verlet_integration [8] Wikipedia.org, Collision detection: http://en.wikipedia.org/wiki/Collision_detection [9] G. Czilli, Particle-based simulation of deformable bodies: http://www10.informatik.uni- erlangen.de/Publications/Theses/2010/Czilli_BA_10.pdf [10] C. Ericson, Real-Time Collision Detection, San Francisco, USA: Elsevier 2005 [11] P. Terdiman, Sweep-and-prune, 2007: http://www.codercorner.com/SAP.pdf [12] C. O’Sullivan, J. Dingliana, Real-Time Collision Detection and Response Using Sphere-Trees: http://www.tara.tcd.ie/jspui/bitstream/2262/64112/1/spheres.PDF [13] E. G. Gilbert, D. W. Johnson, S. S. Keerthi, A Fast Procedure for Computing the Distance Between Complex Objects in Three-Dimensional Space, IEEE Journal of Robotics And Automation, vol. 4, no. 2, kwiecień 1988, s. 193 – 203 [14] E. Drumwright, A Fast and Stable Penalty Method for Rigid Body Simulation: http://www-robotics.usc.edu/~drumwrig/pubs/tvcg.pdf [15] Wiki silnika ODE, How Collision Detection Works, http://ode- wiki.org/wiki/index.php?title=How_Collision_Detection_Works [16] E. Catto, Iterative Dynamics with Temporal Coherence, 2005: http://erwincoumans.com/ftp/pub/test/physics/papers/IterativeDynamics.pdf

Wszystkie podane w bibliografii odsyłacze były aktualne w dniu 9.01.2013.

56

7. Spis rysunków

Rys. 1. Przepływ danych w implementowanym silniku fizycznym ...... 26

Rys. 2. Ogólny schemat klas w silniku fizycznym ...... 28

Rys. 3. Hierarchia klas kształtów w silniku fizycznym ...... 30

Rys. 4. Ogólny schemat reprezentacji ciał miękkich ...... 32

Rys. 5 Schemat działania algorytmu SAP ...... 34

Rys. 6. Schemat działania algorytmu narrow phase opartego o SAT ...... 36

Rys. 7. Ogólny schemat klas kontrolki DuctileQGL ...... 47

Rys. 8. Interfejs programu testowego ...... 49

Rys. 9. Zakładki menu programu symulacyjnego ...... 51

8. Spis załączników

Na płycie CD dołączonej do pracy załączono:

 w katalogu DuctilePhysics – kod źródłowy silnika zaimplementowanego w ramach pracy inżynierskiej;  w katalogu DuctilePreview – kod źródłowy oraz plik wykonywalny programu testowego;  w katalogu documentation – wygenerowaną na podstawie kodu źródłowego silnika dokumentację.

57