#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 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) { } 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); #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); #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(); } 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::drawShip() { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; renderer.shaderManager.PushShader(defaultShaderName); renderer.RenderUniform1i(textureUniformName, 0); renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vTexCoordName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), 1, 1000); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); renderer.DrawVertexRenderStruct(spaceship); renderer.PopMatrix(); renderer.PopProjectionMatrix(); renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vTexCoordName); renderer.shaderManager.PopShader(); CheckGlError(); } void Game::drawBoxes() { static const std::string defaultShaderName = "default"; static const std::string envShaderName = "env"; static const std::string vPositionName = "vPosition"; static const std::string vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; renderer.shaderManager.PushShader(defaultShaderName); renderer.RenderUniform1i(textureUniformName, 0); renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vTexCoordName); renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(Environment::height), 1, 1000); for (int i = 0; i < boxCoordsArr.size(); i++) { 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); 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