💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

2318 lines
77 KiB

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 <algorithm>
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <sstream>
#include <SDL2/SDL.h>
#include <dirent.h>
#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<float>(desktopDisplayMode.w);
desktopResolution.y = static_cast<float>(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<float>::max();
float minFullscreenResolutionDistance = std::numeric_limits<float>::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<int>(resolution.x), static_cast<int>(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<float>(frameTimer.microseconds().count()) / 1000.0f;
frameTimer.reset();
// Add frame time (in seconds) to accumulator
accumulator += std::min<float>(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<SDL_Scancode>(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<float>(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<int>(resolution.x), static_cast<int>(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<int>(resolution.x), static_cast<int>(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<GLsizei>(resolution.x), static_cast<GLsizei>(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<GLsizei>(resolution.x), static_cast<GLsizei>(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<int>(resolution.x);
framebufferARenderTarget.height = static_cast<int>(resolution.y);
framebufferARenderTarget.framebuffer = framebufferA;
// Setup texture class
framebufferAColorTexture.setTextureID(framebufferAColorTextureID);
framebufferAColorTexture.setWidth(static_cast<int>(resolution.x));
framebufferAColorTexture.setHeight(static_cast<int>(resolution.y));
}
{
// Generate color texture
glGenTextures(1, &framebufferBColorTextureID);
glBindTexture(GL_TEXTURE_2D, framebufferBColorTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, static_cast<GLsizei>(resolution.x), static_cast<GLsizei>(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<int>(resolution.x);
framebufferBRenderTarget.height = static_cast<int>(resolution.y);
framebufferBRenderTarget.framebuffer = framebufferB;
// Setup texture class
framebufferBColorTexture.setTextureID(framebufferBColorTextureID);
framebufferBColorTexture.setWidth(static_cast<int>(resolution.x));
framebufferBColorTexture.setHeight(static_cast<int>(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<int>(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<int>(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<int>(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<Vector4>(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<Vector4>(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<Vector4>(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<Vector4>(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<float>(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<float>(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<Vector4>(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<float>(EaseFunction::OUT_CUBIC, 0.0f, 1.0f, 0.0f, 1.0f);
tweener->addTween(splashHangTween);
splashFadeOutTween = new Tween<Vector4>(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<Vector4>(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<Vector4>(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<Vector4>(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<Vector4>(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<Vector4>(EaseFunction::OUT_QUINT, 0.0f, menuFadeInDuration, menuFadeInStartColor, menuFadeInDeltaColor);
tweener->addTween(menuFadeInTween);
menuActivateTween = new Tween<float>(EaseFunction::OUT_QUINT, 0.0f, 0.01f, 0.0f, 0.0f);
tweener->addTween(menuActivateTween);
menuFadeOutTween = new Tween<Vector4>(EaseFunction::OUT_QUINT, 0.0f, menuFadeOutDuration, menuFadeOutStartColor, menuFadeOutDeltaColor);
tweener->addTween(menuFadeOutTween);
// Camera translation tween
cameraTranslationTween = new Tween<Vector3>(EaseFunction::OUT_CUBIC, 0.0f, 0.0f, Vector3(0.0f), Vector3(0.0f));
tweener->addTween(cameraTranslationTween);
// Tool tweens
forcepsSwoopTween = new Tween<float>(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<int>(resolution.x);
defaultRenderTarget.height = static_cast<int>(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<int>(resolution.x);
defaultRenderTarget.height = static_cast<int>(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<std::string, std::u32string> stringMap;
const std::map<std::string, std::string>* 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<char32_t> 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<UnicodeRange> 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", &copyrightFontBasename);
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<int>(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<int>(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<int>(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<std::codecvt_utf8<char32_t>, 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<ShaderTexture2D*>(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<int>(world + 1), static_cast<int>(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<int>(resolution.x), static_cast<int>(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<int>(resolution.x), static_cast<int>(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<int>(resolution.x), static_cast<int>(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<int>(resolution.x), static_cast<int>(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();
}