Files
grafikaKBT/main.cpp
2026-01-26 22:46:33 +01:00

1211 lines
38 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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
#endif
#ifdef _MSC_VER
# ifndef _MBCS
# define _MBCS
# endif
# ifdef _UNICODE
# undef _UNICODE
# endif
# ifdef UNICODE
# undef UNICODE
# endif
#endif
// #define GLEW_STATIC
#include <windows.h> // Window defines
#include "GL/glew.h" // Dołączony jako pliki glew.c i glew.h
//#include <gl/gl.h> // OpenGL
#include <gl/glu.h> // biblioteka GLU; program kompiluje się bez niej, ale w celu uniknięcia w przyszłości niespodzianek to została dołączona
#include <math.h> // Define for sqrt
//#include <stdio.h>
#include <iostream>
#include "RESOURCE.H" // About box resource identifiers.
#include "loadOBJ.h"
#include "lazik.hpp"
#include "plane.hpp"
//#include <vector>
#include "GL/glm/glm.hpp"
#include "GL/glfw3.h"
#include <ctime>
#include "timeh.hpp"
#include "texture.hpp"
#include "shader.hpp"
#include "FPSCounter.cpp"
#include <thread>
#include "teksturowane.hpp"
#include "fabula.hpp"
#include "GL/wglew.h"
#include "Logger.hpp"
using namespace glm;
#define glRGB(x, y, z) glColor3ub((GLubyte)x, (GLubyte)y, (GLubyte)z)
#define BITMAP_ID 0x4D42 // identyfikator formatu BMP
#define GL_PI 3.1415
//#define getTime lastTime = std::time(nullptr);
//#define timestampedCout(msg) {getTime; std::cout << "( " << lastTime << ") " << msg << "\n";}
//using namespace std;
HPALETTE hPalette = NULL;
// Application name and instance storage
static LPCTSTR lpszAppName = "grafikaKBT";
static HINSTANCE hInstance;
GLFWwindow* window;
///////////////////////
void DisableQuickEdit();
// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
static GLfloat zRot = 0.0f;
static GLsizei lastHeight;
static GLsizei lastWidth;
// Opis tekstury
BITMAPINFOHEADER bitmapInfoHeader; // nagłówek obrazu
unsigned char* bitmapData; // dane tekstury
unsigned int texture[4]; // obiekt tekstury
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL APIENTRY AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam);
void SetDCPixelFormat(HDC hDC);
int polygonmode = 0;
std::time_t lastTime = std::time(nullptr);
int monitormode = 1;
int monitormodecounter = 0;
std::time_t monitormodehelper;
short biezacy_wzor = 0;
bool panoramic_view = 0;
//Zmienne do ruchu ##############################################^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FPSCounter fpsCounter;
static const int targetFPS = 444; // Celowany FPS
//Fps counter
static void LimitFPS(int targetFPS) {
static auto lastTime = std::chrono::high_resolution_clock::now();
// Obliczamy czas na jedną klatkę
double targetFrameDuration = 1.0 / targetFPS;
auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = currentTime - lastTime;
// --- CZYSTA PĘTLA (Busy Wait) ---
// Usunęliśmy sleep_for, bo w Windows jest zbyt nieprecyzyjny dla FPS > 64.
// Teraz procesor będzie pracował na 100% jednego wątku, ale FPS będzie idealny.
while (elapsed.count() < targetFrameDuration) {
// Tylko aktualizujemy czas, nic więcej.
// Jeśli chcesz trochę odciążyć CPU, można użyć instrukcji procesora 'pause',
// ale w standardowym C++ po prostu pusta pętla z pomiarem czasu jest najpewniejsza.
currentTime = std::chrono::high_resolution_clock::now();
elapsed = currentTime - lastTime;
}
lastTime = std::chrono::high_resolution_clock::now();
}
bool Kolizja = false;
bool keyWPressed = false;
bool keySPressed = false;
bool keyAPressed = false;
bool keyDPressed = false;
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)
bool fpv_view = false; // Flaga widoku z pierwszej osoby
const float MinDistance = 20.0f;
const float MaxDistance = 1000.0f;
float CameraHeight = 150.0f; // Wysokość kamery
float MoveSpeed = 0.5f; // Prędkość poruszania się
float velocity = 0.0f; // Aktualna prędkość łazika
float rotationVelocity = 0.0f; // Prędkość obrotu łazika
const float friction = 0.05f; // Współczynnik tarcia (μ)
const float maxSpeed = 2.0f; // Maksymalna prędkość łazika
const float acceleration = 0.2f;
float rotationAcceleration = 0.075f; // Przyspieszenie obrotu
float rotationFriction = 0.1f; // Współczynnik tarcia obrotu
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 (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)
};
static bool CheckFenceCollision(float rXMin, float rXMax, float rZMin, float rZMax, const Plot& plot) {
float fXMin, fXMax, fZMin, fZMax;
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;
}
// Standardowy test nakładania się prostokątów (AABB vs AABB)
return (rXMax >= fXMin && rXMin <= fXMax &&
rZMax >= fZMin && rZMin <= fZMax);
}
static bool CheckAllFencesCollision(float rXMin, float rXMax, float rZMin, float rZMax, const std::vector<Plot>& fences) {
for (const auto& fence : fences) {
if (CheckFenceCollision(rXMin, rXMax, rZMin, rZMax, fence)) {
return true;
}
}
return false;
}
static void UpdateRover(const std::vector<Plot>& fences) {
// --- 1. OBSŁUGA PRZYSPIESZENIA (W / S) ---
if (keyWPressed) {
velocity += acceleration;
if (velocity > maxSpeed) velocity = maxSpeed;
}
else if (keySPressed) {
velocity -= acceleration;
if (velocity < -maxSpeed) velocity = -maxSpeed;
}
else {
// Hamowanie (tarcie)
if (velocity > 0) {
velocity -= friction;
if (velocity < 0) velocity = 0;
}
else if (velocity < 0) {
velocity += friction;
if (velocity > 0) velocity = 0;
}
}
// --- 2. OBSŁUGA OBROTU (A / D) ---
if (keyAPressed) {
rotationVelocity += rotationAcceleration;
if (rotationVelocity > maxRotationSpeed) rotationVelocity = maxRotationSpeed;
}
else if (keyDPressed) {
rotationVelocity -= rotationAcceleration;
if (rotationVelocity < -maxRotationSpeed) rotationVelocity = -maxRotationSpeed;
}
else {
// Driftowanie (wytracanie rotacji)
float driftFactor = 0.1f;
if (rotationVelocity > 0) {
rotationVelocity -= rotationFriction * driftFactor;
if (rotationVelocity < 0) rotationVelocity = 0;
}
else if (rotationVelocity < 0) {
rotationVelocity += rotationFriction * driftFactor;
if (rotationVelocity > 0) rotationVelocity = 0;
}
}
// --- 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;
}
// --- 4. WYLICZENIE NOWEJ POZYCJI ---
float radRotation = Rotation * GL_PI / 180.0f;
float newSides = Sides - velocity * cos(radRotation);
float newFoward = Foward - velocity * sin(radRotation);
// 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;
// --- 5. KOLIZJE I AKTUALIZACJA ---
if (!Kolizja) {
// Kolizja przy ruchu przód/tył
if (CheckAllFencesCollision(roverZMin, roverZMax, roverXMin, roverXMax, fences)) {
velocity = 0.0f;
}
else {
Sides = newSides;
Foward = newFoward;
}
// Kolizja przy obrocie
if (actualRotationStep != 0.0f) {
float newRotation = Rotation + actualRotationStep;
float radNewRotation = newRotation * GL_PI / 180.0f;
// Punkty narożne do sprawdzenia po obrocie (OBB)
std::vector<std::pair<float, float>> corners = {
{Sides - roverHalfWidthX, Foward - roverHalfLengthZ},
{Sides + roverHalfWidthX, Foward - roverHalfLengthZ},
{Sides - roverHalfWidthX, Foward + roverHalfLengthZ},
{Sides + roverHalfWidthX, Foward + roverHalfLengthZ}
};
bool collisionDetected = false;
for (auto& corner : corners) {
float x = corner.first;
float z = corner.second;
// 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);
if (CheckAllFencesCollision(rotatedX, rotatedX, rotatedZ, rotatedZ, fences)) {
collisionDetected = true;
break;
}
}
if (collisionDetected) {
rotationVelocity = 0.0f; // Blokujemy obrót
}
else {
Rotation = newRotation;
}
}
}
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 = {
{ 450.0f, 3.0f, -90.0f, 900.0f, 4.0f, 1}, // 1 - poziomo
{ 0.0f, 3.0f, 405.0f, 990.0f, 4.0f, 0}, // 0 - pionowo
{ 450.0f, 3.0f, 10*90.0f, 900.0f, 4.0f, 1}, // 1 - poziomo
{10*90.0f, 3.0f, 405.0f, 990.0f, 4.0f, 0} // 0 - pionowo
};
// Change viewing volume and viewport. Called when window is resized
void static ChangeSize(GLsizei w, GLsizei h) {
// Zabezpieczenie przed dzieleniem przez zero
if (h == 0) h = 1;
lastWidth = w;
lastHeight = h;
// Obliczenie proporcji okna (Aspect Ratio)
GLfloat fAspect = (GLfloat)w / (GLfloat)h;
// Ustawienie obszaru renderowania
glViewport(0, 0, w, h);
// Reset macierzy projekcji
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 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();
}
// LoadBitmapFile
// opis: ładuje mapę bitową z pliku i zwraca jej adres.
// Wypełnia strukturę nagłówka.
// Nie obsługuje map 8-bitowych.
static unsigned char* LoadBitmapFile(char* filename, BITMAPINFOHEADER* bitmapInfoHeader) {
FILE* filePtr; // wskaźnik pozycji pliku
BITMAPFILEHEADER bitmapFileHeader; // nagłówek pliku
unsigned char* bitmapImage; // dane obrazu
int imageIdx = 0; // licznik pikseli
unsigned char tempRGB; // zmienna zamiany składowych
// otwiera plik w trybie "read binary"
filePtr = fopen(filename, "rb");
if (filePtr == NULL) return NULL;
// wczytuje nagłówek pliku
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
// sprawdza, czy jest to plik formatu BMP
if (bitmapFileHeader.bfType != BITMAP_ID) {
fclose(filePtr);
return NULL;
}
// wczytuje nagłówek obrazu
fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
// ustawia wskaźnik pozycji pliku na początku danych obrazu
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
// przydziela pamięć buforowi obrazu
bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);
// sprawdza, czy udało się przydzielić pamięć
if (!bitmapImage) {
free(bitmapImage);
fclose(filePtr);
return NULL;
}
// wczytuje dane obrazu
fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
// sprawdza, czy dane zostały wczytane
if (bitmapImage == NULL) {
fclose(filePtr);
return NULL;
}
// zamienia miejscami składowe R i B
for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}
// zamyka plik i zwraca wskaźnik bufora zawierającego wczytany obraz
fclose(filePtr);
return bitmapImage;
}
void SetDCPixelFormat(HDC hDC) {
int nPixelFormat;
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1, // Version of this structure
PFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap)
PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
PFD_DOUBLEBUFFER, // Double buffered
PFD_TYPE_RGBA, // RGBA Color mode
24, // Want 24bit color
0, 0, 0, 0, 0, 0, // Not used to select mode
0, 0, // Not used to select mode
0, 0, 0, 0, 0, // Not used to select mode
32, // Size of depth buffer
0, // Not used to select mode
0, // Not used to select mode
PFD_MAIN_PLANE, // Draw in main plane
0, // Not used to select mode
0, 0, 0 }; // Not used to select mode
// Choose a pixel format that best matches that described in pfd
nPixelFormat = ChoosePixelFormat(hDC, &pfd);
// Set the pixel format for the device context
SetPixelFormat(hDC, nPixelFormat, &pfd);
}
//GLuint programID, VertexArrayID, MatrixID;
lazik user(10.0f, 0.0f, 0.0f, "res/models/lazik4.obj"); // obiekty eksportujemy z Forward Axis Z, Up Axis Y.
plane mapa( 0.0f, 0.0f, 0.0f, "res/models/mapka3_nofence_noplatform.obj");
static void SetupRC() {
glEnable(GL_MULTISAMPLE); //
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
// Light values and coordinates
GLfloat ambientLight[] = {0.3f, 0.3f, 0.3f, 1.0f};
GLfloat diffuseLight[] = {0.7f, 0.7f, 0.7f, 1.0f};
GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
// Multiple light positions (for light coming from all sides)
GLfloat lightPos1[] = { 50.0f, -100.0f, 50.0f, 1.0f}; // Light 0 position
GLfloat lightPos2[] = {-50.0f, -100.0f, 50.0f, 1.0f}; // Light 1 position
GLfloat lightPos3[] = { 50.0f, -100.0f, -50.0f, 1.0f}; // Light 2 position
GLfloat lightPos4[] = {-50.0f, -100.0f, -50.0f, 1.0f}; // Light 3 position
glEnable(GL_DEPTH_TEST); // Hidden surface removal
glFrontFace(GL_CCW); // Counter clockwise polygons face out
glDepthFunc(GL_LESS);
// Enable lighting
glEnable(GL_LIGHTING);
// Setup and enable light 0
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos1);
glEnable(GL_LIGHT0);
// Setup and enable light 1
glLightfv(GL_LIGHT1, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT1, GL_SPECULAR, specular);
glLightfv(GL_LIGHT1, GL_POSITION, lightPos2);
glEnable(GL_LIGHT1);
// Setup and enable light 2
glLightfv(GL_LIGHT2, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT2, GL_SPECULAR, specular);
glLightfv(GL_LIGHT2, GL_POSITION, lightPos3);
glEnable(GL_LIGHT2);
// Setup and enable light 3
// glLightfv(GL_LIGHT3, GL_AMBIENT, ambientLight);
// glLightfv(GL_LIGHT3, GL_DIFFUSE, diffuseLight);
// glLightfv(GL_LIGHT3, GL_SPECULAR, specular);
// glLightfv(GL_LIGHT3, GL_POSITION, lightPos4);
// glEnable(GL_LIGHT3);
// Enable color tracking
glEnable(GL_COLOR_MATERIAL);
// Set Material properties to follow glColor values
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// All materials hereafter have full specular reflectivity
// with a high shine
GLfloat specref2[] = {0.2f, 0.2f, 0.2f, 0.2f};
glMaterialfv(GL_FRONT, GL_SPECULAR, specref2);
glMateriali(GL_FRONT, GL_SHININESS, 128);
// White background
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
// Initialize GLEW
timestampedCout("Inicjalizowanie GLEW...");
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
timestampedCout("Failed to initialize GLEW");
return;
}
timestampedCout("Zainicjalizowano GLEW.");
// Sprawdź czy rozszerzenie jest dostępne i wyłącz V-Sync
if (wglewIsSupported("WGL_EXT_swap_control")) {
wglSwapIntervalEXT(0); // 0 = WYŁĄCZ V-Sync (nielimitowane FPS)
timestampedCout("V-Sync wylaczony (unlocked FPS).");
}
else {
timestampedCout("Brak obslugi WGL_EXT_swap_control - nie mozna programowo wylaczyc V-Sync.");
}
// Initialize GLFW3
timestampedCout("Inicjalizowanie GLFW3...");
if (!glfwInit()) {
timestampedCout("Failed to initialize GLFW");
}
timestampedCout("Zainicjalizowano GLFW3.");
// Load models
timestampedCout("Ladowanie modelu lazika...");
user.loadModel();
timestampedCout("Ladowanie modelu mapki...");
mapa.loadModel();
glfwSwapInterval(0);
}
void static RenderScene(void) {
// PS: to nie zadziała, bo okno nie jest tworzone przez glfw
// Ustawienie liczby próbek dla antyaliasingu
glfwWindowHint(GLFW_SAMPLES, 16); // 4x MSAA (Wielokrotne próbkowanie)
// Włączenie antyaliasingu (MSAA)
glEnable(GL_MULTISAMPLE);
// Przywrócenie macierzy modelu i ustawienie obrotów
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f);
// Ustawienie trybu rysowania wielokątów
switch (polygonmode) {
case 1:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Rysowanie linii
break;
default:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Wypełnianie poligonów
}
// Czyszczenie ekranu przed rysowaniem
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) {
// --- WIDOK Z GÓRY ---
maxRotationSpeed = 1.0f;
rotationFriction = 0.5f;
float mapZoom = 400.0f;
gluLookAt(Foward, mapZoom, Sides, Foward, 0.0f, Sides, 1.0f, 0.0f, 0.0f);
}
else if (fpv_view) {
// --- WIDOK FIRST PERSON (FPV) ---
maxRotationSpeed = 0.5f;
rotationFriction = 0.1f;
float rad = Rotation * GL_PI / 180.0f;
// Pozycja oczu: środek łazika + lekko w górę
float eyeX = Foward;
float eyeY = 15.0f; // Wysokość "oczu" nad ziemią
float eyeZ = Sides;
// Kierunek patrzenia: zgodnie z rotacją łazika
// Używamy ujemnego sin/cos, bo w Twoim kodzie Rotation steruje modelem w ten sposób
float lookAtX = Foward - 10.0f * sin(rad);
float lookAtZ = Sides - 10.0f * cos(rad);
gluLookAt(
eyeX, eyeY, eyeZ, // Oko jest wewnątrz/nad łazikiem
lookAtX, eyeY, lookAtZ, // Patrzymy przed siebie (na tej samej wysokości)
0.0f, 1.0f, 0.0f // Góra to oś Y
);
}
else {
// --- WIDOK TRZECIOOSOBOWY (TPP) ---
maxRotationSpeed = 0.5f;
rotationFriction = 0.1f;
float rad = Rotation * GL_PI / 180.0f;
float camX = Foward + CameraHeight * sin(rad);
float camZ = Sides + CameraHeight * cos(rad);
float dynamicHeight = CameraHeight * 0.4f;
gluLookAt(camX, dynamicHeight, camZ, Foward, 10.0f, Sides, 0.0f, 1.0f, 0.0f);
}
// Rysowanie mapy
glPushMatrix();
// glColor3f(0.0, 1.0, 0.0); // Zielony kolor
// mapa.draw(); // nie rysuj mapy/terenu .obj
// Platforma niebędąca częścią siatki:
glColor3d(0.031, 0.51, 0.094); // ciemnozielony
platforma(450.0f, 0.0f, -45.0f, 450.0f, 45.0f);
glPopMatrix();
// Rysowanie łazika
glPushMatrix();
glTranslatef(Foward, 0.0f, Sides); // Translacja łazika
glRotatef(Rotation, 0.0f, 1.0f, 0.0f); // Obrót łazika
glColor3f(1.0, 0.0, 0.0); // Czerwony kolor dla łazika
user.draw(); // Rysuj łazik z pomocą lazik.cpp
UpdateRover(fences);
fpsCounter.update();
glPopMatrix();
// std::cout << "X: " << Foward << " Z: " << Sides << " Rotation: " << Rotation << "\n";
// Rysowanie innych obiektów
// 1 pole siatki = 90x90m
plot( 450.0f, 3.0f, -90.0f, 900.0f, 4.0f, 1); // 1 - poziomo
plot( 0.0f, 3.0f, 405.0f, 990.0f, 4.0f, 0); // 0 - pionowo
plot( 450.0f, 3.0f, 10*90.0f, 900.0f, 4.0f, 1); // 1 - poziomo
plot(10*90.0f, 3.0f, 405.0f, 990.0f, 4.0f, 0); // 0 - pionowo
stodola(45.0f, 0.0f, -45.0f, 70.0f);
// Mechanika gry
short grid_x, grid_z;
ustalPozycjeGracza(Foward, Sides, grid_x, grid_z);
// std::cout << "grid_X: " << grid_x << " grid_Z: " << grid_z << " status: " << siatka[10*grid_x + (9 - grid_z)] << "\n";
ustawSiatkeNaWzorNieNadpisujacPostepu();
tworzKratkiZSiatki();
aktualizujBiezacaKratke(grid_x, grid_z);
// Zamiana buforów (double buffering)
// glfwSwapBuffers(window); // Przełączenie buforów
// glfwPollEvents(); // Obsługa zdarzeń
glPopMatrix(); // Przywrócenie poprzedniej macierzy
glMatrixMode(GL_MODELVIEW); // Ustawienie trybu modelu-widoku
// Wymuszenie wykonania wszystkich rysunków
glFlush();
}
// If necessary, creates a 3-3-2 palette for the device context listed.
HPALETTE static GetOpenGLPalette(HDC hDC) {
HPALETTE hRetPal = NULL; // Handle to palette to be created
PIXELFORMATDESCRIPTOR pfd; // Pixel Format Descriptor
LOGPALETTE* pPal; // Pointer to memory for logical palette
int nPixelFormat; // Pixel format index
int nColors; // Number of entries in palette
int i; // Counting variable
BYTE RedRange, GreenRange, BlueRange;
// Range for each color entry (7,7,and 3)
// Get the pixel format index and retrieve the pixel format description
nPixelFormat = GetPixelFormat(hDC);
DescribePixelFormat(hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
// Does this pixel format require a palette? If not, do not create a
// palette and just return NULL
if (!(pfd.dwFlags & PFD_NEED_PALETTE)) return NULL;
// Number of entries in palette. 8 bits yeilds 256 entries
nColors = 1 << pfd.cColorBits;
// Allocate space for a logical palette structure plus all the palette entries
pPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY));
// Fill in palette header
pPal->palVersion = 0x300; // Windows 3.0
pPal->palNumEntries = nColors; // table size
// Build mask of all 1's. This creates a number represented by having
// the low order x bits set, where x = pfd.cRedBits, pfd.cGreenBits, and
// pfd.cBlueBits.
RedRange = (1 << pfd.cRedBits) - 1;
GreenRange = (1 << pfd.cGreenBits) - 1;
BlueRange = (1 << pfd.cBlueBits) - 1;
// Loop through all the palette entries
for (i = 0; i < nColors; i++) {
// Fill in the 8-bit equivalents for each component
pPal->palPalEntry[i].peRed = (i >> pfd.cRedShift) & RedRange;
pPal->palPalEntry[i].peRed = (unsigned char)(
(double)pPal->palPalEntry[i].peRed * 255.0 / RedRange);
pPal->palPalEntry[i].peGreen = (i >> pfd.cGreenShift) & GreenRange;
pPal->palPalEntry[i].peGreen = (unsigned char)(
(double)pPal->palPalEntry[i].peGreen * 255.0 / GreenRange);
pPal->palPalEntry[i].peBlue = (i >> pfd.cBlueShift) & BlueRange;
pPal->palPalEntry[i].peBlue = (unsigned char)(
(double)pPal->palPalEntry[i].peBlue * 255.0 / BlueRange);
// pPal->palPalEntry[i].peFlags = (unsigned char) NULL;
pPal->palPalEntry[i].peFlags = 0;
}
// Create the palette
hRetPal = CreatePalette(pPal);
// Go ahead and select and realize the palette for this device context
SelectPalette(hDC, hRetPal, FALSE);
RealizePalette(hDC);
// Free the memory used for the logical palette structure
free(pPal);
// Return the handle to the new palette
return hRetPal;
}
void static CreateConsole() {
// Tworzenie nowej konsoli
if (AllocConsole()) {
// Przekierowanie standardowych strumieni do konsoli
FILE* conin;
FILE* conout;
FILE* conerr;
freopen_s(&conin, "conin$", "r", stdin);
freopen_s(&conout, "conout$", "w", stdout);
freopen_s(&conerr, "conout$", "w", stderr);
} else {
MessageBox(NULL, "Nie udalo sie utworzyc konsoli.", "Blad", MB_OK | MB_ICONERROR);
}
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
CreateConsole();
DisableQuickEdit();
AsyncLogger::getInstance().start();
MSG msg;
WNDCLASS wc{};
HWND hWnd;
hInstance = hInst;
// Rejestracja klasy okna
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // DODANO CS_OWNDC - ważne dla OpenGL
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
wc.lpszClassName = lpszAppName;
if (RegisterClass(&wc) == 0) return FALSE;
hWnd = CreateWindow(
lpszAppName, lpszAppName,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
50, 50, 800, 800,
NULL, NULL, hInstance, NULL);
if (hWnd == NULL) return FALSE;
// --- POPRAWKA 1: Pobierz HDC raz ---
HDC hDC = GetDC(hWnd);
const WORD ID_TIMER = 1;
SetTimer(hWnd, ID_TIMER, 100, NULL);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
RenderScene();
// --- POPRAWKA 2: Używaj zapisanego hDC, a nie pobieraj nowego ---
SwapBuffers(hDC);
LOG("FPS: " << fpsCounter.getFPS() << " | Czas: " << fpsCounter.getFrameTime() << "ms");
LimitFPS(targetFPS);
}
}
// --- POPRAWKA 3: Zwolnij HDC przy zamykaniu ---
ReleaseDC(hWnd, hDC);
return msg.wParam;
}
// Window procedure, handles all messages for this program
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static HGLRC hRC; // Permenant Rendering context
static HDC hDC; // Private GDI Device context
float radRotation = Rotation * GL_PI / 180.0f;
switch (message) {
// Window creation, setup for OpenGL
case WM_CREATE:
{
// 1. NAJPIERW INICJALIZACJA KONTEKSTU WINDOWS I OPENGL
// Bez tego funkcje gl... nie będą działać!
hDC = GetDC(hWnd);
SetDCPixelFormat(hDC);
hPalette = GetOpenGLPalette(hDC);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
// Inicjalizacja GLEW i modeli (Twoja funkcja)
SetupRC();
// 2. SPRAWDZENIE MOŻLIWOŚCI KARTY GRAFICZNEJ
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
// 3. GENEROWANIE OBIEKTÓW TEKSTUR
// Używasz indeksów 0, 1, 2, 3 więc potrzebujesz 4 slotów
glGenTextures(4, &texture[0]);
// --- TEKSTURA 0: PŁOTKI ---
bitmapData = LoadBitmapFile((char*)"res/img/woodenTextureHighExposure.bmp", &bitmapInfoHeader);
if (bitmapData) {
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest); // Ostrość pod kątem
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Rozdzielczość w dali (Mipmapy)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Rozdzielczość z bliska
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);
free(bitmapData);
}
// --- TEKSTURA 1: ZIEMIA (Najważniejsza dla efektu oddali) ---
bitmapData = LoadBitmapFile((char*)"res/img/grass02.bmp", &bitmapInfoHeader);
if (bitmapData) {
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);
free(bitmapData);
}
// --- TEKSTURA 2: DACH STODOŁY ---
bitmapData = LoadBitmapFile((char*)"res/img/barnroof.bmp", &bitmapInfoHeader);
if (bitmapData) {
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);
free(bitmapData);
}
// --- TEKSTURA 3: ŚCIANA ---
bitmapData = LoadBitmapFile((char*)"res/img/brickwall.bmp", &bitmapInfoHeader);
if (bitmapData) {
glBindTexture(GL_TEXTURE_2D, texture[3]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);
free(bitmapData);
}
// Ustalenie sposobu mieszania tekstury z tłem
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
break;
// Window is being destroyed, cleanup
case WM_DESTROY:
user.unload();
//glDeleteProgram(programID);
//glDeleteVertexArrays(1, &VertexArrayID);
// Deselect the current rendering context and delete it
wglMakeCurrent(hDC, NULL);
wglDeleteContext(hRC);
// Delete the palette if it was created
if (hPalette != NULL) DeleteObject(hPalette);
// Tell the application to terminate after the window
// is gone.
PostQuitMessage(0);
AsyncLogger::getInstance().stop();
break;
// Window is resized.
case WM_SIZE:
// Call our function which modifies the clipping
// volume and viewport
ChangeSize(LOWORD(lParam), HIWORD(lParam));
break;
// The painting function. This message sent by Windows
// whenever the screen needs updating.
case WM_PAINT:
ValidateRect(hWnd, NULL);
break;
case WM_QUERYNEWPALETTE:
// If the palette was created.
if (hPalette) {
int nRet;
// Selects the palette into the current device context
SelectPalette(hDC, hPalette, FALSE);
// Map entries from the currently selected palette to
// the system palette. The return value is the number
// of palette entries modified.
nRet = RealizePalette(hDC);
// Repaint, forces remap of palette in current window
InvalidateRect(hWnd, NULL, FALSE);
return nRet;
}
break;
// This window may set the palette, even though it is not the
// currently active window.
case WM_PALETTECHANGED:
// Don't do anything if the palette does not exist, or if
// this is the window that changed the palette.
if ((hPalette != NULL) && ((HWND)wParam != hWnd)) {
// Select the palette into the device context
SelectPalette(hDC, hPalette, FALSE);
// Map entries to system palette
RealizePalette(hDC);
// Remap the current colors to the newly realized palette
UpdateColors(hDC);
return 0;
}
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:
switch (wParam) {
case 'W':
keyWPressed = false;
break;
case 'S':
keySPressed = false;
break;
case 'A':
keyAPressed = false;
break;
case 'D':
keyDPressed = false;
break;
case 112: // F1 - Widok z pierwszej osoby (FPV)
fpv_view = !fpv_view;
if (fpv_view) panoramic_view = false; // Wyłącz panoramę, jeśli włączasz FPV
break;
// Obsługa innych klawiszy
}
break;
case WM_KEYDOWN:
switch (wParam) {
case VK_UP:
xRot -= 5.0f;
break;
case VK_DOWN:
xRot += 5.0f;
break;
case VK_LEFT:
yRot -= 5.0f;
break;
case VK_RIGHT:
yRot += 5.0f;
break;
case 'Q':
zRot += 5.0f;
break;
case 'E':
zRot -= 5.0f;
break;
case 'R':
xRot = 0;
yRot = 0;
zRot = 0;
break;
case ' ': // 32
polygonmode = !polygonmode;
if (polygonmode) timestampedCout("Uwaga! Tryb wireframe jest niewydajny i powinien sluzyc tylko do debugowania!");
break;
case 'K':
Kolizja = !Kolizja;
break;
case 'W':
keyWPressed = true;
break;
case 'S':
keySPressed = true;
break;
case 'A':
keyAPressed = true;
break;
case 'D':
keyDPressed = true;
break;
// case 114: // F3
// monitormode = !monitormode;
// if (monitormode) {
// monitormodehelper = std::time(nullptr) - 1;
// timestampedCout("Wlaczono tryb monitorowania wydajnosci.");
// }
// if (!monitormode) timestampedCout("Wylaczono tryb monitorowania wydajnosci.");
// break;
case 116: // F5 włącza widok panoramiczny
panoramic_view = !panoramic_view;
break;
case 16: // Shift również
panoramic_view = !panoramic_view;
break;
case 8: // Backspace czyści postęp
nadpiszNowaSiatke(biezacy_wzor);
break;
default:
timestampedCout("Nacisnieto nierozpoznany klawisz: " << (int)wParam);
}
xRot = (const int)xRot % 360;
yRot = (const int)yRot % 360;
zRot = (const int)zRot % 360;
InvalidateRect(hWnd, NULL, FALSE);
break;
// A menu command
case WM_COMMAND:
switch (LOWORD(wParam)) {
// Exit the program
case ID_FILE_EXIT:
DestroyWindow(hWnd);
break;
// Display the about box
case ID_HELP_ABOUT:
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_ABOUT), hWnd, (DLGPROC)AboutDlgProc);
break;
}
break;
case WM_TIMER:
break;
default: // Passes it on if unproccessed
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0L);
}
// Dialog procedure.
BOOL APIENTRY AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam) {
switch (message) {
// Initialize the dialog box
case WM_INITDIALOG:
int i;
GLenum glError;
// glGetString demo
SetDlgItemText(hDlg, IDC_OPENGL_VENDOR, reinterpret_cast<LPCSTR>(glGetString(GL_VENDOR)));
SetDlgItemText(hDlg, IDC_OPENGL_RENDERER, (LPCSTR)glGetString(GL_RENDERER));
SetDlgItemText(hDlg, IDC_OPENGL_VERSION, (LPCSTR)glGetString(GL_VERSION));
SetDlgItemText(hDlg, IDC_OPENGL_EXTENSIONS, (LPCSTR)glGetString(GL_EXTENSIONS));
// gluGetString demo
SetDlgItemText(hDlg, IDC_GLU_VERSION, (LPCSTR)gluGetString(GLU_VERSION));
SetDlgItemText(hDlg, IDC_GLU_EXTENSIONS, (LPCSTR)gluGetString(GLU_EXTENSIONS));
// Display any recent error messages
i = 0;
do {
glError = glGetError();
SetDlgItemText(hDlg, IDC_ERROR1 + i, (LPCSTR)gluErrorString(glError));
i++;
} while (i < 6 && glError != GL_NO_ERROR);
return (TRUE);
break;
// Process command messages
case WM_COMMAND:
// Validate and Make the changes
if (LOWORD(wParam) == IDOK) EndDialog(hDlg, TRUE);
break;
// Closed from sysbox
case WM_CLOSE:
EndDialog(hDlg, TRUE);
break;
}
return FALSE;
}
void DisableQuickEdit() {
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD prev_mode;
GetConsoleMode(hInput, &prev_mode);
// Wyłączamy opcję ENABLE_QUICK_EDIT_MODE oraz ENABLE_INSERT_MODE
SetConsoleMode(hInput, prev_mode & ~ENABLE_QUICK_EDIT_MODE & ~ENABLE_INSERT_MODE);
}