/* * Copyright (C) 2017 Christopher J. Howard * * This file is part of Antkeeper Source Code. * * Antkeeper Source Code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper Source Code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper Source Code. If not, see . */ #include "application.hpp" #include "application-state.hpp" #include "model-loader.hpp" #include "material-loader.hpp" #include "states/loading-state.hpp" #include "states/splash-state.hpp" #include "states/title-state.hpp" #include "states/game-state.hpp" #include "game/colony.hpp" #include "game/pheromone-matrix.hpp" #include "game/tool.hpp" #include "ui/menu.hpp" #include "ui/toolbar.hpp" #include "ui/pie-menu.hpp" #include "debug.hpp" #include "camera-rig.hpp" #include "configuration.hpp" #include #include #include #include #include #include #include #define OPENGL_VERSION_MAJOR 3 #define OPENGL_VERSION_MINOR 3 #undef max Application::Application(int argc, char* argv[]): state(nullptr), nextState(nullptr), terminationCode(EXIT_SUCCESS) { window = nullptr; context = nullptr; // Initialize SDL std::cout << std::string("Initializing SDL... "); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER) < 0) { std::cout << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; close(EXIT_FAILURE); return; } else { std::cout << std::string("success") << std::endl; } // Print SDL version strings SDL_version compiled; SDL_version linked; SDL_VERSION(&compiled); SDL_GetVersion(&linked); std::cout << std::string("Compiled with SDL ") << (int)compiled.major << std::string(".") << (int)compiled.minor << std::string(".") << (int)compiled.patch << std::endl; std::cout << std::string("Linking to SDL ") << (int)linked.major << std::string(".") << (int)linked.minor << std::string(".") << (int)linked.patch << std::endl; // Find app and user data paths appDataPath = std::string(SDL_GetBasePath()) + std::string("data/"); userDataPath = SDL_GetPrefPath("cjhoward", "antkeeper"); std::cout << std::string("Application data path: \"") << appDataPath << std::string("\"") << std::endl; std::cout << std::string("User data path: \"") << userDataPath << std::string("\"") << std::endl; // Form pathes to settings files defaultSettingsFilename = appDataPath + std::string("default-settings.txt"); userSettingsFilename = userDataPath + std::string("settings.txt"); // Load default settings std::cout << std::string("Loading default settings from \"") << defaultSettingsFilename << std::string("\"... "); if (!settings.load(defaultSettingsFilename)) { std::cout << std::string("failed") << std::endl; close(EXIT_FAILURE); return; } else { std::cout << std::string("success") << std::endl; } // Load user settings std::cout << std::string("Loading user settings from \"") << userSettingsFilename << std::string("\"... "); if (!settings.load(userSettingsFilename)) { // Failed, save default settings as user settings std::cout << std::string("failed") << std::endl; saveUserSettings(); } else { std::cout << std::string("success") << std::endl; } // Get values of required settings settings.get("fullscreen", &fullscreen); settings.get("swap_interval", &swapInterval); // Select OpenGL version SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_VERSION_MAJOR); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_VERSION_MINOR); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Set OpenGL buffer attributes SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); // Get all possible display modes for the default display int displayModeCount = SDL_GetNumDisplayModes(0); for (int i = displayModeCount - 1; i >= 0; --i) { SDL_DisplayMode displayMode; if (SDL_GetDisplayMode(0, i, &displayMode) != 0) { std::cerr << std::string("Failed to get display mode: \"") << SDL_GetError() << std::string("\"") << std::endl; close(EXIT_FAILURE); return; } resolutions.push_back(Vector2(displayMode.w, displayMode.h)); } // Read requested windowed and fullscreen resolutions from settings Vector2 requestedWindowedResolution; Vector2 requestedFullscreenResolution; settings.get("windowed_width", &requestedWindowedResolution.x); settings.get("windowed_height", &requestedWindowedResolution.y); settings.get("fullscreen_width", &requestedFullscreenResolution.x); settings.get("fullscreen_height", &requestedFullscreenResolution.y); // Determine desktop resolution SDL_DisplayMode desktopDisplayMode; if (SDL_GetDesktopDisplayMode(0, &desktopDisplayMode) != 0) { std::cerr << std::string("Failed to get desktop display mode: \"") << SDL_GetError() << std::string("\"") << std::endl; close(EXIT_FAILURE); return; } Vector2 desktopResolution; desktopResolution.x = static_cast(desktopDisplayMode.w); desktopResolution.y = static_cast(desktopDisplayMode.h); // Replace requested resolutions of -1 with native resolution requestedWindowedResolution.x = (requestedWindowedResolution.x == -1.0f) ? desktopResolution.x : requestedWindowedResolution.x; requestedWindowedResolution.y = (requestedWindowedResolution.y == -1.0f) ? desktopResolution.y : requestedWindowedResolution.y; requestedFullscreenResolution.x = (requestedFullscreenResolution.x == -1.0f) ? desktopResolution.x : requestedFullscreenResolution.x; requestedFullscreenResolution.y = (requestedFullscreenResolution.y == -1.0f) ? desktopResolution.y : requestedFullscreenResolution.y; // Find indices of closest resolutions to requested windowed and fullscreen resolutions windowedResolutionIndex = 0; fullscreenResolutionIndex = 0; float minWindowedResolutionDistance = std::numeric_limits::max(); float minFullscreenResolutionDistance = std::numeric_limits::max(); for (std::size_t i = 0; i < resolutions.size(); ++i) { Vector2 windowedResolutionDifference = resolutions[i] - requestedWindowedResolution; float windowedResolutionDistance = glm::dot(windowedResolutionDifference, windowedResolutionDifference); if (windowedResolutionDistance <= minWindowedResolutionDistance) { minWindowedResolutionDistance = windowedResolutionDistance; windowedResolutionIndex = i; } Vector2 fullscreenResolutionDifference = resolutions[i] - requestedFullscreenResolution; float fullscreenResolutionDistance = glm::dot(fullscreenResolutionDifference, fullscreenResolutionDifference); if (fullscreenResolutionDistance <= minFullscreenResolutionDistance) { minFullscreenResolutionDistance = fullscreenResolutionDistance; fullscreenResolutionIndex = i; } } // Determine window parameters and current resolution Uint32 windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI; if (fullscreen) { resolution = resolutions[fullscreenResolutionIndex]; windowFlags |= SDL_WINDOW_FULLSCREEN; } else { resolution = resolutions[windowedResolutionIndex]; } // Get requested language languageIndex = 0; std::string requestedLanguage; settings.get("language", &requestedLanguage); std::string stringsDirectory = appDataPath + std::string("strings/"); // Find available languages { // Open strings directory DIR* dir = opendir(stringsDirectory.c_str()); if (dir == nullptr) { std::cout << std::string("Failed to open strings directory \"") << stringsDirectory << std::string("\"") << std::endl; close(EXIT_FAILURE); return; } // Scan directory for .txt files for (struct dirent* entry = readdir(dir); entry != nullptr; entry = readdir(dir)) { if (entry->d_type == DT_DIR || *entry->d_name == '.') { continue; } std::string filename = entry->d_name; std::string::size_type delimeter = filename.find_last_of('.'); if (delimeter == std::string::npos) { continue; } std::string extension = filename.substr(delimeter + 1); if (extension != "txt") { continue; } // Add language std::string language = filename.substr(0, delimeter); languages.push_back(language); if (language == requestedLanguage) { languageIndex = languages.size() - 1; } } // Close biomes directory closedir(dir); } // Load strings std::string stringsFile = appDataPath + std::string("strings/") + languages[languageIndex] + std::string(".txt"); std::cout << std::string("Loading strings from \"") << stringsFile << std::string("\"... "); if (!strings.load(stringsFile)) { std::cout << std::string("failed") << std::endl; } else { std::cout << std::string("success") << std::endl; } // Get window title string std::string title; strings.get("title", &title); // Create window std::cout << std::string("Creating a ") << resolution.x << std::string("x") << resolution.y; std::cout << ((fullscreen) ? " fullscreen" : " windowed"); std::cout << std::string(" window... "); window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast(resolution.x), static_cast(resolution.y), windowFlags); if (window == nullptr) { std::cout << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; close(EXIT_FAILURE); return; } else { std::cout << std::string("success") << std::endl; } // Print video driver const char* videoDriver = SDL_GetCurrentVideoDriver(); if (!videoDriver) { std::cout << std::string("Unable to determine video driver") << std::endl; } else { std::cout << std::string("Using video driver \"") << videoDriver << std::string("\"") << std::endl; } // Create an OpenGL context std::cout << std::string("Creating an OpenGL context... "); context = SDL_GL_CreateContext(window); if (context == nullptr) { std::cout << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; close(EXIT_FAILURE); return; } else { std::cout << std::string("success") << std::endl; } // Initialize GL3W std::cout << std::string("Initializing GL3W... "); if (gl3wInit()) { std::cout << std::string("failed") << std::endl; close(EXIT_FAILURE); return; } else { std::cout << std::string("success") << std::endl; } // Check if OpenGL version is supported if (!gl3wIsSupported(OPENGL_VERSION_MAJOR, OPENGL_VERSION_MINOR)) { std::cout << std::string("OpenGL ") << OPENGL_VERSION_MAJOR << std::string(".") << OPENGL_VERSION_MINOR << std::string(" not supported") << std::endl; close(EXIT_FAILURE); return; } // Print OpenGL and GLSL version strings std::cout << std::string("Using OpenGL ") << glGetString(GL_VERSION) << std::string(", GLSL ") << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; // Set swap interval (vsync) if (swapInterval) { std::cout << std::string("Enabling vertical sync... "); } else { std::cout << std::string("Disabling vertical sync... "); } if (SDL_GL_SetSwapInterval(swapInterval) != 0) { std::cout << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; swapInterval = SDL_GL_GetSwapInterval(); } else { std::cout << std::string("success") << std::endl; } // Clear screen to black glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); SDL_GL_SwapWindow(window); // Get display DPI std::cout << std::string("Getting DPI of display 0... "); if (SDL_GetDisplayDPI(0, &dpi, nullptr, nullptr) != 0) { std::cerr << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; std::cout << std::string("Reverting to default DPI") << std::endl; settings.get("default_dpi", &dpi); } else { std::cout << std::string("success") << std::endl; } // Print DPI std::cout << std::string("Rendering at ") << dpi << std::string(" DPI") << std::endl; // Determine base font size settings.get("font_size", &fontSizePT); fontSizePX = fontSizePT * (1.0f / 72.0f) * dpi; // Print font size std::cout << std::string("Base font size is ") << fontSizePT << std::string("pt (") << fontSizePX << std::string("px)") << std::endl; // Setup input inputManager = new SDLInputManager(); keyboard = (*inputManager->getKeyboards()).front(); mouse = (*inputManager->getMice()).front(); bindingControl = nullptr; // Allocate states loadingState = new LoadingState(this); splashState = new SplashState(this); titleState = new TitleState(this); gameState = new GameState(this); // Setup loaders textureLoader = new TextureLoader(); materialLoader = new MaterialLoader(); modelLoader = new ModelLoader(); modelLoader->setMaterialLoader(materialLoader); // Allocate game variables orbitCam = new OrbitCam(); freeCam = new FreeCam(); activeRig = orbitCam; // Enter loading state state = nextState = loadingState; state->enter(); displayDebugInfo = false; } Application::~Application() { SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); SDL_Quit(); } int Application::execute() { // Fixed timestep // @see http://gafferongames.com/game-physics/fix-your-timestep/ t = 0.0f; dt = 1.0f / 60.0f; float accumulator = 0.0f; float maxFrameTime = 0.25f; int performanceSampleSize = 15; // Number of frames to sample int performanceSampleFrame = 0; // Current sample frame float performanceSampleTime = 0.0f; // Current sample time // Start frame timer frameTimer.start(); while (state != nullptr) { // Calculate frame time (in milliseconds) then reset frame timer float frameTime = static_cast(frameTimer.microseconds().count()) / 1000.0f; frameTimer.reset(); // Add frame time (in seconds) to accumulator accumulator += std::min(frameTime / 1000.0f, maxFrameTime); // If the user tried to close the application if (inputManager->wasClosed()) { // Close the application close(EXIT_SUCCESS); } else { // Execute current state //while (accumulator >= dt) { state->execute(); // Update controls menuControlProfile->update(); gameControlProfile->update(); // Perform tweening tweener->update(dt); //accumulator -= dt; //t += dt; } } // Check for state change if (nextState != state) { // Exit current state state->exit(); // Enter next state (if valid) state = nextState; if (nextState != nullptr) { state->enter(); tweener->update(0.0f); // Reset frame timer to counteract frames eaten by state exit() and enter() functions frameTimer.reset(); } else { break; } } // Bind controls if (bindingControl != nullptr) { InputEvent event; inputManager->listen(&event); if (event.type != InputEvent::Type::NONE) { bindingControl->bind(event); bindingControl = nullptr; if (activeMenu != nullptr) { MenuItem* item = activeMenu->getSelectedItem(); if (item != nullptr) { if (event.type == InputEvent::Type::KEY) { const char* keyName = SDL_GetKeyName(SDL_GetKeyFromScancode(static_cast(event.key.second))); std::stringstream stream; stream << keyName; std::string streamstring = stream.str(); std::u32string label; label.assign(streamstring.begin(), streamstring.end()); item->setValueName(item->getValueIndex(), label); } } } } } // Update input inputManager->update(); // Check if fullscreen was toggled if (toggleFullscreen.isTriggered() && !toggleFullscreen.wasTriggered()) { changeFullscreen(); } // Check if debug display was toggled if (toggleDebugDisplay.isTriggered() && !toggleDebugDisplay.wasTriggered()) { setDisplayDebugInfo(!displayDebugInfo); } // Add frame time to performance sample time and increment the frame count performanceSampleTime += frameTime; ++performanceSampleFrame; // If performance sample is complete if (performanceSampleFrame >= performanceSampleSize) { // Calculate mean frame time float meanFrameTime = performanceSampleTime / static_cast(performanceSampleSize); // Reset perform sample timers performanceSampleTime = 0.0f; performanceSampleFrame = 0; // Update frame time label if (frameTimeLabel->isVisible()) { std::u32string label; std::stringstream stream; stream.precision(2); stream << std::fixed << meanFrameTime; std::string streamstring = stream.str(); label.assign(streamstring.begin(), streamstring.end()); frameTimeLabel->setText(label); } } // Update UI if (activeMenu != nullptr) { activeMenu->update(dt); } uiRootElement->update(); uiBatcher->batch(uiBatch, uiRootElement); // Render scene renderer.render(scene); // Swap buffers SDL_GL_SwapWindow(window); } return terminationCode; } void Application::changeState(ApplicationState* state) { nextState = state; } void Application::setTerminationCode(int code) { terminationCode = code; } void Application::close(int terminationCode) { setTerminationCode(terminationCode); changeState(nullptr); } void Application::changeFullscreen() { fullscreen = !fullscreen; if (fullscreen) { resolution = resolutions[fullscreenResolutionIndex]; SDL_SetWindowSize(window, static_cast(resolution.x), static_cast(resolution.y)); if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0) { std::cerr << std::string("Failed to set fullscreen mode: \"") << SDL_GetError() << std::string("\"") << std::endl; fullscreen = false; } } else { resolution = resolutions[windowedResolutionIndex]; if (SDL_SetWindowFullscreen(window, 0) != 0) { std::cerr << std::string("Failed to set windowed mode: \"") << SDL_GetError() << std::string("\"") << std::endl; fullscreen = true; } else { SDL_SetWindowSize(window, static_cast(resolution.x), static_cast(resolution.y)); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); } } // Print mode and resolution if (fullscreen) { std::cout << std::string("Changed to fullscreen mode at resolution ") << resolution.x << std::string("x") << resolution.y << std::endl; } else { std::cout << std::string("Changed to windowed mode at resolution ") << resolution.x << std::string("x") << resolution.y << std::endl; } // Save settings settings.set("fullscreen", fullscreen); saveUserSettings(); // Resize UI resizeUI(); // Notify window observers inputManager->update(); } void Application::changeVerticalSync() { swapInterval = (swapInterval == 1) ? 0 : 1; if (swapInterval == 1) { std::cout << std::string("Enabling vertical sync... "); } else { std::cout << std::string("Disabling vertical sync... "); } if (SDL_GL_SetSwapInterval(swapInterval) != 0) { std::cout << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; swapInterval = SDL_GL_GetSwapInterval(); } else { std::cout << std::string("success") << std::endl; } // Save settings settings.set("swap_interval", swapInterval); saveUserSettings(); } void Application::saveUserSettings() { std::cout << std::string("Saving user setttings to \"") << userSettingsFilename << std::string("\"... "); if (!settings.save(userSettingsFilename)) { std::cout << std::string("failed") << std::endl; } else { std::cout << std::string("success") << std::endl; } } bool Application::loadModels() { antModel = modelLoader->load("data/models/common-worker-ant.mdl"); antHillModel = modelLoader->load("data/models/ant-hill.mdl"); nestModel = modelLoader->load("data/models/nest.mdl"); forcepsModel = modelLoader->load("data/models/forceps.mdl"); lensModel = modelLoader->load("data/models/lens.mdl"); brushModel = modelLoader->load("data/models/brush.mdl"); sidewalkPanelModel = modelLoader->load("data/models/sidewalk-panel.mdl"); soilModel = modelLoader->load("data/models/soil.mdl"); if (!antModel || !antHillModel || !nestModel || !forcepsModel || !lensModel || !brushModel) { return false; } antModelInstance.setModel(antModel); antModelInstance.setTransform(Transform::getIdentity()); antHillModelInstance.setModel(antHillModel); antHillModelInstance.setRotation(glm::angleAxis(glm::radians(90.0f), Vector3(1, 0, 0))); nestModelInstance.setModel(nestModel); sidewalkPanelInstance.setModel(sidewalkPanelModel); sidewalkPanelInstance1.setModel(sidewalkPanelModel); sidewalkPanelInstance2.setModel(sidewalkPanelModel); sidewalkPanelInstance3.setModel(sidewalkPanelModel); sidewalkPanelInstance4.setModel(sidewalkPanelModel); soilInstance.setModel(soilModel); float offset = 100.5f; sidewalkPanelInstance1.setTranslation(Vector3(-offset, 0.0f, 0.0f)); sidewalkPanelInstance2.setTranslation(Vector3(-offset * 2.0f, 0.0f, 0.0f)); sidewalkPanelInstance3.setTranslation(Vector3(offset, 0.0f, 0.0f)); sidewalkPanelInstance4.setTranslation(Vector3(offset * 2.0f, 0.0f, 0.0f)); soilInstance.setTranslation(Vector3(0.0f, -3.0f, 0.0f)); return true; } bool Application::loadScene() { // Create scene layers defaultLayer = scene.addLayer(); uiLayer = scene.addLayer(); // Set shadow map resolution shadowMapResolution = 4096; // Generate shadow map framebuffer glGenFramebuffers(1, &shadowMapFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, shadowMapFramebuffer); // Generate shadow map depth texture glGenTextures(1, &shadowMapDepthTextureID); glBindTexture(GL_TEXTURE_2D, shadowMapDepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, shadowMapResolution, shadowMapResolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); // Attach depth texture to framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMapDepthTextureID, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); // Unbind shadow map depth texture glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); // Setup shadow map render target shadowMapRenderTarget.width = shadowMapResolution; shadowMapRenderTarget.height = shadowMapResolution; shadowMapRenderTarget.framebuffer = shadowMapFramebuffer; // Setup texture class shadowMapDepthTexture.setTextureID(shadowMapDepthTextureID); shadowMapDepthTexture.setWidth(shadowMapResolution); shadowMapDepthTexture.setHeight(shadowMapResolution); // Setup shadow map render pass shadowMapPass.setRenderTarget(&shadowMapRenderTarget); shadowMapPass.setViewCamera(&camera); shadowMapPass.setLightCamera(&sunlightCamera); // Setup shadow map compositor shadowMapCompositor.addPass(&shadowMapPass); shadowMapCompositor.load(nullptr); // Post-processing framebuffers { // Generate color texture glGenTextures(1, &framebufferAColorTextureID); glBindTexture(GL_TEXTURE_2D, framebufferAColorTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, static_cast(resolution.x), static_cast(resolution.y), 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Generate depth texture glGenTextures(1, &framebufferADepthTextureID); glBindTexture(GL_TEXTURE_2D, framebufferADepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, static_cast(resolution.x), static_cast(resolution.y), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); // Generate framebuffer glGenFramebuffers(1, &framebufferA); glBindFramebuffer(GL_FRAMEBUFFER, framebufferA); // Attach textures to framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferAColorTextureID, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, framebufferADepthTextureID, 0); glDrawBuffer(GL_COLOR_ATTACHMENT0); //glReadBuffer(GL_COLOR_ATTACHMENT0); // Unbind framebuffer and texture glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); // Setup render target framebufferARenderTarget.width = static_cast(resolution.x); framebufferARenderTarget.height = static_cast(resolution.y); framebufferARenderTarget.framebuffer = framebufferA; // Setup texture class framebufferAColorTexture.setTextureID(framebufferAColorTextureID); framebufferAColorTexture.setWidth(static_cast(resolution.x)); framebufferAColorTexture.setHeight(static_cast(resolution.y)); } { // Generate color texture glGenTextures(1, &framebufferBColorTextureID); glBindTexture(GL_TEXTURE_2D, framebufferBColorTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, static_cast(resolution.x), static_cast(resolution.y), 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Generate framebuffer glGenFramebuffers(1, &framebufferB); glBindFramebuffer(GL_FRAMEBUFFER, framebufferB); // Attach textures to framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferBColorTextureID, 0); glDrawBuffer(GL_COLOR_ATTACHMENT0); //glReadBuffer(GL_COLOR_ATTACHMENT0); // Unbind framebuffer and texture glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); // Setup render target framebufferBRenderTarget.width = static_cast(resolution.x); framebufferBRenderTarget.height = static_cast(resolution.y); framebufferBRenderTarget.framebuffer = framebufferB; // Setup texture class framebufferBColorTexture.setTextureID(framebufferBColorTextureID); framebufferBColorTexture.setWidth(static_cast(resolution.x)); framebufferBColorTexture.setHeight(static_cast(resolution.y)); } // Pheromone PBO { glGenBuffers(1, &pheromonePBO); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pheromonePBO); glBufferData(GL_PIXEL_UNPACK_BUFFER, 4 * PHEROMONE_MATRIX_COLUMNS * PHEROMONE_MATRIX_ROWS, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glGenTextures(1, &pheromoneTextureID); glBindTexture(GL_TEXTURE_2D, pheromoneTextureID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PHEROMONE_MATRIX_COLUMNS, PHEROMONE_MATRIX_ROWS, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, 0); // Setup texture class pheromoneTexture.setWidth(PHEROMONE_MATRIX_COLUMNS); pheromoneTexture.setHeight(PHEROMONE_MATRIX_ROWS); pheromoneTexture.setTextureID(pheromoneTextureID); } // Enable seamless cubemap filtering glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Setup skybox pass //skyboxPass.setRenderTarget(&framebufferARenderTarget); skyboxPass.setRenderTarget(&defaultRenderTarget); // Setup clear depth pass //clearDepthPass.setRenderTarget(&framebufferARenderTarget); clearDepthPass.setRenderTarget(&defaultRenderTarget); clearDepthPass.setClear(false, true, false); clearDepthPass.setClearDepth(1.0f); // Setup lighting pass //lightingPass.setRenderTarget(&framebufferARenderTarget); lightingPass.setRenderTarget(&defaultRenderTarget); lightingPass.setShadowMap(&shadowMapDepthTexture); lightingPass.setShadowCamera(&sunlightCamera); lightingPass.setShadowMapPass(&shadowMapPass); // Setup blur passes horizontalBlurPass.setRenderTarget(&framebufferBRenderTarget); horizontalBlurPass.setTexture(&framebufferAColorTexture); horizontalBlurPass.setDirection(Vector2(0.0f, 0.0f)); verticalBlurPass.setRenderTarget(&framebufferARenderTarget); verticalBlurPass.setTexture(&framebufferBColorTexture); verticalBlurPass.setDirection(Vector2(0.0f, 0.0f)); horizontalBlurPass2.setRenderTarget(&framebufferBRenderTarget); horizontalBlurPass2.setTexture(&framebufferAColorTexture); horizontalBlurPass2.setDirection(Vector2(0.0f, 0.0f)); verticalBlurPass2.setRenderTarget(&defaultRenderTarget); verticalBlurPass2.setTexture(&framebufferBColorTexture); verticalBlurPass2.setDirection(Vector2(0.0f, 0.0f)); verticalBlurPass2.setGammaCorrect(true); // Setup debug pass debugPass.setRenderTarget(&defaultRenderTarget); defaultCompositor.addPass(&clearDepthPass); defaultCompositor.addPass(&skyboxPass); defaultCompositor.addPass(&lightingPass); //defaultCompositor.addPass(&horizontalBlurPass); //defaultCompositor.addPass(&verticalBlurPass); //defaultCompositor.addPass(&horizontalBlurPass2); //defaultCompositor.addPass(&verticalBlurPass2); //defaultCompositor.addPass(&debugPass); defaultCompositor.load(nullptr); // Setup sunlight camera sunlightCamera.lookAt(Vector3(0, 0, 0), -Vector3(0.5f, 2.0f, 2.0f), Vector3(0, 1, 0)); sunlightCamera.setOrthographic(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); sunlightCamera.setCompositor(&shadowMapCompositor); sunlightCamera.setCompositeIndex(0); sunlightCamera.setCullingMask(nullptr); defaultLayer->addObject(&sunlightCamera); // Setup camera camera.lookAt(Vector3(0.0f, 0.0f, 10.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 1.0f, 0.0f)); camera.setCompositor(&defaultCompositor); camera.setCompositeIndex(1); defaultLayer->addObject(&camera); // Debug lineBatcher = new LineBatcher(4096); BillboardBatch* lineBatch = lineBatcher->getBatch(); lineBatch->setAlignment(&camera, BillboardAlignmentMode::CYLINDRICAL); lineBatch->setAlignmentVector(Vector3(1, 0, 0)); defaultLayer->addObject(lineBatch); return true; } bool Application::loadUI() { // Load fonts FontLoader* fontLoader = new FontLoader(); menuFont = new Font(512, 512); if (!fontLoader->load("data/fonts/NotoSansCJKsc-Regular.otf", static_cast(fontSizePX + 0.5f), {UnicodeRange::BASIC_LATIN}, menuFont)) { std::cerr << std::string("Failed to load menu font") << std::endl; } copyrightFont = new Font(256, 256); if (!fontLoader->load("data/fonts/Varela-Regular.ttf", static_cast(fontSizePX * 0.8f + 0.5f), {UnicodeRange::BASIC_LATIN}, copyrightFont)) { std::cerr << std::string("Failed to load copyright font") << std::endl; } levelNameFont = new Font(512, 512); if (!fontLoader->load("data/fonts/Vollkorn-Regular.ttf", static_cast(fontSizePX * 2.0f + 0.5f), {UnicodeRange::BASIC_LATIN}, levelNameFont)) { std::cerr << std::string("Failed to load level name font") << std::endl; } delete fontLoader; // Load UI textures textureLoader->setGamma(1.0f); textureLoader->setMipmapChain(false); textureLoader->setMaxAnisotropy(1.0f); textureLoader->setWrapS(false); textureLoader->setWrapT(false); splashTexture = textureLoader->load2D("data/textures/ui-splash.png"); titleTexture = textureLoader->load2D("data/textures/ui-title.png"); rectangularPaletteTexture = textureLoader->load2D("data/textures/rectangular-palette.png"); foodIndicatorTexture = textureLoader->load2D("data/textures/food-indicator.png"); toolBrushTexture = textureLoader->load2D("data/textures/tool-brush.png"); toolLensTexture = textureLoader->load2D("data/textures/tool-lens.png"); toolForcepsTexture = textureLoader->load2D("data/textures/tool-forceps.png"); toolTrowelTexture = textureLoader->load2D("data/textures/tool-trowel.png"); toolbarTopTexture = textureLoader->load2D("data/textures/toolbar-top.png"); toolbarBottomTexture = textureLoader->load2D("data/textures/toolbar-bottom.png"); toolbarMiddleTexture = textureLoader->load2D("data/textures/toolbar-middle.png"); toolbarButtonRaisedTexture = textureLoader->load2D("data/textures/toolbar-button-raised.png"); toolbarButtonDepressedTexture = textureLoader->load2D("data/textures/toolbar-button-depressed.png"); arcNorthTexture = textureLoader->load2D("data/textures/pie-menu-arc-north.png"); arcEastTexture = textureLoader->load2D("data/textures/pie-menu-arc-east.png"); arcSouthTexture = textureLoader->load2D("data/textures/pie-menu-arc-south.png"); arcWestTexture = textureLoader->load2D("data/textures/pie-menu-arc-west.png"); mouseLeftTexture = textureLoader->load2D("data/textures/mouse-left.png"); mouseRightTexture = textureLoader->load2D("data/textures/mouse-right.png"); // Set colors selectedColor = Vector4(1.0f, 1.0f, 1.0f, 1.0f); deselectedColor = Vector4(1.0f, 1.0f, 1.0f, 0.35f); // Create tweener tweener = new Tweener(); // Setup root UI element uiRootElement = new UIContainer(); uiRootElement->setDimensions(resolution); mouse->addMouseMotionObserver(uiRootElement); mouse->addMouseButtonObserver(uiRootElement); // Create blackout element (for screen transitions) blackoutImage = new UIImage(); blackoutImage->setLayerOffset(ANTKEEPER_UI_LAYER_BLACKOUT); blackoutImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f)); blackoutImage->setVisible(false); uiRootElement->addChild(blackoutImage); // Create darken element (for darkening title screen) darkenImage = new UIImage(); darkenImage->setLayerOffset(ANTKEEPER_UI_LAYER_DARKEN); darkenImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 0.35f)); darkenImage->setVisible(false); uiRootElement->addChild(darkenImage); // Create splash screen background element splashBackgroundImage = new UIImage(); splashBackgroundImage->setLayerOffset(-1); splashBackgroundImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f)); splashBackgroundImage->setVisible(false); uiRootElement->addChild(splashBackgroundImage); // Create splash screen element splashImage = new UIImage(); splashImage->setTexture(splashTexture); splashImage->setVisible(false); uiRootElement->addChild(splashImage); // Create game title element titleImage = new UIImage(); titleImage->setTexture(titleTexture); titleImage->setVisible(false); titleImage->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); uiRootElement->addChild(titleImage); frameTimeLabel = new UILabel(); frameTimeLabel->setLayerOffset(99); frameTimeLabel->setTintColor(Vector4(1.0f, 1.0f, 0.0f, 1.0f)); frameTimeLabel->setVisible(false); uiRootElement->addChild(frameTimeLabel); //bool frameTimeLabelVisible = false; //settings.get("show_frame_time", &frameTimeLabelVisible); //frameTimeLabel->setVisible(frameTimeLabelVisible); // Create "Press any key" element anyKeyLabel = new UILabel(); anyKeyLabel->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); anyKeyLabel->setVisible(false); uiRootElement->addChild(anyKeyLabel); // Create copyright element copyrightLabel = new UILabel(); copyrightLabel->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); copyrightLabel->setVisible(false); copyrightLabel->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.15f)); uiRootElement->addChild(copyrightLabel); rectangularPaletteImage = new UIImage(); rectangularPaletteImage->setTexture(rectangularPaletteTexture); rectangularPaletteImage->setVisible(false); rectangularPaletteImage->setActive(false); rectangularPaletteImage->setLayerOffset(ANTKEEPER_UI_LAYER_HUD); uiRootElement->addChild(rectangularPaletteImage); contextButtonImage0 = new UIImage(); contextButtonImage0->setTexture(mouseLeftTexture); //uiRootElement->addChild(contextButtonImage0); foodIndicatorImage = new UIImage(); foodIndicatorImage->setTexture(foodIndicatorTexture); //uiRootElement->addChild(foodIndicatorImage); depthTextureImage = new UIImage(); depthTextureImage->setTexture(&shadowMapDepthTexture); depthTextureImage->setVisible(false); //uiRootElement->addChild(depthTextureImage); // Create level name label levelNameLabel = new UILabel(); levelNameLabel->setVisible(false); levelNameLabel->setLayerOffset(ANTKEEPER_UI_LAYER_HUD); uiRootElement->addChild(levelNameLabel); // Create toolbar toolbar = new Toolbar(); toolbar->setToolbarTopTexture(toolbarTopTexture); toolbar->setToolbarBottomTexture(toolbarBottomTexture); toolbar->setToolbarMiddleTexture(toolbarMiddleTexture); toolbar->setButtonRaisedTexture(toolbarButtonRaisedTexture); toolbar->setButtonDepressedTexture(toolbarButtonDepressedTexture); toolbar->addButton(toolBrushTexture, std::bind(&std::printf, "0\n"), std::bind(&std::printf, "0\n")); toolbar->addButton(toolLensTexture, std::bind(&std::printf, "1\n"), std::bind(&std::printf, "1\n")); toolbar->addButton(toolForcepsTexture, std::bind(&std::printf, "2\n"), std::bind(&std::printf, "2\n")); toolbar->addButton(toolTrowelTexture, std::bind(&std::printf, "3\n"), std::bind(&std::printf, "3\n")); toolbar->resize(); //uiRootElement->addChild(toolbar->getContainer()); toolbar->getContainer()->setVisible(false); toolbar->getContainer()->setActive(false); // Create pie menu pieMenu = new PieMenu(tweener); pieMenu->addOption(arcNorthTexture, toolLensTexture, std::bind(&Application::selectTool, this, lens), std::bind(&Application::deselectTool, this, lens)); pieMenu->addOption(arcEastTexture, toolForcepsTexture, std::bind(&Application::selectTool, this, forceps), std::bind(&Application::deselectTool, this, forceps)); pieMenu->addOption(arcSouthTexture, toolTrowelTexture, std::bind(&Application::selectTool, this, nullptr), std::bind(&Application::deselectTool, this, nullptr)); pieMenu->addOption(arcWestTexture, toolBrushTexture, std::bind(&Application::selectTool, this, brush), std::bind(&Application::deselectTool, this, brush)); uiRootElement->addChild(pieMenu->getContainer()); pieMenu->resize(); pieMenu->getContainer()->setVisible(false); pieMenu->getContainer()->setActive(true); // Setup screen fade in/fade out tween fadeInTween = new Tween(EaseFunction::IN_QUINT, 0.0f, 2.0f, Vector4(0.0f, 0.0f, 0.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, -1.0f)); fadeInTween->setUpdateCallback(std::bind(&UIElement::setTintColor, blackoutImage, std::placeholders::_1)); tweener->addTween(fadeInTween); fadeOutTween = new Tween(EaseFunction::OUT_QUINT, 0.0f, 2.0f, Vector4(0.0f, 0.0f, 0.0f, 0.0f), Vector4(0.0f, 0.0f, 0.0f, 1.0f)); fadeOutTween->setUpdateCallback(std::bind(&UIElement::setTintColor, blackoutImage, std::placeholders::_1)); tweener->addTween(fadeOutTween); // Setup darken fade in/fade out tweens darkenFadeInTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.15f, Vector4(0.0f, 0.0f, 0.0f, 0.0f), Vector4(0.0f, 0.0f, 0.0f, 0.4f)); darkenFadeInTween->setStartCallback(std::bind(&UIElement::setVisible, darkenImage, true)); darkenFadeInTween->setUpdateCallback(std::bind(&UIElement::setTintColor, darkenImage, std::placeholders::_1)); tweener->addTween(darkenFadeInTween); darkenFadeOutTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.15f, Vector4(0.0f, 0.0f, 0.0f, 0.4f), Vector4(0.0f, 0.0f, 0.0f, -0.4f)); darkenFadeOutTween->setUpdateCallback(std::bind(&UIElement::setTintColor, darkenImage, std::placeholders::_1)); darkenFadeOutTween->setEndCallback(std::bind(&UIElement::setVisible, darkenImage, false)); tweener->addTween(darkenFadeOutTween); // Setup blur fade in/fade out tweens blurFadeInTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.15f, 0.0f, 1.0f); blurFadeInTween->setUpdateCallback ( [this](float t) { float factor = blurFadeInTween->getTweenValue(); horizontalBlurPass.setDirection(Vector2(1.0f, 0.0f) * t); horizontalBlurPass2.setDirection(Vector2(3.0f, 0.0f) * t); verticalBlurPass.setDirection(Vector2(0.0f, 1.0f) * t); verticalBlurPass2.setDirection(Vector2(0.0f, 3.0f) * t); } ); tweener->addTween(blurFadeInTween); blurFadeOutTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.15f, 1.0f, -1.0f); blurFadeOutTween->setUpdateCallback ( [this](float t) { float factor = blurFadeInTween->getTweenValue(); horizontalBlurPass.setDirection(Vector2(1.0f, 0.0f) * t); horizontalBlurPass2.setDirection(Vector2(3.0f, 0.0f) * t); verticalBlurPass.setDirection(Vector2(0.0f, 1.0f) * t); verticalBlurPass2.setDirection(Vector2(0.0f, 3.0f) * t); } ); tweener->addTween(blurFadeOutTween); // Setup splash screen tween splashFadeInTween = new Tween(EaseFunction::IN_CUBIC, 0.0f, 0.5f, Vector4(1.0f, 1.0f, 1.0f, 0.0f), Vector4(0.0f, 0.0f, 0.0f, 1.0f)); splashFadeInTween->setUpdateCallback(std::bind(&UIElement::setTintColor, splashImage, std::placeholders::_1)); tweener->addTween(splashFadeInTween); splashHangTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 1.0f, 0.0f, 1.0f); tweener->addTween(splashHangTween); splashFadeOutTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.5f, Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, -1.0f)); splashFadeOutTween->setUpdateCallback(std::bind(&UIElement::setTintColor, splashImage, std::placeholders::_1)); tweener->addTween(splashFadeOutTween); splashFadeInTween->setEndCallback(std::bind(&TweenBase::start, splashHangTween)); splashHangTween->setEndCallback(std::bind(&TweenBase::start, splashFadeOutTween)); splashFadeOutTween->setEndCallback(std::bind(&Application::changeState, this, titleState)); // Setup game title tween titleFadeInTween = new Tween(EaseFunction::IN_CUBIC, 0.0f, 2.0f, Vector4(1.0f, 1.0f, 1.0f, 0.0f), Vector4(0.0f, 0.0f, 0.0f, 1.0f)); titleFadeInTween->setUpdateCallback(std::bind(&UIElement::setTintColor, titleImage, std::placeholders::_1)); tweener->addTween(titleFadeInTween); titleFadeOutTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.25f, Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, -1.0f)); titleFadeOutTween->setUpdateCallback(std::bind(&UIElement::setTintColor, titleImage, std::placeholders::_1)); tweener->addTween(titleFadeOutTween); // Setup "Press any key" tween anyKeyFadeInTween = new Tween(EaseFunction::LINEAR, 0.0f, 1.5f, Vector4(1.0f, 1.0f, 1.0f, 0.0f), Vector4(1.0f, 1.0f, 1.0f, 1.0f)); anyKeyFadeInTween->setUpdateCallback(std::bind(&UIElement::setTintColor, anyKeyLabel, std::placeholders::_1)); tweener->addTween(anyKeyFadeInTween); anyKeyFadeOutTween = new Tween(EaseFunction::LINEAR, 0.0f, 1.5f, Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(1.0f, 1.0f, 1.0f, -1.0f)); anyKeyFadeOutTween->setUpdateCallback(std::bind(&UIElement::setTintColor, anyKeyLabel, std::placeholders::_1)); anyKeyFadeInTween->setEndCallback(std::bind(&TweenBase::start, anyKeyFadeOutTween)); anyKeyFadeOutTween->setEndCallback(std::bind(&TweenBase::start, anyKeyFadeInTween)); tweener->addTween(anyKeyFadeOutTween); float menuFadeInDuration = 0.5f; Vector4 menuFadeInStartColor = Vector4(1.0f, 1.0f, 1.0f, 0.0f); Vector4 menuFadeInDeltaColor = Vector4(0.0f, 0.0f, 0.0f, 1.0f); float menuFadeOutDuration = 0.25f; Vector4 menuFadeOutStartColor = Vector4(1.0f, 1.0f, 1.0f, 1.0f); Vector4 menuFadeOutDeltaColor = Vector4(0.0f, 0.0f, 0.0f, -1.0f); // Setup main menu tween menuFadeInTween = new Tween(EaseFunction::OUT_QUINT, 0.0f, menuFadeInDuration, menuFadeInStartColor, menuFadeInDeltaColor); tweener->addTween(menuFadeInTween); menuActivateTween = new Tween(EaseFunction::OUT_QUINT, 0.0f, 0.01f, 0.0f, 0.0f); tweener->addTween(menuActivateTween); menuFadeOutTween = new Tween(EaseFunction::OUT_QUINT, 0.0f, menuFadeOutDuration, menuFadeOutStartColor, menuFadeOutDeltaColor); tweener->addTween(menuFadeOutTween); // Camera translation tween cameraTranslationTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 0.0f, Vector3(0.0f), Vector3(0.0f)); tweener->addTween(cameraTranslationTween); // Tool tweens forcepsSwoopTween = new Tween(EaseFunction::OUT_CUBIC, 0.0f, 1.0f, 0.0f, 0.5f); tweener->addTween(forcepsSwoopTween); // Build menu system activeMenu = nullptr; previousActiveMenu = nullptr; // Allocate menus mainMenu = new Menu(); levelsMenu = new Menu(); optionsMenu = new Menu(); controlsMenu = new Menu(); pauseMenu = new Menu(); // Main menu { mainMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f)); mainMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); mainMenu->setLineSpacing(1.0f); mainMenu->getUIContainer()->setActive(false); mainMenu->getUIContainer()->setVisible(false); uiRootElement->addChild(mainMenu->getUIContainer()); mainMenuContinueItem = mainMenu->addItem(); mainMenuContinueItem->setActivatedCallback(std::bind(&Application::continueGame, this)); mainMenuLevelsItem = mainMenu->addItem(); mainMenuLevelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu)); mainMenuNewGameItem = mainMenu->addItem(); mainMenuNewGameItem->setActivatedCallback(std::bind(&Application::newGame, this)); mainMenuSandboxItem = mainMenu->addItem(); mainMenuSandboxItem->setActivatedCallback(std::bind(&std::printf, "1\n")); mainMenuOptionsItem = mainMenu->addItem(); mainMenuOptionsItem->setActivatedCallback ( [this]() { optionsMenuBackItem->setActivatedCallback(std::bind(&Application::openMenu, this, mainMenu)); openMenu(optionsMenu); } ); mainMenuExitItem = mainMenu->addItem(); mainMenuExitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS)); } // Levels menu { levelsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f)); levelsMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); levelsMenu->setLineSpacing(1.0f); for (std::size_t world = 0; world < campaign.getWorldCount(); ++world) { for (std::size_t level = 0; level < campaign.getLevelCount(world); ++level) { MenuItem* levelItem = levelsMenu->addItem(); levelItem->setActivatedCallback ( [this, world, level]() { loadWorld(world); loadLevel(level); // Close levels menu closeMenu(); // Begin title fade-out titleFadeOutTween->reset(); titleFadeOutTween->start(); // Begin fade-out fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState)); fadeOutTween->reset(); fadeOutTween->start(); } ); } } levelsMenuBackItem = levelsMenu->addItem(); levelsMenuBackItem->setActivatedCallback ( [this]() { openMenu(previousActiveMenu); } ); levelsMenu->getUIContainer()->setActive(false); levelsMenu->getUIContainer()->setVisible(false); uiRootElement->addChild(levelsMenu->getUIContainer()); } // Options menu { optionsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f)); optionsMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); optionsMenu->setLineSpacing(1.0f); optionsMenu->setColumnMargin(menuFont->getWidth(U"MM")); optionsMenuWindowedResolutionItem = optionsMenu->addItem(); optionsMenuFullscreenResolutionItem = optionsMenu->addItem(); for (const Vector2& resolution: resolutions) { optionsMenuWindowedResolutionItem->addValue(); optionsMenuFullscreenResolutionItem->addValue(); } optionsMenuWindowedResolutionItem->setValueIndex(windowedResolutionIndex); optionsMenuWindowedResolutionItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this)); optionsMenuWindowedResolutionItem->setValueChangedCallback(std::bind(&Application::selectWindowedResolution, this, std::placeholders::_1)); optionsMenuFullscreenResolutionItem->setValueIndex(fullscreenResolutionIndex); optionsMenuFullscreenResolutionItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this)); optionsMenuFullscreenResolutionItem->setValueChangedCallback(std::bind(&Application::selectFullscreenResolution, this, std::placeholders::_1)); optionsMenuFullscreenItem = optionsMenu->addItem(); optionsMenuFullscreenItem->addValue(); optionsMenuFullscreenItem->addValue(); optionsMenuFullscreenItem->setValueIndex((fullscreen == 0) ? 0 : 1); optionsMenuFullscreenItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this)); optionsMenuFullscreenItem->setValueChangedCallback(std::bind(&Application::selectFullscreenMode, this, std::placeholders::_1)); optionsMenuVSyncItem = optionsMenu->addItem(); optionsMenuVSyncItem->addValue(); optionsMenuVSyncItem->addValue(); optionsMenuVSyncItem->setValueIndex((swapInterval == 0) ? 0 : 1); optionsMenuVSyncItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this)); optionsMenuVSyncItem->setValueChangedCallback(std::bind(&Application::selectVSyncMode, this, std::placeholders::_1)); optionsMenuLanguageItem = optionsMenu->addItem(); for (std::size_t i = 0; i < languages.size(); ++i) { optionsMenuLanguageItem->addValue(); } optionsMenuLanguageItem->setValueIndex(languageIndex); optionsMenuLanguageItem->setActivatedCallback(std::bind(&Application::incrementMenuItem, this)); optionsMenuLanguageItem->setValueChangedCallback(std::bind(&Application::selectLanguage, this, std::placeholders::_1)); optionsMenuControlsItem = optionsMenu->addItem(); optionsMenuControlsItem->setActivatedCallback ( [this]() { controlsMenuBackItem->setActivatedCallback(std::bind(&Application::openMenu, this, optionsMenu)); openMenu(controlsMenu); } ); optionsMenuBackItem = optionsMenu->addItem(); optionsMenuBackItem->setActivatedCallback ( [this]() { openMenu(previousActiveMenu); } ); optionsMenu->getUIContainer()->setActive(false); optionsMenu->getUIContainer()->setVisible(false); uiRootElement->addChild(optionsMenu->getUIContainer()); } // Controls menu { controlsMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.8f)); controlsMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); controlsMenu->setLineSpacing(1.0f); controlsMenu->setColumnMargin(menuFont->getWidth(U"MM")); controlsMenu->getUIContainer()->setActive(false); controlsMenu->getUIContainer()->setVisible(false); uiRootElement->addChild(controlsMenu->getUIContainer()); controlsMenuResetToDefaultItem = controlsMenu->addItem(); controlsMenuMoveForwardItem = controlsMenu->addItem(); controlsMenuMoveForwardItem->addValue(); controlsMenuMoveForwardItem->setActivatedCallback(std::bind(&Application::bindControl, this, &cameraMoveForward)); controlsMenuMoveBackItem = controlsMenu->addItem(); controlsMenuMoveBackItem->addValue(); controlsMenuMoveBackItem->setActivatedCallback(std::bind(&Application::bindControl, this, &cameraMoveBack)); controlsMenuMoveLeftItem = controlsMenu->addItem(); controlsMenuMoveLeftItem->addValue(); controlsMenuMoveLeftItem->setActivatedCallback(std::bind(&Application::bindControl, this, &cameraMoveLeft)); controlsMenuMoveRightItem = controlsMenu->addItem(); controlsMenuMoveRightItem->addValue(); controlsMenuMoveRightItem->setActivatedCallback(std::bind(&Application::bindControl, this, &cameraMoveRight)); controlsMenuBackItem = controlsMenu->addItem(); controlsMenuBackItem->setActivatedCallback ( [this]() { openMenu(optionsMenu); } ); } // Pause menu { pauseMenu->getUIContainer()->setAnchor(Vector2(0.5f, 0.5f)); pauseMenu->getUIContainer()->setLayerOffset(ANTKEEPER_UI_LAYER_MENU); pauseMenu->setLineSpacing(1.0f); pauseMenuResumeItem = pauseMenu->addItem(); pauseMenuResumeItem->setActivatedCallback(std::bind(&Application::closePauseMenu, this)); pauseMenuLevelsItem = pauseMenu->addItem(); pauseMenuLevelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu)); pauseMenuOptionsItem = pauseMenu->addItem(); pauseMenuOptionsItem->setActivatedCallback ( [this]() { optionsMenuBackItem->setActivatedCallback(std::bind(&Application::openMenu, this, pauseMenu)); openMenu(optionsMenu); } ); pauseMenuMainMenuItem = pauseMenu->addItem(); pauseMenuMainMenuItem->setActivatedCallback ( [this]() { // Close pause menu closeMenu(); // Begin fade-out to title state fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, titleState)); fadeOutTween->reset(); fadeOutTween->start(); } ); pauseMenuExitItem = pauseMenu->addItem(); pauseMenuExitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS)); pauseMenu->getUIContainer()->setActive(false); pauseMenu->getUIContainer()->setVisible(false); uiRootElement->addChild(pauseMenu->getUIContainer()); } // Set UI strings restringUI(); resizeUI(); // Setup UI batch uiBatch = new BillboardBatch(); uiBatch->resize(512); uiBatcher = new UIBatcher(); // Setup UI render pass and compositor uiPass.setRenderTarget(&defaultRenderTarget); uiCompositor.addPass(&uiPass); uiCompositor.load(nullptr); // Setup UI camera uiCamera.lookAt(glm::vec3(0), glm::vec3(0, 0, -1), glm::vec3(0, 1, 0)); uiCamera.setCompositor(&uiCompositor); uiCamera.setCompositeIndex(0); // Setup UI scene uiLayer->addObject(uiBatch); uiLayer->addObject(&uiCamera); defaultRenderTarget.width = static_cast(resolution.x); defaultRenderTarget.height = static_cast(resolution.y); defaultRenderTarget.framebuffer = 0; resizeUI(); return true; } bool Application::loadControls() { // Setup menu navigation controls menuControlProfile = new ControlProfile(inputManager); menuControlProfile->registerControl("menu_left", &menuLeft); menuControlProfile->registerControl("menu_right", &menuRight); menuControlProfile->registerControl("menu_up", &menuUp); menuControlProfile->registerControl("menu_down", &menuDown); menuControlProfile->registerControl("menu_select", &menuSelect); menuControlProfile->registerControl("menu_cancel", &menuCancel); menuControlProfile->registerControl("toggle_fullscreen", &toggleFullscreen); menuControlProfile->registerControl("toggle_debug_display", &toggleDebugDisplay); menuControlProfile->registerControl("escape", &escape); menuLeft.bindKey(keyboard, SDL_SCANCODE_LEFT); menuLeft.bindKey(keyboard, SDL_SCANCODE_A); menuRight.bindKey(keyboard, SDL_SCANCODE_RIGHT); menuRight.bindKey(keyboard, SDL_SCANCODE_D); menuUp.bindKey(keyboard, SDL_SCANCODE_UP); menuUp.bindKey(keyboard, SDL_SCANCODE_W); menuDown.bindKey(keyboard, SDL_SCANCODE_DOWN); menuDown.bindKey(keyboard, SDL_SCANCODE_S); menuSelect.bindKey(keyboard, SDL_SCANCODE_RETURN); menuSelect.bindKey(keyboard, SDL_SCANCODE_SPACE); menuSelect.bindKey(keyboard, SDL_SCANCODE_Z); menuCancel.bindKey(keyboard, SDL_SCANCODE_BACKSPACE); menuCancel.bindKey(keyboard, SDL_SCANCODE_X); toggleFullscreen.bindKey(keyboard, SDL_SCANCODE_F11); toggleDebugDisplay.bindKey(keyboard, SDL_SCANCODE_GRAVE); escape.bindKey(keyboard, SDL_SCANCODE_ESCAPE); // Setup in-game controls gameControlProfile = new ControlProfile(inputManager); gameControlProfile->registerControl("camera-move-forward", &cameraMoveForward); gameControlProfile->registerControl("camera-move-back", &cameraMoveBack); gameControlProfile->registerControl("camera-move-left", &cameraMoveLeft); gameControlProfile->registerControl("camera-move-right", &cameraMoveRight); gameControlProfile->registerControl("camera-rotate-cw", &cameraRotateCW); gameControlProfile->registerControl("camera-rotate-ccw", &cameraRotateCCW); gameControlProfile->registerControl("camera-zoom-in", &cameraZoomIn); gameControlProfile->registerControl("camera-zoom-out", &cameraZoomOut); gameControlProfile->registerControl("camera-toggle-nest-view", &cameraToggleNestView); gameControlProfile->registerControl("camera-toggle-overhead-view", &cameraToggleOverheadView); gameControlProfile->registerControl("walk-forward", &walkForward); gameControlProfile->registerControl("walk-back", &walkBack); gameControlProfile->registerControl("turn-left", &turnLeft); gameControlProfile->registerControl("turn-right", &turnRight); gameControlProfile->registerControl("toggle-pause", &togglePause); gameControlProfile->registerControl("toggle-pause-menu", &togglePauseMenu); gameControlProfile->registerControl("fast-forward", &fastForward); gameControlProfile->registerControl("switch-rig", &switchRig); cameraMoveForward.bindKey(keyboard, SDL_SCANCODE_W); cameraMoveBack.bindKey(keyboard, SDL_SCANCODE_S); cameraMoveLeft.bindKey(keyboard, SDL_SCANCODE_A); cameraMoveRight.bindKey(keyboard, SDL_SCANCODE_D); cameraRotateCW.bindKey(keyboard, SDL_SCANCODE_Q); cameraRotateCCW.bindKey(keyboard, SDL_SCANCODE_E); cameraZoomIn.bindKey(keyboard, SDL_SCANCODE_EQUALS); cameraZoomOut.bindKey(keyboard, SDL_SCANCODE_MINUS); cameraZoomIn.bindMouseWheelAxis(mouse, MouseWheelAxis::POSITIVE_Y); cameraZoomOut.bindMouseWheelAxis(mouse, MouseWheelAxis::NEGATIVE_Y); cameraToggleOverheadView.bindKey(keyboard, SDL_SCANCODE_R); cameraToggleNestView.bindKey(keyboard, SDL_SCANCODE_F); walkForward.bindKey(keyboard, SDL_SCANCODE_UP); walkBack.bindKey(keyboard, SDL_SCANCODE_DOWN); turnLeft.bindKey(keyboard, SDL_SCANCODE_LEFT); turnRight.bindKey(keyboard, SDL_SCANCODE_RIGHT); togglePause.bindKey(keyboard, SDL_SCANCODE_SPACE); togglePauseMenu.bindKey(keyboard, SDL_SCANCODE_ESCAPE); fastForward.bindKey(keyboard, SDL_SCANCODE_F); switchRig.bindKey(keyboard, SDL_SCANCODE_TAB); return true; } bool Application::loadGame() { // Load biosphere biosphere.load("data/biomes/"); // Load campaign campaign.load("data/levels/"); currentWorldIndex = 0; currentLevelIndex = 0; simulationPaused = false; // Allocate level currentLevel = new Level(); // Create colony colony = new Colony(); colony->setAntModel(antModel); currentTool = nullptr; // Create tools forceps = new Forceps(forcepsModel); forceps->setColony(colony); forceps->setOrbitCam(orbitCam); lens = new Lens(lensModel); lens->setOrbitCam(orbitCam); lens->setSunDirection(glm::normalize(-Vector3(0.5f, 2.0f, 2.0f))); brush = new Brush(brushModel); brush->setColony(colony); brush->setOrbitCam(orbitCam); loadWorld(0); loadLevel(0); return true; } void Application::resizeUI() { // Adjust render target dimensions defaultRenderTarget.width = static_cast(resolution.x); defaultRenderTarget.height = static_cast(resolution.y); // Adjust UI dimensions uiRootElement->setDimensions(resolution); uiRootElement->update(); // Adjust title titleImage->setAnchor(Vector2(0.5f, 0.0f)); titleImage->setDimensions(Vector2(titleTexture->getWidth(), titleTexture->getHeight())); titleImage->setTranslation(Vector2(0.0f, (int)(resolution.y * (1.0f / 4.0f) - titleTexture->getHeight() * 0.5f))); blackoutImage->setDimensions(resolution); darkenImage->setDimensions(resolution); splashBackgroundImage->setDimensions(resolution); splashImage->setAnchor(Anchor::CENTER); splashImage->setDimensions(Vector2(splashTexture->getWidth(), splashTexture->getHeight())); frameTimeLabel->setAnchor(Vector2(0.0f, 0.0f)); frameTimeLabel->setTranslation(Vector2(0.0f)); anyKeyLabel->setAnchor(Vector2(0.5f, 1.0f)); anyKeyLabel->setTranslation(Vector2(0.0f, (int)(-resolution.y * (1.0f / 4.0f) - menuFont->getMetrics().getHeight() * 0.5f))); copyrightLabel->setAnchor(Vector2(0.0f, 1.0f)); copyrightLabel->setTranslation(Vector2(resolution.x, -resolution.y) * 0.02f); rectangularPaletteImage->setAnchor(Vector2(0.0f, 1.0f)); rectangularPaletteImage->setDimensions(Vector2(rectangularPaletteTexture->getWidth(), rectangularPaletteTexture->getHeight())); rectangularPaletteImage->setTranslation(Vector2(16.0f, -16.0f)); contextButtonImage0->setAnchor(Vector2(0.5f, 1.0f)); contextButtonImage0->setDimensions(Vector2(mouseLeftTexture->getWidth(), mouseLeftTexture->getHeight())); contextButtonImage0->setTranslation(Vector2(0.0f, -16.0f)); foodIndicatorImage->setAnchor(Vector2(1.0f, 0.0f)); foodIndicatorImage->setDimensions(Vector2(foodIndicatorTexture->getWidth(), foodIndicatorTexture->getHeight())); foodIndicatorImage->setTranslation(Vector2(-16.0f, 16.0f)); depthTextureImage->setAnchor(Vector2(0.0f, 1.0f)); depthTextureImage->setDimensions(Vector2(256, 256)); depthTextureImage->setTranslation(Vector2(0.0f, 0.0f)); levelNameLabel->setAnchor(Vector2(0.5f, 0.5f)); // Adjust UI camera projection uiCamera.setOrthographic(0.0f, resolution.x, resolution.y, 0.0f, -1.0f, 1.0f); } void Application::restringUI() { // Build map of UTF-8 string names to UTF-32 string values std::map stringMap; const std::map* stringParameters = strings.getParameters(); for (auto it = stringParameters->begin(); it != stringParameters->end(); ++it) { std::u32string u32value; strings.get(it->first, &stringMap[it->first]); } // Build set of Unicode characters which encompass all strings std::set unicodeSet; for (auto it = stringMap.begin(); it != stringMap.end(); ++it) { for (char32_t charcode: it->second) { unicodeSet.insert(charcode); } } // Insert basic latin Unicode block for (char32_t charcode = UnicodeRange::BASIC_LATIN.start; charcode <= UnicodeRange::BASIC_LATIN.end; ++charcode) { unicodeSet.insert(charcode); } // Transform character set into character ranges std::vector unicodeRanges; for (auto it = unicodeSet.begin(); it != unicodeSet.end(); ++it) { char32_t charcode = *it; unicodeRanges.push_back(UnicodeRange(charcode)); } // Delete previously loaded fonts delete menuFont; delete copyrightFont; delete levelNameFont; // Determine fonts for current language std::string menuFontBasename; std::string copyrightFontBasename; std::string levelNameFontBasename; strings.get("menu-font", &menuFontBasename); strings.get("copyright-font", ©rightFontBasename); strings.get("level-name-font", &levelNameFontBasename); std::string fontsDirectory = appDataPath + std::string("fonts/"); // Load fonts with the custom Unicode ranges FontLoader* fontLoader = new FontLoader(); menuFont = new Font(512, 512); if (!fontLoader->load(fontsDirectory + menuFontBasename, static_cast(fontSizePX + 0.5f), unicodeRanges, menuFont)) { std::cerr << std::string("Failed to load menu font") << std::endl; } copyrightFont = new Font(256, 256); if (!fontLoader->load(fontsDirectory + copyrightFontBasename, static_cast(fontSizePX * 0.8f + 0.5f), unicodeRanges, copyrightFont)) { std::cerr << std::string("Failed to load copyright font") << std::endl; } levelNameFont = new Font(512, 512); if (!fontLoader->load(fontsDirectory + levelNameFontBasename, static_cast(fontSizePX * 2.0f + 0.5f), unicodeRanges, levelNameFont)) { std::cerr << std::string("Failed to load level name font") << std::endl; } delete fontLoader; // Set fonts levelNameLabel->setFont(levelNameFont); frameTimeLabel->setFont(copyrightFont); anyKeyLabel->setFont(menuFont); copyrightLabel->setFont(copyrightFont); mainMenu->setFont(menuFont); levelsMenu->setFont(menuFont); optionsMenu->setFont(menuFont); controlsMenu->setFont(menuFont); pauseMenu->setFont(menuFont); // Title screen anyKeyLabel->setText(stringMap["press-any-key"]); copyrightLabel->setText(stringMap["copyright"]); // Main menu mainMenuContinueItem->setName(stringMap["continue"]); mainMenuLevelsItem->setName(stringMap["levels"]); mainMenuNewGameItem->setName(stringMap["new-game"]); mainMenuSandboxItem->setName(stringMap["sandbox"]); mainMenuOptionsItem->setName(stringMap["options"]); mainMenuExitItem->setName(stringMap["exit"]); // Levels menu std::size_t levelItemIndex = 0; for (std::size_t world = 0; world < campaign.getWorldCount(); ++world) { for (std::size_t level = 0; level < campaign.getLevelCount(world); ++level) { // Look up level name std::u32string levelName = getLevelName(world, level); // Create label /* std::u32string label; std::stringstream stream; stream << (world + 1) << std::string("-") << (level + 1) << std::string(": "; label = std::wstring_convert, char32_t>().from_bytes(stream.str()) + levelName; */ // Set item name MenuItem* levelItem = levelsMenu->getItem(levelItemIndex); levelItem->setName(levelName); ++levelItemIndex; } } levelsMenuBackItem->setName(stringMap["back"]); // Options menu optionsMenuWindowedResolutionItem->setName(stringMap["windowed-resolution"]); optionsMenuFullscreenResolutionItem->setName(stringMap["fullscreen-resolution"]); std::size_t resolutionIndex = 0; for (std::size_t i = 0; i < resolutions.size(); ++i) { std::u32string label; std::stringstream stream; stream << resolutions[i].x << std::string("x") << resolutions[i].y; std::string streamstring = stream.str(); label.assign(streamstring.begin(), streamstring.end()); optionsMenuWindowedResolutionItem->setValueName(i, label); optionsMenuFullscreenResolutionItem->setValueName(i, label); } optionsMenuFullscreenItem->setName(stringMap["fullscreen"]); optionsMenuFullscreenItem->setValueName(0, stringMap["off"]); optionsMenuFullscreenItem->setValueName(1, stringMap["on"]); optionsMenuVSyncItem->setName(stringMap["vertical-sync"]); optionsMenuVSyncItem->setValueName(0, stringMap["off"]); optionsMenuVSyncItem->setValueName(1, stringMap["on"]); optionsMenuLanguageItem->setName(stringMap["language"]); for (std::size_t i = 0; i < languages.size(); ++i) { optionsMenuLanguageItem->setValueName(i, stringMap[languages[i]]); } optionsMenuControlsItem->setName(stringMap["controls"]); optionsMenuBackItem->setName(stringMap["back"]); // Controls menu controlsMenuResetToDefaultItem->setName(stringMap["reset-to-default"]); controlsMenuMoveForwardItem->setName(stringMap["move-forward"]); controlsMenuMoveForwardItem->setValueName(0, U"W"); controlsMenuMoveBackItem->setName(stringMap["move-back"]); controlsMenuMoveBackItem->setValueName(0, U"S"); controlsMenuMoveLeftItem->setName(stringMap["move-left"]); controlsMenuMoveLeftItem->setValueName(0, U"A"); controlsMenuMoveRightItem->setName(stringMap["move-right"]); controlsMenuMoveRightItem->setValueName(0, U"D"); controlsMenuBackItem->setName(stringMap["back"]); // Pause menu pauseMenuResumeItem->setName(stringMap["resume"]); pauseMenuLevelsItem->setName(stringMap["levels"]); pauseMenuOptionsItem->setName(stringMap["options"]); pauseMenuMainMenuItem->setName(stringMap["main-menu"]); pauseMenuExitItem->setName(stringMap["exit"]); } void Application::openMenu(Menu* menu) { if (activeMenu != nullptr) { closeMenu(); } activeMenu = menu; activeMenu->select(0); activeMenu->getUIContainer()->setVisible(true); activeMenu->getUIContainer()->setActive(false); activeMenu->getUIContainer()->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.0f)); /* if (activeMenu->getSelectedItem() == nullptr) { activeMenu->select(0); } */ // Delay menu activation menuActivateTween->setEndCallback(std::bind(&UIElement::setActive, activeMenu->getUIContainer(), true)); menuActivateTween->reset(); menuActivateTween->start(); // Begin menu fade-in menuFadeInTween->setUpdateCallback(std::bind(&UIElement::setTintColor, activeMenu->getUIContainer(), std::placeholders::_1)); menuFadeInTween->reset(); menuFadeInTween->start(); } void Application::closeMenu() { if (activeMenu != nullptr) { // Deactivate menu activeMenu->getUIContainer()->setActive(false); activeMenu->getUIContainer()->setVisible(false); // Begin menu fade-out /* menuFadeOutTween->setUpdateCallback(std::bind(&UIElement::setTintColor, activeMenu->getUIContainer(), std::placeholders::_1)); menuFadeOutTween->setEndCallback(std::bind(&UIElement::setVisible, activeMenu->getUIContainer(), false)); menuFadeOutTween->reset(); menuFadeOutTween->start(); */ previousActiveMenu = activeMenu; activeMenu = nullptr; } } void Application::selectMenuItem(std::size_t index) { if (activeMenu != nullptr) { activeMenu->select(index); } } void Application::activateMenuItem() { if (activeMenu != nullptr) { activeMenu->activate(); } } void Application::incrementMenuItem() { if (activeMenu != nullptr) { MenuItem* item = activeMenu->getSelectedItem(); if (item != nullptr) { if (item->getValueCount() != 0) { item->setValueIndex((item->getValueIndex() + 1) % item->getValueCount()); } } } } void Application::decrementMenuItem() { if (activeMenu != nullptr) { MenuItem* item = activeMenu->getSelectedItem(); if (item != nullptr) { if (item->getValueCount() != 0) { if (!item->getValueIndex()) { item->setValueIndex(item->getValueCount() - 1); } else { item->setValueIndex(item->getValueIndex() - 1); } } } } } void Application::continueGame() { closeMenu(); int world = 0; int level = 0; settings.get("continue_world", &world); settings.get("continue_level", &level); if (world != currentWorldIndex) { loadWorld(world); } if (level != currentLevelIndex) { loadLevel(level); } // Begin title fade-out titleFadeOutTween->reset(); titleFadeOutTween->start(); // Begin fade-out fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState)); fadeOutTween->reset(); fadeOutTween->start(); } void Application::newGame() { // Close main menu closeMenu(); // Begin title fade-out titleFadeOutTween->reset(); titleFadeOutTween->start(); if (currentWorldIndex != 0 || currentLevelIndex != 0) { // Select first level of the first world currentWorldIndex = 0; currentLevelIndex = 0; // Begin fade-out fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState)); fadeOutTween->reset(); fadeOutTween->start(); } else { // Begin fade-out fadeOutTween->setEndCallback(std::bind(&Application::changeState, this, gameState)); fadeOutTween->reset(); fadeOutTween->start(); // Change state //changeState(gameState); } } void Application::deselectTool(Tool* tool) { if (tool != nullptr) { tool->setActive(false); return; } } void Application::selectTool(Tool* tool) { if (tool != nullptr) { tool->setActive(true); } currentTool = tool; } void Application::loadWorld(std::size_t index) { // Set current world currentWorldIndex = index; // Get world biome const LevelParameterSet* levelParams = campaign.getLevelParams(currentWorldIndex, 0); const Biome* biome = &biosphere.biomes[levelParams->biome]; // Setup rendering passes lightingPass.setDiffuseCubemap(biome->diffuseCubemap); lightingPass.setSpecularCubemap(biome->specularCubemap); skyboxPass.setCubemap(biome->specularCubemap); } void Application::loadLevel(std::size_t index) { // Set current level currentLevelIndex = index; // Load level const LevelParameterSet* levelParams = campaign.getLevelParams(currentWorldIndex, currentLevelIndex); currentLevel->load(*levelParams); Material* material = materialLoader->load("data/materials/debug-terrain-surface.mtl"); ShaderTexture2D* albedoOpacityMap = static_cast(material->getVariable("albedoOpacityMap")); if (albedoOpacityMap != nullptr) { albedoOpacityMap->setValue(&pheromoneTexture); } material->setFlags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_DISABLE_SHADOW_CASTING); //material->shadowCaster = false; //material->flags |= (unsigned int)PhysicalMaterial::Flags::TRANSLUCENT; currentLevel->terrain.getSurfaceModel()->getGroup(0)->material = material; } void Application::pauseSimulation() { simulationPaused = true; } void Application::unpauseSimulation() { simulationPaused = false; } void Application::openPauseMenu() { simulationPaused = true; darkenFadeOutTween->stop(); darkenFadeInTween->reset(); darkenFadeInTween->start(); blurFadeOutTween->stop(); blurFadeInTween->reset(); blurFadeInTween->start(); openMenu(pauseMenu); pauseMenu->select(0); } void Application::closePauseMenu() { simulationPaused = false; darkenFadeInTween->stop(); darkenFadeOutTween->reset(); darkenFadeOutTween->start(); blurFadeInTween->stop(); blurFadeOutTween->reset(); blurFadeOutTween->start(); closeMenu(); } void Application::setDisplayDebugInfo(bool display) { displayDebugInfo = display; frameTimeLabel->setVisible(displayDebugInfo); depthTextureImage->setVisible(displayDebugInfo); } std::u32string Application::getLevelName(std::size_t world, std::size_t level) const { // Form level ID string char levelIDBuffer[6]; std::sprintf(levelIDBuffer, "%02d-%02d", static_cast(world + 1), static_cast(level + 1)); std::string levelID(levelIDBuffer); // Look up level name std::u32string levelName; strings.get(levelIDBuffer, &levelName); return levelName; } void Application::selectWindowedResolution(std::size_t index) { windowedResolutionIndex = index; if (!fullscreen) { // Select resolution resolution = resolutions[windowedResolutionIndex]; // Resize window SDL_SetWindowSize(window, static_cast(resolution.x), static_cast(resolution.y)); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); // Resize UI resizeUI(); // Notify window observers inputManager->update(); } // Save settings settings.set("windowed_width", resolutions[windowedResolutionIndex].x); settings.set("windowed_height", resolutions[windowedResolutionIndex].y); saveUserSettings(); } void Application::selectFullscreenResolution(std::size_t index) { fullscreenResolutionIndex = index; if (fullscreen) { // Select resolution resolution = resolutions[fullscreenResolutionIndex]; // Resize window SDL_SetWindowSize(window, static_cast(resolution.x), static_cast(resolution.y)); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); // Resize UI resizeUI(); // Notify window observers inputManager->update(); } // Save settings settings.set("fullscreen_width", resolutions[fullscreenResolutionIndex].x); settings.set("fullscreen_height", resolutions[fullscreenResolutionIndex].y); saveUserSettings(); } void Application::selectFullscreenMode(std::size_t index) { fullscreen = (index == 1); if (fullscreen) { resolution = resolutions[fullscreenResolutionIndex]; SDL_SetWindowSize(window, static_cast(resolution.x), static_cast(resolution.y)); if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0) { std::cerr << std::string("Failed to set fullscreen mode: \"") << SDL_GetError() << std::string("\"") << std::endl; fullscreen = false; } } else { resolution = resolutions[windowedResolutionIndex]; if (SDL_SetWindowFullscreen(window, 0) != 0) { std::cerr << std::string("Failed to set windowed mode: \"") << SDL_GetError() << std::string("\"") << std::endl; fullscreen = true; } else { SDL_SetWindowSize(window, static_cast(resolution.x), static_cast(resolution.y)); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); } } // Print mode and resolution if (fullscreen) { std::cout << std::string("Changed to fullscreen mode at resolution ") << resolution.x << std::string("x") << resolution.y << std::endl; } else { std::cout << std::string("Changed to windowed mode at resolution ") << resolution.x << std::string("x") << resolution.y << std::endl; } // Save settings settings.set("fullscreen", fullscreen); saveUserSettings(); // Resize UI resizeUI(); // Notify window observers inputManager->update(); } // index: 0 = off, 1 = on void Application::selectVSyncMode(std::size_t index) { swapInterval = (index == 0) ? 0 : 1; if (swapInterval == 1) { std::cout << std::string("Enabling vertical sync... "); } else { std::cout << std::string("Disabling vertical sync... "); } if (SDL_GL_SetSwapInterval(swapInterval) != 0) { std::cout << std::string("failed: \"") << SDL_GetError() << std::string("\"") << std::endl; swapInterval = SDL_GL_GetSwapInterval(); } else { std::cout << std::string("success") << std::endl; } // Save settings settings.set("swap_interval", swapInterval); saveUserSettings(); } void Application::selectLanguage(std::size_t index) { // Set language index languageIndex = index; // Clear strings strings.clear(); // Load strings std::string stringsFile = appDataPath + std::string("strings/") + languages[languageIndex] + std::string(".txt"); std::cout << std::string("Loading strings from \"") << stringsFile << std::string("\"... "); if (!strings.load(stringsFile)) { std::cout << std::string("failed") << std::endl; } else { std::cout << std::string("success") << std::endl; } // Save settings settings.set("language", languages[languageIndex]); saveUserSettings(); // Change window title std::string title; strings.get("title", &title); SDL_SetWindowTitle(window, title.c_str()); // Restring UI restringUI(); } void Application::bindControl(Control* control) { bindingControl = control; bindingControl->unbind(); }