#include "Game.h" #include "AnimatedModel.h" #include "BoneAnimatedModel.h" #include "Utils.h" #include "OpenGlExtensions.h" #include #include "TextureManager.h" #include "TextModel.h" #include #include namespace ZL { #ifdef EMSCRIPTEN const char* CONST_ZIP_FILE = "space-game001.zip"; #else const char* CONST_ZIP_FILE = ""; #endif // Вспомогательная функция для получения случайного числа в диапазоне [min, max] float getRandomFloat(std::mt19937& gen, float min, float max) { std::uniform_real_distribution<> distrib(min, max); return static_cast(distrib(gen)); } // Икосаэдр (на основе золотого сечения phi) // Координаты могут быть вычислены заранее для константного икосаэдра. // Здесь только объявление, чтобы показать идею. VertexDataStruct CreateConvexPolyhedron(uint64_t seed) { // --- КОНСТАНТЫ ПАРАМЕТРОВ (как вы просили) --- const float BASE_SCALE = 3.0f; // Общий размер камня const float MIN_AXIS_SCALE = 0.5f; // Минимальное растяжение/сжатие по оси const float MAX_AXIS_SCALE = 1.5f; // Максимальное растяжение/сжатие по оси const float MIN_PERTURBATION = 0.05f; // Минимальное радиальное возмущение вершины const float MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины // const size_t SUBDIVISION_LEVEL = 1; // Уровень подразделения (для более круглого камня, пока опустим) std::mt19937 engine(static_cast(seed)); // Золотое сечение const float t = (1.0f + std::sqrt(5.0f)) / 2.0f; // 12 вершин икосаэдра std::vector initialVertices = { { -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 }, { 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t }, { t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 } }; // 20 треугольных граней (индексы вершин) std::vector> faces = { // 5 треугольников вокруг вершины 0 {0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11}, // 5 смежных полос {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8}, // 5 треугольников вокруг вершины 3 {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9}, // 5 смежных полос {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1} }; // 1. Нормализация и Возмущение (Perturbation) for (Vector3f& v : initialVertices) { v = v.normalized() * BASE_SCALE; // Нормализация к сфере радиуса BASE_SCALE // Радиальное возмущение: float perturbation = getRandomFloat(engine, MIN_PERTURBATION, MAX_PERTURBATION); v = v * (1.0f + perturbation); } // 2. Трансформация (Масштабирование и Поворот) // Случайные масштабы по осям Vector3f scaleFactors = { getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE), getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE), getRandomFloat(engine, MIN_AXIS_SCALE, MAX_AXIS_SCALE) }; // Применяем масштабирование for (Vector3f& v : initialVertices) { v.v[0] *= scaleFactors.v[0]; v.v[1] *= scaleFactors.v[1]; v.v[2] *= scaleFactors.v[2]; } // Случайный поворот (например, вокруг трех осей) Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qFinal = slerp(qx, qy, 0.5f); // Простой пример комбинирования qFinal = slerp(qFinal, qz, 0.5f).normalized(); Matrix3f rotationMatrix = QuatToMatrix(qFinal); for (Vector3f& v : initialVertices) { v = MultMatrixVector(rotationMatrix, v); } // 3. Сглаженные Нормали и Формирование Mesh VertexDataStruct result; // Карта для накопления нормалей по уникальным позициям вершин // (Требует определенного оператора < для Vector3f в ZLMath.h, который у вас есть) std::map smoothNormalsMap; // Предварительное заполнение карты нормалями for (const auto& face : faces) { Vector3f p1 = initialVertices[face[0]]; Vector3f p2 = initialVertices[face[1]]; Vector3f p3 = initialVertices[face[2]]; // Нормаль грани: (p2 - p1) x (p3 - p1) Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized(); smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal; smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal; smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal; } // Нормализация накопленных нормалей for (auto& pair : smoothNormalsMap) { pair.second = pair.second.normalized(); } // 4. Финальное заполнение VertexDataStruct и Текстурные Координаты for (const auto& face : faces) { Vector3f p1 = initialVertices[face[0]]; Vector3f p2 = initialVertices[face[1]]; Vector3f p3 = initialVertices[face[2]]; // Позиции result.PositionData.push_back(p1); result.PositionData.push_back(p2); result.PositionData.push_back(p3); // Сглаженные Нормали (из карты) result.NormalData.push_back(smoothNormalsMap[p1]); result.NormalData.push_back(smoothNormalsMap[p2]); result.NormalData.push_back(smoothNormalsMap[p3]); // Текстурные Координаты (Планарная проекция на плоскость грани) // p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle)) // Где L_xy - длина вектора, angle - угол между p2-p1 и p3-p1 Vector3f uAxis = (p2 - p1).normalized(); Vector3f vRaw = p3 - p1; // Проекция vRaw на uAxis float uProjLen = vRaw.dot(uAxis); // Вектор V перпендикулярный U: vRaw - uProj Vector3f vAxisRaw = vRaw - (uAxis * uProjLen); // Длина оси V float vLen = vAxisRaw.length(); // Нормализованная ось V Vector3f vAxis = vAxisRaw.normalized(); // Координаты (u, v) для p1, p2, p3 относительно p1 Vector2f uv1 = { 0.0f, 0.0f }; Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 вдоль оси U Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u-компонента = uProjLen, v-компонента = vLen // Находим максимальный размер, чтобы масштабировать в [0, 1] float maxUV = max( uv2.v[0], uv3.v[0], uv3.v[1] ); if (maxUV > 0.000001f) { // Масштабируем: result.TexCoordData.push_back(uv1); result.TexCoordData.push_back(uv2 * (1.f/ maxUV)); result.TexCoordData.push_back(uv3 * (1.f/ maxUV)); } else { // Предотвращение деления на ноль для вырожденных граней result.TexCoordData.push_back({ 0.0f, 0.0f }); result.TexCoordData.push_back({ 0.0f, 0.0f }); result.TexCoordData.push_back({ 0.0f, 0.0f }); } } return result; } 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) }; 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; boxCoordsArr.reserve(N); // Ðåçåðâèðóåì ïàìÿòü // 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); 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) { // 2. Ãåíåðèðóåì ñëó÷àéíûé êâàòåðíèîí Vector4f randomQuat = generateRandomQuaternion(gen); // 3. Ïðåîáðàçóåì åãî â ìàòðèöó âðàùåíèÿ Matrix3f randomMatrix = QuatToMatrix(randomQuat); // 4. Äîáàâëÿåì îáúåêò ñ íîâîé ñëó÷àéíîé ìàòðèöåé boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); generatedCount++; } attempts++; } // Åñëè ïðåâûøåíî ìàêñèìàëüíîå êîëè÷åñòâî ïîïûòîê, âûõîäèì èç öèêëà, // ÷òîáû èçáåæàòü çàâèñàíèÿ, åñëè N ñëèøêîì âåëèêî èëè äèàïàçîí ñëèøêîì ìàë. if (!accepted) { std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl; break; } } 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() { 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); #else renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_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].data = CreateConvexPolyhedron(1999); 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); planetObject.init(); rockTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); } void Game::drawCubemap(float skyPercent) { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; static const std::string skyPercentUniformName = "skyPercent"; renderer.shaderManager.PushShader(envShaderName); renderer.RenderUniform1i(textureUniformName, 0); renderer.RenderUniform1f(skyPercentUniformName, skyPercent); renderer.EnableVertexAttribArray(vPositionName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); 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), Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); renderer.DrawVertexRenderStruct(spaceship); sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); renderer.PopMatrix(); renderer.PopProjectionMatrix(); renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vTexCoordName); renderer.shaderManager.PopShader(); CheckGlError(); } void Game::drawBoxes() { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; renderer.shaderManager.PushShader(defaultShaderName); renderer.RenderUniform1i(textureUniformName, 0); renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vTexCoordName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), 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); 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 > 1900.f) { skyPercent = 0.0f; } else if (distance < 1000.f) { skyPercent = 1.0f; } else { skyPercent = (1900.f - distance) / 900.f; } drawCubemap(skyPercent); planetObject.draw(renderer); if (planetObject.distanceToPlanetSurface(Environment::shipPosition) > 100.f) { glClear(GL_DEPTH_BUFFER_BIT); } 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); sparkEmitter.update(static_cast(delta)); planetObject.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. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè 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 += 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