#ifndef RAIN_HPP #define RAIN_HPP #include #include #include #include #include "GL/glew.h" // Struktura pojedynczej kropli struct RainDrop { float x, y, z; // Pozycja float speed; // Prędkość spadania bool isSplash; // Czy to jest moment rozbicia? float splashTime; // Czas trwania animacji rozbicia float life; // Do losowania długości życia kropli }; class RainSystem { private: std::vector drops; int maxDrops; float range; // Promień wokół gracza, gdzie pada deszcz float skyHeight; // Wysokość, z której spada deszcz // Funkcja pomocnicza do losowania liczb float float randomFloat(float min, float max) { return min + static_cast(rand()) / (static_cast(RAND_MAX / (max - min))); } public: RainSystem(int count = 2000, float rangeRadius = 300.0f, float height = 200.0f) { maxDrops = count; range = rangeRadius; skyHeight = height; // Inicjalizacja kropel for (int i = 0; i < maxDrops; i++) { RainDrop drop; drop.isSplash = false; drop.splashTime = 0.0f; drop.speed = randomFloat(200.0f, 300.0f); // Szybkość deszczu // Losowa pozycja początkowa drop.x = randomFloat(-range, range); drop.y = randomFloat(0.0f, skyHeight * 2.0f); // Rozrzucamy je w pionie na start drop.z = randomFloat(-range, range); drops.push_back(drop); } } // Aktualizacja fizyki deszczu void update(float deltaTime, float playerX, float playerZ) { for (auto& drop : drops) { if (drop.isSplash) { // Obsługa animacji rozbicia drop.splashTime -= deltaTime; if (drop.splashTime <= 0.0f) { // Koniec rozbicia, reset kropli do nieba drop.isSplash = false; drop.y = skyHeight + randomFloat(0.0f, 50.0f); // Reset pozycji względem gracza (deszcz podąża za łazikiem) drop.x = playerX + randomFloat(-range, range); drop.z = playerZ + randomFloat(-range, range); drop.speed = randomFloat(200.0f, 300.0f); } } else { // Spadanie drop.y -= drop.speed * deltaTime; // Sprawdzenie kolizji z podłogą (y <= 0) if (drop.y <= 0.0f) { drop.y = 0.1f; // Lekko nad ziemią drop.isSplash = true; drop.splashTime = 0.2f; // Czas trwania "plusku" (0.2 sekundy) } // Jeśli gracz uciekł zbyt daleko od kropli, przenieś ją bliżej gracza // (Optymalizacja: nie liczymy deszczu tam, gdzie nas nie ma) if (std::abs(drop.x - playerX) > range) drop.x = playerX + randomFloat(-range, range); if (std::abs(drop.z - playerZ) > range) drop.z = playerZ + randomFloat(-range, range); } } } // Rysowanie deszczu void draw() { // Zapisujemy aktualny stan OpenGL glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_POINT_BIT | GL_LINE_BIT); glDisable(GL_LIGHTING); // Deszcz ma własny kolor, nie potrzebuje cieniowania glDisable(GL_TEXTURE_2D); // Deszcz to linie, nie tekstury glEnable(GL_BLEND); // Przezroczystość glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); // Deszcz nie powinien zasłaniać obiektów w buforze głębokości (opcjonalne) glLineWidth(1.0f); glBegin(GL_LINES); glColor4f(0.7f, 0.8f, 1.0f, 0.6f); // Jasnoniebieski, lekko przezroczysty for (const auto& drop : drops) { if (!drop.isSplash) { // Rysuj spadającą linię (długość zależna od prędkości dla efektu rozmycia) glVertex3f(drop.x, drop.y, drop.z); glVertex3f(drop.x, drop.y + 4.0f, drop.z); } } glEnd(); // Rysowanie rozbryzgów (jako punkty) glPointSize(3.0f); // Grubsze kropki dla rozbryzgów glBegin(GL_POINTS); glColor4f(0.9f, 0.9f, 1.0f, 0.8f); // Bardziej białe przy uderzeniu for (const auto& drop : drops) { if (drop.isSplash) { // Mały losowy rozrzut dla efektu rozbicia float splashOffset = (0.2f - drop.splashTime) * 10.0f; // Rozszerza się z czasem // Rysujemy 3 małe kropelki odbijające się od ziemi glVertex3f(drop.x + splashOffset, drop.y + splashOffset * 0.5f, drop.z); glVertex3f(drop.x - splashOffset, drop.y + splashOffset * 0.5f, drop.z); glVertex3f(drop.x, drop.y + splashOffset, drop.z + splashOffset); } } glEnd(); glDepthMask(GL_TRUE); // Przywracamy zapis do bufora głębokości glPopAttrib(); // Przywracamy stare ustawienia } }; #endif