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/Environment.cpp b/Environment.cpp index 342edc6..8d6479d 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 = 6.f; +float Environment::zoom = 18.f; bool Environment::leftPressed = false; bool Environment::rightPressed = false; @@ -31,9 +31,13 @@ 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; +const float Environment::CONST_Z_NEAR = 5.f; +const float Environment::CONST_Z_FAR = 5000.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 70c9387..08330e8 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 @@ -205,24 +207,87 @@ namespace ZL throw std::runtime_error("Failed to load spark emitter config file!"); } + + 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 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() + 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), - 1, 1000); + Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.RotateMatrix(Environment::inverseShipMatrix); @@ -258,7 +323,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(); @@ -267,7 +332,6 @@ namespace ZL glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); renderer.DrawVertexRenderStruct(spaceship); - sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); renderer.PopMatrix(); @@ -294,7 +358,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++) { @@ -319,6 +383,77 @@ namespace ZL 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; + Environment::shipVelocity = (musicVolume) * (20.0); + 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"; + 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"; @@ -327,16 +462,38 @@ 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()) + { + glClear(GL_DEPTH_BUFFER_BIT); + } drawShip(); - drawBoxes(); + //drawBoxes(); + + drawUI(); CheckGlError(); } @@ -422,20 +579,43 @@ namespace ZL } else if (event.type == SDL_MOUSEBUTTONDOWN) { // 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè - 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 - 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; @@ -458,15 +638,23 @@ namespace ZL { if (event.key.keysym.sym == SDLK_i) { - Environment::shipVelocity += 1.f; + Environment::shipVelocity += 500.f; } if (event.key.keysym.sym == SDLK_k) { - Environment::shipVelocity -= 1.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; } } } render(); } -} // namespace ZL \ No newline at end of file +} // namespace ZL diff --git a/Game.h b/Game.h index 236e8ce..987e3e1 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 { @@ -25,14 +26,16 @@ namespace ZL { void update(); void render(); + bool shouldExit() const { return Environment::exitGameLoop; } private: void processTickCount(); void drawScene(); - void drawCubemap(); + void drawCubemap(float skyPercent); void drawShip(); void drawBoxes(); + void drawUI(); SDL_Window* window; SDL_GLContext glContext; @@ -41,6 +44,32 @@ namespace ZL { size_t newTickCount; 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; + + + 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; @@ -55,10 +84,9 @@ namespace ZL { std::shared_ptr boxTexture; VertexDataStruct boxBase; - std::vector boxCoordsArr; - std::vector boxRenderArr; - SparkEmitter sparkEmitter; + PlanetObject planetObject; }; + } // namespace ZL \ No newline at end of file diff --git a/PlanetObject.cpp b/PlanetObject.cpp new file mode 100644 index 0000000..22e368f --- /dev/null +++ b/PlanetObject.cpp @@ -0,0 +1,582 @@ +#include "PlanetObject.h" +#include +#include +#include "OpenGlExtensions.h" +#include "Environment.h" + +namespace ZL { + + static constexpr float PLANET_RADIUS = 20000.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; + static constexpr float FAR_Z_FAR = 200000.0f; + + // Дистанция, где НАЧИНАЕТСЯ переход FAR -> MIDDLE + static constexpr float TRANSITION_FAR_START = 3000.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 = 500.f; + + // --- 3. Ближний диапазон (NEAR) --- + // Новые константы для максимальной точности + static constexpr float NEAR_Z_NEAR = 100.0f; + static constexpr float NEAR_Z_FAR = 10000.0f; + + + // Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR + 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) { + + // 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 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 = SUPER_NEAR_Z_NEAR; + currentZFar = SUPER_NEAR_Z_FAR; + } + + return { currentZNear, currentZFar }; + } + + 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; // * 0.2 даст вариацию высоты + //float height = 1.0f - (noiseValue * 0.01f); // * 0.2 даст вариацию высоты + + return height; + } + + + + bool PlanetObject::planetIsFar() + { + const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET; + 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() + : perlin(77777) + , colorPerlin(123123) + { + + } + + void PlanetObject::init() { + + planetMesh = generateSphere(8); + + planetMesh.Scale(PLANET_RADIUS); + planetMesh.Move(PLANET_CENTER_OFFSET); + + planetRenderStruct.data = planetMesh; + planetRenderStruct.RefreshVBO(); + + sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); + //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); + + planetAtmosphere.data = generateSphere(5); + planetAtmosphere.data.Scale(PLANET_RADIUS*1.03); + planetAtmosphere.data.Move(PLANET_CENTER_OFFSET); + + planetAtmosphere.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"; + 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); + + 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); + + 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]); + + 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(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + //renderer.DisableVertexAttribArray(vTexCoord3Name); + renderer.DisableVertexAttribArray(vTexCoordName); + renderer.DisableVertexAttribArray(vNormalName); + renderer.DisableVertexAttribArray(vColorName); + renderer.DisableVertexAttribArray(vPositionName); + 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 vNormalName = "vNormal"; + //glClear(GL_DEPTH_BUFFER_BIT); + glDepthMask(GL_FALSE); + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vNormalName); + + + + 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); + + const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix(); + + Vector3f color = { 0.0, 0.5, 1.0 }; + + renderer.RenderUniform3fv("uColor", &color.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.DisableVertexAttribArray(vNormalName); + 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); + + 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 PlanetObject::calculateSurfaceNormal(Vector3f p_sphere) { + // 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 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 вершинам треугольника + 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); + + buffer.PositionData.push_back({ p_geometry }); + buffer.NormalData.push_back({ normal }); + //buffer.TexCoord3Data.push_back(barycentricCoords[i]); + buffer.TexCoordData.push_back(triangleUVs[i]); + + } + } + return buffer; + } + + + 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 + }; + + // 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); + } + + // 4. Генерируем вершины И НОРМАЛИ с помощью trianglesToVertices + // ЭТО ЗАПОЛНИТ PositionData И NormalData + VertexDataStruct buffer = trianglesToVertices(geometry); + + // Теперь нам нужно заполнить ColorData, используя ту же логику, что и раньше. + // Сначала резервируем место, так как trianglesToVertices не заполняет ColorData + buffer.ColorData.reserve(geometry.size() * 3); + + // Базовый цвет и параметры + 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 }; + + + 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(); + + // Вычисление цветового шума (как вы делали) + 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 + }; + + Vector3f finalColor = baseColor + colorOffset; + + 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); + } + } + + return buffer; + } + + + +} // namespace ZL \ No newline at end of file diff --git a/PlanetObject.h b/PlanetObject.h new file mode 100644 index 0000000..2c67bfe --- /dev/null +++ b/PlanetObject.h @@ -0,0 +1,79 @@ +#pragma once + +#include "ZLMath.h" +#include "Renderer.h" +#include "TextureManager.h" +#include +#include +#include +#include +#include +#include +#include +#include + +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(); + PerlinNoise(uint64_t seed); + + 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); + + float getSurfaceHeight(Vector3f pos); + }; + + + class PlanetObject { + private: + PerlinNoise perlin; + PerlinNoise colorPerlin; + void prepareDrawData(); + VertexDataStruct planetMesh; + VertexRenderStruct planetRenderStruct; + + VertexRenderStruct planetAtmosphere; + + + std::shared_ptr sandTexture; + + public: + PlanetObject(); + + void init(); + + void update(float deltaTimeMs); + + void draw(Renderer& renderer); + void drawAtmosphere(Renderer& renderer); + + bool planetIsFar(); + float distanceToPlanetSurface(); + + + private: + bool drawDataDirty = true; + + std::vector subdivideTriangles(const std::vector& inputTriangles); + Vector3f calculateSurfaceNormal(Vector3f p_sphere); + VertexDataStruct trianglesToVertices(const std::vector& triangles); + VertexDataStruct generateSphere(int subdivisions); + }; + +} // namespace ZL \ No newline at end of file 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 diff --git a/Renderer.cpp b/Renderer.cpp index e39c946..69a206b 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]); } @@ -708,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) @@ -733,6 +764,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 +795,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..c102d77 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(); @@ -121,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/SparkEmitter.cpp b/SparkEmitter.cpp index ba93977..ef5ed3e 100644 --- a/SparkEmitter.cpp +++ b/SparkEmitter.cpp @@ -5,6 +5,7 @@ #include #include "external/nlohmann/json.hpp" #include +#include "Environment.h" namespace ZL { @@ -132,7 +133,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 609f14d..23b3017 100755 --- a/ZLMath.h +++ b/ZLMath.h @@ -56,6 +56,23 @@ 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]; + } + + 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]); @@ -67,6 +84,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/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 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/defaultAtmosphere.fragment b/shaders/defaultAtmosphere.fragment new file mode 100644 index 0000000..04d43b2 --- /dev/null +++ b/shaders/defaultAtmosphere.fragment @@ -0,0 +1,48 @@ +// Фрагментный шейдер: +uniform vec3 uColor; +uniform float uDistanceToPlanetSurface; // Расстояние корабля до поверхности + +// Константы затухания, определенные прямо в шейдере +const float DIST_FOG_MAX = 2000.0; +const float DIST_FOG_MIN = 1000.0; + +varying vec3 vViewNormal; +varying vec3 vViewPosition; + +void main() +{ + // --- 1. Расчет плотности по Френелю (краевой эффект) --- + vec3 viewVector = normalize(-vViewPosition); + float NdotV = dot(vViewNormal, viewVector); + float factor = 1.0 - abs(NdotV); + float atmosphereDensity = pow(factor, 5.0); + + // --- 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 new file mode 100644 index 0000000..2faba8b --- /dev/null +++ b/shaders/defaultAtmosphere.vertex @@ -0,0 +1,28 @@ +// Вершинный шейдер: +attribute vec3 vPosition; +attribute vec3 vNormal; // <-- Новый атрибут + +uniform mat4 ProjectionModelViewMatrix; +uniform mat4 ModelViewMatrix; + +// Выходные переменные (передаются во фрагментный шейдер) +varying vec3 vViewNormal; +varying vec3 vViewPosition; + +void main() +{ + // 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.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 diff --git a/shaders/defaultColor_fog.vertex b/shaders/defaultColor_fog.vertex new file mode 100644 index 0000000..d34873f --- /dev/null +++ b/shaders/defaultColor_fog.vertex @@ -0,0 +1,35 @@ +// Вершинный шейдер (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) +varying vec3 pos; + +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; + + pos = vPosition.xyz; + + 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..09bf95f --- /dev/null +++ b/shaders/defaultColor_fog_desktop.fragment @@ -0,0 +1,105 @@ +// ---Фрагментный шейдер (Fragment Shader) + +varying vec3 color; +varying vec3 normal; +varying vec2 TexCoord; +varying float viewZ; +varying vec3 pos; + +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((abs(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. Смешивание цвета с туманом + + //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 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