feat: new type of camera

fix: movement works like it should
This commit is contained in:
Pc
2026-01-24 00:22:05 +01:00
parent 2bcf493f8a
commit 2602b9a523
2 changed files with 156 additions and 164 deletions

View File

@@ -0,0 +1 @@

319
main.cpp
View File

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