diff --git a/Rover.cpp b/Rover.cpp index e69de29..5f28270 100644 --- a/Rover.cpp +++ b/Rover.cpp @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/main.cpp b/main.cpp index 2fa6ae3..e506446 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS #ifdef _MSC_VER // Check if MS Visual C compiler # pragma comment(lib, "opengl32.lib") // Compiler-specific directive to avoid manually configuration # pragma comment(lib, "glu32.lib") // Link libraries @@ -115,6 +115,10 @@ float Foward = 45.0f; // Pozycja łazika w przód/tył float Sides = -45.0f; // Pozycja łazika w lewo/prawo float Rotation = 270.0f; // Rotacja łazika (w stopniach) + + +const float MinDistance = 20.0f; +const float MaxDistance = 1000.0f; float CameraHeight = 50.0f; // Wysokość kamery float MoveSpeed = 0.5f; // Prędkość poruszania się @@ -134,202 +138,168 @@ float maxRotationSpeed = 0.5f; // Maksymalna prędkość obrotu // Struktura do reprezentacji płotu struct Plot { - GLfloat xc; // Środek płotu w osi X - GLfloat yc; // Środek płotu w osi Y (nieużywane w kolizji, ale może być pomocne) - GLfloat zc; // Środek płotu w osi Z - GLfloat length; // Długość płotu - GLfloat gruboscY; // Grubość płotu - bool mod_x; // 0 - płot pionowy, 1 - płot poziomy + GLfloat xc; // Środek płotu w osi X + GLfloat yc; // Środek (nieużywany w 2D) + GLfloat zc; // Środek płotu w osi Z + GLfloat length; // Długość (dłuższy wymiar) + GLfloat grubosc; // Szerokość/Grubość (krótszy wymiar) + bool mod_x; // 0 - płot wzdłuż osi Z (pionowy), 1 - płot wzdłuż osi X (poziomy) }; -// Funkcja sprawdzająca kolizję z płotem, uwzględniając wymiary łazika -static bool CheckFenceCollision(float roverXMin, float roverXMax, float roverZMin, float roverZMax, const Plot& plot) { - if (plot.mod_x == 0) { // Płot pionowy (równoległy do osi Z) - float xMin = plot.xc - plot.gruboscY / 2.0f; - float xMax = plot.xc + plot.gruboscY / 2.0f; - float zMin = plot.zc - plot.length / 2.0f; - float zMax = plot.zc + plot.length / 2.0f; +static bool CheckFenceCollision(float rXMin, float rXMax, float rZMin, float rZMax, const Plot& plot) { + float fXMin, fXMax, fZMin, fZMax; - // Sprawdzenie, czy którykolwiek fragment łazika wchodzi w obszar płotu - if (roverXMax >= xMin && roverXMin <= xMax && // Kolizja w osi X - roverZMax >= zMin && roverZMin <= zMax) { // Kolizja w osi Z - return true; - } - } else { + if (plot.mod_x == 0) { // Płot pionowy (rozciąga się w osi Z) + fXMin = plot.xc - plot.grubosc / 2.0f; + fXMax = plot.xc + plot.grubosc / 2.0f; + fZMin = plot.zc - plot.length / 2.0f; + fZMax = plot.zc + plot.length / 2.0f; + } else { // Płot poziomy (rozciąga się w osi X) + fXMin = plot.xc - plot.length / 2.0f; + fXMax = plot.xc + plot.length / 2.0f; + fZMin = plot.zc - plot.grubosc / 2.0f; + fZMax = plot.zc + plot.grubosc / 2.0f; + } - // Płot poziomy (równoległy do osi X) - float xMin = plot.xc - plot.length / 2.0f; - float xMax = plot.xc + plot.length / 2.0f; - float zMin = plot.zc - plot.gruboscY / 2.0f; - float zMax = plot.zc + plot.gruboscY / 2.0f; - - // Sprawdzenie, czy którykolwiek fragment łazika wchodzi w obszar płotu - if (roverXMax >= xMin && roverXMin <= xMax && // Kolizja w osi X - roverZMax >= zMin && roverZMin <= zMax) { // Kolizja w osi Z - return true; - } - } - return false; + // Standardowy test nakładania się prostokątów (AABB vs AABB) + return (rXMax >= fXMin && rXMin <= fXMax && + rZMax >= fZMin && rZMin <= fZMax); } -// Funkcja ogólna do sprawdzania kolizji ze wszystkimi płotami -static bool CheckAllFencesCollision(float roverXMin, float roverXMax, float roverZMin, float roverZMax, const std::vector& fences) { - for (const auto& fence: fences) { - if (CheckFenceCollision(roverXMin, roverXMax, roverZMin, roverZMax, fence)) { - // Kolizja wykryta z którymś płotem - return true; - } - } - return false; // Brak kolizji +static bool CheckAllFencesCollision(float rXMin, float rXMax, float rZMin, float rZMax, const std::vector& fences) { + for (const auto& fence : fences) { + if (CheckFenceCollision(rXMin, rXMax, rZMin, rZMax, fence)) { + return true; + } + } + return false; } static void UpdateRover(const std::vector& fences) { - + // --- 1. OBSŁUGA PRZYSPIESZENIA (W / S) --- if (keyWPressed) { - - // Przyspieszanie w przód velocity += acceleration; if (velocity > maxSpeed) velocity = maxSpeed; - - } else if (keySPressed) { - - // Przyspieszanie w tył + } + else if (keySPressed) { velocity -= acceleration; if (velocity < -maxSpeed) velocity = -maxSpeed; - - } else { - - // Hamowanie (wytracanie prędkości z powodu tarcia) + } + else { + // Hamowanie (tarcie) if (velocity > 0) { - velocity -= friction; if (velocity < 0) velocity = 0; - - } else if (velocity < 0) { - + } + else if (velocity < 0) { velocity += friction; if (velocity > 0) velocity = 0; - } - } - // Obracanie (rotacja z driftowaniem) + // --- 2. OBSŁUGA OBROTU (A / D) --- if (keyAPressed) { - rotationVelocity += rotationAcceleration; if (rotationVelocity > maxRotationSpeed) rotationVelocity = maxRotationSpeed; - - } else if (keyDPressed) { - + } + else if (keyDPressed) { rotationVelocity -= rotationAcceleration; if (rotationVelocity < -maxRotationSpeed) rotationVelocity = -maxRotationSpeed; - - } else { - - // Jeśli żaden z klawiszy A/D nie jest wciśnięty, - // to stopniowo spowalniamy rotację (drift) - - // Mniejsza wartość = dłuższy drift - float driftFactor = 0.1f; - + } + else { + // Driftowanie (wytracanie rotacji) + float driftFactor = 0.1f; if (rotationVelocity > 0) { - rotationVelocity -= rotationFriction * driftFactor; if (rotationVelocity < 0) rotationVelocity = 0; - - } else if (rotationVelocity < 0) { - + } + else if (rotationVelocity < 0) { rotationVelocity += rotationFriction * driftFactor; if (rotationVelocity > 0) rotationVelocity = 0; - } - } - // Wyliczenie nowej pozycji na podstawie prędkości - float radRotation = Rotation * GL_PI / 180.0f; // Przeliczamy rotację na radiany - float newSides = Sides - velocity * cos(radRotation); // Nowa pozycja w osi X - float newFoward = Foward - velocity * sin(radRotation); // Nowa pozycja w osi Z + // --- 3. LOGIKA ODWRÓCENIA SKRĘTU PRZY COFANIU --- + // Jeśli prędkość jest ujemna, odwracamy wpływ rotationVelocity na kąt, + // aby klawisz D zawsze kierował pojazd w prawo względem kamery. + float actualRotationStep = rotationVelocity; + if (velocity < 0.0f) { + actualRotationStep = -rotationVelocity; + } - // Wymiary łazika (połówki w osi X i Z) - const float roverHalfWidthX = 19.0f; // 38/2 - const float roverHalfLengthZ = 12.0f; // 24/2 + // --- 4. WYLICZENIE NOWEJ POZYCJI --- + float radRotation = Rotation * GL_PI / 180.0f; + float newSides = Sides - velocity * cos(radRotation); + float newFoward = Foward - velocity * sin(radRotation); - // Wyliczenie obszaru zajmowanego przez łazik - float roverXMin = newSides - roverHalfWidthX; - float roverXMax = newSides + roverHalfWidthX; + // Wymiary łazika + const float roverHalfWidthX = 19.0f; + const float roverHalfLengthZ = 12.0f; + + // Obszar zajmowany przez łazik (AABB dla ruchu prostoliniowego) + float roverXMin = newSides - roverHalfWidthX; + float roverXMax = newSides + roverHalfWidthX; float roverZMin = newFoward - roverHalfLengthZ; float roverZMax = newFoward + roverHalfLengthZ; - // Sprawdzanie kolizji przed aktualizacją pozycji + // --- 5. KOLIZJE I AKTUALIZACJA --- if (!Kolizja) { - + // Kolizja przy ruchu przód/tył if (CheckAllFencesCollision(roverZMin, roverZMax, roverXMin, roverXMax, fences)) { - // Jeśli jest kolizja, zatrzymujemy łazik - // cout << "Kolizja podczas ruchu!\n"; velocity = 0.0f; - } else { - // Jeśli brak kolizji, aktualizujemy pozycję + } + else { Sides = newSides; Foward = newFoward; } - // Sprawdzanie kolizji podczas obrotu - if (rotationVelocity != 0.0f) { - // Wyliczamy nową rotację - float newRotation = Rotation + rotationVelocity; + // Kolizja przy obrocie + if (actualRotationStep != 0.0f) { + float newRotation = Rotation + actualRotationStep; float radNewRotation = newRotation * GL_PI / 180.0f; - // Obracamy narożniki łazika - std::vector> corners(4); - corners[0] = {Sides - roverHalfWidthX, Foward - roverHalfLengthZ}; // Lewy dolny - corners[1] = {Sides + roverHalfWidthX, Foward - roverHalfLengthZ}; // Prawy dolny - corners[2] = {Sides - roverHalfWidthX, Foward + roverHalfLengthZ}; // Lewy górny - corners[3] = {Sides + roverHalfWidthX, Foward + roverHalfLengthZ}; // Prawy górny - bool collisionDetected = false; + // Punkty narożne do sprawdzenia po obrocie (OBB) + std::vector> corners = { + {Sides - roverHalfWidthX, Foward - roverHalfLengthZ}, + {Sides + roverHalfWidthX, Foward - roverHalfLengthZ}, + {Sides - roverHalfWidthX, Foward + roverHalfLengthZ}, + {Sides + roverHalfWidthX, Foward + roverHalfLengthZ} + }; - // Obracamy wszystkie narożniki - for (auto& corner: corners) { + bool collisionDetected = false; + for (auto& corner : corners) { float x = corner.first; float z = corner.second; - corner.first = Sides + (x - Sides) * cos(radNewRotation) - (z - Foward) * sin(radNewRotation); - corner.second = Foward + (x - Sides) * sin(radNewRotation) + (z - Foward) * cos(radNewRotation); + // Rotacja punktów wokół środka łazika + float rotatedX = Sides + (x - Sides) * cos(radNewRotation) - (z - Foward) * sin(radNewRotation); + float rotatedZ = Foward + (x - Sides) * sin(radNewRotation) + (z - Foward) * cos(radNewRotation); - // Sprawdzamy kolizję na podstawie obróconych narożników - if (CheckAllFencesCollision( corner.first, corner.first, corner.second, corner.second, fences)) { - collisionDetected = true; - break; - } - if (CheckAllFencesCollision(corner.second, corner.second, corner.first, corner.first, fences)) { + if (CheckAllFencesCollision(rotatedX, rotatedX, rotatedZ, rotatedZ, fences)) { collisionDetected = true; break; } } if (collisionDetected) { - //cout << "Kolizja podczas obrotu!\n"; - rotationVelocity = 0.0f; // Zatrzymujemy obrót - } else { - // Aktualizujemy rotację, jeśli nie ma kolizji - Rotation = newRotation; - if (Rotation >= 360.0f) Rotation -= 360.0f; - if (Rotation < 0.0f) Rotation += 360.0f; + rotationVelocity = 0.0f; // Blokujemy obrót + } + else { + Rotation = newRotation; } - } - - } else { - // Jeśli kolizje są wyłączone, aktualizujemy wszystko bez sprawdzania - Sides = newSides; - Foward = newFoward; - Rotation += rotationVelocity; - if (Rotation >= 360.0f) Rotation -= 360.0f; - if (Rotation < 0.0f) Rotation += 360.0f; } -} + else { + // Kolizje wyłączone - aktualizuj bez pytań + Sides = newSides; + Foward = newFoward; + Rotation += actualRotationStep; + } + // Normalizacja kąta do zakresu 0-360 + if (Rotation >= 360.0f) Rotation -= 360.0f; + if (Rotation < 0.0f) Rotation += 360.0f; +} std::vector fences = { { 450.0f, 3.0f, -90.0f, 900.0f, 4.0f, 1}, // 1 - poziomo @@ -340,35 +310,28 @@ std::vector fences = { // Change viewing volume and viewport. Called when window is resized void static ChangeSize(GLsizei w, GLsizei h) { - GLfloat nRange = 100.0f; - //GLfloat fAspect; - // Prevent a divide by zero + // Zabezpieczenie przed dzieleniem przez zero if (h == 0) h = 1; lastWidth = w; lastHeight = h; - //fAspect = (GLfloat)w / (GLfloat)h; - // Set Viewport to window dimensions + // Obliczenie proporcji okna (Aspect Ratio) + GLfloat fAspect = (GLfloat)w / (GLfloat)h; + + // Ustawienie obszaru renderowania glViewport(0, 0, w, h); - // Reset coordinate system + // Reset macierzy projekcji glMatrixMode(GL_PROJECTION); glLoadIdentity(); - // // Establish clipping volume (left, right, bottom, top, near, far) - // if (w <= h) glOrtho(-nRange, nRange, -nRange * h / w, nRange * h / w, -nRange, nRange); - // else glOrtho(-nRange * w / h, nRange * w / h, -nRange, nRange, -nRange, nRange); - - // Establish clipping volume (left, right, bottom, top, near, far) - if (w <= h) glOrtho(-nRange, nRange, -nRange * h / w, nRange * h / w, -20 * nRange, 20 * nRange); - else glOrtho(-nRange * w / h, nRange * w / h, -nRange, nRange, -20 * nRange, 20 * nRange); - - // Establish perspective: - /* - gluPerspective(60.0f, fAspect, 1.0, 400); - */ + // USTAWIENIE PERSPEKTYWY + // Parametry: (Kąt widzenia w stopniach, Proporcje okna, Bliska płaszczyzna, Daleka płaszczyzna) + // UWAGA: Bliska płaszczyzna (near) w perspektywie MUSI być większa od 0 (np. 1.0f) + gluPerspective(45.0f, fAspect, 1.0f, 2000.0f); + // Powrót do macierzy widoku modelu glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } @@ -584,27 +547,38 @@ void static RenderScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Widok panoramiczny (SHIFT/F5) + // Pomocnicza konwersja na radiany + float rad = Rotation * GL_PI / 180.0f; + if (panoramic_view) { - // Zwiększ prędkość obrotu oraz tarcie + // --- WIDOK Z GÓRY (STRATEGICZNY) --- maxRotationSpeed = 1.0f; rotationFriction = 0.5f; + + float mapZoom = 400.0f; // Wysokość, z której patrzymy gluLookAt( - Foward, // Pozycja kamery - 123, // Wysokość kamery, nie ma znaczenia bo nie mamy perspektywy - Sides - 1.0f, // Kamera wzdłuż osi X i Z, z jakiegoś powodu działa po "- 1.0f" - Foward, 0.0f, Sides, // Punkt patrzenia (łazik) - 0.0f, 1.0f, 0.0f // Wektor "góry" + Foward, mapZoom, Sides, // Kamera wysoko nad łazikiem + Foward, 0.0f, Sides, // Patrzymy prosto na łazik + 1.0f, 0.0f, 0.0f // ZMIANA: Wektor góry to oś X (bo patrzymy w dół osi Y) ); - } else { - // Ustaw tarcie i prędkość obrotu na domyślną wartość + } + else { maxRotationSpeed = 0.5f; rotationFriction = 0.1f; + + float rad = Rotation * GL_PI / 180.0f; + + // Obliczamy pozycję kamery korzystając z dynamicznego CameraDistance + float camX = Foward + CameraHeight * sin(rad); + float camZ = Sides + CameraHeight * cos(rad); + + // Wysokość kamery też może się skalować z dystansem (opcjonalnie) + float dynamicHeight = CameraHeight * 0.4f; + gluLookAt( - Foward - 50.0f * sin((Rotation + 180.0f) * GL_PI / 180.0f), // Pozycja kamery - CameraHeight / 4, // Wysokość kamery - Sides - 50.0f * cos((Rotation + 180.0f) * GL_PI / 180.0f), // Kamera wzdłuż osi X i Z - Foward, 0.0f, Sides, // Punkt patrzenia (łazik) - 0.0f, 1.0f, 0.0f // Wektor "góry" + camX, dynamicHeight, camZ, // Pozycja kamery + Foward, 10.0f, Sides, // Patrzymy na łazik + 0.0f, 1.0f, 0.0f // Góra ); } @@ -993,6 +967,23 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } break; + + case WM_MOUSEWHEEL: + { + // Pobieramy informację o tym, jak mocno obrócono kółko + int zDelta = GET_WHEEL_DELTA_WPARAM(wParam); + + // Zmieniamy dystans kamery (podzielone przez 120, bo tyle wynosi jeden "skok" scrolla) + CameraHeight -= (float)zDelta * 0.1f; + + // Ograniczamy zoom, żeby nie wejść kamerą "w łazik" ani nie odlecieć w kosmos + if (CameraHeight < MinDistance) CameraHeight = MinDistance; + if (CameraHeight > MaxDistance) CameraHeight = MaxDistance; + + // Odświeżamy okno + InvalidateRect(hWnd, NULL, FALSE); + } + break; case WM_KEYUP: