#include "Core.hpp" //Core::Core():toRenderMutex("toRender"),availableChunksMutex("availableChunks") { Core::Core() { LOG(INFO) << "Core initializing..."; InitSfml(900, 450, "AltCraft"); glCheckError(); InitGlew(); glCheckError(); client = new NetworkClient("127.0.0.1", 25565, "HelloOne", isRunning); gameState = new GameState(client, isRunning); gameStateLoopThread = std::thread(&Core::UpdateGameState, this); sectionUpdateLoopThread = std::thread(&Core::UpdateSections, this); assetManager = new AssetManager; PrepareToRendering(); LOG(INFO) << "Core is initialized"; glCheckError(); } Core::~Core() { LOG(INFO) << "Core stopping..."; gameStateLoopThread.join(); sectionUpdateLoopThread.join(); delete shader; delete gameState; delete client; delete assetManager; delete window; LOG(INFO) << "Core is stopped"; } void Core::Exec() { LOG(INFO) << "Main loop is executing!"; isRunning = true; while (isRunning) { static sf::Clock clock, clock1; deltaTime = clock.getElapsedTime().asSeconds(); absTime = clock1.getElapsedTime().asSeconds(); clock.restart(); static bool alreadyDone = false; if (gameState->g_IsGameStarted && !alreadyDone) { alreadyDone = true; UpdateChunksToRender(); } std::ostringstream toWindow; auto camPos = gameState->Position(); auto velPos = glm::vec3(gameState->g_PlayerVelocityX, gameState->g_PlayerVelocityY, gameState->g_PlayerVelocityZ); toWindow << std::setprecision(2) << std::fixed; toWindow << "Pos: " << camPos.x << ", " << camPos.y << ", " << camPos.z << "; "; toWindow << "Health: " << gameState->g_PlayerHealth << "; "; //toWindow << "OG: " << gameState->g_OnGround << "; "; toWindow << "Vel: " << velPos.x << ", " << velPos.y << ", " << velPos.z << "; "; toWindow << "FPS: " << (1.0f / deltaTime) << " "; toWindow << " (" << deltaTime * 1000 << "ms); "; toWindow << "Tickrate: " << tickRate << " (" << (1.0 / tickRate * 1000) << "ms); "; toWindow << "Sections: " << sectionRate << " (" << (1.0 / sectionRate * 1000) << "ms); "; window->setTitle(toWindow.str()); HandleEvents(); if (isMouseCaptured) HandleMouseCapture(); glCheckError(); RenderFrame(); if (isRendersShouldBeCreated) { availableChunksMutex.lock(); for (auto &it:renders) { auto pair = std::make_pair(it, RenderSection(&gameState->world, it)); availableChunks.insert(pair); } renders.clear(); availableChunksMutex.unlock(); isRendersShouldBeCreated = false; waitRendersCreated.notify_all(); } } } void Core::RenderFrame() { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); switch (currentState) { case MainMenu: //RenderGui(MenuScreen); break; case Loading: //RenderGui(LoadingScreen); break; case Playing: RenderWorld(); //RenderGui(HUD); break; case PauseMenu: RenderWorld(); //RenderGui(PauseGui); break; } window->display(); } void Core::InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle) { LOG(INFO) << "Creating window: " << WinWidth << "x" << WinHeight << " \"" << WinTitle << "\""; sf::ContextSettings contextSetting; contextSetting.majorVersion = 3; contextSetting.minorVersion = 3; contextSetting.attributeFlags = contextSetting.Core; contextSetting.depthBits = 24; window = new sf::Window(sf::VideoMode(WinWidth, WinHeight), WinTitle, sf::Style::Default, contextSetting); glCheckError(); //window->setVerticalSyncEnabled(true); //window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2, sf::VideoMode::getDesktopMode().height / 2)); window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2 - window->getSize().x / 2, sf::VideoMode::getDesktopMode().height / 2 - window->getSize().y / 2)); SetMouseCapture(false); } void Core::InitGlew() { LOG(INFO) << "Initializing GLEW"; glewExperimental = GL_TRUE; GLenum glewStatus = glewInit(); glCheckError(); if (glewStatus != GLEW_OK) { LOG(FATAL) << "Failed to initialize GLEW: " << glewGetErrorString(glewStatus); } glViewport(0, 0, width(), height()); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); //glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glCheckError(); } unsigned int Core::width() { return window->getSize().x; } unsigned int Core::height() { return window->getSize().y; } void Core::HandleEvents() { sf::Event event; while (window->pollEvent(event)) { switch (event.type) { case sf::Event::Closed: LOG(INFO) << "Received close event by window closing"; isRunning = false; break; case sf::Event::Resized: glViewport(0, 0, width(), height()); break; case sf::Event::KeyPressed: if (!window->hasFocus()) break; switch (event.key.code) { case sf::Keyboard::Escape: LOG(INFO) << "Received close event by esc"; isRunning = false; break; case sf::Keyboard::T: SetMouseCapture(!isMouseCaptured); break; case sf::Keyboard::L: ChunkDistance++; LOG(INFO) << "Increased render distance: " << ChunkDistance; break; case sf::Keyboard::K: if (ChunkDistance > 1) { ChunkDistance--; LOG(INFO) << "Decreased render distance: " << ChunkDistance; } break; default: break; } default: break; } } if (window->hasFocus()) { if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) gameState->HandleMovement(GameState::FORWARD, deltaTime); if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) gameState->HandleMovement(GameState::BACKWARD, deltaTime); if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) gameState->HandleMovement(GameState::LEFT, deltaTime); if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) gameState->HandleMovement(GameState::RIGHT, deltaTime); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) gameState->HandleMovement(GameState::JUMP, deltaTime); UpdateChunksToRender(); } } void Core::HandleMouseCapture() { sf::Vector2i mousePos = sf::Mouse::getPosition(*window); sf::Vector2i center = sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2); sf::Mouse::setPosition(center, *window); mouseXDelta = (mousePos - center).x, mouseYDelta = (center - mousePos).y; const float Sensetivity = 0.7f; gameState->HandleRotation(mouseXDelta * Sensetivity, mouseYDelta * Sensetivity); //camera.ProcessMouseMovement(mouseXDelta, mouseYDelta); } void Core::RenderWorld() { shader->Use(); glCheckError(); GLint projectionLoc = glGetUniformLocation(shader->Program, "projection"); GLint viewLoc = glGetUniformLocation(shader->Program, "view"); GLint timeLoc = glGetUniformLocation(shader->Program, "time"); glm::mat4 projection = glm::perspective(45.0f, (float) width() / (float) height(), 0.1f, 10000000.0f); glm::mat4 view = gameState->GetViewMatrix(); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); glUniform1f(timeLoc, absTime); glUniform2f(glGetUniformLocation(shader->Program, "windowSize"), width(), height()); glCheckError(); toRenderMutex.lock(); for (auto &render : toRender) { availableChunksMutex.lock(); auto iterator = availableChunks.find(render); if (iterator == availableChunks.end()) { availableChunksMutex.unlock(); continue; } /*Section §ion = *iterator->second.GetSection(); //availableChunksMutex.unlock(); std::vector sectionCorners = { Vector(0, 0, 0), Vector(0, 0, 16), Vector(0, 16, 0), Vector(0, 16, 16), Vector(16, 0, 0), Vector(16, 0, 16), Vector(16, 16, 0), Vector(16, 16, 16), }; bool isBreak = true; glm::mat4 vp = projection * view; for (auto &it:sectionCorners) { glm::vec3 point(section.GetPosition().GetX() * 16 + it.GetX(), section.GetPosition().GetY() * 16 + it.GetY(), section.GetPosition().GetZ() * 16 + it.GetZ()); glm::vec4 p = vp * glm::vec4(point, 1); glm::vec3 res = glm::vec3(p) / p.w; if (res.x < 1 && res.x > -1 && res.y < 1 && res.y > -1 && res.z > 0) { isBreak = false; break; } } if (isBreak && glm::length(gameState->Position() - glm::vec3(section.GetPosition().GetX() * 16, section.GetPosition().GetY() * 16, section.GetPosition().GetZ() * 16)) > 30.0f) { availableChunksMutex.unlock(); continue; } //availableChunksMutex.lock();*/ iterator->second.Render(renderState); availableChunksMutex.unlock(); } toRenderMutex.unlock(); glCheckError(); } void Core::SetMouseCapture(bool IsCaptured) { window->setMouseCursorVisible(!isMouseCaptured); sf::Mouse::setPosition(sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2), *window); isMouseCaptured = IsCaptured; window->setMouseCursorVisible(!IsCaptured); } void Core::PrepareToRendering() { shader = new Shader("./shaders/face.vs", "./shaders/face.fs"); shader->Use(); //TextureAtlas texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, assetManager->GetTextureAtlas()); glUniform1i(glGetUniformLocation(shader->Program, "textureAtlas"), 0); } void Core::UpdateChunksToRender() { return; Vector playerChunk = Vector(floor(gameState->g_PlayerX / 16.0f), 0, floor(gameState->g_PlayerZ / 16.0f)); static Vector previousPlayerChunk = playerChunk; static bool firstTime = true; static int previousRenderDistance = ChunkDistance; if (previousPlayerChunk == playerChunk && !firstTime && ChunkDistance == previousRenderDistance) { return; } previousPlayerChunk = playerChunk; previousRenderDistance = ChunkDistance; toRender.clear(); for (auto &it:gameState->world.GetSectionsList()) { Vector chunkPosition = it; chunkPosition.SetY(0); Vector delta = chunkPosition - playerChunk; if (delta.GetMagnitude() > ChunkDistance) continue; toRender.push_back(it); } for (auto &it:toRender) { if (availableChunks.find(it) == availableChunks.end()) { auto pair = std::make_pair(it, RenderSection(&gameState->world, it)); pair.second.UpdateState(assetManager->GetTextureAtlasIndexes()); availableChunks.insert(pair); } else { //availableChunks.find(it)->second.UpdateState(); } } if (firstTime) LOG(INFO) << "Chunks to render: " << toRender.size() << " of " << availableChunks.size(); firstTime = false; } void Core::UpdateGameState() { el::Helpers::setThreadName("Game"); LOG(INFO) << "GameState thread is started"; sf::Clock delta; while (isRunning) { float deltaTime = delta.getElapsedTime().asSeconds(); delta.restart(); gameState->Update(deltaTime); const double targetDelta = 1 / 60.0; std::chrono::duration> timeToSleep(targetDelta - delta.getElapsedTime().asSeconds()); std::this_thread::sleep_for(timeToSleep); tickRate = 1 / delta.getElapsedTime().asSeconds(); } LOG(INFO) << "GameState thread is stopped"; } void Core::UpdateSections() { glm::vec3 playerPosition = gameState->Position(); float playerPitch = gameState->Pitch(); float playerYaw = gameState->Yaw(); sf::Clock delta; std::vector chunksToRender; auto currentSectionIterator = chunksToRender.begin(); while (isRunning) { delta.restart(); if (glm::length(glm::distance(gameState->Position(), playerPosition)) > 5.0f) { chunksToRender.clear(); playerPosition = gameState->Position(); Vector playerChunk = Vector(floor(playerPosition.x / 16.0f), 0, floor(playerPosition.z / 16.0f)); for (auto &it:gameState->world.GetSectionsList()) { Vector chunkPosition = it; chunkPosition.SetY(0); Vector delta = chunkPosition - playerChunk; if (delta.GetMagnitude() > ChunkDistance) continue; chunksToRender.push_back(it); } std::sort(chunksToRender.begin(), chunksToRender.end(), [playerChunk](auto first, auto second) { glm::vec3 fDistance = first - playerChunk; glm::vec3 sDistance = second - playerChunk; return glm::length(fDistance) < glm::length(sDistance); }); for (auto &it:chunksToRender) { availableChunksMutex.lock(); if (availableChunks.find(it) == availableChunks.end()) { availableChunksMutex.unlock(); renders.push_back(it); } else availableChunksMutex.unlock(); } if (!renders.empty()) { std::mutex mutex; std::unique_lock lock(mutex); isRendersShouldBeCreated = true; while (isRendersShouldBeCreated) waitRendersCreated.wait(lock); } currentSectionIterator = chunksToRender.begin(); toRenderMutex.lock(); toRender = chunksToRender; toRenderMutex.unlock(); } if (currentSectionIterator != chunksToRender.end()) { availableChunksMutex.lock(); auto iterator = availableChunks.find(*currentSectionIterator); if (iterator != availableChunks.end() && iterator->second.IsNeedUpdate()) { RenderSection rs = std::move(iterator->second); availableChunks.erase(iterator); auto pair = std::make_pair(*currentSectionIterator, rs); availableChunksMutex.unlock(); pair.second.UpdateState(assetManager->GetTextureAtlasIndexes()); availableChunksMutex.lock(); availableChunks.insert(pair); } availableChunksMutex.unlock(); currentSectionIterator = std::next(currentSectionIterator); } if (gameState->Pitch() != playerPitch || gameState->Yaw() != playerYaw) { playerPitch = gameState->Pitch(); playerYaw = gameState->Yaw(); const std::vector sectionCorners = { Vector(0, 0, 0), Vector(0, 0, 16), Vector(0, 16, 0), Vector(0, 16, 16), Vector(16, 0, 0), Vector(16, 0, 16), Vector(16, 16, 0), Vector(16, 16, 16), }; const glm::mat4 projection = glm::perspective(45.0f, (float)width() / (float)height(), 0.1f, 10000000.0f); const glm::mat4 view = gameState->GetViewMatrix(); const glm::mat4 vp = projection * view; for (auto& section: toRender) { bool isCulled = true; for (auto &it : sectionCorners) { glm::vec3 point(section.GetX() * 16 + it.GetX(), section.GetY() * 16 + it.GetY(), section.GetZ() * 16 + it.GetZ()); glm::vec4 p = vp * glm::vec4(point, 1); glm::vec3 res = glm::vec3(p) / p.w; if (res.x < 1 && res.x > -1 && res.y < 1 && res.y > -1 && res.z > 0) { isCulled = false; break; } } bool isVisible = !isCulled || glm::length(gameState->Position() - glm::vec3(section.GetX() * 16, section.GetY() * 16, section.GetZ() * 16)) < 30.0f; availableChunksMutex.lock(); auto iter = availableChunks.find(section); if (iter != availableChunks.end()) iter->second.SetEnabled(isVisible); availableChunksMutex.unlock(); } } using namespace std::chrono_literals; std::this_thread::sleep_for(5ms); sectionRate = delta.getElapsedTime().asSeconds(); delta.restart(); } } MyMutex::MyMutex(std::string name) { str = name; } void MyMutex::lock() { LOG(WARNING) << "Thread " << std::this_thread::get_id() << " locked mutex " << str; mtx.lock(); } void MyMutex::unlock() { LOG(WARNING) << "Thread " << std::this_thread::get_id() << " unlocked mutex " << str; mtx.unlock(); }