#include "Game.h" #include "AnimatedModel.h" #include "BoneAnimatedModel.h" #include "utils/Utils.h" #include "render/OpenGlExtensions.h" #include #include "render/TextureManager.h" #include "TextModel.h" #include #include #include namespace ZL { #ifdef EMSCRIPTEN const char* CONST_ZIP_FILE = "space-game001a.zip"; #else const char* CONST_ZIP_FILE = ""; #endif Eigen::Quaternionf generateRandomQuaternion(std::mt19937& gen) { std::normal_distribution<> distrib(0.0, 1.0); Eigen::Quaternionf randomQuat = { (float)distrib(gen), (float)distrib(gen), (float)distrib(gen), (float)distrib(gen) }; 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 boxCoordsArr; std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD); int generatedCount = 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) ); accepted = true; for (const auto& existingBox : boxCoordsArr) { Vector3f diff = newPos - existingBox.pos; float distanceSquared = diff.squaredNorm(); if (distanceSquared < MIN_DISTANCE_SQUARED) { accepted = false; break; } } if (accepted) { Eigen::Quaternionf randomQuat = generateRandomQuaternion(gen); Matrix3f randomMatrix = randomQuat.toRotationMatrix(); boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); generatedCount++; } attempts++; } if (!accepted) { break; } } return boxCoordsArr; } Game::Game() : window(nullptr) , glContext(nullptr) , newTickCount(0) , lastTickCount(0) { projectiles.reserve(maxProjectiles); for (int i = 0; i < maxProjectiles; ++i) { projectiles.emplace_back(std::make_unique()); } } 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); ZL::BindOpenGlFunctions(); ZL::CheckGlError(); // 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("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColorPlanet", "./shaders/defaultColorPlanet.vertex", "./shaders/defaultColorPlanet_web.fragment", CONST_ZIP_FILE); */ renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "./shaders/env_sky.vertex", "./shaders/env_sky_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "./shaders/planet_bake.vertex", "./shaders/planet_bake_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetStone", "./shaders/planet_stone.vertex", "./shaders/planet_stone_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetLand", "./shaders/planet_land.vertex", "./shaders/planet_land_web.fragment", CONST_ZIP_FILE); #else #ifndef SIMPLIFIED renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "./shaders/planet_bake.vertex", "./shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetStone", "./shaders/planet_stone.vertex", "./shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetLand", "./shaders/planet_land.vertex", "./shaders/planet_land_desktop.fragment", CONST_ZIP_FILE); #else /*renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "./shaders/default_env.vertex", "./shaders/default_env_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/default_texture.vertex", "./shaders/default_texture_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "./shaders/default_texture.vertex", "./shaders/default_texture_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetStone", "./shaders/default_texture.vertex", "./shaders/default_texture_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetLand", "./shaders/default_texture.vertex", "./shaders/default_texture_desktop.fragment", CONST_ZIP_FILE); */ renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "./shaders/env_sky.vertex", "./shaders/env_sky_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "./shaders/planet_bake.vertex", "./shaders/planet_bake_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetStone", "./shaders/planet_stone.vertex", "./shaders/planet_stone_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetLand", "./shaders/planet_land.vertex", "./shaders/planet_land_web.fragment", CONST_ZIP_FILE); #endif #endif #ifndef SIMPLIFIED bool cfgLoaded = sparkEmitter.loadFromJsonFile("../config/spark_config.json", renderer, CONST_ZIP_FILE); bool projCfgLoaded = projectileEmitter.loadFromJsonFile("../config/spark_projectile_config.json", renderer, CONST_ZIP_FILE); projectileEmitter.setEmissionPoints(std::vector()); uiManager.loadFromFile("../config/ui.json", renderer, CONST_ZIP_FILE); uiManager.setButtonCallback("playButton", [this](const std::string& name) { std::cerr << "Play button pressed: " << name << std::endl; }); uiManager.setButtonCallback("exitButton", [](const std::string& name) { Environment::exitGameLoop = true; }); uiManager.setButtonCallback("settingsButton", [this](const std::string& name) { if (uiManager.pushMenuFromFile("../config/settings.json", this->renderer, CONST_ZIP_FILE)) { uiManager.setButtonCallback("Opt1", [this](const std::string& n) { std::cerr << "Opt1 pressed: " << n << std::endl; }); uiManager.setButtonCallback("Opt2", [this](const std::string& n) { std::cerr << "Opt2 pressed: " << n << std::endl; }); uiManager.setButtonCallback("backButton", [this](const std::string& n) { uiManager.popMenu(); }); } else { std::cerr << "Failed to open settings menu" << std::endl; } }); uiManager.setSliderCallback("musicVolumeSlider", [this](const std::string& name, float value) { std::cerr << "Music volume slider changed to: " << value << std::endl; musicVolume = value; Environment::shipVelocity = musicVolume * 20.0f; }); #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_shine.png", CONST_ZIP_FILE)); spaceshipBase = LoadFromTextFile02("./resources/spaceship006.txt", CONST_ZIP_FILE); spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI / 2.0, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix()); //spaceshipTexture = std::make_unique(CreateTextureDataFromPng("./resources/cap_D.png", CONST_ZIP_FILE)); //spaceshipBase = LoadFromTextFile02("./resources/spaceship006x.txt", CONST_ZIP_FILE); //spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI / 2.0, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix()); //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); std::cout << "Init step 1 " << std::endl; boxCoordsArr = generateRandomBoxCoords(50); std::cout << "Init step 2 " << std::endl; boxRenderArr.resize(boxCoordsArr.size()); std::cout << "Init step 3x " << std::endl; for (int i = 0; i < boxCoordsArr.size(); i++) { boxRenderArr[i].AssignFrom(boxBase); //boxRenderArr[i].data = CreateBaseConvexPolyhedron(1999); boxRenderArr[i].RefreshVBO(); } std::cout << "Init step 4 " << std::endl; #ifndef SIMPLIFIED if (!cfgLoaded) { throw std::runtime_error("Failed to load spark emitter config file!"); } #endif /* 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();*/ std::cout << "Init step 5 " << std::endl; renderer.InitOpenGL(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); std::cout << "Init step 6 " << std::endl; planetObject.init(); std::cout << "Init step 7 " << std::endl; //rockTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", CONST_ZIP_FILE)); std::cout << "Init step 8 " << std::endl; } void Game::drawCubemap(float skyPercent) { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env_sky"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; static const std::string skyPercentUniformName = "skyPercent"; renderer.shaderManager.PushShader(envShaderName); renderer.RenderUniform1i(textureUniformName, 0); renderer.RenderUniform1f(skyPercentUniformName, skyPercent); renderer.EnableVertexAttribArray(vPositionName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.RotateMatrix(Environment::inverseShipMatrix); Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized(); Matrix3f viewMatrix = Environment::inverseShipMatrix; Vector3f viewLightDir = (viewMatrix * worldLightDir).normalized(); // Передаем вектор НА источник света Vector3f lightToSource = -viewLightDir; renderer.RenderUniform3fv("uLightDirView", lightToSource.data()); // 2. Базовый цвет атмосферы (голубой) Vector3f skyColor = { 0.0f, 0.5f, 1.0f }; renderer.RenderUniform3fv("uSkyColor", skyColor.data()); // 1. Вектор направления от центра планеты к игроку (в мировых координатах) // Предполагаем, что планета в (0,0,0). Если нет, то (shipPosition - planetCenter) Vector3f playerDirWorld = Environment::shipPosition.normalized(); // 2. Направление света в мировом пространстве //Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized(); // 3. Считаем глобальную освещенность для игрока (насколько он на свету) // Это одно число для всего кадра float playerLightFactor = playerDirWorld.dot(-worldLightDir); // Ограничиваем и делаем переход мягче playerLightFactor = std::clamp((playerLightFactor + 0.2f) / 1.2f, 0.0f, 1.0f); renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor); renderer.RenderUniform1f("skyPercent", skyPercent); 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), Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); renderer.TranslateMatrix({ 0, -Environment::zoom * 0.03f, 0 }); glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); renderer.DrawVertexRenderStruct(spaceship); for (const auto& p : projectiles) { if (p && p->isActive()) { p->draw(renderer); } } #ifndef SIMPLIFIED sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); #endif 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), Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); 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({ 0.f, 0.f, 45000.f }); renderer.TranslateMatrix(boxCoordsArr[i].pos); renderer.RotateMatrix(boxCoordsArr[i].m); glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID()); //glBindTexture(GL_TEXTURE_2D, rockTexture->getTexID()); renderer.DrawVertexRenderStruct(boxRenderArr[i]); renderer.PopMatrix(); } renderer.PopProjectionMatrix(); renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vTexCoordName); renderer.shaderManager.PopShader(); CheckGlError(); } /*void Game::UpdateVolumeKnob() { float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY); auto& pos = musicVolumeBarButton.data.PositionData; pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }; pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }; musicVolumeBarButton.RefreshVBO(); } void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) { int uiX = mouseX; int uiY = Environment::height - mouseY; Environment::shipVelocity = (musicVolume) * (20.0); if (uiY < volumeBarMinY || uiY > volumeBarMaxY) { return; } float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY); if (t < 0.0f) t = 0.0f; if (t > 1.0f) t = 1.0f; musicVolume = t; UpdateVolumeKnob(); }*/ void Game::drawUI() { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; glClear(GL_DEPTH_BUFFER_BIT); renderer.shaderManager.PushShader(defaultShaderName); renderer.RenderUniform1i(textureUniformName, 0); renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vTexCoordName); //renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); //renderer.PushMatrix(); //renderer.LoadIdentity(); //glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); //renderer.DrawVertexRenderStruct(button); //glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID()); //renderer.DrawVertexRenderStruct(musicVolumeBar); //glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); //renderer.DrawVertexRenderStruct(musicVolumeBarButton); //renderer.PopMatrix(); //renderer.PopProjectionMatrix(); renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vTexCoordName); uiManager.draw(renderer); 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, 1.0f, 0.0f, 1.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glViewport(0, 0, Environment::width, Environment::height); CheckGlError(); float skyPercent = 0.0; float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition); if (distance > 1500.f) { skyPercent = 0.0f; } else if (distance < 800.f) { skyPercent = 1.0f; } else { skyPercent = (1500.f - distance) / (1500.f - 800.f); } drawCubemap(skyPercent); planetObject.draw(renderer); if (planetObject.distanceToPlanetSurface(Environment::shipPosition) > 100.f) { glClear(GL_DEPTH_BUFFER_BIT); } drawShip(); drawBoxes(); #ifndef SIMPLIFIED drawUI(); #endif 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; #ifndef SIMPLIFIED //gameObjects.updateScene(delta); sparkEmitter.update(static_cast(delta)); #endif planetObject.update(static_cast(delta)); if (Environment::tapDownHold) { float diffx = Environment::tapDownCurrentPos(0) - Environment::tapDownStartPos(0); float diffy = Environment::tapDownCurrentPos(1) - Environment::tapDownStartPos(1); if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold { float rotationPower = sqrtf(diffx * diffx + diffy * diffy); float deltaAlpha = rotationPower * delta * static_cast(M_PI) / 500000.f; Eigen::Vector3f rotationDirection(diffy, diffx, 0.0f); rotationDirection.normalize(); // Eigen-way нормализация // Создаем кватернион через AngleAxis // Конструктор принимает (угол_в_радианах, ось_вращения) Eigen::Quaternionf rotateQuat(Eigen::AngleAxisf(deltaAlpha, rotationDirection)); Matrix3f rotateMat = rotateQuat.toRotationMatrix(); Environment::shipMatrix = Environment::shipMatrix * rotateMat; Environment::inverseShipMatrix = Environment::shipMatrix.inverse(); } } if (fabs(Environment::shipVelocity) > 0.01f) { Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f }; Vector3f velocityDirectionAdjusted = Environment::shipMatrix * velocityDirection; Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; } #ifndef SIMPLIFIED for (auto& p : projectiles) { if (p && p->isActive()) { p->update(static_cast(delta), renderer); } } std::vector projCameraPoints; for (const auto& p : projectiles) { if (p && p->isActive()) { Vector3f worldPos = p->getPosition(); Vector3f rel = worldPos - Environment::shipPosition; Vector3f camPos = Environment::inverseShipMatrix * rel; projCameraPoints.push_back(camPos); } } if (!projCameraPoints.empty()) { projectileEmitter.setEmissionPoints(projCameraPoints); projectileEmitter.emit(); } else { projectileEmitter.setEmissionPoints(std::vector()); } std::vector shipCameraPoints; for (const auto& lp : shipLocalEmissionPoints) { Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f }; shipCameraPoints.push_back(adjusted); } if (!shipCameraPoints.empty()) { sparkEmitter.setEmissionPoints(shipCameraPoints); } sparkEmitter.update(static_cast(delta)); projectileEmitter.update(static_cast(delta)); #endif lastTickCount = newTickCount; } } void Game::fireProjectiles() { std::vector localOffsets = { Vector3f{ -1.5f, 0.9f, 5.0f }, Vector3f{ 1.5f, 0.9f, 5.0f } }; const float projectileSpeed = 60.0f; const float lifeMs = 5000.0f; const float size = 0.5f; Vector3f localForward = { 0,0,-1 }; Vector3f worldForward = (Environment::shipMatrix * localForward).normalized(); for (const auto& lo : localOffsets) { Vector3f worldPos = Environment::shipPosition + Environment::shipMatrix * lo; Vector3f worldVel = worldForward * projectileSpeed; for (auto& p : projectiles) { if (!p->isActive()) { p->init(worldPos, worldVel, lifeMs, size, projectileTexture, renderer); break; } } } } 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) { int mx = event.button.x; int my = event.button.y; int uiX = mx; int uiY = Environment::height - my; uiManager.onMouseDown(uiX, uiY); bool uiHandled = false; #ifndef SIMPLIFIED if (event.button.button == SDL_BUTTON_LEFT && !uiManager.isUiInteraction()) { uint64_t now = SDL_GetTicks64(); if (now - lastProjectileFireTime >= static_cast(projectileCooldownMs)) { lastProjectileFireTime = now; fireProjectiles(); } } #endif for (const auto& button : uiManager.findButton("") ? std::vector>{} : std::vector>{}) { (void)button; } auto pressedSlider = [&]() -> std::shared_ptr { for (const auto& slider : uiManager.findSlider("") ? std::vector>{} : std::vector>{}) { (void)slider; } return nullptr; }(); if (!uiManager.isUiInteraction()) { Environment::tapDownHold = true; Environment::tapDownStartPos(0) = mx; Environment::tapDownStartPos(1) = my; Environment::tapDownCurrentPos(0) = mx; Environment::tapDownCurrentPos(1) = my; } } else if (event.type == SDL_MOUSEBUTTONUP) { int mx = event.button.x; int my = event.button.y; int uiX = mx; int uiY = Environment::height - my; uiManager.onMouseUp(uiX, uiY); if (!uiManager.isUiInteraction()) { Environment::tapDownHold = false; } } else if (event.type == SDL_MOUSEMOTION) { int mx = event.motion.x; int my = event.motion.y; int uiX = mx; int uiY = Environment::height - my; uiManager.onMouseMove(uiX, uiY); if (Environment::tapDownHold && !uiManager.isUiInteraction()) { Environment::tapDownCurrentPos(0) = mx; Environment::tapDownCurrentPos(1) = my; } } 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 += 500.f; } if (event.key.keysym.sym == SDLK_k) { Environment::shipVelocity -= 500.f; } if (event.key.keysym.sym == SDLK_o) { Environment::shipVelocity += 50.f; } if (event.key.keysym.sym == SDLK_l) { Environment::shipVelocity -= 50.f; } } } render(); } } // namespace ZL