diff --git a/Game.cpp b/Game.cpp index 7d9b83e..fe5f577 100755 --- a/Game.cpp +++ b/Game.cpp @@ -12,516 +12,528 @@ 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) - }); - - - 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); - - 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)); - - /*singleSpark.data.PositionData.push_back({-1, -1, 0}); - singleSpark.data.PositionData.push_back({ -1, 1, 0 }); - singleSpark.data.PositionData.push_back({ 1, 1, 0 }); - singleSpark.data.PositionData.push_back({ -1, -1, 0 }); - singleSpark.data.PositionData.push_back({ 1, 1, 0 }); - singleSpark.data.PositionData.push_back({ 1, -1, 0 }); - - singleSpark.data.TexCoordData.push_back({0,0}); - singleSpark.data.TexCoordData.push_back({ 0,1 }); - singleSpark.data.TexCoordData.push_back({1,1}); - singleSpark.data.TexCoordData.push_back({0,0}); - singleSpark.data.TexCoordData.push_back({ 1,1 }); - singleSpark.data.TexCoordData.push_back({ 1,0 }); - - singleSpark.RefreshVBO();*/ - sparkQuad.data = CreateRect2D({ 0, 0 }, { 0.08f, 0.08f }, 0); - sparkQuad.RefreshVBO(); - - renderer.InitOpenGL(); - -} - -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::drawEffects() -{ - static const std::string defaultShaderName = "default"; - static const std::string vPositionName = "vPosition"; - static const std::string vTexCoordName = "vTexCoord"; - static const std::string textureUniformName = "Texture"; - - if (sparkEmitter.getActiveParticleCount() == 0) { - return; - } - - 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); - - glBindTexture(GL_TEXTURE_2D, sparkTexture->getTexID()); - - const auto& particles = sparkEmitter.getParticles(); - for (const auto& particle : particles) { - if (!particle.active) continue; - - renderer.PushMatrix(); - renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0, 0, -1.0f * Environment::zoom }); - renderer.TranslateMatrix(particle.position); - renderer.ScaleMatrix(particle.scale); - renderer.RotateMatrix(Environment::inverseShipMatrix); - renderer.DrawVertexRenderStruct(sparkQuad); - renderer.PopMatrix(); -} - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); - renderer.DisableVertexAttribArray(vTexCoordName); - 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); - - drawEffects(); - - 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::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(); - - 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); - sparkEmitter.update(static_cast(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) - { - 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. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè - 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. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè - Environment::tapDownHold = false; - } - else if (event.type == SDL_MOUSEMOTION) { - // 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè - 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(); -} + + 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(); + + //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()); + + 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)); + + sparkQuad.data = CreateRect2D({ 0, 0 }, { 0.08f, 0.08f }, 0); + sparkQuad.RefreshVBO(); + + renderer.InitOpenGL(); + + } + + 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::drawEffects() + { + if (sparkEmitter.getActiveParticleCount() == 0) { + return; + } + + sparkQuad.data.PositionData.clear(); + sparkQuad.data.TexCoordData.clear(); + + const auto& particles = sparkEmitter.getParticles(); + for (const auto& particle : particles) { + if (!particle.active) continue; + + Vector3f pos = particle.position; + float size = 0.04f * particle.scale; + + sparkQuad.data.PositionData.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] }); + sparkQuad.data.TexCoordData.push_back({ 0.0f, 0.0f }); + + sparkQuad.data.PositionData.push_back({ pos.v[0] - size, pos.v[1] + size, pos.v[2] }); + sparkQuad.data.TexCoordData.push_back({ 0.0f, 1.0f }); + + sparkQuad.data.PositionData.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] }); + sparkQuad.data.TexCoordData.push_back({ 1.0f, 1.0f }); + + sparkQuad.data.PositionData.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] }); + sparkQuad.data.TexCoordData.push_back({ 0.0f, 0.0f }); + + sparkQuad.data.PositionData.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] }); + sparkQuad.data.TexCoordData.push_back({ 1.0f, 1.0f }); + + sparkQuad.data.PositionData.push_back({ pos.v[0] + size, pos.v[1] - size, pos.v[2] }); + sparkQuad.data.TexCoordData.push_back({ 1.0f, 0.0f }); + } + + if (sparkQuad.data.PositionData.empty()) return; + + 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); + + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + + glBindTexture(GL_TEXTURE_2D, sparkTexture->getTexID()); + + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0, 0, -1.0f * Environment::zoom }); + + renderer.DrawVertexRenderStruct(sparkQuad); + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.DisableVertexAttribArray(vTexCoordName); + 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); + + drawEffects(); + + 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::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(); + + 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); + sparkEmitter.update(static_cast(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) + { + 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. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè + 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. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè + Environment::tapDownHold = false; + } + else if (event.type == SDL_MOUSEMOTION) { + // 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè + 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 \ No newline at end of file diff --git a/Game.h b/Game.h index a6bd0fb..5f65f98 100755 --- a/Game.h +++ b/Game.h @@ -9,59 +9,59 @@ 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 drawEffects(); - void drawShip(); - void drawBoxes(); + 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; + bool shouldExit() const { return Environment::exitGameLoop; } - std::shared_ptr sparkTexture; - std::shared_ptr spaceshipTexture; - std::shared_ptr cubemapTexture; - VertexDataStruct spaceshipBase; - VertexRenderStruct spaceship; + private: + void processTickCount(); + void drawScene(); + void drawCubemap(); + void drawEffects(); + void drawShip(); + void drawBoxes(); - VertexRenderStruct cubemap; + SDL_Window* window; + SDL_GLContext glContext; + Renderer renderer; - std::shared_ptr boxTexture; - VertexDataStruct boxBase; + size_t newTickCount; + size_t lastTickCount; - std::vector boxCoordsArr; - std::vector boxRenderArr; + static const size_t CONST_TIMER_INTERVAL = 10; + static const size_t CONST_MAX_TIME_INTERVAL = 1000; - //VertexRenderStruct singleSpark; - SparkEmitter sparkEmitter; - VertexRenderStruct sparkQuad; -}; + std::shared_ptr sparkTexture; + std::shared_ptr spaceshipTexture; + std::shared_ptr cubemapTexture; + VertexDataStruct spaceshipBase; + VertexRenderStruct spaceship; + + VertexRenderStruct cubemap; + + std::shared_ptr boxTexture; + VertexDataStruct boxBase; + + std::vector boxCoordsArr; + std::vector boxRenderArr; + + //VertexRenderStruct singleSpark; + SparkEmitter sparkEmitter; + VertexRenderStruct sparkQuad; + }; } // namespace ZL \ No newline at end of file diff --git a/SparkEmitter.cpp b/SparkEmitter.cpp index 66ec93d..2ca1392 100644 --- a/SparkEmitter.cpp +++ b/SparkEmitter.cpp @@ -5,143 +5,207 @@ namespace ZL { - SparkEmitter::SparkEmitter() - : emissionRate(100.0f), isActive(true) { - particles.resize(200); - lastEmissionTime = std::chrono::steady_clock::now(); - } + SparkEmitter::SparkEmitter() + : emissionRate(100.0f), isActive(true), buffersDirty(true), maxParticles(200) { + particles.resize(maxParticles); + positionBuffer.resize(maxParticles * 4, 0.0f); + texCoordBuffer.resize(maxParticles * 2, 0.0f); + lastEmissionTime = std::chrono::steady_clock::now(); + } - SparkEmitter::SparkEmitter(const std::vector& positions, float rate) - : emissionPoints(positions), emissionRate(rate), isActive(true) { - particles.resize(positions.size() * 100); - lastEmissionTime = std::chrono::steady_clock::now(); - } + SparkEmitter::SparkEmitter(const std::vector& positions, float rate) + : emissionPoints(positions), emissionRate(rate), isActive(true), + buffersDirty(true), maxParticles(positions.size() * 100) { + particles.resize(maxParticles); + positionBuffer.resize(maxParticles * 4, 0.0f); + texCoordBuffer.resize(maxParticles * 2, 0.0f); + lastEmissionTime = std::chrono::steady_clock::now(); + } - void SparkEmitter::setEmissionPoints(const std::vector& positions) { - emissionPoints = positions; - particles.resize(positions.size() * 100); - } + void SparkEmitter::setEmissionPoints(const std::vector& positions) { + emissionPoints = positions; + maxParticles = positions.size() * 100; + particles.resize(maxParticles); + positionBuffer.resize(maxParticles * 4, 0.0f); + texCoordBuffer.resize(maxParticles * 2, 0.0f); + buffersDirty = true; + } - void SparkEmitter::setEmissionRate(float rateMs) { - emissionRate = rateMs; - } + void SparkEmitter::updateBuffers() { + if (!buffersDirty) return; - void SparkEmitter::setActive(bool active) { - isActive = active; - } + size_t bufferIndex = 0; + for (const auto& particle : particles) { + if (particle.active) { + positionBuffer[bufferIndex * 4] = particle.position.v[0]; + positionBuffer[bufferIndex * 4 + 1] = particle.position.v[1]; + positionBuffer[bufferIndex * 4 + 2] = particle.position.v[2]; + positionBuffer[bufferIndex * 4 + 3] = particle.scale; - void SparkEmitter::update(float deltaTimeMs) { - auto currentTime = std::chrono::steady_clock::now(); - auto elapsed = std::chrono::duration_cast( - currentTime - lastEmissionTime).count(); + texCoordBuffer[bufferIndex * 2] = 0.0f; + texCoordBuffer[bufferIndex * 2 + 1] = 0.0f; - if (isActive && elapsed >= emissionRate) { - emit(); - lastEmissionTime = currentTime; - } + bufferIndex++; + } + } - for (auto& particle : particles) { - if (particle.active) { - 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; + for (size_t i = bufferIndex; i < maxParticles; i++) { + positionBuffer[i * 4] = 0.0f; + positionBuffer[i * 4 + 1] = 0.0f; + positionBuffer[i * 4 + 2] = 0.0f; + positionBuffer[i * 4 + 3] = 0.0f; + texCoordBuffer[i * 2] = 0.0f; + texCoordBuffer[i * 2 + 1] = 0.0f; + } - particle.lifeTime += deltaTimeMs; + buffersDirty = false; + } - if (particle.lifeTime >= particle.maxLifeTime) { - particle.active = false; - } + void SparkEmitter::update(float deltaTimeMs) { + auto currentTime = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast( + currentTime - lastEmissionTime).count(); - float lifeRatio = particle.lifeTime / particle.maxLifeTime; - particle.scale = 1.0f - lifeRatio * 0.8f; - } - } - } + if (isActive && elapsed >= emissionRate) { + emit(); + lastEmissionTime = currentTime; + } - void SparkEmitter::emit() { - if (emissionPoints.empty()) return; + bool anyChanged = false; + for (auto& particle : particles) { + if (particle.active) { + Vector3f oldPosition = particle.position; - for (int i = 0; i < emissionPoints.size(); ++i) { - bool particleFound = false; + 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; - 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; - break; - } - } + particle.lifeTime += deltaTimeMs; - if (!particleFound && !particles.empty()) { - size_t oldestIndex = 0; - float maxLifeTime = 0; + if (particle.lifeTime >= particle.maxLifeTime) { + particle.active = false; + anyChanged = true; + } + else { + float lifeRatio = particle.lifeTime / particle.maxLifeTime; + particle.scale = 1.0f - lifeRatio * 0.8f; - for (size_t j = 0; j < particles.size(); ++j) { - if (particles[j].lifeTime > maxLifeTime) { - maxLifeTime = particles[j].lifeTime; - oldestIndex = j; - } - } + if (oldPosition.v[0] != particle.position.v[0] || + oldPosition.v[1] != particle.position.v[1] || + oldPosition.v[2] != particle.position.v[2]) { + anyChanged = true; + } + } + } + } - initParticle(particles[oldestIndex], i); - particles[oldestIndex].active = true; - particles[oldestIndex].lifeTime = 0; - particles[oldestIndex].position = emissionPoints[i]; - particles[oldestIndex].emitterIndex = i; - } - } - } + if (anyChanged) { + buffersDirty = true; + } + } - void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) { - particle.velocity = getRandomVelocity(emitterIndex); - particle.scale = 1.0f; - particle.maxLifeTime = 800.0f + (rand() % 400); // От 800 до 1200 мс - particle.emitterIndex = emitterIndex; - } + void SparkEmitter::emit() { + if (emissionPoints.empty()) return; + bool emitted = false; - 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); + for (int i = 0; i < emissionPoints.size(); ++i) { + bool particleFound = false; - float angle = angleDist(gen); - float speed = speedDist(gen); - float zSpeed = zSpeedDist(gen); + 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 (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 - }; - } - } + if (!particleFound && !particles.empty()) { + size_t oldestIndex = 0; + float maxLifeTime = 0; - const std::vector& SparkEmitter::getParticles() const { - return particles; - } + for (size_t j = 0; j < particles.size(); ++j) { + if (particles[j].lifeTime > maxLifeTime) { + maxLifeTime = particles[j].lifeTime; + oldestIndex = j; + } + } - size_t SparkEmitter::getActiveParticleCount() const { - size_t count = 0; - for (const auto& particle : particles) { - if (particle.active) { - count++; - } - } - return count; - } + 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) { + buffersDirty = 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; + } + + size_t SparkEmitter::getActiveParticleBufferIndex(size_t particleIndex) const { + size_t activeIndex = 0; + for (size_t i = 0; i <= particleIndex; i++) { + if (particles[i].active) { + if (i == particleIndex) return activeIndex; + activeIndex++; + } + } + return 0; + } } // namespace ZL \ No newline at end of file diff --git a/SparkEmitter.h b/SparkEmitter.h index b058c65..d5356b3 100644 --- a/SparkEmitter.h +++ b/SparkEmitter.h @@ -6,45 +6,56 @@ namespace ZL { - struct SparkParticle { - Vector3f position; - Vector3f velocity; - float scale; - float lifeTime; - float maxLifeTime; - bool active; - int emitterIndex; + struct SparkParticle { + Vector3f position; + Vector3f velocity; + float scale; + float lifeTime; + float maxLifeTime; + bool active; + int emitterIndex; + Vector2f texCoord; - SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f), - lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) { - } - }; + 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; + class SparkEmitter { + private: + std::vector particles; + std::vector emissionPoints; + std::chrono::steady_clock::time_point lastEmissionTime; + float emissionRate; + bool isActive; - public: - SparkEmitter(); - SparkEmitter(const std::vector& positions, float rate = 100.0f); + std::vector positionBuffer; + std::vector texCoordBuffer; + bool buffersDirty; + int maxParticles; - void setEmissionPoints(const std::vector& positions); - void setEmissionRate(float rateMs); - void setActive(bool active); + public: + SparkEmitter(); + SparkEmitter(const std::vector& positions, float rate = 100.0f); - void update(float deltaTimeMs); - void emit(); + void setEmissionPoints(const std::vector& positions); - const std::vector& getParticles() const; - size_t getActiveParticleCount() const; + void update(float deltaTimeMs); + void emit(); - private: - void initParticle(SparkParticle& particle, int emitterIndex); - Vector3f getRandomVelocity(int emitterIndex); - }; + const std::vector& getParticles() const; + size_t getActiveParticleCount() const; + + const float* getPositionBuffer() const { return positionBuffer.data(); } + const float* getTexCoordBuffer() const { return texCoordBuffer.data(); } + size_t getBufferSize() const { return positionBuffer.size() / 4; } + void updateBuffers(); + + size_t getActiveParticleBufferIndex(size_t particleIndex) const; + + private: + void initParticle(SparkParticle& particle, int emitterIndex); + Vector3f getRandomVelocity(int emitterIndex); + }; } // namespace ZL \ No newline at end of file