From 0f937e2e02ae50c03a226570845045feb0c30971 Mon Sep 17 00:00:00 2001 From: Ariari04 Date: Tue, 9 Dec 2025 21:12:54 +0600 Subject: [PATCH 01/11] added music volume bar, without logic --- Game.cpp | 105 ++++++++++++++++++++++++++++ Game.h | 10 +++ resources/button.png | 3 + resources/musicVolumeBarButton.png | 3 + resources/musicVolumeBarTexture.png | 3 + 5 files changed, 124 insertions(+) create mode 100644 resources/button.png create mode 100644 resources/musicVolumeBarButton.png create mode 100644 resources/musicVolumeBarTexture.png diff --git a/Game.cpp b/Game.cpp index d19757a..d0f3956 100755 --- a/Game.cpp +++ b/Game.cpp @@ -193,7 +193,68 @@ void Game::setup() { boxRenderArr[i].RefreshVBO(); } + + buttonTexture = std::make_unique(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE)); + + button.data.PositionData.push_back({ 100, 100, 0 }); + button.data.PositionData.push_back({ 100, 150, 0 }); + button.data.PositionData.push_back({ 300, 150, 0 }); + button.data.PositionData.push_back({ 100, 100, 0 }); + button.data.PositionData.push_back({ 300, 150, 0 }); + button.data.PositionData.push_back({ 300, 100, 0 }); + + button.data.TexCoordData.push_back({ 0,0 }); + button.data.TexCoordData.push_back({ 0,1 }); + button.data.TexCoordData.push_back({ 1,1 }); + button.data.TexCoordData.push_back({ 0,0 }); + button.data.TexCoordData.push_back({ 1,1 }); + button.data.TexCoordData.push_back({ 1,0 }); + + button.RefreshVBO(); + + musicVolumeBarTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE)); + + musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 }); + + musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBar.data.TexCoordData.push_back({ 0,1 }); + musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBar.data.TexCoordData.push_back({ 1,0 }); + + musicVolumeBar.RefreshVBO(); + + + musicVolumeBarButtonTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE)); + + float musicVolumeBarButtonButtonCenterX = 1195.0f; + float musicVolumeBarButtonButtonCenterY = 350.0f; + float musicVolumeBarButtonButtonRadius = 25.0f; + + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); + + musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 }); + + musicVolumeBarButton.RefreshVBO(); renderer.InitOpenGL(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -230,6 +291,7 @@ void Game::drawCubemap() renderer.shaderManager.PopShader(); CheckGlError(); } + void Game::drawShip() { static const std::string defaultShaderName = "default"; @@ -305,6 +367,46 @@ void Game::drawBoxes() CheckGlError(); } +void Game::drawUI() +{ + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; + + + glClear(GL_DEPTH_BUFFER_BIT); + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vTexCoordName); + + renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); + renderer.PushMatrix(); + + renderer.LoadIdentity(); + + + glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); + renderer.DrawVertexRenderStruct(button); + + glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID()); + renderer.DrawVertexRenderStruct(musicVolumeBar); + + glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); + renderer.DrawVertexRenderStruct(musicVolumeBarButton); + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); + + renderer.shaderManager.PopShader(); + CheckGlError(); +} + void Game::drawScene() { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; @@ -322,6 +424,8 @@ void Game::drawScene() { drawCubemap(); drawShip(); drawBoxes(); + + drawUI(); CheckGlError(); } @@ -406,6 +510,7 @@ void Game::update() { } else if (event.type == SDL_MOUSEBUTTONDOWN) { // 1. + std::cout << event.button.x << " " << event.button.y << '\n'; Environment::tapDownHold = true; // Environment::tapDownStartPos.v[0] = event.button.x; diff --git a/Game.h b/Game.h index b97d62e..852df0c 100755 --- a/Game.h +++ b/Game.h @@ -32,6 +32,7 @@ private: void drawCubemap(); void drawShip(); void drawBoxes(); + void drawUI(); SDL_Window* window; SDL_GLContext glContext; @@ -55,7 +56,16 @@ private: std::vector boxCoordsArr; std::vector boxRenderArr; + + std::shared_ptr buttonTexture; + VertexRenderStruct button; + + std::shared_ptr musicVolumeBarTexture; + VertexRenderStruct musicVolumeBar; + + std::shared_ptr musicVolumeBarButtonTexture; + VertexRenderStruct musicVolumeBarButton; }; } // namespace ZL \ No newline at end of file diff --git a/resources/button.png b/resources/button.png new file mode 100644 index 0000000..1a7ed12 --- /dev/null +++ b/resources/button.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db17a81ad6da6b62c1ecbe688bf18c5a45ef6351eadad5031c1ec694875257f8 +size 2025 diff --git a/resources/musicVolumeBarButton.png b/resources/musicVolumeBarButton.png new file mode 100644 index 0000000..156ffe5 --- /dev/null +++ b/resources/musicVolumeBarButton.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a1bf17f2e6006ef9002d1abd2dc13addc9a78544d65627923683ebc7504404b +size 863 diff --git a/resources/musicVolumeBarTexture.png b/resources/musicVolumeBarTexture.png new file mode 100644 index 0000000..a0b03e4 --- /dev/null +++ b/resources/musicVolumeBarTexture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85871e9d63fb83badf14c011adca37fa223b1c375c14728a43edc277f1ef69fb +size 777 From fec7e08c2b5b0b9e745a0df0bdc6f090f6d9c6be Mon Sep 17 00:00:00 2001 From: Ariari04 Date: Wed, 10 Dec 2025 20:39:09 +0600 Subject: [PATCH 02/11] added dragging scroll button for music volume --- Game.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++-------- Game.h | 12 ++++++++++ 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/Game.cpp b/Game.cpp index d0f3956..33602a0 100755 --- a/Game.cpp +++ b/Game.cpp @@ -233,9 +233,7 @@ void Game::setup() { musicVolumeBarButtonTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE)); - float musicVolumeBarButtonButtonCenterX = 1195.0f; float musicVolumeBarButtonButtonCenterY = 350.0f; - float musicVolumeBarButtonButtonRadius = 25.0f; musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); @@ -366,7 +364,37 @@ void Game::drawBoxes() renderer.shaderManager.PopShader(); CheckGlError(); } +void Game::UpdateVolumeKnob() { + float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY); + auto& pos = musicVolumeBarButton.data.PositionData; + + pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; + pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; + pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; + pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; + pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; + pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; + + musicVolumeBarButton.RefreshVBO(); + +} + +void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) { + + int uiX = mouseX; + int uiY = Environment::height - mouseY; + if (uiY < volumeBarMinY || uiY > volumeBarMaxY) + { + return; + } + + float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY); + if (t < 0.0f) t = 0.0f; + if (t > 1.0f) t = 1.0f; + musicVolume = t; + UpdateVolumeKnob(); +} void Game::drawUI() { static const std::string defaultShaderName = "default"; @@ -510,21 +538,43 @@ void Game::update() { } else if (event.type == SDL_MOUSEBUTTONDOWN) { // 1. - std::cout << event.button.x << " " << event.button.y << '\n'; - Environment::tapDownHold = true; - // - Environment::tapDownStartPos.v[0] = event.button.x; - Environment::tapDownStartPos.v[1] = event.button.y; - // - Environment::tapDownCurrentPos.v[0] = event.button.x; - Environment::tapDownCurrentPos.v[1] = event.button.y; + + int mx = event.button.x; + int my = event.button.y; + + std::cout << mx << " " << my << '\n'; + int uiX = mx; + int uiY = Environment::height - my; + if (uiX >= volumeBarMinX && uiX <= volumeBarMaxX && + uiY >= volumeBarMinY && uiY <= volumeBarMaxY) { + isDraggingVolume = true; + UpdateVolumeFromMouse(mx, my); + } + else { + Environment::tapDownHold = true; + // + Environment::tapDownStartPos.v[0] = event.button.x; + Environment::tapDownStartPos.v[1] = event.button.y; + // + Environment::tapDownCurrentPos.v[0] = event.button.x; + Environment::tapDownCurrentPos.v[1] = event.button.y; + } + } else if (event.type == SDL_MOUSEBUTTONUP) { // 2. + isDraggingVolume = false; Environment::tapDownHold = false; } else if (event.type == SDL_MOUSEMOTION) { // 3. + int mx = event.motion.x; + int my = event.motion.y; + + if (isDraggingVolume) { + // + UpdateVolumeFromMouse(mx, my); + } if (Environment::tapDownHold) { // , Environment::tapDownCurrentPos.v[0] = event.motion.x; diff --git a/Game.h b/Game.h index 852df0c..34b608a 100755 --- a/Game.h +++ b/Game.h @@ -66,6 +66,18 @@ private: std::shared_ptr musicVolumeBarButtonTexture; VertexRenderStruct musicVolumeBarButton; + + + bool isDraggingVolume = false; + float musicVolume = 1.0f; + float volumeBarMinX = 1190.0f; + float volumeBarMaxX = 1200.0f; + float volumeBarMinY = 100.0f; + float volumeBarMaxY = 600.0f; + float musicVolumeBarButtonButtonCenterX = 1195.0f; + float musicVolumeBarButtonButtonRadius = 25.0f; + void UpdateVolumeFromMouse(int mouseX, int mouseY); + void UpdateVolumeKnob(); }; } // namespace ZL \ No newline at end of file From c02273d29d98b332744adf82a1b56e4f2fcda7c4 Mon Sep 17 00:00:00 2001 From: Ariari04 Date: Wed, 10 Dec 2025 20:58:10 +0600 Subject: [PATCH 03/11] added ship velocity using dragging volume --- Game.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Game.cpp b/Game.cpp index 33602a0..75e9130 100755 --- a/Game.cpp +++ b/Game.cpp @@ -384,6 +384,7 @@ void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) { int uiX = mouseX; int uiY = Environment::height - mouseY; + Environment::shipVelocity = (musicVolume - 0.2) * (20.0); if (uiY < volumeBarMinY || uiY > volumeBarMaxY) { return; From ae1ca71a20b05371cde3fd7117f72e67051a5fa1 Mon Sep 17 00:00:00 2001 From: vottozi Date: Thu, 11 Dec 2025 06:42:02 +0000 Subject: [PATCH 04/11] test --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 4fd844b..ca37df1 100644 --- a/Readme.md +++ b/Readme.md @@ -2,6 +2,7 @@ download from https://cmake.org/download/ + Windows x64 Installer: cmake-4.2.0-windows-x86_64.msi From 3887208b8d9517ad0988fe3aaf27943856b23c63 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Fri, 12 Dec 2025 23:53:34 +0300 Subject: [PATCH 05/11] Working on planet --- CMakeLists.txt | 2 + Game.cpp | 1059 +++++++++++++++++++++++----------------------- Game.h | 46 +- PlanetObject.cpp | 292 +++++++++++++ PlanetObject.h | 100 +++++ ZLMath.h | 12 + 6 files changed, 961 insertions(+), 550 deletions(-) create mode 100644 PlanetObject.cpp create mode 100644 PlanetObject.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5462c7d..819c8b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,6 +438,8 @@ add_executable(space-game001 Utils.h SparkEmitter.cpp SparkEmitter.h + PlanetObject.cpp + PlanetObject.h ) # Установка проекта по умолчанию для Visual Studio diff --git a/Game.cpp b/Game.cpp index 5066988..38f04d3 100755 --- a/Game.cpp +++ b/Game.cpp @@ -12,612 +12,615 @@ namespace ZL { #ifdef EMSCRIPTEN - const char* CONST_ZIP_FILE = "space-game001.zip"; + const char* CONST_ZIP_FILE = "space-game001.zip"; #else - const char* CONST_ZIP_FILE = ""; + const char* CONST_ZIP_FILE = ""; #endif - Vector4f generateRandomQuaternion(std::mt19937& gen) - { - // Ðàñïðåäåëåíèå äëÿ ãåíåðàöèè ñëó÷àéíûõ êîîðäèíàò êâàòåðíèîíà - std::normal_distribution<> distrib(0.0, 1.0); + Vector4f generateRandomQuaternion(std::mt19937& gen) + { + // Ðàñïðåäåëåíèå äëÿ ãåíåðàöèè ñëó÷àéíûõ êîîðäèíàò êâàòåðíèîíà + std::normal_distribution<> distrib(0.0, 1.0); - // Ãåíåðèðóåì ÷åòûðå ñëó÷àéíûõ ÷èñëà èç íîðìàëüíîãî ðàñïðåäåëåíèÿ N(0, 1). - // Íîðìàëèçàöèÿ ýòîãî âåêòîðà äàåò ðàâíîìåðíîå ðàñïðåäåëåíèå ïî 4D-ñôåðå (ò.å. êâàòåðíèîí åäèíè÷íîé äëèíû). - Vector4f randomQuat = { - (float)distrib(gen), - (float)distrib(gen), - (float)distrib(gen), - (float)distrib(gen) - }; + // Ãåíåðèðóåì ÷åòûðå ñëó÷àéíûõ ÷èñëà èç íîðìàëüíîãî ðàñïðåäåëåíèÿ N(0, 1). + // Íîðìàëèçàöèÿ ýòîãî âåêòîðà äàåò ðàâíîìåðíîå ðàñïðåäåëåíèå ïî 4D-ñôåðå (ò.å. êâàòåðíèîí åäèíè÷íîé äëèíû). + Vector4f randomQuat = { + (float)distrib(gen), + (float)distrib(gen), + (float)distrib(gen), + (float)distrib(gen) + }; - return randomQuat.normalized(); - } + return randomQuat.normalized(); + } - // --- Îñíîâíàÿ ôóíêöèÿ ãåíåðàöèè --- - std::vector generateRandomBoxCoords(int N) - { - // Êîíñòàíòû - const float MIN_DISTANCE = 3.0f; - const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Ðàáîòàåì ñ êâàäðàòîì ðàññòîÿíèÿ - const float MIN_COORD = -100.0f; - const float MAX_COORD = 100.0f; - const int MAX_ATTEMPTS = 1000; // Îãðàíè÷åíèå íà êîëè÷åñòâî ïîïûòîê, ÷òîáû èçáåæàòü áåñêîíå÷íîãî öèêëà + // --- Îñíîâíàÿ ôóíêöèÿ ãåíåðàöèè --- + std::vector generateRandomBoxCoords(int N) + { + // Êîíñòàíòû + const float MIN_DISTANCE = 3.0f; + const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Ðàáîòàåì ñ êâàäðàòîì ðàññòîÿíèÿ + const float MIN_COORD = -100.0f; + const float MAX_COORD = 100.0f; + const int MAX_ATTEMPTS = 1000; // Îãðàíè÷åíèå íà êîëè÷åñòâî ïîïûòîê, ÷òîáû èçáåæàòü áåñêîíå÷íîãî öèêëà - std::vector boxCoordsArr; - boxCoordsArr.reserve(N); // Ðåçåðâèðóåì ïàìÿòü + std::vector boxCoordsArr; + boxCoordsArr.reserve(N); // Ðåçåðâèðóåì ïàìÿòü - // 1. Èíèöèàëèçàöèÿ ãåíåðàòîðà ïñåâäîñëó÷àéíûõ ÷èñåë - // Èñïîëüçóåì Mersenne Twister (mt19937) êàê âûñîêîêà÷åñòâåííûé ãåíåðàòîð - std::random_device rd; - std::mt19937 gen(rd()); + // 1. Èíèöèàëèçàöèÿ ãåíåðàòîðà ïñåâäîñëó÷àéíûõ ÷èñåë + // Èñïîëüçóåì Mersenne Twister (mt19937) êàê âûñîêîêà÷åñòâåííûé ãåíåðàòîð + std::random_device rd; + std::mt19937 gen(rd()); - // 2. Îïðåäåëåíèå ðàâíîìåðíîãî ðàñïðåäåëåíèÿ äëÿ êîîðäèíàò [MIN_COORD, MAX_COORD] - std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD); + // 2. Îïðåäåëåíèå ðàâíîìåðíîãî ðàñïðåäåëåíèÿ äëÿ êîîðäèíàò [MIN_COORD, MAX_COORD] + std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD); - int generatedCount = 0; + int generatedCount = 0; - while (generatedCount < N) - { - bool accepted = false; - int attempts = 0; + while (generatedCount < N) + { + bool accepted = false; + int attempts = 0; - // Ïîïûòêà íàéòè ïîäõîäÿùèå êîîðäèíàòû - while (!accepted && attempts < MAX_ATTEMPTS) - { - // Ãåíåðèðóåì íîâûå ñëó÷àéíûå êîîðäèíàòû - Vector3f newPos( - (float)distrib(gen), - (float)distrib(gen), - (float)distrib(gen) - ); + // Ïîïûòêà íàéòè ïîäõîäÿùèå êîîðäèíàòû + while (!accepted && attempts < MAX_ATTEMPTS) + { + // Ãåíåðèðóåì íîâûå ñëó÷àéíûå êîîðäèíàòû + Vector3f newPos( + (float)distrib(gen), + (float)distrib(gen), + (float)distrib(gen) + ); - // Ïðîâåðêà ðàññòîÿíèÿ äî âñåõ óæå ñóùåñòâóþùèõ îáúåêòîâ - accepted = true; // Ïðåäïîëàãàåì, ÷òî ïîäõîäèò, ïîêà íå äîêàçàíî îáðàòíîå - for (const auto& existingBox : boxCoordsArr) - { - // Ðàñ÷åò âåêòîðà ðàçíîñòè - Vector3f diff = newPos - existingBox.pos; + // Ïðîâåðêà ðàññòîÿíèÿ äî âñåõ óæå ñóùåñòâóþùèõ îáúåêòîâ + accepted = true; // Ïðåäïîëàãàåì, ÷òî ïîäõîäèò, ïîêà íå äîêàçàíî îáðàòíîå + for (const auto& existingBox : boxCoordsArr) + { + // Ðàñ÷åò âåêòîðà ðàçíîñòè + Vector3f diff = newPos - existingBox.pos; - // Ðàñ÷åò êâàäðàòà ðàññòîÿíèÿ - float distanceSquared = diff.squaredNorm(); + // Ðàñ÷åò êâàäðàòà ðàññòîÿíèÿ + float distanceSquared = diff.squaredNorm(); - // Åñëè êâàäðàò ðàññòîÿíèÿ ìåíüøå êâàäðàòà ìèíèìàëüíîãî ðàññòîÿíèÿ - if (distanceSquared < MIN_DISTANCE_SQUARED) - { - accepted = false; // Îòêëîíÿåì, ñëèøêîì áëèçêî - break; // Íåò ñìûñëà ïðîâåðÿòü äàëüøå, åñëè îäíî íàðóøåíèå íàéäåíî - } - } + // Åñëè êâàäðàò ðàññòîÿíèÿ ìåíüøå êâàäðàòà ìèíèìàëüíîãî ðàññòîÿíèÿ + if (distanceSquared < MIN_DISTANCE_SQUARED) + { + accepted = false; // Îòêëîíÿåì, ñëèøêîì áëèçêî + break; // Íåò ñìûñëà ïðîâåðÿòü äàëüøå, åñëè îäíî íàðóøåíèå íàéäåíî + } + } - if (accepted) - { - // 2. Ãåíåðèðóåì ñëó÷àéíûé êâàòåðíèîí - Vector4f randomQuat = generateRandomQuaternion(gen); + if (accepted) + { + // 2. Ãåíåðèðóåì ñëó÷àéíûé êâàòåðíèîí + Vector4f randomQuat = generateRandomQuaternion(gen); - // 3. Ïðåîáðàçóåì åãî â ìàòðèöó âðàùåíèÿ - Matrix3f randomMatrix = QuatToMatrix(randomQuat); + // 3. Ïðåîáðàçóåì åãî â ìàòðèöó âðàùåíèÿ + Matrix3f randomMatrix = QuatToMatrix(randomQuat); - // 4. Äîáàâëÿåì îáúåêò ñ íîâîé ñëó÷àéíîé ìàòðèöåé - boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); - generatedCount++; - } - attempts++; - } + // 4. Äîáàâëÿåì îáúåêò ñ íîâîé ñëó÷àéíîé ìàòðèöåé + boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); + generatedCount++; + } + attempts++; + } - // Åñëè ïðåâûøåíî ìàêñèìàëüíîå êîëè÷åñòâî ïîïûòîê, âûõîäèì èç öèêëà, - // ÷òîáû èçáåæàòü çàâèñàíèÿ, åñëè N ñëèøêîì âåëèêî èëè äèàïàçîí ñëèøêîì ìàë. - if (!accepted) { - std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl; - break; - } - } + // Åñëè ïðåâûøåíî ìàêñèìàëüíîå êîëè÷åñòâî ïîïûòîê, âûõîäèì èç öèêëà, + // ÷òîáû èçáåæàòü çàâèñàíèÿ, åñëè N ñëèøêîì âåëèêî èëè äèàïàçîí ñëèøêîì ìàë. + if (!accepted) { + std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl; + break; + } + } - return boxCoordsArr; - } + return boxCoordsArr; + } - Game::Game() - : window(nullptr) - , glContext(nullptr) - , newTickCount(0) - , lastTickCount(0) - { - std::vector emissionPoints = { - Vector3f{-2.1f, 0.9f, 5.0f}, - Vector3f{2.1f, 0.9f, 5.0f} - }; - sparkEmitter = SparkEmitter(emissionPoints, 100.0f); - } + Game::Game() + : window(nullptr) + , glContext(nullptr) + , newTickCount(0) + , lastTickCount(0) + { + std::vector emissionPoints = { + Vector3f{-2.1f, 0.9f, 5.0f}, + Vector3f{2.1f, 0.9f, 5.0f} + }; + sparkEmitter = SparkEmitter(emissionPoints, 100.0f); + } - Game::~Game() { - if (glContext) { - SDL_GL_DeleteContext(glContext); - } - if (window) { - SDL_DestroyWindow(window); - } - SDL_Quit(); - } + Game::~Game() { + if (glContext) { + SDL_GL_DeleteContext(glContext); + } + if (window) { + SDL_DestroyWindow(window); + } + SDL_Quit(); + } - void Game::setup() { - glContext = SDL_GL_CreateContext(ZL::Environment::window); + void Game::setup() { + glContext = SDL_GL_CreateContext(ZL::Environment::window); - ZL::BindOpenGlFunctions(); - ZL::CheckGlError(); + ZL::BindOpenGlFunctions(); + ZL::CheckGlError(); - // Initialize renderer + // Initialize renderer #ifdef EMSCRIPTEN - renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE); - renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); - renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE); #else - renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); - renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE); - renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_desktop.fragment", CONST_ZIP_FILE); #endif - cubemapTexture = std::make_shared( - std::array{ - CreateTextureDataFromBmp24("./resources/sky/space_rt.bmp", CONST_ZIP_FILE), - CreateTextureDataFromBmp24("./resources/sky/space_lf.bmp", CONST_ZIP_FILE), - CreateTextureDataFromBmp24("./resources/sky/space_up.bmp", CONST_ZIP_FILE), - CreateTextureDataFromBmp24("./resources/sky/space_dn.bmp", CONST_ZIP_FILE), - CreateTextureDataFromBmp24("./resources/sky/space_bk.bmp", CONST_ZIP_FILE), - CreateTextureDataFromBmp24("./resources/sky/space_ft.bmp", CONST_ZIP_FILE) - }); + cubemapTexture = std::make_shared( + std::array{ + CreateTextureDataFromBmp24("./resources/sky/space_rt.bmp", CONST_ZIP_FILE), + CreateTextureDataFromBmp24("./resources/sky/space_lf.bmp", CONST_ZIP_FILE), + CreateTextureDataFromBmp24("./resources/sky/space_up.bmp", CONST_ZIP_FILE), + CreateTextureDataFromBmp24("./resources/sky/space_dn.bmp", CONST_ZIP_FILE), + CreateTextureDataFromBmp24("./resources/sky/space_bk.bmp", CONST_ZIP_FILE), + CreateTextureDataFromBmp24("./resources/sky/space_ft.bmp", CONST_ZIP_FILE) + }); - cubemap.data = ZL::CreateCubemap(500); - cubemap.RefreshVBO(); + cubemap.data = ZL::CreateCubemap(500); + cubemap.RefreshVBO(); - //Load texture - spaceshipTexture = std::make_unique(CreateTextureDataFromPng("./resources/DefaultMaterial_BaseColor.png", CONST_ZIP_FILE)); - spaceshipBase = LoadFromTextFile02("./resources/spaceship005.txt", CONST_ZIP_FILE); - spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0))); - //spaceshipBase.Move(Vector3f{ -0.52998, -13, 0 }); - spaceshipBase.Move(Vector3f{ -0.52998, -10, 10 }); - - spaceship.AssignFrom(spaceshipBase); - spaceship.RefreshVBO(); - - //Boxes - boxTexture = std::make_unique(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE)); - boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE); - - boxCoordsArr = generateRandomBoxCoords(50); + //Load texture + spaceshipTexture = std::make_unique(CreateTextureDataFromPng("./resources/DefaultMaterial_BaseColor.png", CONST_ZIP_FILE)); + spaceshipBase = LoadFromTextFile02("./resources/spaceship005.txt", CONST_ZIP_FILE); + spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0))); + //spaceshipBase.Move(Vector3f{ -0.52998, -13, 0 }); + spaceshipBase.Move(Vector3f{ -0.52998, -10, 10 }); + + spaceship.AssignFrom(spaceshipBase); + spaceship.RefreshVBO(); + + //Boxes + boxTexture = std::make_unique(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE)); + boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE); + + boxCoordsArr = generateRandomBoxCoords(50); - boxRenderArr.resize(boxCoordsArr.size()); + boxRenderArr.resize(boxCoordsArr.size()); - for (int i = 0; i < boxCoordsArr.size(); i++) - { - boxRenderArr[i].AssignFrom(boxBase); - boxRenderArr[i].RefreshVBO(); - } + for (int i = 0; i < boxCoordsArr.size(); i++) + { + boxRenderArr[i].AssignFrom(boxBase); + boxRenderArr[i].RefreshVBO(); + } - sparkTexture = std::make_unique(CreateTextureDataFromPng("./resources/spark.png", CONST_ZIP_FILE)); + sparkTexture = std::make_unique(CreateTextureDataFromPng("./resources/spark.png", CONST_ZIP_FILE)); - sparkEmitter.setTexture(sparkTexture); + sparkEmitter.setTexture(sparkTexture); - buttonTexture = std::make_unique(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE)); - - button.data.PositionData.push_back({ 100, 100, 0 }); - button.data.PositionData.push_back({ 100, 150, 0 }); - button.data.PositionData.push_back({ 300, 150, 0 }); - button.data.PositionData.push_back({ 100, 100, 0 }); - button.data.PositionData.push_back({ 300, 150, 0 }); - button.data.PositionData.push_back({ 300, 100, 0 }); - - button.data.TexCoordData.push_back({ 0,0 }); - button.data.TexCoordData.push_back({ 0,1 }); - button.data.TexCoordData.push_back({ 1,1 }); - button.data.TexCoordData.push_back({ 0,0 }); - button.data.TexCoordData.push_back({ 1,1 }); - button.data.TexCoordData.push_back({ 1,0 }); - - button.RefreshVBO(); + buttonTexture = std::make_unique(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE)); - musicVolumeBarTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE)); + button.data.PositionData.push_back({ 100, 100, 0 }); + button.data.PositionData.push_back({ 100, 150, 0 }); + button.data.PositionData.push_back({ 300, 150, 0 }); + button.data.PositionData.push_back({ 100, 100, 0 }); + button.data.PositionData.push_back({ 300, 150, 0 }); + button.data.PositionData.push_back({ 300, 100, 0 }); + + button.data.TexCoordData.push_back({ 0,0 }); + button.data.TexCoordData.push_back({ 0,1 }); + button.data.TexCoordData.push_back({ 1,1 }); + button.data.TexCoordData.push_back({ 0,0 }); + button.data.TexCoordData.push_back({ 1,1 }); + button.data.TexCoordData.push_back({ 1,0 }); - musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 }); + button.RefreshVBO(); + + musicVolumeBarTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE)); - musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); - musicVolumeBar.data.TexCoordData.push_back({ 0,1 }); - musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); - musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); - musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); - musicVolumeBar.data.TexCoordData.push_back({ 1,0 }); - - musicVolumeBar.RefreshVBO(); - - - musicVolumeBarButtonTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE)); - - float musicVolumeBarButtonButtonCenterY = 350.0f; - - musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); - musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); - musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); - musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); - musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); - musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); - - musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); - musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 }); - musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); - musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); - musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); - musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 }); - - musicVolumeBarButton.RefreshVBO(); - renderer.InitOpenGL(); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - } - - void Game::drawCubemap() - { - static const std::string defaultShaderName = "default"; - static const std::string envShaderName = "env"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; - - renderer.shaderManager.PushShader(envShaderName); - renderer.RenderUniform1i(textureUniformName, 0); - renderer.EnableVertexAttribArray(vPositionName); - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, - static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); - renderer.PushMatrix(); - renderer.LoadIdentity(); - renderer.RotateMatrix(Environment::inverseShipMatrix); - - CheckGlError(); - - glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID()); - renderer.DrawVertexRenderStruct(cubemap); - - CheckGlError(); - - - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); - - renderer.shaderManager.PopShader(); - CheckGlError(); - } - - void Game::drawShip() - { - static const std::string defaultShaderName = "default"; - static const std::string envShaderName = "env"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; - - renderer.shaderManager.PushShader(defaultShaderName); - renderer.RenderUniform1i(textureUniformName, 0); - renderer.EnableVertexAttribArray(vPositionName); - renderer.EnableVertexAttribArray(vTexCoordName); - - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, - static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); - renderer.PushMatrix(); - - renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - - - glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); - renderer.DrawVertexRenderStruct(spaceship); - sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); - - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); - renderer.DisableVertexAttribArray(vTexCoordName); - - renderer.shaderManager.PopShader(); - CheckGlError(); - } - - void Game::drawBoxes() - { - static const std::string defaultShaderName = "default"; - static const std::string envShaderName = "env"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; - - renderer.shaderManager.PushShader(defaultShaderName); - renderer.RenderUniform1i(textureUniformName, 0); - renderer.EnableVertexAttribArray(vPositionName); - renderer.EnableVertexAttribArray(vTexCoordName); - - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, - static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); - - for (int i = 0; i < boxCoordsArr.size(); i++) - { - renderer.PushMatrix(); - - renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - renderer.RotateMatrix(Environment::inverseShipMatrix); - renderer.TranslateMatrix(-Environment::shipPosition); - renderer.TranslateMatrix(boxCoordsArr[i].pos); - renderer.RotateMatrix(boxCoordsArr[i].m); - - glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID()); - renderer.DrawVertexRenderStruct(boxRenderArr[i]); - - renderer.PopMatrix(); - } - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); - renderer.DisableVertexAttribArray(vTexCoordName); + musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); + musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 }); - renderer.shaderManager.PopShader(); - CheckGlError(); - } - void Game::UpdateVolumeKnob() { - float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY); + musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBar.data.TexCoordData.push_back({ 0,1 }); + musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBar.data.TexCoordData.push_back({ 1,0 }); - auto& pos = musicVolumeBarButton.data.PositionData; + musicVolumeBar.RefreshVBO(); - pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; - pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; - pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; - pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; - pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; - pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; - musicVolumeBarButton.RefreshVBO(); + musicVolumeBarButtonTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE)); + + float musicVolumeBarButtonButtonCenterY = 350.0f; + + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); + musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); + + musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); + musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 }); + + musicVolumeBarButton.RefreshVBO(); + renderer.InitOpenGL(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + planetObject.init(); + + } + + void Game::drawCubemap() + { + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; + + renderer.shaderManager.PushShader(envShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.RotateMatrix(Environment::inverseShipMatrix); + + CheckGlError(); + + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID()); + renderer.DrawVertexRenderStruct(cubemap); + + CheckGlError(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + + renderer.shaderManager.PopShader(); + CheckGlError(); + } + + void Game::drawShip() + { + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vTexCoordName); + + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + renderer.PushMatrix(); + + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + + + glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); + renderer.DrawVertexRenderStruct(spaceship); + sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); + + renderer.shaderManager.PopShader(); + CheckGlError(); + } + + void Game::drawBoxes() + { + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vTexCoordName); + + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + + for (int i = 0; i < boxCoordsArr.size(); i++) + { + renderer.PushMatrix(); + + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Environment::inverseShipMatrix); + renderer.TranslateMatrix(-Environment::shipPosition); + renderer.TranslateMatrix(boxCoordsArr[i].pos); + renderer.RotateMatrix(boxCoordsArr[i].m); + + glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID()); + renderer.DrawVertexRenderStruct(boxRenderArr[i]); + + renderer.PopMatrix(); + } + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); + + renderer.shaderManager.PopShader(); + CheckGlError(); + } + void Game::UpdateVolumeKnob() { + float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY); - } + auto& pos = musicVolumeBarButton.data.PositionData; - void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) { + pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; + pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; + pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; + pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; + pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; + pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; - int uiX = mouseX; - int uiY = Environment::height - mouseY; - Environment::shipVelocity = (musicVolume) * (20.0); - if (uiY < volumeBarMinY || uiY > volumeBarMaxY) - { - return; - } + musicVolumeBarButton.RefreshVBO(); - float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY); - if (t < 0.0f) t = 0.0f; - if (t > 1.0f) t = 1.0f; - musicVolume = t; - UpdateVolumeKnob(); - } - void Game::drawUI() - { - static const std::string defaultShaderName = "default"; - static const std::string envShaderName = "env"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; + } + void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) { - glClear(GL_DEPTH_BUFFER_BIT); + int uiX = mouseX; + int uiY = Environment::height - mouseY; + Environment::shipVelocity = (musicVolume) * (20.0); + if (uiY < volumeBarMinY || uiY > volumeBarMaxY) + { + return; + } - renderer.shaderManager.PushShader(defaultShaderName); - renderer.RenderUniform1i(textureUniformName, 0); - renderer.EnableVertexAttribArray(vPositionName); - renderer.EnableVertexAttribArray(vTexCoordName); + float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY); + if (t < 0.0f) t = 0.0f; + if (t > 1.0f) t = 1.0f; + musicVolume = t; + UpdateVolumeKnob(); + } + void Game::drawUI() + { + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; - renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); - renderer.PushMatrix(); - renderer.LoadIdentity(); + glClear(GL_DEPTH_BUFFER_BIT); + renderer.shaderManager.PushShader(defaultShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vTexCoordName); - glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); - renderer.DrawVertexRenderStruct(button); + renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); + renderer.PushMatrix(); - glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID()); - renderer.DrawVertexRenderStruct(musicVolumeBar); + renderer.LoadIdentity(); - glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); - renderer.DrawVertexRenderStruct(musicVolumeBarButton); - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); - renderer.DisableVertexAttribArray(vTexCoordName); + glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); + renderer.DrawVertexRenderStruct(button); - renderer.shaderManager.PopShader(); - CheckGlError(); - } + glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID()); + renderer.DrawVertexRenderStruct(musicVolumeBar); - void Game::drawScene() { - static const std::string defaultShaderName = "default"; - static const std::string envShaderName = "env"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; + glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); + renderer.DrawVertexRenderStruct(musicVolumeBarButton); - glClearColor(0.0f, 0.5f, 1.0f, 1.0f); - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); - glViewport(0, 0, Environment::width, Environment::height); + renderer.shaderManager.PopShader(); + CheckGlError(); + } - CheckGlError(); + void Game::drawScene() { + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; - drawCubemap(); - drawShip(); - drawBoxes(); + glClearColor(0.0f, 0.5f, 1.0f, 1.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - drawUI(); + glViewport(0, 0, Environment::width, Environment::height); - CheckGlError(); - } + CheckGlError(); - void Game::processTickCount() { + drawCubemap(); + drawShip(); + planetObject.draw(renderer); + drawBoxes(); - if (lastTickCount == 0) { - lastTickCount = SDL_GetTicks64(); - return; - } + drawUI(); - newTickCount = SDL_GetTicks64(); - if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) { - size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ? - CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount; + CheckGlError(); + } - //gameObjects.updateScene(delta); - sparkEmitter.update(static_cast(delta)); + void Game::processTickCount() { - if (Environment::tapDownHold) { + if (lastTickCount == 0) { + lastTickCount = SDL_GetTicks64(); + return; + } - float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0]; - float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1]; + newTickCount = SDL_GetTicks64(); + if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) { + size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ? + CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount; + //gameObjects.updateScene(delta); + sparkEmitter.update(static_cast(delta)); - if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold - { - - float rotationPower = sqrtf(diffx * diffx + diffy * diffy); - - //std::cout << rotationPower << std::endl; - - float deltaAlpha = rotationPower * delta * M_PI / 500000.f; - - Vector3f rotationDirection = { diffy, diffx, 0 }; - - rotationDirection = rotationDirection.normalized(); - - Vector4f rotateQuat = { - rotationDirection.v[0] * sin(deltaAlpha * 0.5f), - rotationDirection.v[1] * sin(deltaAlpha * 0.5f), - rotationDirection.v[2] * sin(deltaAlpha * 0.5f), - cos(deltaAlpha * 0.5f) }; - - Matrix3f rotateMat = QuatToMatrix(rotateQuat); - - Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat); - Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix); - - } - } - - if (fabs(Environment::shipVelocity) > 0.01f) - { - Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f }; - Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection); - - Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; - } - - lastTickCount = newTickCount; - } - } - - void Game::render() { - SDL_GL_MakeCurrent(ZL::Environment::window, glContext); - ZL::CheckGlError(); - - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - drawScene(); - processTickCount(); - - SDL_GL_SwapWindow(ZL::Environment::window); - } - void Game::update() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - Environment::exitGameLoop = true; - - } - else if (event.type == SDL_MOUSEBUTTONDOWN) { - // 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè - - int mx = event.button.x; - int my = event.button.y; - - std::cout << mx << " " << my << '\n'; - int uiX = mx; - int uiY = Environment::height - my; - if (uiX >= volumeBarMinX-40 && uiX <= volumeBarMaxX+40 && - uiY >= volumeBarMinY-40 && uiY <= volumeBarMaxY+40) { - isDraggingVolume = true; - UpdateVolumeFromMouse(mx, my); - } - else { - Environment::tapDownHold = true; - // Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ - Environment::tapDownStartPos.v[0] = event.button.x; - Environment::tapDownStartPos.v[1] = event.button.y; - // Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé - Environment::tapDownCurrentPos.v[0] = event.button.x; - Environment::tapDownCurrentPos.v[1] = event.button.y; - } - - } - else if (event.type == SDL_MOUSEBUTTONUP) { - // 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè - isDraggingVolume = false; - Environment::tapDownHold = false; - } - else if (event.type == SDL_MOUSEMOTION) { - // 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè - int mx = event.motion.x; - int my = event.motion.y; - - if (isDraggingVolume) { - // Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà - UpdateVolumeFromMouse(mx, my); - } - if (Environment::tapDownHold) { - // Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ - Environment::tapDownCurrentPos.v[0] = event.motion.x; - Environment::tapDownCurrentPos.v[1] = event.motion.y; - } - } - else if (event.type == SDL_MOUSEWHEEL) { - static const float zoomstep = 2.0f; - if (event.wheel.y > 0) { - Environment::zoom -= zoomstep; - } - else if (event.wheel.y < 0) { - Environment::zoom += zoomstep; - } - if (Environment::zoom < zoomstep) { - Environment::zoom = zoomstep; - } - } - else if (event.type == SDL_KEYUP) - { - if (event.key.keysym.sym == SDLK_i) - { - Environment::shipVelocity += 1.f; - } - if (event.key.keysym.sym == SDLK_k) - { - Environment::shipVelocity -= 1.f; - } - } - } - render(); - } + if (Environment::tapDownHold) { + + float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0]; + float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1]; + + + if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold + { + + float rotationPower = sqrtf(diffx * diffx + diffy * diffy); + + //std::cout << rotationPower << std::endl; + + float deltaAlpha = rotationPower * delta * M_PI / 500000.f; + + Vector3f rotationDirection = { diffy, diffx, 0 }; + + rotationDirection = rotationDirection.normalized(); + + Vector4f rotateQuat = { + rotationDirection.v[0] * sin(deltaAlpha * 0.5f), + rotationDirection.v[1] * sin(deltaAlpha * 0.5f), + rotationDirection.v[2] * sin(deltaAlpha * 0.5f), + cos(deltaAlpha * 0.5f) }; + + Matrix3f rotateMat = QuatToMatrix(rotateQuat); + + Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat); + Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix); + + } + } + + if (fabs(Environment::shipVelocity) > 0.01f) + { + Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f }; + Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection); + + Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; + } + + lastTickCount = newTickCount; + } + } + + void Game::render() { + SDL_GL_MakeCurrent(ZL::Environment::window, glContext); + ZL::CheckGlError(); + + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + drawScene(); + processTickCount(); + + SDL_GL_SwapWindow(ZL::Environment::window); + } + void Game::update() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + Environment::exitGameLoop = true; + + } + else if (event.type == SDL_MOUSEBUTTONDOWN) { + // 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè + + int mx = event.button.x; + int my = event.button.y; + + std::cout << mx << " " << my << '\n'; + int uiX = mx; + int uiY = Environment::height - my; + if (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 && + uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) { + isDraggingVolume = true; + UpdateVolumeFromMouse(mx, my); + } + else { + Environment::tapDownHold = true; + // Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ + Environment::tapDownStartPos.v[0] = event.button.x; + Environment::tapDownStartPos.v[1] = event.button.y; + // Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé + Environment::tapDownCurrentPos.v[0] = event.button.x; + Environment::tapDownCurrentPos.v[1] = event.button.y; + } + + } + else if (event.type == SDL_MOUSEBUTTONUP) { + // 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè + isDraggingVolume = false; + Environment::tapDownHold = false; + } + else if (event.type == SDL_MOUSEMOTION) { + // 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè + int mx = event.motion.x; + int my = event.motion.y; + + if (isDraggingVolume) { + // Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà + UpdateVolumeFromMouse(mx, my); + } + if (Environment::tapDownHold) { + // Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ + Environment::tapDownCurrentPos.v[0] = event.motion.x; + Environment::tapDownCurrentPos.v[1] = event.motion.y; + } + } + else if (event.type == SDL_MOUSEWHEEL) { + static const float zoomstep = 2.0f; + if (event.wheel.y > 0) { + Environment::zoom -= zoomstep; + } + else if (event.wheel.y < 0) { + Environment::zoom += zoomstep; + } + if (Environment::zoom < zoomstep) { + Environment::zoom = zoomstep; + } + } + else if (event.type == SDL_KEYUP) + { + if (event.key.keysym.sym == SDLK_i) + { + Environment::shipVelocity += 1.f; + } + if (event.key.keysym.sym == SDLK_k) + { + Environment::shipVelocity -= 1.f; + } + } + } + render(); + } } // namespace ZL diff --git a/Game.h b/Game.h index ea6e393..65bb5b6 100755 --- a/Game.h +++ b/Game.h @@ -5,6 +5,7 @@ #include "Environment.h" #include "TextureManager.h" #include "SparkEmitter.h" +#include "PlanetObject.h" namespace ZL { @@ -44,30 +45,30 @@ namespace ZL { size_t lastTickCount; - std::vector boxCoordsArr; - std::vector boxRenderArr; - - - std::shared_ptr buttonTexture; - VertexRenderStruct button; - - std::shared_ptr musicVolumeBarTexture; - VertexRenderStruct musicVolumeBar; - - std::shared_ptr musicVolumeBarButtonTexture; - VertexRenderStruct musicVolumeBarButton; + std::vector boxCoordsArr; + std::vector boxRenderArr; - bool isDraggingVolume = false; - float musicVolume = 0.0f; - float volumeBarMinX = 1190.0f; - float volumeBarMaxX = 1200.0f; - float volumeBarMinY = 100.0f; - float volumeBarMaxY = 600.0f; - float musicVolumeBarButtonButtonCenterX = 1195.0f; - float musicVolumeBarButtonButtonRadius = 25.0f; - void UpdateVolumeFromMouse(int mouseX, int mouseY); - void UpdateVolumeKnob(); + std::shared_ptr buttonTexture; + VertexRenderStruct button; + + std::shared_ptr musicVolumeBarTexture; + VertexRenderStruct musicVolumeBar; + + std::shared_ptr musicVolumeBarButtonTexture; + VertexRenderStruct musicVolumeBarButton; + + + bool isDraggingVolume = false; + float musicVolume = 0.0f; + float volumeBarMinX = 1190.0f; + float volumeBarMaxX = 1200.0f; + float volumeBarMinY = 100.0f; + float volumeBarMaxY = 600.0f; + float musicVolumeBarButtonButtonCenterX = 1195.0f; + float musicVolumeBarButtonButtonRadius = 25.0f; + void UpdateVolumeFromMouse(int mouseX, int mouseY); + void UpdateVolumeKnob(); static const size_t CONST_TIMER_INTERVAL = 10; static const size_t CONST_MAX_TIME_INTERVAL = 1000; @@ -84,6 +85,7 @@ namespace ZL { VertexDataStruct boxBase; SparkEmitter sparkEmitter; + PlanetObject planetObject; }; diff --git a/PlanetObject.cpp b/PlanetObject.cpp new file mode 100644 index 0000000..b428f3f --- /dev/null +++ b/PlanetObject.cpp @@ -0,0 +1,292 @@ +#include "PlanetObject.h" +#include +#include +#include "OpenGlExtensions.h" +#include "Environment.h" + +namespace ZL { + + struct Triangle + { + std::array data; + + Triangle(Vector3f p1, Vector3f p2, Vector3f p3) + : data{ p1, p2, p3 } + { + } + }; + + std::vector subdivideTriangles(const std::vector& inputTriangles, PerlinNoise& perlin) { + std::vector output; + output.reserve(inputTriangles.size() * 4); + + for (const auto& t : inputTriangles) { + Vector3f a = t.data[0]; + Vector3f b = t.data[1]; + Vector3f c = t.data[2]; + + // 1. Вычисляем "сырые" середины + Vector3f m_ab = (a + b) * 0.5f; + Vector3f m_bc = (b + c) * 0.5f; + Vector3f m_ac = (a + c) * 0.5f; + + // 2. Нормализуем их (получаем идеальную сферу радиуса 1) + m_ab = m_ab.normalized(); + m_bc = m_bc.normalized(); + m_ac = m_ac.normalized(); + + // 3. ПРИМЕНЯЕМ ШУМ: Смещаем точку по радиусу + m_ab = m_ab * perlin.getSurfaceHeight(m_ab); + m_bc = m_bc * perlin.getSurfaceHeight(m_bc); + m_ac = m_ac * perlin.getSurfaceHeight(m_ac); + + // 4. Формируем новые треугольники + output.emplace_back(a, m_ab, m_ac); + output.emplace_back(m_ab, b, m_bc); + output.emplace_back(m_ac, m_bc, c); + output.emplace_back(m_ab, m_bc, m_ac); + } + + return output; + } + + Vector3f calculateSurfaceNormal(Vector3f p_sphere, PerlinNoise& perlin) { + // p_sphere - это нормализованный вектор (точка на идеальной сфере) + + float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon) + + // Нам нужно найти два вектора, касательных к сфере в точке p_sphere. + // Для этого берем любой вектор (например UP), делаем Cross Product, чтобы получить касательную. + // Если p_sphere совпадает с UP, берем RIGHT. + Vector3f up = Vector3f(0.0f, 1.0f, 0.0f); + if (abs(p_sphere.dot(up)) > 0.99f) { + up = Vector3f(1.0f, 0.0f, 0.0f); + } + + Vector3f tangentX = (up.cross(p_sphere)).normalized(); + Vector3f tangentY = (p_sphere.cross(tangentX)).normalized(); + + // Точки на идеальной сфере со смещением + Vector3f p0_dir = p_sphere; + Vector3f p1_dir = (p_sphere + tangentX * theta).normalized(); + Vector3f p2_dir = (p_sphere + tangentY * theta).normalized(); + + // Реальные точки на искаженной поверхности + // p = dir * height(dir) + Vector3f p0 = p0_dir * perlin.getSurfaceHeight(p0_dir); + Vector3f p1 = p1_dir * perlin.getSurfaceHeight(p1_dir); + Vector3f p2 = p2_dir * perlin.getSurfaceHeight(p2_dir); + + // Вектора от центральной точки к соседям + Vector3f v1 = p1 - p0; + Vector3f v2 = p2 - p0; + + // Нормаль - это перпендикуляр к этим двум векторам + // Порядок (v2, v1) или (v1, v2) зависит от системы координат, + // здесь подбираем так, чтобы нормаль смотрела наружу. + return (-v2.cross(v1)).normalized(); + } + + VertexDataStruct trianglesToVertices(const std::vector& triangles, PerlinNoise& perlin) { + VertexDataStruct buffer; + buffer.PositionData.reserve(triangles.size() * 3); + buffer.NormalData.reserve(triangles.size() * 3); + + for (const auto& t : triangles) { + // Проходим по всем 3 вершинам треугольника + for (int i = 0; i < 3; i++) { + // p_geometry - это уже точка на поверхности (с шумом) + Vector3f p_geometry = t.data[i]; + + // Нам нужно восстановить направление от центра к этой точке, + // чтобы передать его в функцию расчета нормали. + // Так как (0,0,0) - центр, то normalize(p) даст нам направление. + Vector3f p_dir = p_geometry.normalized(); + + // Считаем аналитическую нормаль для этой конкретной точки + Vector3f normal = calculateSurfaceNormal(p_dir, perlin); + + buffer.PositionData.push_back({ p_geometry }); + //buffer.NormalData.push_back({ normal }); + //buffer.TexCoordData.push_back({ 0.0f, 0.0f }); + } + } + return buffer; + } + + // Генерация геометрии октаэдра с дублированием вершин для Flat Shading + VertexDataStruct generateOctahedron(PerlinNoise& perlin) { + VertexDataStruct buffer; + + std::vector v = { + Triangle{ + { 0.0f, 1.0f, 0.0f}, // Top + { 0.0f, 0.0f, 1.0f}, // Front + { 1.0f, 0.0f, 0.0f}, // Right + }, + Triangle{ + { 0.0f, 1.0f, 0.0f}, // Top + { 1.0f, 0.0f, 0.0f}, // Right + { 0.0f, 0.0f, -1.0f}, // Back + }, + Triangle{ + { 0.0f, 1.0f, 0.0f}, // Top + { 0.0f, 0.0f, -1.0f}, // Back + {-1.0f, 0.0f, 0.0f}, // Left + }, + Triangle{ + { 0.0f, 1.0f, 0.0f}, // Top + {-1.0f, 0.0f, 0.0f}, // Left + { 0.0f, 0.0f, 1.0f}, // Front + }, + Triangle{ + { 0.0f, -1.0f, 0.0f}, // Bottom + { 1.0f, 0.0f, 0.0f}, // Right + { 0.0f, 0.0f, 1.0f}, // Front + }, + Triangle{ + { 0.0f, -1.0f, 0.0f}, // Bottom + { 0.0f, 0.0f, 1.0f}, // Front + {-1.0f, 0.0f, 0.0f}, // Left + }, + Triangle{ + { 0.0f, -1.0f, 0.0f}, // Bottom + {-1.0f, 0.0f, 0.0f}, // Left + { 0.0f, 0.0f, -1.0f}, // Back + }, + Triangle{ + { 0.0f, -1.0f, 0.0f}, // Bottom + { 0.0f, 0.0f, -1.0f}, // Back + { 1.0f, 0.0f, 0.0f}, // Right + } + }; + + v = subdivideTriangles(v, perlin); + v = subdivideTriangles(v, perlin); + v = subdivideTriangles(v, perlin); + v = subdivideTriangles(v, perlin); + v = subdivideTriangles(v, perlin); + + + for (int i = 0; i < v.size(); i++) { + Vector3f p1 = v[i].data[0]; + Vector3f p2 = v[i].data[1]; + Vector3f p3 = v[i].data[2]; + + // Считаем нормаль грани через векторное произведение + Vector3f edge1 = p2 - p1; + Vector3f edge2 = p3 - p1; + Vector3f normal = (edge1.cross(edge2)).normalized(); + + // Дублируем нормаль для всех трех вершин треугольника (Flat shading) + buffer.PositionData.push_back({ p1 }); + buffer.PositionData.push_back({ p2 }); + buffer.PositionData.push_back({ p3 }); + /*buffer.NormalData.push_back({normal}); + buffer.NormalData.push_back({ normal }); + buffer.NormalData.push_back({ normal }); + buffer.TexCoordData.push_back({ 0.0f, 0.0f }); + buffer.TexCoordData.push_back({ 0.0f, 0.0f }); + buffer.TexCoordData.push_back({ 0.0f, 0.0f });*/ + } + return buffer; + } + + VertexDataStruct generateSphere(int subdivisions, PerlinNoise& perlin) { + // 1. Исходный октаэдр + std::vector geometry = { + Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // Top-Front-Right + Triangle{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Top-Right-Back + Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // Top-Back-Left + Triangle{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Top-Left-Front + Triangle{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Bottom-Right-Front + Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // Bottom-Front-Left + Triangle{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Bottom-Left-Back + Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}} // Bottom-Back-Right + }; + + // 2. ПРИМЕНЯЕМ ШУМ К ИСХОДНЫМ ВЕРШИНАМ + // Проходимся по всем треугольникам и всем вершинам в них + for (auto& t : geometry) { + for (int i = 0; i < 3; i++) { + // Нормализуем (на всякий случай, хотя у октаэдра они и так норм) + Vector3f dir = t.data[i].normalized(); + // Применяем высоту + t.data[i] = dir * perlin.getSurfaceHeight(dir); + } + } + + // 3. Разбиваем N раз (новые вершины будут получать шум внутри функции) + for (int i = 0; i < subdivisions; i++) { + geometry = subdivideTriangles(geometry, perlin); + } + + // 4. Генерируем нормали + // Благодаря тому, что мы реально сдвигали вершины, Flat Shading нормали + // рассчитаются правильно относительно нового рельефа. + return trianglesToVertices(geometry, perlin); + } + + PlanetObject::PlanetObject() + { + } + + void PlanetObject::init() { + + planetMesh = generateSphere(7, perlin); + + planetMesh.Scale(100.f); + planetMesh.Move({ 0,0,-200 }); + + planetRenderStruct.data = planetMesh; + planetRenderStruct.RefreshVBO(); + + } + + void PlanetObject::prepareDrawData() { + if (!drawDataDirty) return; + + drawDataDirty = false; + } + + void PlanetObject::draw(Renderer& renderer) { + + prepareDrawData(); + + static const std::string defaultShaderName = "defaultColor"; + static const std::string vPositionName = "vPosition"; + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.EnableVertexAttribArray(vPositionName); + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Environment::inverseShipMatrix); + renderer.TranslateMatrix(-Environment::shipPosition); + + + renderer.DrawVertexRenderStruct(planetRenderStruct); + + CheckGlError(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + + renderer.shaderManager.PopShader(); + CheckGlError(); + + + } + + void PlanetObject::update(float deltaTimeMs) { + + } + + + +} // namespace ZL \ No newline at end of file diff --git a/PlanetObject.h b/PlanetObject.h new file mode 100644 index 0000000..c7857a8 --- /dev/null +++ b/PlanetObject.h @@ -0,0 +1,100 @@ +#pragma once + +#include "ZLMath.h" +#include "Renderer.h" +#include "TextureManager.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ZL { + + class PerlinNoise { + std::vector p; + public: + PerlinNoise() { + p.resize(256); + std::iota(p.begin(), p.end(), 0); + // Перемешиваем для случайности (можно задать seed) + std::default_random_engine engine(12345); + std::shuffle(p.begin(), p.end(), engine); + p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения + } + + float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } + float lerp(float t, float a, float b) { return a + t * (b - a); } + float grad(int hash, float x, float y, float z) { + int h = hash & 15; + float u = h < 8 ? x : y; + float v = h < 4 ? y : (h == 12 || h == 14 ? x : z); + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + float noise(float x, float y, float z) { + int X = (int)floor(x) & 255; + int Y = (int)floor(y) & 255; + int Z = (int)floor(z) & 255; + + x -= floor(x); + y -= floor(y); + z -= floor(z); + + float u = fade(x); + float v = fade(y); + float w = fade(z); + + int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z; + int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; + + return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)), + lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))), + lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)), + lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)))); + } + + float getSurfaceHeight(Vector3f pos) { + // Частота шума (чем больше, тем больше "холмов") + float frequency = 7.0f; + + // Получаем значение шума (обычно от -1 до 1) + float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency); + + // Переводим из диапазона [-1, 1] в [0, 1] + noiseValue = (noiseValue + 1.0f) * 0.5f; + + // Масштабируем: хотим отклонение от 1.0 до 1.1 + // Значит амплитуда = 0.1 + float height = 1.0f + (noiseValue * 0.1f); // * 0.2 даст вариацию высоты + + return height; + } + }; + + + class PlanetObject { + private: + PerlinNoise perlin; + void prepareDrawData(); + VertexDataStruct planetMesh; + VertexRenderStruct planetRenderStruct; + + public: + PlanetObject(); + + void init(); + + void update(float deltaTimeMs); + + void draw(Renderer& renderer); + + + private: + bool drawDataDirty = true; + }; + +} // namespace ZL \ No newline at end of file diff --git a/ZLMath.h b/ZLMath.h index 609f14d..d8168df 100755 --- a/ZLMath.h +++ b/ZLMath.h @@ -56,6 +56,18 @@ namespace ZL { return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } + float dot(const Vector3f& other) const { + return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2]; + } + + Vector3f cross(const Vector3f& other) const { + return Vector3f( + v[1] * other.v[2] - v[2] * other.v[1], + v[2] * other.v[0] - v[0] * other.v[2], + v[0] * other.v[1] - v[1] * other.v[0] + ); + } + // /*Vector3f operator-(const Vector3f& other) const { return Vector3f(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]); From fe361885c0f39e89bc5e4614a195fb4d19a54c66 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 13 Dec 2025 15:02:03 +0300 Subject: [PATCH 06/11] Working on planet --- Game.cpp | 4 +- PlanetObject.cpp | 408 ++++++++++++++++---------- PlanetObject.h | 79 ++--- Renderer.cpp | 33 ++- Renderer.h | 3 + ZLMath.h | 9 + resources/rock.png | 3 + resources/sand.png | 3 + shaders/defaultColor.vertex | 18 +- shaders/defaultColor_desktop.fragment | 25 +- 10 files changed, 358 insertions(+), 227 deletions(-) create mode 100644 resources/rock.png create mode 100644 resources/sand.png diff --git a/Game.cpp b/Game.cpp index 38f04d3..7d4365b 100755 --- a/Game.cpp +++ b/Game.cpp @@ -612,11 +612,11 @@ namespace ZL { if (event.key.keysym.sym == SDLK_i) { - Environment::shipVelocity += 1.f; + Environment::shipVelocity += 5.f; } if (event.key.keysym.sym == SDLK_k) { - Environment::shipVelocity -= 1.f; + Environment::shipVelocity -= 5.f; } } } diff --git a/PlanetObject.cpp b/PlanetObject.cpp index b428f3f..1fddf74 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -6,17 +6,174 @@ namespace ZL { - struct Triangle + + PerlinNoise::PerlinNoise() { + p.resize(256); + std::iota(p.begin(), p.end(), 0); + // Перемешиваем для случайности (можно задать seed) + std::default_random_engine engine(77777); + std::shuffle(p.begin(), p.end(), engine); + p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения + } + + PerlinNoise::PerlinNoise(uint64_t seed) { + p.resize(256); + std::iota(p.begin(), p.end(), 0); + // Перемешиваем для случайности (используем переданный seed) + std::default_random_engine engine(seed); // <-- Использование сида + std::shuffle(p.begin(), p.end(), engine); + p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения + } + + float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } + float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); } + float PerlinNoise::grad(int hash, float x, float y, float z) { + int h = hash & 15; + float u = h < 8 ? x : y; + float v = h < 4 ? y : (h == 12 || h == 14 ? x : z); + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + float PerlinNoise::noise(float x, float y, float z) { + int X = (int)floor(x) & 255; + int Y = (int)floor(y) & 255; + int Z = (int)floor(z) & 255; + + x -= floor(x); + y -= floor(y); + z -= floor(z); + + float u = fade(x); + float v = fade(y); + float w = fade(z); + + int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z; + int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; + + return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)), + lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))), + lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)), + lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)))); + } + + float PerlinNoise::getSurfaceHeight(Vector3f pos) { + // Частота шума (чем больше, тем больше "холмов") + float frequency = 7.0f; + + // Получаем значение шума (обычно от -1 до 1) + float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency); + + // Переводим из диапазона [-1, 1] в [0, 1] + noiseValue = (noiseValue + 1.0f) * 0.5f; + + // Масштабируем: хотим отклонение от 1.0 до 1.1 + // Значит амплитуда = 0.1 + float height = 1.0f + (noiseValue * 0.1f); // * 0.2 даст вариацию высоты + + return height; + } + + + + + PlanetObject::PlanetObject() + : perlin(77777) + , colorPerlin(123123) { - std::array data; - Triangle(Vector3f p1, Vector3f p2, Vector3f p3) - : data{ p1, p2, p3 } - { - } - }; + } - std::vector subdivideTriangles(const std::vector& inputTriangles, PerlinNoise& perlin) { + void PlanetObject::init() { + + planetMesh = generateSphere(7); + + planetMesh.Scale(300.f); + planetMesh.Move({ 0,0,-600 }); + + planetRenderStruct.data = planetMesh; + planetRenderStruct.RefreshVBO(); + + //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); + sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); + + } + + void PlanetObject::prepareDrawData() { + if (!drawDataDirty) return; + + drawDataDirty = false; + } + + void PlanetObject::draw(Renderer& renderer) { + + prepareDrawData(); + + static const std::string defaultShaderName = "defaultColor"; + static const std::string vPositionName = "vPosition"; + static const std::string vColorName = "vColor"; + static const std::string vNormalName = "vNormal"; + static const std::string vTexCoordName = "vTexCoord"; + //static const std::string vTexCoord3Name = "vTexCoord3"; + static const std::string textureUniformName = "Texture"; + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vColorName); + renderer.EnableVertexAttribArray(vNormalName); + renderer.EnableVertexAttribArray(vTexCoordName); + //renderer.EnableVertexAttribArray(vTexCoord3Name); + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Environment::inverseShipMatrix); + renderer.TranslateMatrix(-Environment::shipPosition); + + const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix(); + + + Vector3f lightDir_World = Vector3f(1.0f, 0.0f, -1.0f).normalized(); + // В OpenGL/шейдерах удобнее работать с вектором, указывающим ОТ источника к поверхности. + Vector3f lightDirection_World = -lightDir_World; // Вектор, направленный от источника + Vector3f lightDirection_View; + + lightDirection_View.v[0] = viewMatrix.m[0] * lightDirection_World.v[0] + viewMatrix.m[4] * lightDirection_World.v[1] + viewMatrix.m[8] * lightDirection_World.v[2]; + lightDirection_View.v[1] = viewMatrix.m[1] * lightDirection_World.v[0] + viewMatrix.m[5] * lightDirection_World.v[1] + viewMatrix.m[9] * lightDirection_World.v[2]; + lightDirection_View.v[2] = viewMatrix.m[2] * lightDirection_World.v[0] + viewMatrix.m[6] * lightDirection_World.v[1] + viewMatrix.m[10] * lightDirection_World.v[2]; + lightDirection_View = lightDirection_View.normalized(); // Нормализуем на всякий случай + + // Установка uniform-переменной + // Предполагается, что RenderUniform3fv определена в Renderer.h + renderer.RenderUniform3fv("uLightDirection", &lightDirection_View.v[0]); + renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]); + + glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); + renderer.DrawVertexRenderStruct(planetRenderStruct); + + CheckGlError(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + //renderer.DisableVertexAttribArray(vTexCoord3Name); + renderer.DisableVertexAttribArray(vTexCoordName); + renderer.DisableVertexAttribArray(vNormalName); + renderer.DisableVertexAttribArray(vColorName); + renderer.DisableVertexAttribArray(vPositionName); + renderer.shaderManager.PopShader(); + CheckGlError(); + + + } + + void PlanetObject::update(float deltaTimeMs) { + + } + + std::vector PlanetObject::subdivideTriangles(const std::vector& inputTriangles) { std::vector output; output.reserve(inputTriangles.size() * 4); @@ -50,7 +207,7 @@ namespace ZL { return output; } - Vector3f calculateSurfaceNormal(Vector3f p_sphere, PerlinNoise& perlin) { + Vector3f PlanetObject::calculateSurfaceNormal(Vector3f p_sphere) { // p_sphere - это нормализованный вектор (точка на идеальной сфере) float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon) @@ -86,11 +243,27 @@ namespace ZL { // здесь подбираем так, чтобы нормаль смотрела наружу. return (-v2.cross(v1)).normalized(); } - - VertexDataStruct trianglesToVertices(const std::vector& triangles, PerlinNoise& perlin) { + + VertexDataStruct PlanetObject::trianglesToVertices(const std::vector& triangles) { VertexDataStruct buffer; buffer.PositionData.reserve(triangles.size() * 3); buffer.NormalData.reserve(triangles.size() * 3); + buffer.TexCoordData.reserve(triangles.size() * 3); // <-- РЕЗЕРВИРУЕМ + //buffer.TexCoord3Data.reserve(triangles.size() * 3); // <-- РЕЗЕРВИРУЕМ + + // Стандартные UV-координаты для покрытия одного треугольника + // Покрывает текстурой всю грань. + const std::array triangleUVs = { + Vector2f(0.0f, 0.0f), + Vector2f(1.0f, 0.0f), + Vector2f(0.0f, 1.0f) + }; + /* + const std::array barycentricCoords = { + Vector3f(1.0f, 0.0f, 0.0f), // Вершина 1 + Vector3f(0.0f, 1.0f, 0.0f), // Вершина 2 + Vector3f(0.0f, 0.0f, 1.0f) // Вершина 3 + };*/ for (const auto& t : triangles) { // Проходим по всем 3 вершинам треугольника @@ -104,187 +277,104 @@ namespace ZL { Vector3f p_dir = p_geometry.normalized(); // Считаем аналитическую нормаль для этой конкретной точки - Vector3f normal = calculateSurfaceNormal(p_dir, perlin); + Vector3f normal = calculateSurfaceNormal(p_dir); buffer.PositionData.push_back({ p_geometry }); - //buffer.NormalData.push_back({ normal }); - //buffer.TexCoordData.push_back({ 0.0f, 0.0f }); + buffer.NormalData.push_back({ normal }); + //buffer.TexCoord3Data.push_back(barycentricCoords[i]); + buffer.TexCoordData.push_back(triangleUVs[i]); + } } return buffer; } - // Генерация геометрии октаэдра с дублированием вершин для Flat Shading - VertexDataStruct generateOctahedron(PerlinNoise& perlin) { - VertexDataStruct buffer; - std::vector v = { - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - { 0.0f, 0.0f, 1.0f}, // Front - { 1.0f, 0.0f, 0.0f}, // Right - }, - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - { 1.0f, 0.0f, 0.0f}, // Right - { 0.0f, 0.0f, -1.0f}, // Back - }, - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - { 0.0f, 0.0f, -1.0f}, // Back - {-1.0f, 0.0f, 0.0f}, // Left - }, - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - {-1.0f, 0.0f, 0.0f}, // Left - { 0.0f, 0.0f, 1.0f}, // Front - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - { 1.0f, 0.0f, 0.0f}, // Right - { 0.0f, 0.0f, 1.0f}, // Front - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - { 0.0f, 0.0f, 1.0f}, // Front - {-1.0f, 0.0f, 0.0f}, // Left - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - {-1.0f, 0.0f, 0.0f}, // Left - { 0.0f, 0.0f, -1.0f}, // Back - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - { 0.0f, 0.0f, -1.0f}, // Back - { 1.0f, 0.0f, 0.0f}, // Right - } - }; - - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - - - for (int i = 0; i < v.size(); i++) { - Vector3f p1 = v[i].data[0]; - Vector3f p2 = v[i].data[1]; - Vector3f p3 = v[i].data[2]; - - // Считаем нормаль грани через векторное произведение - Vector3f edge1 = p2 - p1; - Vector3f edge2 = p3 - p1; - Vector3f normal = (edge1.cross(edge2)).normalized(); - - // Дублируем нормаль для всех трех вершин треугольника (Flat shading) - buffer.PositionData.push_back({ p1 }); - buffer.PositionData.push_back({ p2 }); - buffer.PositionData.push_back({ p3 }); - /*buffer.NormalData.push_back({normal}); - buffer.NormalData.push_back({ normal }); - buffer.NormalData.push_back({ normal }); - buffer.TexCoordData.push_back({ 0.0f, 0.0f }); - buffer.TexCoordData.push_back({ 0.0f, 0.0f }); - buffer.TexCoordData.push_back({ 0.0f, 0.0f });*/ - } - return buffer; - } - - VertexDataStruct generateSphere(int subdivisions, PerlinNoise& perlin) { + VertexDataStruct PlanetObject::generateSphere(int subdivisions) { // 1. Исходный октаэдр std::vector geometry = { - Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // Top-Front-Right - Triangle{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Top-Right-Back - Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // Top-Back-Left - Triangle{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Top-Left-Front - Triangle{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Bottom-Right-Front - Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // Bottom-Front-Left - Triangle{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Bottom-Left-Back - Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}} // Bottom-Back-Right + Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // Top-Front-Right + Triangle{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Top-Right-Back + Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // Top-Back-Left + Triangle{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Top-Left-Front + Triangle{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Bottom-Right-Front + Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // Bottom-Front-Left + Triangle{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Bottom-Left-Back + Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}} // Bottom-Back-Right }; // 2. ПРИМЕНЯЕМ ШУМ К ИСХОДНЫМ ВЕРШИНАМ - // Проходимся по всем треугольникам и всем вершинам в них for (auto& t : geometry) { for (int i = 0; i < 3; i++) { - // Нормализуем (на всякий случай, хотя у октаэдра они и так норм) Vector3f dir = t.data[i].normalized(); - // Применяем высоту t.data[i] = dir * perlin.getSurfaceHeight(dir); } } - // 3. Разбиваем N раз (новые вершины будут получать шум внутри функции) + // 3. Разбиваем N раз for (int i = 0; i < subdivisions; i++) { - geometry = subdivideTriangles(geometry, perlin); + geometry = subdivideTriangles(geometry); } - // 4. Генерируем нормали - // Благодаря тому, что мы реально сдвигали вершины, Flat Shading нормали - // рассчитаются правильно относительно нового рельефа. - return trianglesToVertices(geometry, perlin); - } + // 4. Генерируем вершины И НОРМАЛИ с помощью trianglesToVertices + // ЭТО ЗАПОЛНИТ PositionData И NormalData + VertexDataStruct buffer = trianglesToVertices(geometry); - PlanetObject::PlanetObject() - { - } + // Теперь нам нужно заполнить ColorData, используя ту же логику, что и раньше. + // Сначала резервируем место, так как trianglesToVertices не заполняет ColorData + buffer.ColorData.reserve(geometry.size() * 3); - void PlanetObject::init() { - - planetMesh = generateSphere(7, perlin); - - planetMesh.Scale(100.f); - planetMesh.Move({ 0,0,-200 }); - - planetRenderStruct.data = planetMesh; - planetRenderStruct.RefreshVBO(); - - } - - void PlanetObject::prepareDrawData() { - if (!drawDataDirty) return; - - drawDataDirty = false; - } - - void PlanetObject::draw(Renderer& renderer) { - - prepareDrawData(); - - static const std::string defaultShaderName = "defaultColor"; - static const std::string vPositionName = "vPosition"; - - renderer.shaderManager.PushShader(defaultShaderName); - renderer.EnableVertexAttribArray(vPositionName); - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, - static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); - renderer.PushMatrix(); - renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - renderer.RotateMatrix(Environment::inverseShipMatrix); - renderer.TranslateMatrix(-Environment::shipPosition); + // Базовый цвет и параметры + const Vector3f baseColor = { 0.498f, 0.416f, 0.0f }; + const float colorFrequency = 5.0f; + const float colorAmplitude = 0.2f; + const Vector3f offsetR = { 0.1f, 0.2f, 0.3f }; + const Vector3f offsetG = { 0.5f, 0.4f, 0.6f }; + const Vector3f offsetB = { 0.9f, 0.8f, 0.7f }; - renderer.DrawVertexRenderStruct(planetRenderStruct); + for (const auto& t : geometry) { + for (int i = 0; i < 3; i++) { + // ВАЖНО: Мы используем геометрию из t.data[i] для получения направления, + // а не PositionData из buffer, поскольку там может не быть нужного порядка. + Vector3f p_geometry = t.data[i]; + Vector3f dir = p_geometry.normalized(); - CheckGlError(); + // Вычисление цветового шума (как вы делали) + float noiseR = colorPerlin.noise( + (dir.v[0] + offsetR.v[0]) * colorFrequency, + (dir.v[1] + offsetR.v[1]) * colorFrequency, + (dir.v[2] + offsetR.v[2]) * colorFrequency + ); + float noiseG = colorPerlin.noise( + (dir.v[0] + offsetG.v[0]) * colorFrequency, + (dir.v[1] + offsetG.v[1]) * colorFrequency, + (dir.v[2] + offsetG.v[2]) * colorFrequency + ); + float noiseB = colorPerlin.noise( + (dir.v[0] + offsetB.v[0]) * colorFrequency, + (dir.v[1] + offsetB.v[1]) * colorFrequency, + (dir.v[2] + offsetB.v[2]) * colorFrequency + ); + Vector3f colorOffset = { + noiseR * colorAmplitude, + noiseG * colorAmplitude, + noiseB * colorAmplitude + }; - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); + Vector3f finalColor = baseColor + colorOffset; - renderer.shaderManager.PopShader(); - CheckGlError(); + finalColor.v[0] = max(0.0f, min(1.0f, finalColor.v[0])); + finalColor.v[1] = max(0.0f, min(1.0f, finalColor.v[1])); + finalColor.v[2] = max(0.0f, min(1.0f, finalColor.v[2])); + // ДОБАВЛЯЕМ ТОЛЬКО ЦВЕТ, так как PositionData и NormalData уже заполнены! + buffer.ColorData.push_back(finalColor); + } + } - } - - void PlanetObject::update(float deltaTimeMs) { - + return buffer; } diff --git a/PlanetObject.h b/PlanetObject.h index c7857a8..5f4741b 100644 --- a/PlanetObject.h +++ b/PlanetObject.h @@ -14,75 +14,42 @@ namespace ZL { + struct Triangle + { + std::array data; + + Triangle(Vector3f p1, Vector3f p2, Vector3f p3) + : data{ p1, p2, p3 } + { + } + }; + class PerlinNoise { std::vector p; public: - PerlinNoise() { - p.resize(256); - std::iota(p.begin(), p.end(), 0); - // Перемешиваем для случайности (можно задать seed) - std::default_random_engine engine(12345); - std::shuffle(p.begin(), p.end(), engine); - p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения - } + PerlinNoise(); + PerlinNoise(uint64_t seed); - float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } - float lerp(float t, float a, float b) { return a + t * (b - a); } - float grad(int hash, float x, float y, float z) { - int h = hash & 15; - float u = h < 8 ? x : y; - float v = h < 4 ? y : (h == 12 || h == 14 ? x : z); - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); - } + float fade(float t); + float lerp(float t, float a, float b); + float grad(int hash, float x, float y, float z); - float noise(float x, float y, float z) { - int X = (int)floor(x) & 255; - int Y = (int)floor(y) & 255; - int Z = (int)floor(z) & 255; + float noise(float x, float y, float z); - x -= floor(x); - y -= floor(y); - z -= floor(z); - - float u = fade(x); - float v = fade(y); - float w = fade(z); - - int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z; - int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; - - return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)), - lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))), - lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)), - lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)))); - } - - float getSurfaceHeight(Vector3f pos) { - // Частота шума (чем больше, тем больше "холмов") - float frequency = 7.0f; - - // Получаем значение шума (обычно от -1 до 1) - float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency); - - // Переводим из диапазона [-1, 1] в [0, 1] - noiseValue = (noiseValue + 1.0f) * 0.5f; - - // Масштабируем: хотим отклонение от 1.0 до 1.1 - // Значит амплитуда = 0.1 - float height = 1.0f + (noiseValue * 0.1f); // * 0.2 даст вариацию высоты - - return height; - } + float getSurfaceHeight(Vector3f pos); }; class PlanetObject { private: PerlinNoise perlin; + PerlinNoise colorPerlin; void prepareDrawData(); VertexDataStruct planetMesh; VertexRenderStruct planetRenderStruct; + std::shared_ptr sandTexture; + public: PlanetObject(); @@ -95,6 +62,12 @@ namespace ZL { private: bool drawDataDirty = true; + + std::vector subdivideTriangles(const std::vector& inputTriangles); + Vector3f calculateSurfaceNormal(Vector3f p_sphere); + VertexDataStruct trianglesToVertices(const std::vector& triangles); + VertexDataStruct generateOctahedron(); + VertexDataStruct generateSphere(int subdivisions); }; } // namespace ZL \ No newline at end of file diff --git a/Renderer.cpp b/Renderer.cpp index e39c946..fd10526 100755 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -294,6 +294,18 @@ namespace ZL { glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW); } + if (data.TexCoord3Data.size() > 0) + { + if (!texCoord3VBO) + { + texCoord3VBO = std::make_shared(); + } + + glBindBuffer(GL_ARRAY_BUFFER, texCoord3VBO->getBuffer()); + + glBufferData(GL_ARRAY_BUFFER, data.TexCoord3Data.size() * 12, &data.TexCoord3Data[0], GL_STATIC_DRAW); + } + if (data.NormalData.size() > 0) { @@ -459,6 +471,12 @@ namespace ZL { return ProjectionModelViewMatrix; } + Matrix4f Renderer::GetCurrentModelViewMatrix() + { + return ModelviewMatrixStack.top(); + } + + void Renderer::SetMatrix() { if (ProjectionMatrixStack.size() <= 0) @@ -471,15 +489,16 @@ namespace ZL { throw std::runtime_error("Modelview matrix stack out!"); } - ProjectionModelViewMatrix = ProjectionMatrixStack.top() * ModelviewMatrixStack.top(); + Matrix4f& modelViewMatrix = ModelviewMatrixStack.top(); + ProjectionModelViewMatrix = ProjectionMatrixStack.top() * modelViewMatrix; static const std::string ProjectionModelViewMatrixName = "ProjectionModelViewMatrix"; - //static const std::string ProjectionMatrixName = "ProjectionMatrix"; - RenderUniformMatrix4fv(ProjectionModelViewMatrixName, false, &ProjectionModelViewMatrix.m[0]); - //RenderUniformMatrix4fv(ProjectionMatrixName, false, &ProjectionMatrixStack.top().m[0]); + static const std::string ModelViewMatrixName = "ModelViewMatrix"; + + RenderUniformMatrix4fv(ModelViewMatrixName, false, &modelViewMatrix.m[0]); } @@ -733,6 +752,7 @@ namespace ZL { static const std::string vBinormal("vBinormal"); static const std::string vColor("vColor"); static const std::string vTexCoord("vTexCoord"); + static const std::string vTexCoord3("vTexCoord3"); static const std::string vPosition("vPosition"); //glBindVertexArray(VertexRenderStruct.vao->getBuffer()); @@ -763,6 +783,11 @@ namespace ZL { glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer()); VertexAttribPointer2fv(vTexCoord, 0, NULL); } + if (VertexRenderStruct.data.TexCoord3Data.size() > 0) + { + glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoord3VBO->getBuffer()); + VertexAttribPointer2fv(vTexCoord3, 0, NULL); + } glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer()); VertexAttribPointer3fv(vPosition, 0, NULL); diff --git a/Renderer.h b/Renderer.h index f3d0e54..39eca83 100755 --- a/Renderer.h +++ b/Renderer.h @@ -44,6 +44,7 @@ namespace ZL { { std::vector PositionData; std::vector TexCoordData; + std::vector TexCoord3Data; std::vector NormalData; std::vector TangentData; std::vector BinormalData; @@ -63,6 +64,7 @@ namespace ZL { std::shared_ptr vao; std::shared_ptr positionVBO; std::shared_ptr texCoordVBO; + std::shared_ptr texCoord3VBO; std::shared_ptr normalVBO; std::shared_ptr tangentVBO; std::shared_ptr binormalVBO; @@ -108,6 +110,7 @@ namespace ZL { Matrix4f GetProjectionModelViewMatrix(); + Matrix4f GetCurrentModelViewMatrix(); void SetMatrix(); diff --git a/ZLMath.h b/ZLMath.h index d8168df..e1e5dd6 100755 --- a/ZLMath.h +++ b/ZLMath.h @@ -79,6 +79,15 @@ namespace ZL { struct Vector2f { std::array v = {0.f, 0.f}; + + Vector2f() + { + } + + Vector2f(float x, float y) + : v{ x,y } + { + } }; Vector2f operator+(const Vector2f& x, const Vector2f& y); diff --git a/resources/rock.png b/resources/rock.png new file mode 100644 index 0000000..536f006 --- /dev/null +++ b/resources/rock.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26617e84a76c46d1bf5af664e89fa05cc93a2d17a691779d6ba5ef4ac4c1366f +size 638291 diff --git a/resources/sand.png b/resources/sand.png new file mode 100644 index 0000000..3224ac7 --- /dev/null +++ b/resources/sand.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caec35b4ea51d32f361b73419d101485a10fd82e3643ea5d4fc63da25e5457b6 +size 160927 diff --git a/shaders/defaultColor.vertex b/shaders/defaultColor.vertex index 5155c3e..7e9da68 100644 --- a/shaders/defaultColor.vertex +++ b/shaders/defaultColor.vertex @@ -1,11 +1,23 @@ attribute vec3 vPosition; attribute vec3 vColor; +attribute vec3 vNormal; +attribute vec2 vTexCoord; // <-- Обычные UV (если используются) + varying vec3 color; +varying vec3 normal; + +varying vec2 TexCoord; // <-- Передаем UV uniform mat4 ProjectionModelViewMatrix; +uniform mat4 ModelViewMatrix; +uniform vec3 uLightDirection; -void main() +void main() { - gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); - color = vColor; + gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); + + normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz); + + color = vColor; + TexCoord = vTexCoord; } \ No newline at end of file diff --git a/shaders/defaultColor_desktop.fragment b/shaders/defaultColor_desktop.fragment index 3f3571a..f0aeb97 100644 --- a/shaders/defaultColor_desktop.fragment +++ b/shaders/defaultColor_desktop.fragment @@ -1,9 +1,22 @@ -//precision mediump float; -varying vec3 color; +varying vec3 color; // Цвет вершины (с шумом) +varying vec3 normal; +varying vec3 lightDirection_VS; +varying vec2 TexCoord; // UV-координаты -void main() +uniform sampler2D Texture; +uniform vec3 uLightDirection; +void main() { - //gl_FragColor = vec4(color, 1.0); - gl_FragColor = vec4(1.0, 1.0, 0.5, 1.0); - + // 1. Получаем цвет из текстуры и смешиваем с цветовым шумом + vec4 textureColor = texture2D(Texture, TexCoord); + vec3 finalColor = textureColor.rgb * color; + + // 3. Расчет освещения + float diffuse = max(0.0, dot(normal, uLightDirection)); + float ambient = 0.2; + float lightingFactor = min(1.0, ambient + diffuse); + + + gl_FragColor = vec4(finalColor * lightingFactor, 1.0); + } \ No newline at end of file From 4afb9d6f4f0bdf8381cf06938c93ff0890310cd7 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 13 Dec 2025 20:11:48 +0300 Subject: [PATCH 07/11] Somehow planet is working --- Environment.cpp | 4 ++ Environment.h | 4 ++ Game.cpp | 10 ++--- PlanetObject.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++--- PlanetObject.h | 1 - SparkEmitter.cpp | 4 +- ZLMath.h | 5 +++ 7 files changed, 113 insertions(+), 13 deletions(-) diff --git a/Environment.cpp b/Environment.cpp index cdd6088..d71c040 100644 --- a/Environment.cpp +++ b/Environment.cpp @@ -36,4 +36,8 @@ Vector3f Environment::shipPosition = {0,0,0}; float Environment::shipVelocity = 0.f; +const float Environment::CONST_Z_NEAR = 10.f; +const float Environment::CONST_Z_FAR = 10000.f; + + } // namespace ZL diff --git a/Environment.h b/Environment.h index 96487ce..b68f123 100644 --- a/Environment.h +++ b/Environment.h @@ -37,6 +37,10 @@ public: static Vector3f shipPosition; static float shipVelocity; + static const float CONST_Z_NEAR; + static const float CONST_Z_FAR; + + }; diff --git a/Game.cpp b/Game.cpp index 7d4365b..fb94ba9 100755 --- a/Game.cpp +++ b/Game.cpp @@ -280,7 +280,7 @@ namespace ZL renderer.EnableVertexAttribArray(vPositionName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); + Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.RotateMatrix(Environment::inverseShipMatrix); @@ -316,7 +316,7 @@ namespace ZL renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); + Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); renderer.PushMatrix(); renderer.LoadIdentity(); @@ -351,7 +351,7 @@ namespace ZL renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); + Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); for (int i = 0; i < boxCoordsArr.size(); i++) { @@ -612,11 +612,11 @@ namespace ZL { if (event.key.keysym.sym == SDLK_i) { - Environment::shipVelocity += 5.f; + Environment::shipVelocity += 5000.f; } if (event.key.keysym.sym == SDLK_k) { - Environment::shipVelocity -= 5.f; + Environment::shipVelocity -= 5000.f; } } } diff --git a/PlanetObject.cpp b/PlanetObject.cpp index 1fddf74..ce25b5b 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -6,6 +6,85 @@ namespace ZL { + static constexpr float PLANET_RADIUS = 1000000.f; + static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -2250000.f }; + + // --- 1. Дальний диапазон (FAR) --- + static constexpr float FAR_Z_NEAR = 50000.0f; + static constexpr float FAR_Z_FAR = 10000000.0f; + + // Дистанция, где НАЧИНАЕТСЯ переход FAR -> MIDDLE + static constexpr float TRANSITION_FAR_START = 400000.0f; + + // --- 2. Средний диапазон (MIDDLE) --- + static constexpr float MIDDLE_Z_NEAR = 500.f; + static constexpr float MIDDLE_Z_FAR = 50000.f; + + // Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR + static constexpr float TRANSITION_MIDDLE_START = 10000.f; + + // --- 3. Ближний диапазон (NEAR) --- + // Новые константы для максимальной точности + static constexpr float NEAR_Z_NEAR = 50.0f; + static constexpr float NEAR_Z_FAR = 50000.0f; + + // Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR + static constexpr float TRANSITION_NEAR_END = 10.f; + + std::pair calculateZRange(const Vector3f& shipPosition) { + + // 1. Вычисление расстояния до поверхности планеты + const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET; + const float distanceToPlanetCenter = (planetWorldPosition - shipPosition).length(); + const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS; + std::cout << "distanceToPlanetSurface " << distanceToPlanetSurface << std::endl; + + float currentZNear; + float currentZFar; + float alpha; // Коэффициент интерполяции для текущего сегмента + + // Диапазон I: Далеко (FAR) -> Средне (MIDDLE) + if (distanceToPlanetSurface >= TRANSITION_FAR_START) { + // Полностью дальний диапазон + currentZNear = FAR_Z_NEAR; + currentZFar = FAR_Z_FAR; + + } + else if (distanceToPlanetSurface > TRANSITION_MIDDLE_START) { + // Плавный переход от FAR к MIDDLE + const float transitionLength = TRANSITION_FAR_START - TRANSITION_MIDDLE_START; + + // Нормализация расстояния, 0 при TRANSITION_FAR_START (Далеко), 1 при TRANSITION_MIDDLE_START (Близко) + float normalizedDist = (distanceToPlanetSurface - TRANSITION_MIDDLE_START) / transitionLength; + alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко) + + // Интерполяция: FAR * (1-alpha) + MIDDLE * alpha + currentZNear = FAR_Z_NEAR * (1.0f - alpha) + MIDDLE_Z_NEAR * alpha; + currentZFar = FAR_Z_FAR * (1.0f - alpha) + MIDDLE_Z_FAR * alpha; + + // Диапазон II: Средне (MIDDLE) -> Близко (NEAR) + } + else if (distanceToPlanetSurface > TRANSITION_NEAR_END) { + // Плавный переход от MIDDLE к NEAR + const float transitionLength = TRANSITION_MIDDLE_START - TRANSITION_NEAR_END; + + // Нормализация расстояния, 0 при TRANSITION_MIDDLE_START (Далеко), 1 при TRANSITION_NEAR_END (Близко) + float normalizedDist = (distanceToPlanetSurface - TRANSITION_NEAR_END) / transitionLength; + alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко) + + // Интерполяция: MIDDLE * (1-alpha) + NEAR * alpha + currentZNear = MIDDLE_Z_NEAR * (1.0f - alpha) + NEAR_Z_NEAR * alpha; + currentZFar = MIDDLE_Z_FAR * (1.0f - alpha) + NEAR_Z_FAR * alpha; + + } + else { + // Полностью ближний диапазон (distanceToPlanetSurface <= TRANSITION_NEAR_END) + currentZNear = NEAR_Z_NEAR; + currentZFar = NEAR_Z_FAR; + } + + return { currentZNear, currentZFar }; + } PerlinNoise::PerlinNoise() { p.resize(256); @@ -64,11 +143,11 @@ namespace ZL { float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency); // Переводим из диапазона [-1, 1] в [0, 1] - noiseValue = (noiseValue + 1.0f) * 0.5f; + //noiseValue = (noiseValue + 1.0f) * 0.5f; // Масштабируем: хотим отклонение от 1.0 до 1.1 // Значит амплитуда = 0.1 - float height = 1.0f + (noiseValue * 0.1f); // * 0.2 даст вариацию высоты + float height = 1.0f + (noiseValue * 0.002f); // * 0.2 даст вариацию высоты return height; } @@ -85,10 +164,10 @@ namespace ZL { void PlanetObject::init() { - planetMesh = generateSphere(7); + planetMesh = generateSphere(8); - planetMesh.Scale(300.f); - planetMesh.Move({ 0,0,-600 }); + planetMesh.Scale(PLANET_RADIUS); + planetMesh.Move(PLANET_CENTER_OFFSET); planetRenderStruct.data = planetMesh; planetRenderStruct.RefreshVBO(); @@ -123,9 +202,16 @@ namespace ZL { renderer.EnableVertexAttribArray(vNormalName); renderer.EnableVertexAttribArray(vTexCoordName); //renderer.EnableVertexAttribArray(vTexCoord3Name); + + const auto zRange = calculateZRange(Environment::shipPosition); + const float currentZNear = zRange.first; + const float currentZFar = zRange.second; + + // 2. Применяем динамическую матрицу проекции renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); + currentZNear, currentZFar); + renderer.PushMatrix(); renderer.LoadIdentity(); renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); diff --git a/PlanetObject.h b/PlanetObject.h index 5f4741b..a3c0dc1 100644 --- a/PlanetObject.h +++ b/PlanetObject.h @@ -66,7 +66,6 @@ namespace ZL { std::vector subdivideTriangles(const std::vector& inputTriangles); Vector3f calculateSurfaceNormal(Vector3f p_sphere); VertexDataStruct trianglesToVertices(const std::vector& triangles); - VertexDataStruct generateOctahedron(); VertexDataStruct generateSphere(int subdivisions); }; diff --git a/SparkEmitter.cpp b/SparkEmitter.cpp index f5c8dd7..a374725 100644 --- a/SparkEmitter.cpp +++ b/SparkEmitter.cpp @@ -2,6 +2,8 @@ #include #include #include "OpenGlExtensions.h" +#include "Environment.h" + namespace ZL { @@ -125,7 +127,7 @@ namespace ZL { renderer.EnableVertexAttribArray(vTexCoordName); float aspectRatio = static_cast(screenWidth) / static_cast(screenHeight); - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, 1, 1000); + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); glBindTexture(GL_TEXTURE_2D, texture->getTexID()); diff --git a/ZLMath.h b/ZLMath.h index e1e5dd6..23b3017 100755 --- a/ZLMath.h +++ b/ZLMath.h @@ -56,6 +56,11 @@ namespace ZL { return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } + float length() const + { + return sqrt(squaredNorm()); + } + float dot(const Vector3f& other) const { return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2]; } From 2b6af91992a7694a1e0da0a1f16465f5c840e469 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 13 Dec 2025 20:16:40 +0300 Subject: [PATCH 08/11] Good constants --- PlanetObject.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/PlanetObject.cpp b/PlanetObject.cpp index ce25b5b..74861eb 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -6,27 +6,27 @@ namespace ZL { - static constexpr float PLANET_RADIUS = 1000000.f; - static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -2250000.f }; + static constexpr float PLANET_RADIUS = 100000.f; + static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -225000.f }; // --- 1. Дальний диапазон (FAR) --- - static constexpr float FAR_Z_NEAR = 50000.0f; - static constexpr float FAR_Z_FAR = 10000000.0f; + static constexpr float FAR_Z_NEAR = 5000.0f; + static constexpr float FAR_Z_FAR = 1000000.0f; // Дистанция, где НАЧИНАЕТСЯ переход FAR -> MIDDLE - static constexpr float TRANSITION_FAR_START = 400000.0f; + static constexpr float TRANSITION_FAR_START = 40000.0f; // --- 2. Средний диапазон (MIDDLE) --- - static constexpr float MIDDLE_Z_NEAR = 500.f; - static constexpr float MIDDLE_Z_FAR = 50000.f; + static constexpr float MIDDLE_Z_NEAR = 1000.f; + static constexpr float MIDDLE_Z_FAR = 200000.f; // Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR static constexpr float TRANSITION_MIDDLE_START = 10000.f; // --- 3. Ближний диапазон (NEAR) --- // Новые константы для максимальной точности - static constexpr float NEAR_Z_NEAR = 50.0f; - static constexpr float NEAR_Z_FAR = 50000.0f; + static constexpr float NEAR_Z_NEAR = 200.0f; + static constexpr float NEAR_Z_FAR = 4000.0f; // Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR static constexpr float TRANSITION_NEAR_END = 10.f; From 27fb7a448c77a2b5cd3c73b1aabc88382a54e155 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 13 Dec 2025 20:34:47 +0300 Subject: [PATCH 09/11] planet is closed --- Environment.cpp | 6 +++--- Game.cpp | 18 +++++++++++++++--- PlanetObject.cpp | 37 +++++++++++++++++++++++-------------- PlanetObject.h | 2 ++ 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Environment.cpp b/Environment.cpp index d71c040..7e06bc4 100644 --- a/Environment.cpp +++ b/Environment.cpp @@ -8,7 +8,7 @@ namespace ZL { int Environment::windowHeaderHeight = 0; int Environment::width = 0; int Environment::height = 0; -float Environment::zoom = 14.f; +float Environment::zoom = 18.f; bool Environment::leftPressed = false; bool Environment::rightPressed = false; @@ -36,8 +36,8 @@ Vector3f Environment::shipPosition = {0,0,0}; float Environment::shipVelocity = 0.f; -const float Environment::CONST_Z_NEAR = 10.f; -const float Environment::CONST_Z_FAR = 10000.f; +const float Environment::CONST_Z_NEAR = 5.f; +const float Environment::CONST_Z_FAR = 5000.f; } // namespace ZL diff --git a/Game.cpp b/Game.cpp index fb94ba9..a9af59b 100755 --- a/Game.cpp +++ b/Game.cpp @@ -463,8 +463,12 @@ namespace ZL CheckGlError(); drawCubemap(); - drawShip(); planetObject.draw(renderer); + if (planetObject.planetIsFar(Environment::shipPosition)) + { + glClear(GL_DEPTH_BUFFER_BIT); + } + drawShip(); drawBoxes(); drawUI(); @@ -612,11 +616,19 @@ namespace ZL { if (event.key.keysym.sym == SDLK_i) { - Environment::shipVelocity += 5000.f; + Environment::shipVelocity += 500.f; } if (event.key.keysym.sym == SDLK_k) { - Environment::shipVelocity -= 5000.f; + Environment::shipVelocity -= 500.f; + } + if (event.key.keysym.sym == SDLK_o) + { + Environment::shipVelocity += 50.f; + } + if (event.key.keysym.sym == SDLK_l) + { + Environment::shipVelocity -= 50.f; } } } diff --git a/PlanetObject.cpp b/PlanetObject.cpp index 74861eb..a483d2c 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -6,30 +6,30 @@ namespace ZL { - static constexpr float PLANET_RADIUS = 100000.f; - static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -225000.f }; + static constexpr float PLANET_RADIUS = 20000.f; + static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -45000.f }; // --- 1. Дальний диапазон (FAR) --- - static constexpr float FAR_Z_NEAR = 5000.0f; - static constexpr float FAR_Z_FAR = 1000000.0f; + static constexpr float FAR_Z_NEAR = 2000.0f; + static constexpr float FAR_Z_FAR = 200000.0f; // Дистанция, где НАЧИНАЕТСЯ переход FAR -> MIDDLE - static constexpr float TRANSITION_FAR_START = 40000.0f; + static constexpr float TRANSITION_FAR_START = 3000.0f; // --- 2. Средний диапазон (MIDDLE) --- - static constexpr float MIDDLE_Z_NEAR = 1000.f; - static constexpr float MIDDLE_Z_FAR = 200000.f; + static constexpr float MIDDLE_Z_NEAR = 200.f; + static constexpr float MIDDLE_Z_FAR = 20000.f; // Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR - static constexpr float TRANSITION_MIDDLE_START = 10000.f; + static constexpr float TRANSITION_MIDDLE_START = 500.f; // --- 3. Ближний диапазон (NEAR) --- // Новые константы для максимальной точности - static constexpr float NEAR_Z_NEAR = 200.0f; - static constexpr float NEAR_Z_FAR = 4000.0f; + static constexpr float NEAR_Z_NEAR = 5.0f; + static constexpr float NEAR_Z_FAR = 5000.0f; // Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR - static constexpr float TRANSITION_NEAR_END = 10.f; + static constexpr float TRANSITION_NEAR_END = 40.f; std::pair calculateZRange(const Vector3f& shipPosition) { @@ -147,12 +147,21 @@ namespace ZL { // Масштабируем: хотим отклонение от 1.0 до 1.1 // Значит амплитуда = 0.1 - float height = 1.0f + (noiseValue * 0.002f); // * 0.2 даст вариацию высоты + float height = 1.0f + (noiseValue * 0.01f); // * 0.2 даст вариацию высоты return height; } + + bool PlanetObject::planetIsFar(const Vector3f& shipPosition) + { + const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET; + const float distanceToPlanetCenter = (planetWorldPosition - shipPosition).length(); + const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS; + + return distanceToPlanetSurface > 0.1*TRANSITION_MIDDLE_START; + } PlanetObject::PlanetObject() @@ -172,8 +181,8 @@ namespace ZL { planetRenderStruct.data = planetMesh; planetRenderStruct.RefreshVBO(); - //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); - sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); + sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); + //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); } diff --git a/PlanetObject.h b/PlanetObject.h index a3c0dc1..0d3140c 100644 --- a/PlanetObject.h +++ b/PlanetObject.h @@ -59,6 +59,8 @@ namespace ZL { void draw(Renderer& renderer); + bool planetIsFar(const Vector3f& shipPosition); + private: bool drawDataDirty = true; From af550f56a785c09341d746d287c56a3abd65d3ce Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 13 Dec 2025 23:17:03 +0300 Subject: [PATCH 10/11] Cool working --- Game.cpp | 31 ++++- Game.h | 2 +- PlanetObject.cpp | 131 +++++++++++++++++++++- PlanetObject.h | 7 +- Renderer.cpp | 12 ++ Renderer.h | 2 +- shaders/defaultAtmosphere.fragment | 41 +++++++ shaders/defaultAtmosphere.vertex | 20 ++++ shaders/defaultColor_fog.vertex | 32 ++++++ shaders/defaultColor_fog_desktop.fragment | 89 +++++++++++++++ shaders/env_sky.vertex | 12 ++ shaders/env_sky_desktop.fragment | 11 ++ 12 files changed, 379 insertions(+), 11 deletions(-) create mode 100644 shaders/defaultAtmosphere.fragment create mode 100644 shaders/defaultAtmosphere.vertex create mode 100644 shaders/defaultColor_fog.vertex create mode 100644 shaders/defaultColor_fog_desktop.fragment create mode 100644 shaders/env_sky.vertex create mode 100644 shaders/env_sky_desktop.fragment diff --git a/Game.cpp b/Game.cpp index a9af59b..885ad64 100755 --- a/Game.cpp +++ b/Game.cpp @@ -152,11 +152,13 @@ namespace ZL renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE); #else renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); - renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE); - renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE); #endif @@ -267,16 +269,18 @@ namespace ZL } - void Game::drawCubemap() + void Game::drawCubemap(float skyPercent) { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; + static const std::string skyPercentUniformName = "skyPercent"; renderer.shaderManager.PushShader(envShaderName); renderer.RenderUniform1i(textureUniformName, 0); + renderer.RenderUniform1f(skyPercentUniformName, skyPercent); renderer.EnableVertexAttribArray(vPositionName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), @@ -455,16 +459,31 @@ namespace ZL static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; - glClearColor(0.0f, 0.5f, 1.0f, 1.0f); + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glViewport(0, 0, Environment::width, Environment::height); CheckGlError(); - drawCubemap(); + float skyPercent = 0.0; + float distance = planetObject.distanceToPlanetSurface(); + if (distance > 2000.f) + { + skyPercent = 0.0f; + } + else if (distance < 1000.f) + { + skyPercent = 1.0f; + } + else + { + skyPercent = (2000.f - distance) / 1000.f; + } + + drawCubemap(skyPercent); planetObject.draw(renderer); - if (planetObject.planetIsFar(Environment::shipPosition)) + if (planetObject.planetIsFar()) { glClear(GL_DEPTH_BUFFER_BIT); } diff --git a/Game.h b/Game.h index 65bb5b6..987e3e1 100755 --- a/Game.h +++ b/Game.h @@ -32,7 +32,7 @@ namespace ZL { private: void processTickCount(); void drawScene(); - void drawCubemap(); + void drawCubemap(float skyPercent); void drawShip(); void drawBoxes(); void drawUI(); diff --git a/PlanetObject.cpp b/PlanetObject.cpp index a483d2c..a7e8bd1 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -154,14 +154,23 @@ namespace ZL { - bool PlanetObject::planetIsFar(const Vector3f& shipPosition) + bool PlanetObject::planetIsFar() { const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET; - const float distanceToPlanetCenter = (planetWorldPosition - shipPosition).length(); + const float distanceToPlanetCenter = (planetWorldPosition - Environment::shipPosition).length(); const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS; return distanceToPlanetSurface > 0.1*TRANSITION_MIDDLE_START; } + + float PlanetObject::distanceToPlanetSurface() + { + const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET; + const float distanceToPlanetCenter = (planetWorldPosition - Environment::shipPosition).length(); + const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS; + return distanceToPlanetSurface; + + } PlanetObject::PlanetObject() @@ -184,6 +193,18 @@ namespace ZL { sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); + planetAtmosphere.data = CreateRect2D({ 0.f, 0.f }, { 1.f, 1.f }, 0.f); + planetAtmosphere.data.TexCoordData.clear(); + planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); + planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); + planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); + planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); + planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); + planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); + planetAtmosphere.data.Scale(1000.f); + //planetAtmosphere.data.Scale(PLANET_RADIUS*1.25f); + //planetAtmosphere.data.Move(PLANET_CENTER_OFFSET); + planetAtmosphere.RefreshVBO(); } void PlanetObject::prepareDrawData() { @@ -245,6 +266,11 @@ namespace ZL { renderer.RenderUniform3fv("uLightDirection", &lightDirection_View.v[0]); renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]); + float dist = distanceToPlanetSurface(); + + renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); + renderer.RenderUniform1f("uCurrentZFar", currentZFar); + glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); renderer.DrawVertexRenderStruct(planetRenderStruct); @@ -261,6 +287,107 @@ namespace ZL { renderer.shaderManager.PopShader(); CheckGlError(); + //drawAtmosphere(renderer); + } + + void PlanetObject::drawAtmosphere(Renderer& renderer) { + static const std::string defaultShaderName = "defaultAtmosphere"; + static const std::string vPositionName = "vPosition"; + static const std::string vColorName = "vColor"; + + glClear(GL_DEPTH_BUFFER_BIT); + + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vColorName); + + const auto zRange = calculateZRange(Environment::shipPosition); + const float currentZNear = zRange.first; + const float currentZFar = zRange.second; + + // 2. Применяем динамическую матрицу проекции + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + currentZNear, currentZFar); + + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Environment::inverseShipMatrix); + renderer.TranslateMatrix(-Environment::shipPosition); + + Matrix4f projMatrix = renderer.GetProjectionModelViewMatrix(); + Matrix4f modelViewMatrix = renderer.GetCurrentModelViewMatrix(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + + renderer.PushProjectionMatrix((Environment::width), static_cast(Environment::height), -1, 1); + renderer.PushMatrix(); + renderer.LoadIdentity(); + + // Преобразуем центр планеты в экранные координаты + int screenX = 0; + int screenY = 0; + worldToScreenCoordinates( + PLANET_CENTER_OFFSET, + projMatrix, + Environment::width, + Environment::height, + screenX, + screenY + ); + + // Позиция центра в пикселях + Vector3f centerPos = Vector3f(static_cast(screenX), static_cast(screenY), 0.0f); + + renderer.TranslateMatrix(centerPos); + renderer.RenderUniform1f("uCenterRadius", 0.f); + renderer.RenderUniform3fv("uCenterPos", ¢erPos.v[0]); + + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения + + renderer.DrawVertexRenderStruct(planetAtmosphere); + glDisable(GL_BLEND); + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + + + /* + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + currentZNear, currentZFar); + + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Environment::inverseShipMatrix); + renderer.TranslateMatrix(-Environment::shipPosition); + + float centerRadius = PLANET_RADIUS*1.2f; + + Vector3f centerPos = PLANET_CENTER_OFFSET; + + + renderer.RenderUniform1f("uCenterRadius", centerRadius); + renderer.RenderUniform3fv("uCenterPos", ¢erPos.v[0]); + + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения + + renderer.DrawVertexRenderStruct(planetAtmosphere); + glDisable(GL_BLEND); + renderer.PopMatrix(); + renderer.PopProjectionMatrix();*/ + renderer.DisableVertexAttribArray(vColorName); + renderer.DisableVertexAttribArray(vPositionName); + renderer.shaderManager.PopShader(); + CheckGlError(); } diff --git a/PlanetObject.h b/PlanetObject.h index 0d3140c..2c67bfe 100644 --- a/PlanetObject.h +++ b/PlanetObject.h @@ -48,6 +48,9 @@ namespace ZL { VertexDataStruct planetMesh; VertexRenderStruct planetRenderStruct; + VertexRenderStruct planetAtmosphere; + + std::shared_ptr sandTexture; public: @@ -58,8 +61,10 @@ namespace ZL { void update(float deltaTimeMs); void draw(Renderer& renderer); + void drawAtmosphere(Renderer& renderer); - bool planetIsFar(const Vector3f& shipPosition); + bool planetIsFar(); + float distanceToPlanetSurface(); private: diff --git a/Renderer.cpp b/Renderer.cpp index fd10526..69a206b 100755 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -727,6 +727,18 @@ namespace ZL { } + void Renderer::RenderUniform1f(const std::string& uniformName, float value) + { + auto shader = shaderManager.GetCurrentShader(); + + auto uniform = shader->uniformList.find(uniformName); + + if (uniform != shader->uniformList.end()) + { + glUniform1fv(uniform->second, 1, &value); + } + + } void Renderer::VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer) diff --git a/Renderer.h b/Renderer.h index 39eca83..c102d77 100755 --- a/Renderer.h +++ b/Renderer.h @@ -124,7 +124,7 @@ namespace ZL { void RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value); void RenderUniform1i(const std::string& uniformName, const int value); void RenderUniform3fv(const std::string& uniformName, const float* value); - + void RenderUniform1f(const std::string& uniformName, float value); void VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer); diff --git a/shaders/defaultAtmosphere.fragment b/shaders/defaultAtmosphere.fragment new file mode 100644 index 0000000..324256f --- /dev/null +++ b/shaders/defaultAtmosphere.fragment @@ -0,0 +1,41 @@ +varying vec3 color; +varying vec3 vViewPosition; // Получаем позицию фрагмента в пространстве вида + +uniform vec3 uCenterPos; // Центр планеты/атмосферы (в пространстве вида) +uniform float uCenterRadius; // Радиус атмосферы (точка, где прозрачность = 0.5) + +// Определение зоны плавного спада alpha +const float FADE_WIDTH = 0.05; // Например, 5% от радиуса для плавного перехода + +void main() +{ + // 1. Расчет расстояния от фрагмента до центра + float distanceToCenter = length(vViewPosition - uCenterPos); + + // 2. Расчет границ для плавного перехода + // Начало спада (R) + float startRadius = uCenterRadius; + // Конец спада (R + dR) + float endRadius = uCenterRadius + FADE_WIDTH * uCenterRadius; + + // 3. Расчет коэффициента прозрачности с помощью smoothstep + // smoothstep(edge0, edge1, x) возвращает 0.0, если x < edge0, + // 1.0, если x > edge1, и плавно меняется между ними. + + // Нам нужно, чтобы alpha была 0.5 при startRadius и 0.0 при endRadius. + // Если мы используем smoothstep(start, end, distance), то получим: + // - 0.0, когда distance < start + // - 1.0, когда distance > end + // Нам нужен обратный результат (1.0 - smoothstep), чтобы 0.5 было внутри, а 0.0 - снаружи. + + // Вычисляем множитель прозрачности (от 1.0 до 0.0) + float fadeFactor = 1.0 - smoothstep(startRadius, endRadius, distanceToCenter); + + // 4. Применение нового alpha + // Если fadeFactor = 1.0 (внутри R), alpha = 0.5 * 1.0 = 0.5 + // Если fadeFactor = 0.0 (вне R+dR), alpha = 0.5 * 0.0 = 0.0 + // Если между ними - плавная интерполяция. + float finalAlpha = 0.5 * fadeFactor; + + gl_FragColor = vec4(color, 0.5); +} \ No newline at end of file diff --git a/shaders/defaultAtmosphere.vertex b/shaders/defaultAtmosphere.vertex new file mode 100644 index 0000000..629ebe8 --- /dev/null +++ b/shaders/defaultAtmosphere.vertex @@ -0,0 +1,20 @@ +attribute vec3 vPosition; +attribute vec3 vColor; + +uniform mat4 ProjectionModelViewMatrix; +uniform mat4 ModelViewMatrix; + +varying vec3 color; +varying vec3 vViewPosition; // Новая переменная: позиция вершины в пространстве вида + +void main() +{ + // Расчет позиции в пространстве отсечения (как у вас) + gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); + + // Передача позиции в пространстве вида во фрагментный шейдер + vViewPosition = vec3(ModelViewMatrix * vec4(vPosition.xyz, 1.0)); + //vViewPosition = vPosition.xyz; + + color = vColor; +} \ No newline at end of file diff --git a/shaders/defaultColor_fog.vertex b/shaders/defaultColor_fog.vertex new file mode 100644 index 0000000..eadfe41 --- /dev/null +++ b/shaders/defaultColor_fog.vertex @@ -0,0 +1,32 @@ +// Вершинный шейдер (Vertex Shader) + +attribute vec3 vPosition; +attribute vec3 vColor; +attribute vec3 vNormal; +attribute vec2 vTexCoord; + +varying vec3 color; +varying vec3 normal; +varying vec2 TexCoord; +varying float viewZ; // <--- НОВОЕ: Z-координата в пространстве вида (View Space) + +uniform mat4 ProjectionModelViewMatrix; +uniform mat4 ModelViewMatrix; // Нужна для преобразования vPosition в View Space +uniform vec3 uLightDirection; + +void main() +{ + // Преобразование позиции в пространство вида (View Space) + vec4 viewPosition = ModelViewMatrix * vec4(vPosition.xyz, 1.0); + + // Сохраняем отрицательную Z-координату. В OpenGL Z-координата (глубина) + // в пространстве вида обычно отрицательна, но для расчета тумана + // удобнее использовать положительное значение. + viewZ = -viewPosition.z; + + gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); + + normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz); + color = vColor; + TexCoord = vTexCoord; +} \ No newline at end of file diff --git a/shaders/defaultColor_fog_desktop.fragment b/shaders/defaultColor_fog_desktop.fragment new file mode 100644 index 0000000..cc24c23 --- /dev/null +++ b/shaders/defaultColor_fog_desktop.fragment @@ -0,0 +1,89 @@ +// ---Фрагментный шейдер (Fragment Shader) + +varying vec3 color; +varying vec3 normal; +varying vec2 TexCoord; +varying float viewZ; + +uniform sampler2D Texture; +uniform vec3 uLightDirection; +uniform float uDistanceToPlanetSurface; +uniform float uCurrentZFar; + +// Константы для тумана: +const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); // Синий туман + +// Параметры "Distance Fog" +const float DIST_FOG_MAX = 2000.0; +const float DIST_FOG_MIN = 1000.0; + +// Параметры "Z-Far Fog" +// Насколько близко к Zfar должен начинаться туман (например, 10% от Zfar) +const float Z_FOG_START_RATIO = 0.9; +// Какую долю от Zfar должен покрывать туман (например, 10% от Zfar) +const float Z_FOG_RANGE_RATIO = 0.1; + + +void main() +{ + // ... (1. Получаем цвет и 2. Расчет освещения) + vec4 textureColor = texture2D(Texture, TexCoord); + vec3 finalColor = textureColor.rgb * color; + + float diffuse = max(0.0, dot(normal, uLightDirection)); + float ambient = 0.2; + float lightingFactor = min(1.0, ambient + diffuse); + vec3 litColor = finalColor * lightingFactor; + + + // 3. Расчет коэффициента тумана (fogFactor) + float fogFactor = 0.0; + + // --- 3.1. Расчет коэффициента тумана на основе расстояния до поверхности (Distance Fog) --- + // Этот туман работает на больших расстояниях и постепенно исчезает, + // уступая место Z-Far Fog при приближении к планете. + + if (uDistanceToPlanetSurface >= DIST_FOG_MIN && uDistanceToPlanetSurface 0.0) { + // Нормализация Z-глубины: 0.0f при farFogStart, 1.0f при farFogStart + depthRange + farFogFactor = clamp((viewZ - farFogStart) / depthRange, 0.0, 1.0); + } + + // --- 3.3. Объединение --- + + // Когда мы далеко (fogFactor > 0.0), Distance Fog доминирует. + // Когда мы близко (fogFactor = 0.0), Z-Far Fog берет управление. + // Если оба активны, берем максимум (например, когда фрагмент далек от игрока, но игрок все еще в зоне Distance Fog). + //fogFactor = max(fogFactor, farFogFactor); + fogFactor = fogFactor * farFogFactor; + + + // 4. Смешивание цвета с туманом + gl_FragColor = mix(vec4(litColor, 1.0), FOG_COLOR, fogFactor); +} \ No newline at end of file diff --git a/shaders/env_sky.vertex b/shaders/env_sky.vertex new file mode 100644 index 0000000..5903df6 --- /dev/null +++ b/shaders/env_sky.vertex @@ -0,0 +1,12 @@ +attribute vec3 vPosition; + +uniform mat4 ProjectionModelViewMatrix; + +varying vec3 dir; + +void main(){ + vec4 realVertexPos = vec4(vPosition.xyz, 1.0); + gl_Position = ProjectionModelViewMatrix * realVertexPos; + + dir = vPosition; +} \ No newline at end of file diff --git a/shaders/env_sky_desktop.fragment b/shaders/env_sky_desktop.fragment new file mode 100644 index 0000000..c6139e3 --- /dev/null +++ b/shaders/env_sky_desktop.fragment @@ -0,0 +1,11 @@ +uniform samplerCube Texture; +uniform float skyPercent; + +varying vec3 dir; + +void main(){ + vec4 skyBoxColor = textureCube(Texture, normalize(dir)); + vec4 skyColor = vec4(0.0, 0.5, 1.0, 1.0); + gl_FragColor = skyPercent*skyColor + (1.0 - skyPercent) * skyBoxColor; + +} \ No newline at end of file From 7a1961b5d1e024686124c7e9998f18924b9137d9 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sun, 14 Dec 2025 13:00:39 +0300 Subject: [PATCH 11/11] Somehow planet is working --- Environment.cpp | 2 +- Game.cpp | 2 +- PlanetObject.cpp | 144 +++++++++------------- shaders/defaultAtmosphere.fragment | 75 ++++++----- shaders/defaultAtmosphere.vertex | 30 +++-- shaders/defaultColor_fog.vertex | 3 + shaders/defaultColor_fog_desktop.fragment | 24 +++- 7 files changed, 146 insertions(+), 134 deletions(-) diff --git a/Environment.cpp b/Environment.cpp index 7e06bc4..8d6479d 100644 --- a/Environment.cpp +++ b/Environment.cpp @@ -31,7 +31,7 @@ bool Environment::tapDownHold = false; Vector2f Environment::tapDownStartPos = { 0, 0 }; Vector2f Environment::tapDownCurrentPos = { 0, 0 }; -Vector3f Environment::shipPosition = {0,0,0}; +Vector3f Environment::shipPosition = {0,0,45000.f}; float Environment::shipVelocity = 0.f; diff --git a/Game.cpp b/Game.cpp index 885ad64..dcc305c 100755 --- a/Game.cpp +++ b/Game.cpp @@ -488,7 +488,7 @@ namespace ZL glClear(GL_DEPTH_BUFFER_BIT); } drawShip(); - drawBoxes(); + //drawBoxes(); drawUI(); diff --git a/PlanetObject.cpp b/PlanetObject.cpp index a7e8bd1..22e368f 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -7,7 +7,8 @@ namespace ZL { static constexpr float PLANET_RADIUS = 20000.f; - static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -45000.f }; + static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f }; + //static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -45000.f }; // --- 1. Дальний диапазон (FAR) --- static constexpr float FAR_Z_NEAR = 2000.0f; @@ -17,19 +18,30 @@ namespace ZL { static constexpr float TRANSITION_FAR_START = 3000.0f; // --- 2. Средний диапазон (MIDDLE) --- - static constexpr float MIDDLE_Z_NEAR = 200.f; - static constexpr float MIDDLE_Z_FAR = 20000.f; + static constexpr float MIDDLE_Z_NEAR = 500.f; + static constexpr float MIDDLE_Z_FAR = 50000.f; + // Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR static constexpr float TRANSITION_MIDDLE_START = 500.f; // --- 3. Ближний диапазон (NEAR) --- // Новые константы для максимальной точности - static constexpr float NEAR_Z_NEAR = 5.0f; - static constexpr float NEAR_Z_FAR = 5000.0f; + static constexpr float NEAR_Z_NEAR = 100.0f; + static constexpr float NEAR_Z_FAR = 10000.0f; + // Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR - static constexpr float TRANSITION_NEAR_END = 40.f; + static constexpr float TRANSITION_NEAR_END = 100.f; + + // --- 3. Ближний диапазон (NEAR) --- +// Новые константы для максимальной точности + static constexpr float SUPER_NEAR_Z_NEAR = 5.0f; + static constexpr float SUPER_NEAR_Z_FAR = 5000.0f; + + + // Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR + static constexpr float TRANSITION_SUPER_NEAR_END = 10.f; std::pair calculateZRange(const Vector3f& shipPosition) { @@ -76,11 +88,24 @@ namespace ZL { currentZNear = MIDDLE_Z_NEAR * (1.0f - alpha) + NEAR_Z_NEAR * alpha; currentZFar = MIDDLE_Z_FAR * (1.0f - alpha) + NEAR_Z_FAR * alpha; + } + else if (distanceToPlanetSurface > TRANSITION_SUPER_NEAR_END) { + // Плавный переход от MIDDLE к NEAR + const float transitionLength = TRANSITION_NEAR_END - TRANSITION_SUPER_NEAR_END; + + // Нормализация расстояния, 0 при TRANSITION_MIDDLE_START (Далеко), 1 при TRANSITION_NEAR_END (Близко) + float normalizedDist = (distanceToPlanetSurface - TRANSITION_SUPER_NEAR_END) / transitionLength; + alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко) + + // Интерполяция: MIDDLE * (1-alpha) + NEAR * alpha + currentZNear = NEAR_Z_NEAR * (1.0f - alpha) + SUPER_NEAR_Z_NEAR * alpha; + currentZFar = NEAR_Z_FAR * (1.0f - alpha) + SUPER_NEAR_Z_FAR * alpha; + } else { // Полностью ближний диапазон (distanceToPlanetSurface <= TRANSITION_NEAR_END) - currentZNear = NEAR_Z_NEAR; - currentZFar = NEAR_Z_FAR; + currentZNear = SUPER_NEAR_Z_NEAR; + currentZFar = SUPER_NEAR_Z_FAR; } return { currentZNear, currentZFar }; @@ -118,6 +143,7 @@ namespace ZL { int Y = (int)floor(y) & 255; int Z = (int)floor(z) & 255; + x -= floor(x); y -= floor(y); z -= floor(z); @@ -139,6 +165,7 @@ namespace ZL { // Частота шума (чем больше, тем больше "холмов") float frequency = 7.0f; + // Получаем значение шума (обычно от -1 до 1) float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency); @@ -147,7 +174,8 @@ namespace ZL { // Масштабируем: хотим отклонение от 1.0 до 1.1 // Значит амплитуда = 0.1 - float height = 1.0f + (noiseValue * 0.01f); // * 0.2 даст вариацию высоты + float height = 1.0f; // * 0.2 даст вариацию высоты + //float height = 1.0f - (noiseValue * 0.01f); // * 0.2 даст вариацию высоты return height; } @@ -171,7 +199,7 @@ namespace ZL { return distanceToPlanetSurface; } - + PlanetObject::PlanetObject() : perlin(77777) @@ -193,17 +221,10 @@ namespace ZL { sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); - planetAtmosphere.data = CreateRect2D({ 0.f, 0.f }, { 1.f, 1.f }, 0.f); - planetAtmosphere.data.TexCoordData.clear(); - planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); - planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); - planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); - planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); - planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); - planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 }); - planetAtmosphere.data.Scale(1000.f); - //planetAtmosphere.data.Scale(PLANET_RADIUS*1.25f); - //planetAtmosphere.data.Move(PLANET_CENTER_OFFSET); + planetAtmosphere.data = generateSphere(5); + planetAtmosphere.data.Scale(PLANET_RADIUS*1.03); + planetAtmosphere.data.Move(PLANET_CENTER_OFFSET); + planetAtmosphere.RefreshVBO(); } @@ -268,12 +289,15 @@ namespace ZL { float dist = distanceToPlanetSurface(); + renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uCurrentZFar", currentZFar); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); renderer.DrawVertexRenderStruct(planetRenderStruct); - + //glDisable(GL_BLEND); CheckGlError(); @@ -287,20 +311,21 @@ namespace ZL { renderer.shaderManager.PopShader(); CheckGlError(); - //drawAtmosphere(renderer); + drawAtmosphere(renderer); } void PlanetObject::drawAtmosphere(Renderer& renderer) { static const std::string defaultShaderName = "defaultAtmosphere"; static const std::string vPositionName = "vPosition"; - static const std::string vColorName = "vColor"; - - glClear(GL_DEPTH_BUFFER_BIT); - + static const std::string vNormalName = "vNormal"; + //glClear(GL_DEPTH_BUFFER_BIT); + glDepthMask(GL_FALSE); renderer.shaderManager.PushShader(defaultShaderName); renderer.EnableVertexAttribArray(vPositionName); - renderer.EnableVertexAttribArray(vColorName); + renderer.EnableVertexAttribArray(vNormalName); + + const auto zRange = calculateZRange(Environment::shipPosition); const float currentZNear = zRange.first; @@ -317,74 +342,27 @@ namespace ZL { renderer.RotateMatrix(Environment::inverseShipMatrix); renderer.TranslateMatrix(-Environment::shipPosition); - Matrix4f projMatrix = renderer.GetProjectionModelViewMatrix(); - Matrix4f modelViewMatrix = renderer.GetCurrentModelViewMatrix(); + const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix(); + Vector3f color = { 0.0, 0.5, 1.0 }; - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); + renderer.RenderUniform3fv("uColor", &color.v[0]); - renderer.PushProjectionMatrix((Environment::width), static_cast(Environment::height), -1, 1); - renderer.PushMatrix(); - renderer.LoadIdentity(); - - // Преобразуем центр планеты в экранные координаты - int screenX = 0; - int screenY = 0; - worldToScreenCoordinates( - PLANET_CENTER_OFFSET, - projMatrix, - Environment::width, - Environment::height, - screenX, - screenY - ); - - // Позиция центра в пикселях - Vector3f centerPos = Vector3f(static_cast(screenX), static_cast(screenY), 0.0f); - - renderer.TranslateMatrix(centerPos); - renderer.RenderUniform1f("uCenterRadius", 0.f); - renderer.RenderUniform3fv("uCenterPos", ¢erPos.v[0]); + renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]); + float dist = distanceToPlanetSurface(); + renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения renderer.DrawVertexRenderStruct(planetAtmosphere); glDisable(GL_BLEND); + glDepthMask(GL_TRUE); renderer.PopMatrix(); renderer.PopProjectionMatrix(); - - /* - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, - static_cast(Environment::width) / static_cast(Environment::height), - currentZNear, currentZFar); - - renderer.PushMatrix(); - renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - renderer.RotateMatrix(Environment::inverseShipMatrix); - renderer.TranslateMatrix(-Environment::shipPosition); - - float centerRadius = PLANET_RADIUS*1.2f; - - Vector3f centerPos = PLANET_CENTER_OFFSET; - - - renderer.RenderUniform1f("uCenterRadius", centerRadius); - renderer.RenderUniform3fv("uCenterPos", ¢erPos.v[0]); - - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения - - renderer.DrawVertexRenderStruct(planetAtmosphere); - glDisable(GL_BLEND); - renderer.PopMatrix(); - renderer.PopProjectionMatrix();*/ - renderer.DisableVertexAttribArray(vColorName); + renderer.DisableVertexAttribArray(vNormalName); renderer.DisableVertexAttribArray(vPositionName); renderer.shaderManager.PopShader(); CheckGlError(); diff --git a/shaders/defaultAtmosphere.fragment b/shaders/defaultAtmosphere.fragment index 324256f..04d43b2 100644 --- a/shaders/defaultAtmosphere.fragment +++ b/shaders/defaultAtmosphere.fragment @@ -1,41 +1,48 @@ -varying vec3 color; -varying vec3 vViewPosition; // Получаем позицию фрагмента в пространстве вида +// Фрагментный шейдер: +uniform vec3 uColor; +uniform float uDistanceToPlanetSurface; // Расстояние корабля до поверхности -uniform vec3 uCenterPos; // Центр планеты/атмосферы (в пространстве вида) -uniform float uCenterRadius; // Радиус атмосферы (точка, где прозрачность = 0.5) +// Константы затухания, определенные прямо в шейдере +const float DIST_FOG_MAX = 2000.0; +const float DIST_FOG_MIN = 1000.0; -// Определение зоны плавного спада alpha -const float FADE_WIDTH = 0.05; // Например, 5% от радиуса для плавного перехода +varying vec3 vViewNormal; +varying vec3 vViewPosition; void main() { - // 1. Расчет расстояния от фрагмента до центра - float distanceToCenter = length(vViewPosition - uCenterPos); - - // 2. Расчет границ для плавного перехода - // Начало спада (R) - float startRadius = uCenterRadius; - // Конец спада (R + dR) - float endRadius = uCenterRadius + FADE_WIDTH * uCenterRadius; + // --- 1. Расчет плотности по Френелю (краевой эффект) --- + vec3 viewVector = normalize(-vViewPosition); + float NdotV = dot(vViewNormal, viewVector); + float factor = 1.0 - abs(NdotV); + float atmosphereDensity = pow(factor, 5.0); - // 3. Расчет коэффициента прозрачности с помощью smoothstep - // smoothstep(edge0, edge1, x) возвращает 0.0, если x < edge0, - // 1.0, если x > edge1, и плавно меняется между ними. - - // Нам нужно, чтобы alpha была 0.5 при startRadius и 0.0 при endRadius. - // Если мы используем smoothstep(start, end, distance), то получим: - // - 0.0, когда distance < start - // - 1.0, когда distance > end - // Нам нужен обратный результат (1.0 - smoothstep), чтобы 0.5 было внутри, а 0.0 - снаружи. - - // Вычисляем множитель прозрачности (от 1.0 до 0.0) - float fadeFactor = 1.0 - smoothstep(startRadius, endRadius, distanceToCenter); - - // 4. Применение нового alpha - // Если fadeFactor = 1.0 (внутри R), alpha = 0.5 * 1.0 = 0.5 - // Если fadeFactor = 0.0 (вне R+dR), alpha = 0.5 * 0.0 = 0.0 - // Если между ними - плавная интерполяция. - float finalAlpha = 0.5 * fadeFactor; - - gl_FragColor = vec4(color, 0.5); + // --- 2. Расчет коэффициента затухания по дистанции --- + + float distanceFactor = 1.0; + + if (uDistanceToPlanetSurface > DIST_FOG_MAX) + { + // Дистанция > 2000.0: Полностью видно (Factor = 1.0) + distanceFactor = 1.0; + } + else if (uDistanceToPlanetSurface < DIST_FOG_MIN) + { + // Дистанция < 1000.0: Полностью не видно (Factor = 0.0) + distanceFactor = 0.0; + } + else + { + // Плавный переход (линейная интерполяция) + // normalizedDistance: от 0.0 (на DIST_FOG_MIN) до 1.0 (на DIST_FOG_MAX) + float normalizedDistance = (uDistanceToPlanetSurface - DIST_FOG_MIN) / (DIST_FOG_MAX - DIST_FOG_MIN); + + distanceFactor = clamp(normalizedDistance, 0.0, 1.0); + } + + // --- 3. Финальный цвет и прозрачность --- + + float finalAlpha = atmosphereDensity * distanceFactor; + + gl_FragColor = vec4(uColor, finalAlpha); } \ No newline at end of file diff --git a/shaders/defaultAtmosphere.vertex b/shaders/defaultAtmosphere.vertex index 629ebe8..2faba8b 100644 --- a/shaders/defaultAtmosphere.vertex +++ b/shaders/defaultAtmosphere.vertex @@ -1,20 +1,28 @@ +// Вершинный шейдер: attribute vec3 vPosition; -attribute vec3 vColor; +attribute vec3 vNormal; // <-- Новый атрибут uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; -varying vec3 color; -varying vec3 vViewPosition; // Новая переменная: позиция вершины в пространстве вида +// Выходные переменные (передаются во фрагментный шейдер) +varying vec3 vViewNormal; +varying vec3 vViewPosition; void main() { - // Расчет позиции в пространстве отсечения (как у вас) - gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); - - // Передача позиции в пространстве вида во фрагментный шейдер - vViewPosition = vec3(ModelViewMatrix * vec4(vPosition.xyz, 1.0)); - //vViewPosition = vPosition.xyz; - - color = vColor; + // 1. Позиция в пространстве вида (View Space) + vec4 positionView = ModelViewMatrix * vec4(vPosition, 1.0); + vViewPosition = positionView.xyz; + + // 2. Нормаль в пространстве вида (View Space) + // Преобразуем нормаль, используя ModelViewMatrix. Поскольку нормаль - это вектор, + // трансляционная часть (четвертый столбец) матрицы нам не нужна. + // Если бы было неединообразное масштабирование, потребовалась бы Inverse Transpose Matrix. + // В вашем случае, где есть только униформное масштабирование и вращение, + // достаточно просто умножить на ModelViewMatrix: + vViewNormal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz); + // W=0.0 для векторов, чтобы игнорировать трансляцию + + gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); } \ No newline at end of file diff --git a/shaders/defaultColor_fog.vertex b/shaders/defaultColor_fog.vertex index eadfe41..d34873f 100644 --- a/shaders/defaultColor_fog.vertex +++ b/shaders/defaultColor_fog.vertex @@ -9,6 +9,7 @@ varying vec3 color; varying vec3 normal; varying vec2 TexCoord; varying float viewZ; // <--- НОВОЕ: Z-координата в пространстве вида (View Space) +varying vec3 pos; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; // Нужна для преобразования vPosition в View Space @@ -23,6 +24,8 @@ void main() // в пространстве вида обычно отрицательна, но для расчета тумана // удобнее использовать положительное значение. viewZ = -viewPosition.z; + + pos = vPosition.xyz; gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); diff --git a/shaders/defaultColor_fog_desktop.fragment b/shaders/defaultColor_fog_desktop.fragment index cc24c23..09bf95f 100644 --- a/shaders/defaultColor_fog_desktop.fragment +++ b/shaders/defaultColor_fog_desktop.fragment @@ -4,6 +4,7 @@ varying vec3 color; varying vec3 normal; varying vec2 TexCoord; varying float viewZ; +varying vec3 pos; uniform sampler2D Texture; uniform vec3 uLightDirection; @@ -64,15 +65,26 @@ void main() // гарантируя, что все, что подходит к границе uCurrentZFar, будет скрыто. // Точка начала тумана: 90% от uCurrentZFar - float farFogStart = 2000.0; + float farFogStart = 2000.f;//uCurrentZFar * 0.7; // Длина перехода тумана: 10% от uCurrentZFar - float depthRange = 2500.0; + float depthRange = 2000.f;//uCurrentZFar * 0.25; + + if (abs(uDistanceToPlanetSurface) < 410.f) + { + farFogStart = 1800.f * abs(uDistanceToPlanetSurface-10.f) * 0.0025 + 200.f; + depthRange = farFogStart; + } + if (abs(uDistanceToPlanetSurface) < 10.f) + { + farFogStart = 200.f; + depthRange = 200.f; + } float farFogFactor = 0.0; if (depthRange > 0.0) { // Нормализация Z-глубины: 0.0f при farFogStart, 1.0f при farFogStart + depthRange - farFogFactor = clamp((viewZ - farFogStart) / depthRange, 0.0, 1.0); + farFogFactor = clamp((abs(viewZ) - farFogStart) / depthRange, 0.0, 1.0); } // --- 3.3. Объединение --- @@ -85,5 +97,9 @@ void main() // 4. Смешивание цвета с туманом - gl_FragColor = mix(vec4(litColor, 1.0), FOG_COLOR, fogFactor); + + //vec3 mountainColor = vec3((length(pos+vec3(0.0,0.0,45000.0))-20000.0)/100.0, 0.5,0.0); + gl_FragColor = mix(vec4(finalColor.rgb, 0.5), FOG_COLOR, fogFactor); + //gl_FragColor = vec4((length(pos+vec3(0.0,0.0,45000.0))-20000.0)/100.0, 0.0,0.0, 1.0); + //gl_FragColor = vec4(fogFactor, 0.5,0.5, 1.0); } \ No newline at end of file