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]);