diff --git a/CMakeLists.txt b/CMakeLists.txt index 0388759..5462c7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,6 +436,8 @@ add_executable(space-game001 OpenGlExtensions.h Utils.cpp Utils.h + SparkEmitter.cpp + SparkEmitter.h ) # Установка проекта по умолчанию для Visual Studio diff --git a/Environment.cpp b/Environment.cpp index 342edc6..cdd6088 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 = 14.f; bool Environment::leftPressed = false; bool Environment::rightPressed = false; diff --git a/Game.cpp b/Game.cpp index 75e9130..5066988 100755 --- a/Game.cpp +++ b/Game.cpp @@ -1,4 +1,4 @@ -#include "Game.h" +#include "Game.h" #include "AnimatedModel.h" #include "BoneAnimatedModel.h" #include "Utils.h" @@ -19,11 +19,11 @@ namespace ZL Vector4f generateRandomQuaternion(std::mt19937& gen) { - // + // Ðàñïðåäåëåíèå äëÿ ãåíåðàöèè ñëó÷àéíûõ êîîðäèíàò êâàòåðíèîíà std::normal_distribution<> distrib(0.0, 1.0); - // N(0, 1). - // 4D- (.. ). + // Ãåíåðèðóåì ÷åòûðå ñëó÷àéíûõ ÷èñëà èç íîðìàëüíîãî ðàñïðåäåëåíèÿ N(0, 1). + // Íîðìàëèçàöèÿ ýòîãî âåêòîðà äàåò ðàâíîìåðíîå ðàñïðåäåëåíèå ïî 4D-ñôåðå (ò.å. êâàòåðíèîí åäèíè÷íîé äëèíû). Vector4f randomQuat = { (float)distrib(gen), (float)distrib(gen), @@ -35,25 +35,25 @@ namespace ZL } - // --- --- + // --- Îñíîâíàÿ ôóíêöèÿ ãåíåðàöèè --- std::vector generateRandomBoxCoords(int N) { - // + // Êîíñòàíòû const float MIN_DISTANCE = 3.0f; - const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // + 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; // , + const int MAX_ATTEMPTS = 1000; // Îãðàíè÷åíèå íà êîëè÷åñòâî ïîïûòîê, ÷òîáû èçáåæàòü áåñêîíå÷íîãî öèêëà std::vector boxCoordsArr; - boxCoordsArr.reserve(N); // + boxCoordsArr.reserve(N); // Ðåçåðâèðóåì ïàìÿòü - // 1. - // Mersenne Twister (mt19937) + // 1. Èíèöèàëèçàöèÿ ãåíåðàòîðà ïñåâäîñëó÷àéíûõ ÷èñåë + // Èñïîëüçóåì Mersenne Twister (mt19937) êàê âûñîêîêà÷åñòâåííûé ãåíåðàòîð std::random_device rd; std::mt19937 gen(rd()); - // 2. [MIN_COORD, MAX_COORD] + // 2. Îïðåäåëåíèå ðàâíîìåðíîãî ðàñïðåäåëåíèÿ äëÿ êîîðäèíàò [MIN_COORD, MAX_COORD] std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD); int generatedCount = 0; @@ -63,53 +63,53 @@ namespace ZL bool accepted = false; int attempts = 0; - // + // Ïîïûòêà íàéòè ïîäõîäÿùèå êîîðäèíàòû while (!accepted && attempts < MAX_ATTEMPTS) { - // + // Ãåíåðèðóåì íîâûå ñëó÷àéíûå êîîðäèíàòû Vector3f newPos( (float)distrib(gen), (float)distrib(gen), (float)distrib(gen) ); - // - accepted = true; // , , + // Ïðîâåðêà ðàññòîÿíèÿ äî âñåõ óæå ñóùåñòâóþùèõ îáúåêòîâ + accepted = true; // Ïðåäïîëàãàåì, ÷òî ïîäõîäèò, ïîêà íå äîêàçàíî îáðàòíîå for (const auto& existingBox : boxCoordsArr) { - // + // Ðàñ÷åò âåêòîðà ðàçíîñòè Vector3f diff = newPos - existingBox.pos; - // + // Ðàñ÷åò êâàäðàòà ðàññòîÿíèÿ float distanceSquared = diff.squaredNorm(); - // + // Åñëè êâàäðàò ðàññòîÿíèÿ ìåíüøå êâàäðàòà ìèíèìàëüíîãî ðàññòîÿíèÿ if (distanceSquared < MIN_DISTANCE_SQUARED) { - accepted = false; // , - break; // , + accepted = false; // Îòêëîíÿåì, ñëèøêîì áëèçêî + break; // Íåò ñìûñëà ïðîâåðÿòü äàëüøå, åñëè îäíî íàðóøåíèå íàéäåíî } } if (accepted) { - // 2. + // 2. Ãåíåðèðóåì ñëó÷àéíûé êâàòåðíèîí Vector4f randomQuat = generateRandomQuaternion(gen); - // 3. + // 3. Ïðåîáðàçóåì åãî â ìàòðèöó âðàùåíèÿ Matrix3f randomMatrix = QuatToMatrix(randomQuat); - // 4. + // 4. Äîáàâëÿåì îáúåêò ñ íîâîé ñëó÷àéíîé ìàòðèöåé boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); generatedCount++; } attempts++; } - // , , - // , N . + // Åñëè ïðåâûøåíî ìàêñèìàëüíîå êîëè÷åñòâî ïîïûòîê, âûõîäèì èç öèêëà, + // ÷òîáû èçáåæàòü çàâèñàíèÿ, åñëè N ñëèøêîì âåëèêî èëè äèàïàçîí ñëèøêîì ìàë. if (!accepted) { - std::cerr << ": " << N << " . : " << generatedCount << std::endl; + std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl; break; } } @@ -117,496 +117,507 @@ namespace ZL return boxCoordsArr; } -Game::Game() - : window(nullptr) - , glContext(nullptr) - , newTickCount(0) - , lastTickCount(0) -{ -} - -Game::~Game() { - if (glContext) { - SDL_GL_DeleteContext(glContext); + 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); } - if (window) { - SDL_DestroyWindow(window); + + Game::~Game() { + if (glContext) { + SDL_GL_DeleteContext(glContext); + } + if (window) { + SDL_DestroyWindow(window); + } + SDL_Quit(); } - 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{ + + 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) - }); + 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(); - //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 }); + cubemap.data = ZL::CreateCubemap(500); + cubemap.RefreshVBO(); - spaceship.AssignFrom(spaceshipBase); - spaceship.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 }); - //Boxes - boxTexture = std::make_unique(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE)); - boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE); + spaceship.AssignFrom(spaceshipBase); + spaceship.RefreshVBO(); - boxCoordsArr = generateRandomBoxCoords(50); + //Boxes + boxTexture = std::make_unique(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE)); + boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE); - boxRenderArr.resize(boxCoordsArr.size()); + boxCoordsArr = generateRandomBoxCoords(50); + + boxRenderArr.resize(boxCoordsArr.size()); + + 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)); + + 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(); + + 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); - for (int i = 0; i < boxCoordsArr.size(); i++) - { - boxRenderArr[i].AssignFrom(boxBase); - boxRenderArr[i].RefreshVBO(); } - - buttonTexture = std::make_unique(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE)); - - button.data.PositionData.push_back({ 100, 100, 0 }); - button.data.PositionData.push_back({ 100, 150, 0 }); - button.data.PositionData.push_back({ 300, 150, 0 }); - button.data.PositionData.push_back({ 100, 100, 0 }); - button.data.PositionData.push_back({ 300, 150, 0 }); - button.data.PositionData.push_back({ 300, 100, 0 }); - - button.data.TexCoordData.push_back({ 0,0 }); - button.data.TexCoordData.push_back({ 0,1 }); - button.data.TexCoordData.push_back({ 1,1 }); - button.data.TexCoordData.push_back({ 0,0 }); - button.data.TexCoordData.push_back({ 1,1 }); - button.data.TexCoordData.push_back({ 1,0 }); - - button.RefreshVBO(); - - musicVolumeBarTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE)); - - musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); - musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 }); - - musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); - musicVolumeBar.data.TexCoordData.push_back({ 0,1 }); - musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); - musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); - musicVolumeBar.data.TexCoordData.push_back({ 1,1 }); - musicVolumeBar.data.TexCoordData.push_back({ 1,0 }); - - musicVolumeBar.RefreshVBO(); - - - musicVolumeBarButtonTexture = std::make_unique(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE)); - - float 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); - - 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++) + 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 }); - 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]); + + + 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(); } - 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; - - 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 - 0.2) * (20.0); - if (uiY < volumeBarMinY || uiY > volumeBarMaxY) + void Game::drawBoxes() { - return; - } + 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"; - 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.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); - glClear(GL_DEPTH_BUFFER_BIT); - - renderer.shaderManager.PushShader(defaultShaderName); - renderer.RenderUniform1i(textureUniformName, 0); - renderer.EnableVertexAttribArray(vPositionName); - renderer.EnableVertexAttribArray(vTexCoordName); - - renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); - renderer.PushMatrix(); - - renderer.LoadIdentity(); - - - glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); - renderer.DrawVertexRenderStruct(button); - - glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID()); - renderer.DrawVertexRenderStruct(musicVolumeBar); - - glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); - renderer.DrawVertexRenderStruct(musicVolumeBarButton); - - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); - renderer.DisableVertexAttribArray(vTexCoordName); - - renderer.shaderManager.PopShader(); - CheckGlError(); -} - -void Game::drawScene() { - static const std::string defaultShaderName = "default"; - static const std::string envShaderName = "env"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; - - glClearColor(0.0f, 0.5f, 1.0f, 1.0f); - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, Environment::width, Environment::height); - - CheckGlError(); - - drawCubemap(); - drawShip(); - drawBoxes(); - - drawUI(); - - CheckGlError(); -} - -void Game::processTickCount() { - - if (lastTickCount == 0) { - lastTickCount = SDL_GetTicks64(); - return; - } - - 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); - - 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) + for (int i = 0; i < boxCoordsArr.size(); i++) { - Vector3f velocityDirection = { 0,0, -Environment::shipVelocity*delta / 1000.f }; - Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection); + renderer.PushMatrix(); - Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; + 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); - lastTickCount = newTickCount; + renderer.shaderManager.PopShader(); + CheckGlError(); } -} + void Game::UpdateVolumeKnob() { + float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY); -void Game::render() { - SDL_GL_MakeCurrent(ZL::Environment::window, glContext); - ZL::CheckGlError(); + auto& pos = musicVolumeBarButton.data.PositionData; - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - drawScene(); - processTickCount(); + 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 }; - SDL_GL_SwapWindow(ZL::Environment::window); -} -void Game::update() { - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - Environment::exitGameLoop = true; + 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; } - else if (event.type == SDL_MOUSEBUTTONDOWN) { - // 1. - int mx = event.button.x; - int my = event.button.y; + 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"; - std::cout << mx << " " << my << '\n'; - int uiX = mx; - int uiY = Environment::height - my; - if (uiX >= volumeBarMinX && uiX <= volumeBarMaxX && - uiY >= volumeBarMinY && uiY <= volumeBarMaxY) { - isDraggingVolume = true; - UpdateVolumeFromMouse(mx, my); - } - else { - Environment::tapDownHold = true; - // - Environment::tapDownStartPos.v[0] = event.button.x; - Environment::tapDownStartPos.v[1] = event.button.y; - // - Environment::tapDownCurrentPos.v[0] = event.button.x; - Environment::tapDownCurrentPos.v[1] = event.button.y; - } - + + glClear(GL_DEPTH_BUFFER_BIT); + + renderer.shaderManager.PushShader(defaultShaderName); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + renderer.EnableVertexAttribArray(vTexCoordName); + + renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); + renderer.PushMatrix(); + + renderer.LoadIdentity(); + + + glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); + renderer.DrawVertexRenderStruct(button); + + glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID()); + renderer.DrawVertexRenderStruct(musicVolumeBar); + + glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); + renderer.DrawVertexRenderStruct(musicVolumeBarButton); + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); + + renderer.shaderManager.PopShader(); + CheckGlError(); + } + + void Game::drawScene() { + static const std::string defaultShaderName = "default"; + static const std::string envShaderName = "env"; + static const std::string vPositionName = "vPosition"; + static const std::string vTexCoordName = "vTexCoord"; + static const std::string textureUniformName = "Texture"; + + glClearColor(0.0f, 0.5f, 1.0f, 1.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, Environment::width, Environment::height); + + CheckGlError(); + + drawCubemap(); + drawShip(); + drawBoxes(); + + drawUI(); + + CheckGlError(); + } + + void Game::processTickCount() { + + if (lastTickCount == 0) { + lastTickCount = SDL_GetTicks64(); + return; } - 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); - } + + 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 (Environment::tapDownHold) { - // , - Environment::tapDownCurrentPos.v[0] = event.motion.x; - Environment::tapDownCurrentPos.v[1] = event.motion.y; + + 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); + + } } - } - 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) + + if (fabs(Environment::shipVelocity) > 0.01f) { - Environment::shipVelocity += 1.f; - } - if (event.key.keysym.sym == SDLK_k) - { - Environment::shipVelocity -= 1.f; + Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f }; + Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection); + + Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; } + + lastTickCount = newTickCount; } } - render(); -} -} // namespace ZL \ No newline at end of file + 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 34b608a..ea6e393 100755 --- a/Game.h +++ b/Game.h @@ -1,58 +1,48 @@ -#pragma once +#pragma once #include "OpenGlExtensions.h" #include "Renderer.h" #include "Environment.h" #include "TextureManager.h" +#include "SparkEmitter.h" namespace ZL { - struct BoxCoords - { - Vector3f pos; - Matrix3f m; - }; + struct BoxCoords + { + Vector3f pos; + Matrix3f m; + }; -class Game { -public: - Game(); - ~Game(); - - void setup(); - void update(); - void render(); - - bool shouldExit() const { return Environment::exitGameLoop; } + class Game { + public: + Game(); + ~Game(); -private: - void processTickCount(); - void drawScene(); - void drawCubemap(); - void drawShip(); - void drawBoxes(); - void drawUI(); + void setup(); + void update(); + void render(); - SDL_Window* window; - SDL_GLContext glContext; - Renderer renderer; - - size_t newTickCount; - size_t lastTickCount; - - static const size_t CONST_TIMER_INTERVAL = 10; - static const size_t CONST_MAX_TIME_INTERVAL = 1000; - std::shared_ptr spaceshipTexture; - std::shared_ptr cubemapTexture; - VertexDataStruct spaceshipBase; - VertexRenderStruct spaceship; + bool shouldExit() const { return Environment::exitGameLoop; } - VertexRenderStruct cubemap; + private: + void processTickCount(); + void drawScene(); + void drawCubemap(); + void drawShip(); + void drawBoxes(); + void drawUI(); + + SDL_Window* window; + SDL_GLContext glContext; + Renderer renderer; + + size_t newTickCount; + size_t lastTickCount; - std::shared_ptr boxTexture; - VertexDataStruct boxBase; std::vector boxCoordsArr; std::vector boxRenderArr; @@ -69,7 +59,7 @@ private: bool isDraggingVolume = false; - float musicVolume = 1.0f; + float musicVolume = 0.0f; float volumeBarMinX = 1190.0f; float volumeBarMaxX = 1200.0f; float volumeBarMinY = 100.0f; @@ -78,6 +68,23 @@ private: 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; + + std::shared_ptr sparkTexture; + std::shared_ptr spaceshipTexture; + std::shared_ptr cubemapTexture; + VertexDataStruct spaceshipBase; + VertexRenderStruct spaceship; + + VertexRenderStruct cubemap; + + std::shared_ptr boxTexture; + VertexDataStruct boxBase; + + SparkEmitter sparkEmitter; + }; + } // 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/SparkEmitter.cpp b/SparkEmitter.cpp new file mode 100644 index 0000000..f5c8dd7 --- /dev/null +++ b/SparkEmitter.cpp @@ -0,0 +1,297 @@ +#include "SparkEmitter.h" +#include +#include +#include "OpenGlExtensions.h" + +namespace ZL { + + SparkEmitter::SparkEmitter() + : emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200) { + particles.resize(maxParticles); + drawPositions.reserve(maxParticles * 6); + drawTexCoords.reserve(maxParticles * 6); + lastEmissionTime = std::chrono::steady_clock::now(); + + sparkQuad.data = VertexDataStruct(); + } + + SparkEmitter::SparkEmitter(const std::vector& positions, float rate) + : emissionPoints(positions), emissionRate(rate), isActive(true), + drawDataDirty(true), maxParticles(positions.size() * 100) { + particles.resize(maxParticles); + drawPositions.reserve(maxParticles * 6); + drawTexCoords.reserve(maxParticles * 6); + lastEmissionTime = std::chrono::steady_clock::now(); + + sparkQuad.data = VertexDataStruct(); + } + + SparkEmitter::SparkEmitter(const std::vector& positions, + std::shared_ptr tex, + float rate) + : emissionPoints(positions), texture(tex), emissionRate(rate), + isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100) { + particles.resize(maxParticles); + drawPositions.reserve(maxParticles * 6); + drawTexCoords.reserve(maxParticles * 6); + lastEmissionTime = std::chrono::steady_clock::now(); + + sparkQuad.data = VertexDataStruct(); + } + + void SparkEmitter::setTexture(std::shared_ptr tex) { + texture = tex; + } + + void SparkEmitter::prepareDrawData() { + if (!drawDataDirty) return; + + drawPositions.clear(); + drawTexCoords.clear(); + + if (getActiveParticleCount() == 0) { + drawDataDirty = false; + return; + } + + std::vector> sortedParticles; + sortedParticles.reserve(getActiveParticleCount()); + + for (const auto& particle : particles) { + if (particle.active) { + sortedParticles.push_back({ &particle, particle.position.v[2] }); + } + } + + std::sort(sortedParticles.begin(), sortedParticles.end(), + [](const auto& a, const auto& b) { + return a.second > b.second; + }); + + for (const auto& [particlePtr, depth] : sortedParticles) { + const auto& particle = *particlePtr; + Vector3f pos = particle.position; + float size = 0.04f * particle.scale; + + drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] }); + drawTexCoords.push_back({ 0.0f, 0.0f }); + + drawPositions.push_back({ pos.v[0] - size, pos.v[1] + size, pos.v[2] }); + drawTexCoords.push_back({ 0.0f, 1.0f }); + + drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] }); + drawTexCoords.push_back({ 1.0f, 1.0f }); + + drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] }); + drawTexCoords.push_back({ 0.0f, 0.0f }); + + drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] }); + drawTexCoords.push_back({ 1.0f, 1.0f }); + + drawPositions.push_back({ pos.v[0] + size, pos.v[1] - size, pos.v[2] }); + drawTexCoords.push_back({ 1.0f, 0.0f }); + } + + drawDataDirty = false; + } + + void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) { + if (getActiveParticleCount() == 0) { + return; + } + + if (!texture) { + return; + } + + prepareDrawData(); + + if (drawPositions.empty()) { + return; + } + + sparkQuad.data.PositionData = drawPositions; + sparkQuad.data.TexCoordData = drawTexCoords; + sparkQuad.RefreshVBO(); + + static const std::string defaultShaderName = "default"; + 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); + + float aspectRatio = static_cast(screenWidth) / static_cast(screenHeight); + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, 1, 1000); + + glBindTexture(GL_TEXTURE_2D, texture->getTexID()); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения + + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0, 0, -1.0f * zoom }); + + renderer.DrawVertexRenderStruct(sparkQuad); + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + + glDisable(GL_BLEND); + + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); + renderer.shaderManager.PopShader(); + } + + void SparkEmitter::update(float deltaTimeMs) { + auto currentTime = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast( + currentTime - lastEmissionTime).count(); + + if (isActive && elapsed >= emissionRate) { + emit(); + lastEmissionTime = currentTime; + drawDataDirty = true; + } + + bool anyChanged = false; + for (auto& particle : particles) { + if (particle.active) { + Vector3f oldPosition = particle.position; + float oldScale = particle.scale; + + particle.position.v[0] += particle.velocity.v[0] * deltaTimeMs / 1000.0f; + particle.position.v[1] += particle.velocity.v[1] * deltaTimeMs / 1000.0f; + particle.position.v[2] += particle.velocity.v[2] * deltaTimeMs / 1000.0f; + + particle.lifeTime += deltaTimeMs; + + if (particle.lifeTime >= particle.maxLifeTime) { + particle.active = false; + anyChanged = true; + } + else { + float lifeRatio = particle.lifeTime / particle.maxLifeTime; + particle.scale = 1.0f - lifeRatio * 0.8f; + + if (oldPosition.v[0] != particle.position.v[0] || + oldPosition.v[1] != particle.position.v[1] || + oldPosition.v[2] != particle.position.v[2] || + oldScale != particle.scale) { + anyChanged = true; + } + } + } + } + + if (anyChanged) { + drawDataDirty = true; + } + } + + void SparkEmitter::emit() { + if (emissionPoints.empty()) return; + bool emitted = false; + + for (int i = 0; i < emissionPoints.size(); ++i) { + bool particleFound = false; + + for (auto& particle : particles) { + if (!particle.active) { + initParticle(particle, i); + particle.active = true; + particle.lifeTime = 0; + particle.position = emissionPoints[i]; + particle.emitterIndex = i; + particleFound = true; + emitted = true; + break; + } + } + + if (!particleFound && !particles.empty()) { + size_t oldestIndex = 0; + float maxLifeTime = 0; + + for (size_t j = 0; j < particles.size(); ++j) { + if (particles[j].lifeTime > maxLifeTime) { + maxLifeTime = particles[j].lifeTime; + oldestIndex = j; + } + } + + initParticle(particles[oldestIndex], i); + particles[oldestIndex].active = true; + particles[oldestIndex].lifeTime = 0; + particles[oldestIndex].position = emissionPoints[i]; + particles[oldestIndex].emitterIndex = i; + emitted = true; + } + } + + if (emitted) { + drawDataDirty = true; + } + } + + void SparkEmitter::setEmissionPoints(const std::vector& positions) { + emissionPoints = positions; + maxParticles = positions.size() * 100; + particles.resize(maxParticles); + drawDataDirty = true; + } + + void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) { + particle.velocity = getRandomVelocity(emitterIndex); + particle.scale = 1.0f; + particle.maxLifeTime = 800.0f + (rand() % 400); + particle.emitterIndex = emitterIndex; + } + + Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) { + static std::random_device rd; + static std::mt19937 gen(rd()); + static std::uniform_real_distribution<> angleDist(0, 2 * M_PI); + static std::uniform_real_distribution<> speedDist(0.5f, 2.0f); + static std::uniform_real_distribution<> zSpeedDist(1.0f, 3.0f); + + float angle = angleDist(gen); + float speed = speedDist(gen); + float zSpeed = zSpeedDist(gen); + + if (emitterIndex == 0) { + return Vector3f{ + cosf(angle) * speed - 0.3f, + sinf(angle) * speed, + zSpeed + }; + } + else { + return Vector3f{ + cosf(angle) * speed + 0.3f, + sinf(angle) * speed, + zSpeed + }; + } + } + + const std::vector& SparkEmitter::getParticles() const { + return particles; + } + + size_t SparkEmitter::getActiveParticleCount() const { + size_t count = 0; + for (const auto& particle : particles) { + if (particle.active) { + count++; + } + } + return count; + } + +} // namespace ZL \ No newline at end of file diff --git a/SparkEmitter.h b/SparkEmitter.h new file mode 100644 index 0000000..4f1027a --- /dev/null +++ b/SparkEmitter.h @@ -0,0 +1,66 @@ +#pragma once + +#include "ZLMath.h" +#include "Renderer.h" +#include "TextureManager.h" +#include +#include + +namespace ZL { + + struct SparkParticle { + Vector3f position; + Vector3f velocity; + float scale; + float lifeTime; + float maxLifeTime; + bool active; + int emitterIndex; + + SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f), + lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) { + } + }; + + class SparkEmitter { + private: + std::vector particles; + std::vector emissionPoints; + std::chrono::steady_clock::time_point lastEmissionTime; + float emissionRate; + bool isActive; + + std::vector drawPositions; + std::vector drawTexCoords; + bool drawDataDirty; + VertexRenderStruct sparkQuad; + std::shared_ptr texture; + + int maxParticles; + + void prepareDrawData(); + + public: + SparkEmitter(); + SparkEmitter(const std::vector& positions, float rate = 100.0f); + SparkEmitter(const std::vector& positions, + std::shared_ptr tex, + float rate = 100.0f); + + void setEmissionPoints(const std::vector& positions); + void setTexture(std::shared_ptr tex); + + void update(float deltaTimeMs); + void emit(); + + void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight); + + const std::vector& getParticles() const; + size_t getActiveParticleCount() const; + + private: + void initParticle(SparkParticle& particle, int emitterIndex); + Vector3f getRandomVelocity(int emitterIndex); + }; + +} // namespace ZL \ No newline at end of file diff --git a/resources/spark.png b/resources/spark.png new file mode 100644 index 0000000..4ed63fd --- /dev/null +++ b/resources/spark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8885a2b593c5dc0e8ac5cf5204bf3d82eb45ad6dae9b56e2bc88a41e61144c89 +size 2222 diff --git a/shaders/default_desktop.fragment b/shaders/default_desktop.fragment index 2470304..ae06b6a 100644 --- a/shaders/default_desktop.fragment +++ b/shaders/default_desktop.fragment @@ -5,8 +5,8 @@ varying vec2 texCoord; void main() { vec4 color = texture2D(Texture,texCoord).rgba; - gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0); + //gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0); - //gl_FragColor = color; + gl_FragColor = color; } \ No newline at end of file