# Obsługa PhysX PhysX jest silnikiem fizyki. Zadaniem silnika fizyki jest przeprowadzenie obliczeń związanych z symulacją zjawisk fizycznych jak dynamika brył sztywnych, płynów lub *soft body dynamics*. Wyręcza on twórcę gry w samodzielnej implementacji fizyki. Innymi popularnymi silnikami fizyki są: * Havok * Bullet * Advanced Simulation Librar My wykorzystamy PhysXa do symulacji dynamiki brył sztywnych. Dokumentację można znaleźć w poniższych linkach https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/Index.html https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxapi/files/index.html ## Inicjalizacja PhysX W tej części przejdziemy po istniejącym kodzie i omówimy jego działanie. Zaczniemy od klasy `Physics`, która zawiera atrybut `scene`, (która zawiera naszą scenę, czyli obiekty *aktorów*, które mają ze sobą fizycznie reagować i ich własności.) oraz atrybut `physics`, który służy do tworzenia tych obiektów. Jej konstruktor wygląda następująco: ```C++ Physics::Physics(float gravity) { foundation = PxCreateFoundation(PX_PHYSICS_VERSION, allocator, errorCallback); physics = PxCreatePhysics(PX_PHYSICS_VERSION, *foundation, PxTolerancesScale(), true); PxSceneDesc sceneDesc(physics->getTolerancesScale()); // określa siłę i kierunek grawitacji sceneDesc.gravity = PxVec3(0.0f, -gravity, 0.0f); // definicja dispatchera, który rozdziela zadania do wykonania, tutaj korzystamy z domyślnego dispatchera, któy będzie działał na 2 wątkach dispatcher = PxDefaultCpuDispatcherCreate(2); sceneDesc.cpuDispatcher = dispatcher; // definicja filtra, filtr decyduje między jakimi obiektami sceneDesc.filterShader = PxDefaultSimulationFilterShader; scene = physics->createScene(sceneDesc); } ``` Następnie mamy funkcję `step`, która wykonuje symulację, składa się ona z 2 kroków; najpierw wykonywana jest symulacja, następnie pobierane wyniki. Położenie w scenie physXa musimy przenieść do naszej sceny macierze transformacji odpowiadające ich położeniu są pobierane w funkcji ```C++ void updateTransforms() { // Definiujemy flagę jakie obiekty chcemy pobrać, w tym wypadku statyczne i dynamiczne. auto actorFlags = PxActorTypeFlag::eRIGID_DYNAMIC | PxActorTypeFlag::eRIGID_STATIC; PxU32 nbActors = pxScene.scene->getNbActors(actorFlags); if (nbActors) { //pobieramy obiekty std::vector actors(nbActors); pxScene.scene->getActors(actorFlags, (PxActor**)&actors[0], nbActors); for (auto actor : actors) { // Userdata jest atrybutem, któy przechowuje wskaźnik do obiektu zdefiniowanego przez nas, służy do powiądania aktora ze sceny z naszym obiektem. My będziemy tu przechowywać macierz transformacji if (!actor->userData) continue; glm::mat4 *modelMatrix = (glm::mat4*)actor->userData; // pobiera położenie aktora PxMat44 transform = actor->getGlobalPose(); auto &c0 = transform.column0; auto &c1 = transform.column1; auto &c2 = transform.column2; auto &c3 = transform.column3; // ustawia wartości macierzy z userData *modelMatrix = glm::mat4( c0.x, c0.y, c0.z, c0.w, c1.x, c1.y, c1.z, c1.w, c2.x, c2.y, c2.z, c2.w, c3.x, c3.y, c3.z, c3.w); } } } ``` ## Tworzenie aktora Aktora tworzy się za pomocą metod klasy `PxPhysics`, jest ona dostępna u nas po `pxScene.physics` (pamiętaj, że jest to wskaźnik i trzeba używać strzałki zamiast kropki do wywołania metod). Aby stworzyć aktora należy użyć metody `createRigidStatic` lub `createRigidDynamic`, które tworzą odpowiednio statycznego i dynamicznego aktora (o aktorze statycznym można pomyśleć jak o obiekcie z nieskończoną masą, na którego nie działają żadne siły). Funkcja przyjmuje argument `transform`, który jest początkową pozycją. Następnie musimy określić kształt obiektu za pomocą za pomocą metody `createShape` ona z kolei przyjmuje 2 argumenty: geometrię i materiał. Geometria odpowiada za kształt obiektu (przykładowo `PxPlaneGeometry()` daje nam płaszczyznę a `PxBoxGeometry(hx, hy, hz)` daje prostopadłościan o wymiarach 2hx na 2hy na 2hz). Kształt dodaje się do aktora za pomocą metody `attachShape`. Dodany kształt należy usunąć używając jego metody `release` Dodatkowo do aktora można dodać dodatkowe dane poprzez atrybut `userData`, który jest wskaźnikiem typu `void*`. Wykorzystywany jest by powiązać scenę silnika fizycznego z reprezentacją graficzną, która będzie na ekranie ### Zadanie Wykonaj zadania opisane w komentarzach pliku **main_8_1** i **main_8_2** ## Rejestrowanie zdarzeń W tej części skupimy się na rejestrowaniu i obsłudze zderzeń pomiędzy obiektami. Jest to przydatna funkcjonalność przy tworzeniu gier lub ogólnie aplikacji wykorzystującej fizykę. Z ich pomocą można zaimplementować szereg mechanik, jak zadanie obrażeń w wyniku trafienia pociskiem/bronią białą, wywołanie animacji czy przytwierdzenie haka do ściany. Implementacja wymaga najpierw zdefiniowania swojego *filter shadera*, w którym należy zdefiniować jak mają być obsłużone zdarzenia w scenie physx. W poniższej definicji definiujemy, że punkty zderzenia będą obsługiwane przez `OnContact`. ```C++ static PxFilterFlags simulationFilterShader(PxFilterObjectAttributes attributes0, PxFilterData filterData0, PxFilterObjectAttributes attributes1, PxFilterData filterData1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) { pairFlags = PxPairFlag::eCONTACT_DEFAULT | // default contact processing PxPairFlag::eNOTIFY_CONTACT_POINTS | // contact points will be available in onContact callback PxPairFlag::eNOTIFY_TOUCH_FOUND; // onContact callback will be called for this pair return physx::PxFilterFlag::eDEFAULT; } ``` Samą obsługę zdarzeń definiujemy za pomocą wywołań zwrotnych - *callbacków*. > Wywołanie zwrotne można rozumieć jako odwrotność wywołania funkcji. Zwykle programista wykorzystuje biblioteki poprzez wywoływanie zawartych w niej funkcji. W tym przypadku jest odwrotnie: programista pisze funkcję i przekazuje ją bibliotece, która odpowiada za jej użycie w odpowiednim momencie. *Callback* ustala się poprzez ustawienie odpowiedeniego atrybutu: `sceneDesc.simulationEventCallback = simulationEventCallback;` Klasa obiektu `simulationEventCallback` musi dziedziczyć po klasie `PxSimulationEventCallback`, która zawiera szereg metod będących różnymi `callbackami` odpowiadającymi za różne zdarzenia. Nas będzie interesować metoda `onContact`, która jest wywoływana dla każdego zetknięcia się dwóch obiektów w scenie. ### Zadania Wykonaj zadania opisane w **main8_3.cpp**. Jak zrobisz raportowanie na konsoli zderzeń pomiędzy kulą a kostkami zmodyfikuj kod tak, żeby kostki, które zetkną się z kulą znikały