#include "engine.h" #include <iostream> #include <memory> #include <sstream> #include "../scene/mainMenu/developerScreen.h" Engine::Engine() { startSDL(); startOpenGL(); UI::startUI(); startManagers(); if (!loadSettings("../res/preload/settings.txt")) { std::cerr << "Failed to load settings, defaults will be used.\n"; } SDL_ShowWindow(m_window); } Engine::~Engine() { UI::stopUI(); stopOpenGL(); stopSDL(); if (!saveSettings("../res/preload/settings.txt")) { std::cerr << "Failed to save settings.\n"; } } void Engine::startManagers() { if (!m_resources.textureManager.init("../res/preload/textures.txt")) { throw std::runtime_error("Texture manager failed to initialize.\n"); } if (!m_resources.modelManager.init("../res/preload/as1Models.txt")) { throw std::runtime_error("Model manager failed to initialize.\n"); } if (!m_resources.modelManager.init("../res/preload/as2Models.txt")) { throw std::runtime_error("Model manager failed to initialize.\n"); } } void Engine::startOpenGL() { // OpenGL context m_GLContext = SDL_GL_CreateContext(m_window); if (m_GLContext == nullptr) { throw std::runtime_error(SDL_GetError()); } // Load GL extensions using glad if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { throw std::runtime_error("Failed to initialize the OpenGL context.\n"); } // Vsync SDL_GL_SetSwapInterval(1); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Set preferences glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); // Set hints glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST); glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.f, 0.f, 0.f, 1.0f); setViewport(m_resources.settings.width, m_resources.settings.height); } void Engine::setViewport(int width, int height) { if (height <= 0) { height = 1; } glViewport(0, 0, (GLsizei)width, (GLsizei)height); } void Engine::stopOpenGL() { SDL_GL_DeleteContext(m_GLContext); } void Engine::startSDL() { if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { throw std::runtime_error(SDL_GetError()); } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); // MSAA x16 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); m_window = SDL_CreateWindow(m_resources.settings.gameTitle.c_str(), 0, 0, m_resources.settings.width, m_resources.settings.height, SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN | SDL_WINDOW_ALLOW_HIGHDPI); if (m_window == nullptr) { throw std::runtime_error(SDL_GetError()); } } void Engine::stopSDL() { SDL_DestroyWindow(m_window); m_window = nullptr; SDL_Quit(); } void Engine::run() { std::shared_ptr<State> currentState; m_stateManager.push(devScreen::init(m_resources)); m_engineRunning = true; // Update delta to negate start up time deltaTime.update(m_resources); while (m_engineRunning) { // Update settings if (m_resources.settings.settingsUpdated) { updateSettings(); } // retrieve the top state currentState = m_stateManager.top(); if (currentState == nullptr) { m_engineRunning = false; } else { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); currentState->handleEvents(m_event); // Update currentState->update(deltaTime.update(m_resources)); // Draw if the states aren't swapping if (currentState->newState == nullptr) { currentState->draw(); // SDL MAGIC SDL_GL_SwapWindow(m_window); } } } } float Engine::DeltaTime::update(Resources& resources) { current = (float)SDL_GetTicks() / 1000; delta = current - previous; previous = current; fpsDelta += delta; if (fpsDelta >= 1) { resources.settings.fps = fps; fps = 0; fpsDelta = 0; } else { ++fps; } return delta; } void Engine::updateSettings() { if (m_resources.settings.width <= 0) { m_resources.settings.width = 1920; } if (m_resources.settings.height <= 0) { m_resources.settings.height = 1080; } if (m_resources.settings.volume > 100) { m_resources.settings.volume = 100; } if (m_resources.settings.volume < 0) { m_resources.settings.volume = 0; } SDL_SetWindowFullscreen(m_window, (Uint32)m_resources.settings.fullscreen); setViewport(m_resources.settings.width, m_resources.settings.height); SDL_SetWindowSize(m_window, m_resources.settings.width, m_resources.settings.height); auto top = m_stateManager.top(); top->camera.setPerspective(45.f, (float)m_resources.settings.width / (float)m_resources.settings.height, 0.1f, 10000); top->camera.sensitivity = m_resources.settings.mouseSens; top->audioManager.setMasterVolume(m_resources.settings.volume); top->audioManager.setMute(m_resources.settings.mute); m_resources.settings.settingsUpdated = false; SDL_GL_SetSwapInterval(m_resources.settings.vsync); } bool Engine::loadSettings(const std::string& filename) { std::ifstream inFile(filename); if (!inFile) { std::cerr << "Unable to open file: " << filename << '\n'; return false; } std::string line; while (!inFile.eof()) { std::getline(inFile, line); std::stringstream ss(line); std::string setting; ss >> setting; if (setting == "Resolution:") { ss >> m_resources.settings.width; ss >> m_resources.settings.height; } else if (setting == "Fullscreen:") { ss >> m_resources.settings.fullscreen; } else if (setting == "Vsync:") { ss >> m_resources.settings.vsync; } else if (setting == "Volume:") { ss >> m_resources.settings.volume; } else if (setting == "Mute:") { ss >> m_resources.settings.mute; } } m_resources.settings.settingsUpdated = true; return true; } bool Engine::saveSettings(const std::string& filename) { std::ofstream outFile(filename); if (!outFile) { std::cerr << "Unable to open file: " << filename << '\n'; return false; } outFile << "Resolution: " << m_resources.settings.width << ' ' << m_resources.settings.height << '\n'; outFile << "Fullscreen: " << m_resources.settings.fullscreen << '\n'; outFile << "Vsync: " << m_resources.settings.vsync << '\n'; outFile << "Volume: " << m_resources.settings.volume << '\n'; outFile << "Mute: " << m_resources.settings.mute << '\n'; return true; }