Merge remote-tracking branch 'upstream/main'
42
.github/workflows/appimage-wayland.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: C/C++ AppImage (wayland)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build-appimage:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libglfw3-wayland libglfw3-dev libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev cmake squashfs-tools
|
||||
sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a
|
||||
sudo ln -s /usr/include/luajit-2.1 /usr/include/lua
|
||||
- name: configure
|
||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1
|
||||
- name: build
|
||||
run: cmake --build build -t install
|
||||
- name: Build AppImage
|
||||
uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910
|
||||
env:
|
||||
UPDATE_INFO: gh-releases-zsync|MihailRis|VoxelEngine-Cpp|latest|*x86_64.AppImage.zsync
|
||||
with:
|
||||
recipe: dev/AppImageBuilder.yml
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: AppImage
|
||||
path: './*.AppImage*'
|
||||
7
.github/workflows/appimage.yml
vendored
@ -9,7 +9,12 @@ on:
|
||||
jobs:
|
||||
build-appimage:
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@ -45,7 +45,7 @@ if(MSVC)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /source-charset:UTF-8")
|
||||
else()
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -lstdc++fs
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra
|
||||
# additional warnings
|
||||
-Wformat-nonliteral -Wcast-align
|
||||
-Wpointer-arith -Wundef
|
||||
@ -114,7 +114,7 @@ if(UNIX)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs")
|
||||
endif()
|
||||
|
||||
include_directories(${LUA_INCLUDE_DIR})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "base",
|
||||
"title": "Base",
|
||||
"version": "0.18",
|
||||
"version": "0.19",
|
||||
"description": "basic content package"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 779 B |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 357 B |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.8 KiB |
4
res/layouts/inventory.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<inventory color="#1F1F1FE0">
|
||||
<slots-grid pos="0, 164" rows="1" count="10" sharefunc="inventory_share_func"/>
|
||||
<slots-grid rows="3" start-index="10" count="30" sharefunc="inventory_share_func"/>
|
||||
</inventory>
|
||||
3
res/layouts/inventory.xml.lua
Normal file
@ -0,0 +1,3 @@
|
||||
function inventory_share_func(invid, slotid)
|
||||
inventory.set(invid, slotid, 0, 0)
|
||||
end
|
||||
0
res/modules/document.lua
Normal file
65
res/modules/toml.lua
Normal file
@ -0,0 +1,65 @@
|
||||
-- TOML serialization module
|
||||
local toml = {}
|
||||
|
||||
-- Convert table to TOML
|
||||
function toml.serialize(tb, isinner)
|
||||
local text = ""
|
||||
for k, v in pairs(tb) do
|
||||
local tp = type(v)
|
||||
if tp ~= "table" then
|
||||
text = text..k.." = "
|
||||
if tp == "string" then
|
||||
text = text..string.format("%q", v)
|
||||
else
|
||||
text = text..tostring(v)
|
||||
end
|
||||
text = text.."\n"
|
||||
end
|
||||
end
|
||||
for k, v in pairs(tb) do
|
||||
local tp = type(v)
|
||||
if tp == "table" then
|
||||
if isinner then
|
||||
error("only one level of subtables supported")
|
||||
end
|
||||
text = text.."["..k.."]\n"..toml.serialize(v).."\n"
|
||||
end
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
-- Parse TOML to new table
|
||||
function toml.deserialize(s)
|
||||
local output = {}
|
||||
local current = output
|
||||
local lines = {}
|
||||
for line in string.gmatch(s, "[^\r\n]+") do
|
||||
line = string.gsub(line, "%s+", "")
|
||||
table.insert(lines, line)
|
||||
end
|
||||
for i = 1,#lines do
|
||||
local s = lines[i]
|
||||
if string.sub(s, 1, 1) == "[" then
|
||||
local section = s.sub(s, 2, #s-1)
|
||||
current = {}
|
||||
output[section] = current
|
||||
else
|
||||
for k, v in string.gmatch(s, "(%w+)=(.+)" ) do
|
||||
v = string.gsub(v, "%s+", "")
|
||||
if v.sub(v, 1, 1) == "\"" then
|
||||
current[k] = v.sub(v, 2, #v-1)
|
||||
elseif v == "true" or v == "false" then
|
||||
current[k] = v == "true"
|
||||
end
|
||||
|
||||
local num = tonumber(v)
|
||||
if num ~= nil then
|
||||
current[k] = num
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
return toml
|
||||
@ -37,10 +37,14 @@ function load_script(path, nocache)
|
||||
if not nocache and __cached_scripts[fullpath] ~= nil then
|
||||
return __cached_results[fullpath]
|
||||
end
|
||||
local script = loadfile(fullpath)
|
||||
if script == nil then
|
||||
if not file.isfile(path) then
|
||||
error("script '"..filename.."' not found in '"..packname.."'")
|
||||
end
|
||||
|
||||
local script, err = loadfile(fullpath)
|
||||
if script == nil then
|
||||
error(err)
|
||||
end
|
||||
local result = script()
|
||||
if not nocache then
|
||||
__cached_scripts[fullpath] = script
|
||||
@ -49,6 +53,16 @@ function load_script(path, nocache)
|
||||
return result
|
||||
end
|
||||
|
||||
function require(path)
|
||||
local prefix, file = parse_path(path)
|
||||
return load_script(prefix..":modules/"..file..".lua")
|
||||
end
|
||||
|
||||
function __reset_scripts_cache()
|
||||
__cached_scripts = {}
|
||||
__cached_results = {}
|
||||
end
|
||||
|
||||
function sleep(timesec)
|
||||
local start = time.uptime()
|
||||
while time.uptime() - start < timesec do
|
||||
@ -73,3 +87,50 @@ function dofile(path)
|
||||
end
|
||||
return _dofile(path)
|
||||
end
|
||||
|
||||
function pack.is_installed(packid)
|
||||
return file.isfile(packid..":package.json")
|
||||
end
|
||||
|
||||
vec2_mt = {}
|
||||
function vec2_mt.__tostring(self)
|
||||
return "vec2("..self[1]..", "..self[2]..")"
|
||||
end
|
||||
|
||||
vec3_mt = {}
|
||||
function vec3_mt.__tostring(self)
|
||||
return "vec3("..self[1]..", "..self[2]..", "..self[3]..")"
|
||||
end
|
||||
|
||||
vec4_mt = {}
|
||||
function vec4_mt.__tostring(self)
|
||||
return "vec4("..self[1]..", "..self[2]..", "..self[3]..", "..self[4]..")"
|
||||
end
|
||||
|
||||
color_mt = {}
|
||||
function color_mt.__tostring(self)
|
||||
return "rgba("..self[1]..", "..self[2]..", "..self[3]..", "..self[4]..")"
|
||||
end
|
||||
|
||||
-- class designed for simple UI-nodes access via properties syntax
|
||||
local Element = {}
|
||||
function Element.new(docname, name)
|
||||
return setmetatable({docname=docname, name=name}, {
|
||||
__index=function(self, k)
|
||||
return gui.getattr(self.docname, self.name, k)
|
||||
end,
|
||||
__newindex=function(self, k, v)
|
||||
gui.setattr(self.docname, self.name, k, v)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- the engine automatically creates an instance for every ui document (layout)
|
||||
Document = {}
|
||||
function Document.new(docname)
|
||||
return setmetatable({name=docname}, {
|
||||
__index=function(self, k)
|
||||
return Element.new(self.name, k)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
-- use for engine development tests
|
||||
-- must be empty in release
|
||||
-- must not be modified by content-packs
|
||||
@ -26,6 +26,7 @@ uniform float u_torchlightDistance;
|
||||
void main(){
|
||||
vec3 pos3d = (u_model * vec4(v_position, 1.0)).xyz-u_cameraPos.xyz;
|
||||
vec4 modelpos = u_model * vec4(v_position, 1.0);
|
||||
modelpos.y -= pow(length(pos3d.xz)*0.002, 3.0);
|
||||
vec4 viewmodelpos = u_view * modelpos;
|
||||
vec4 decomp_light = decompress_light(v_light);
|
||||
vec3 light = decomp_light.rgb;
|
||||
|
||||
@ -34,6 +34,7 @@ settings.Load Speed=Хуткасць Загрузкі
|
||||
settings.Fog Curve=Крывая Туману
|
||||
settings.Backlight=Падсветка
|
||||
settings.V-Sync=Вертыкальная Сінхранізацыя
|
||||
settings.Camera Shaking=Труска Камеры
|
||||
|
||||
settings.FOV=Поле Зроку
|
||||
settings.Mouse Sensitivity=Адчувальнасць Мышы
|
||||
|
||||
@ -13,8 +13,8 @@ menu.New World = Uusi Maailma
|
||||
menu.Quit=Poistu
|
||||
menu.Continue=Jatka
|
||||
menu.Save and Quit to Menu=Tallenna ja poistu valikkoon
|
||||
menu.missing-content=Puuttuu jotkut lisäosat!
|
||||
menu.Content Error=Sisältövirhe!
|
||||
menu.missing-content=Puuttuu lisäosia!
|
||||
menu.Content Error=Lisäosa virhe!
|
||||
menu.Controls=Ohjaus
|
||||
menu.Back to Main Menu=Takaisin Valikoon
|
||||
menu.Settings=Asetukset
|
||||
|
||||
@ -34,6 +34,7 @@ settings.Load Speed=Скорость Загрузки
|
||||
settings.Fog Curve=Кривая Тумана
|
||||
settings.Backlight=Подсветка
|
||||
settings.V-Sync=Вертикальная Синхронизация
|
||||
settings.Camera Shaking=Тряска Камеры
|
||||
|
||||
settings.FOV=Поле Зрения
|
||||
settings.Mouse Sensitivity=Чувствительность Мыши
|
||||
|
||||
BIN
res/textures/gui/crosshair.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
res/textures/gui/error.png
Normal file
|
After Width: | Height: | Size: 702 B |
BIN
res/textures/gui/warning.png
Normal file
|
After Width: | Height: | Size: 658 B |
@ -4,6 +4,8 @@
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Font.h"
|
||||
#include "../frontend/UiDocument.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
Assets::~Assets() {
|
||||
}
|
||||
@ -62,6 +64,17 @@ void Assets::store(const TextureAnimation& animation) {
|
||||
animations.emplace_back(animation);
|
||||
}
|
||||
|
||||
UiDocument* Assets::getLayout(std::string name) const {
|
||||
auto found = layouts.find(name);
|
||||
if (found == layouts.end())
|
||||
return nullptr;
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
void Assets::store(UiDocument* layout, std::string name) {
|
||||
layouts[name].reset(layout);
|
||||
}
|
||||
|
||||
void Assets::extend(const Assets& assets) {
|
||||
for (auto entry : assets.textures) {
|
||||
textures[entry.first] = entry.second;
|
||||
@ -75,6 +88,9 @@ void Assets::extend(const Assets& assets) {
|
||||
for (auto entry : assets.atlases) {
|
||||
atlases[entry.first] = entry.second;
|
||||
}
|
||||
for (auto entry : assets.layouts) {
|
||||
layouts[entry.first] = entry.second;
|
||||
}
|
||||
animations.clear();
|
||||
for (auto entry : assets.animations) {
|
||||
animations.emplace_back(entry);
|
||||
|
||||
@ -12,12 +12,20 @@ class Texture;
|
||||
class Shader;
|
||||
class Font;
|
||||
class Atlas;
|
||||
class UiDocument;
|
||||
|
||||
struct LayoutCfg {
|
||||
int env;
|
||||
|
||||
LayoutCfg(int env) : env(env) {}
|
||||
};
|
||||
|
||||
class Assets {
|
||||
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
|
||||
std::unordered_map<std::string, std::shared_ptr<Shader>> shaders;
|
||||
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
|
||||
std::unordered_map<std::string, std::shared_ptr<Atlas>> atlases;
|
||||
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
|
||||
std::vector<TextureAnimation> animations;
|
||||
public:
|
||||
~Assets();
|
||||
@ -36,6 +44,9 @@ public:
|
||||
const std::vector<TextureAnimation>& getAnimations();
|
||||
void store(const TextureAnimation& animation);
|
||||
|
||||
UiDocument* getLayout(std::string name) const;
|
||||
void store(UiDocument* layout, std::string name);
|
||||
|
||||
void extend(const Assets& assets);
|
||||
};
|
||||
|
||||
|
||||
@ -8,20 +8,24 @@
|
||||
|
||||
#include "../constants.h"
|
||||
#include "../files/engine_paths.h"
|
||||
|
||||
using std::filesystem::path;
|
||||
using std::unique_ptr;
|
||||
#include "../content/Content.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
|
||||
: assets(assets), paths(paths) {
|
||||
addLoader(ASSET_SHADER, assetload::shader);
|
||||
addLoader(ASSET_TEXTURE, assetload::texture);
|
||||
addLoader(ASSET_FONT, assetload::font);
|
||||
addLoader(ASSET_ATLAS, assetload::atlas);
|
||||
addLoader(ASSET_LAYOUT, assetload::layout);
|
||||
}
|
||||
|
||||
void AssetsLoader::addLoader(int tag, aloader_func func) {
|
||||
loaders[tag] = func;
|
||||
}
|
||||
|
||||
void AssetsLoader::add(int tag, const std::string filename, const std::string alias) {
|
||||
entries.push(aloader_entry{ tag, filename, alias });
|
||||
void AssetsLoader::add(int tag, const std::string filename, const std::string alias, std::shared_ptr<void> settings) {
|
||||
entries.push(aloader_entry{ tag, filename, alias, settings});
|
||||
}
|
||||
|
||||
bool AssetsLoader::hasNext() const {
|
||||
@ -38,32 +42,48 @@ bool AssetsLoader::loadNext() {
|
||||
return false;
|
||||
}
|
||||
aloader_func loader = found->second;
|
||||
bool status = loader(assets, paths, entry.filename, entry.alias);
|
||||
bool status = loader(*this, assets, paths, entry.filename, entry.alias, entry.config);
|
||||
entries.pop();
|
||||
return status;
|
||||
}
|
||||
|
||||
void AssetsLoader::createDefaults(AssetsLoader& loader) {
|
||||
loader.addLoader(ASSET_SHADER, assetload::shader);
|
||||
loader.addLoader(ASSET_TEXTURE, assetload::texture);
|
||||
loader.addLoader(ASSET_FONT, assetload::font);
|
||||
loader.addLoader(ASSET_ATLAS, assetload::atlas);
|
||||
void addLayouts(int env, const std::string& prefix, const fs::path& folder, AssetsLoader& loader) {
|
||||
if (!fs::is_directory(folder)) {
|
||||
return;
|
||||
}
|
||||
for (auto& entry : fs::directory_iterator(folder)) {
|
||||
const fs::path file = entry.path();
|
||||
if (file.extension().u8string() != ".xml")
|
||||
continue;
|
||||
std::string name = prefix+":"+file.stem().u8string();
|
||||
loader.add(ASSET_LAYOUT, file.u8string(), name, std::make_shared<LayoutCfg>(env));
|
||||
}
|
||||
}
|
||||
|
||||
void AssetsLoader::addDefaults(AssetsLoader& loader, bool allAssets) {
|
||||
if (allAssets) {
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui");
|
||||
void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||
loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/menubg.png", "gui/menubg");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/delete_icon.png", "gui/delete_icon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/warning.png", "gui/warning");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/error.png", "gui/error");
|
||||
if (content) {
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/menubg.png", "gui/menubg");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/delete_icon.png", "gui/delete_icon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/moon.png", "misc/moon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/sun.png", "misc/sun");
|
||||
loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/crosshair.png", "gui/crosshair");
|
||||
|
||||
addLayouts(0, "core", loader.getPaths()->getMainRoot()/fs::path("layouts"), loader);
|
||||
for (auto& pack : content->getPacks()) {
|
||||
auto& info = pack->getInfo();
|
||||
fs::path folder = info.folder / fs::path("layouts");
|
||||
addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader);
|
||||
}
|
||||
}
|
||||
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks");
|
||||
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/items", "items");
|
||||
@ -71,4 +91,4 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, bool allAssets) {
|
||||
|
||||
const ResPaths* AssetsLoader::getPaths() const {
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define ASSETS_ASSETS_LOADER_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
@ -10,16 +11,20 @@ const short ASSET_TEXTURE = 1;
|
||||
const short ASSET_SHADER = 2;
|
||||
const short ASSET_FONT = 3;
|
||||
const short ASSET_ATLAS = 4;
|
||||
const short ASSET_LAYOUT = 5;
|
||||
|
||||
class ResPaths;
|
||||
class Assets;
|
||||
class AssetsLoader;
|
||||
class Content;
|
||||
|
||||
typedef std::function<bool(Assets*, const ResPaths*, const std::string&, const std::string&)> aloader_func;
|
||||
using aloader_func = std::function<bool(AssetsLoader&, Assets*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<void>)>;
|
||||
|
||||
struct aloader_entry {
|
||||
int tag;
|
||||
const std::string filename;
|
||||
const std::string alias;
|
||||
std::shared_ptr<void> config;
|
||||
};
|
||||
|
||||
class AssetsLoader {
|
||||
@ -30,13 +35,18 @@ class AssetsLoader {
|
||||
public:
|
||||
AssetsLoader(Assets* assets, const ResPaths* paths);
|
||||
void addLoader(int tag, aloader_func func);
|
||||
void add(int tag, const std::string filename, const std::string alias);
|
||||
void add(
|
||||
int tag,
|
||||
const std::string filename,
|
||||
const std::string alias,
|
||||
std::shared_ptr<void> settings=nullptr
|
||||
);
|
||||
|
||||
|
||||
bool hasNext() const;
|
||||
bool loadNext();
|
||||
|
||||
static void createDefaults(AssetsLoader& loader);
|
||||
static void addDefaults(AssetsLoader& loader, bool allAssets);
|
||||
static void addDefaults(AssetsLoader& loader, const Content* content);
|
||||
|
||||
const ResPaths* getPaths() const;
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "Assets.h"
|
||||
#include "AssetsLoader.h"
|
||||
#include "../files/files.h"
|
||||
#include "../files/engine_paths.h"
|
||||
#include "../coders/png.h"
|
||||
@ -13,193 +14,233 @@
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Font.h"
|
||||
#include "../graphics/TextureAnimation.h"
|
||||
#include "../frontend/UiDocument.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool assetload::texture(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name) {
|
||||
Texture* texture = png::load_texture(paths->find(filename).string());
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load texture '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(texture, name);
|
||||
return true;
|
||||
bool assetload::texture(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
std::unique_ptr<Texture> texture(
|
||||
png::load_texture(paths->find(filename).u8string())
|
||||
);
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load texture '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(texture.release(), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::shader(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name) {
|
||||
fs::path vertexFile = paths->find(filename+".glslv");
|
||||
fs::path fragmentFile = paths->find(filename+".glslf");
|
||||
bool assetload::shader(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
fs::path vertexFile = paths->find(filename+".glslv");
|
||||
fs::path fragmentFile = paths->find(filename+".glslf");
|
||||
|
||||
std::string vertexSource = files::read_string(vertexFile);
|
||||
std::string fragmentSource = files::read_string(fragmentFile);
|
||||
std::string vertexSource = files::read_string(vertexFile);
|
||||
std::string fragmentSource = files::read_string(fragmentFile);
|
||||
|
||||
Shader* shader = Shader::loadShader(
|
||||
vertexFile.string(),
|
||||
fragmentFile.string(),
|
||||
vertexSource, fragmentSource);
|
||||
Shader* shader = Shader::loadShader(
|
||||
vertexFile.string(),
|
||||
fragmentFile.string(),
|
||||
vertexSource, fragmentSource);
|
||||
|
||||
if (shader == nullptr) {
|
||||
std::cerr << "failed to load shader '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(shader, name);
|
||||
return true;
|
||||
if (shader == nullptr) {
|
||||
std::cerr << "failed to load shader '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(shader, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) {
|
||||
// png is only supported format
|
||||
if (file.extension() != ".png")
|
||||
return false;
|
||||
std::string name = file.stem().string();
|
||||
// skip duplicates
|
||||
if (atlas.has(name)) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<ImageData> image(png::load_image(file.string()));
|
||||
if (image == nullptr) {
|
||||
std::cerr << "could not to load " << file.string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
image->fixAlphaColor();
|
||||
atlas.add(name, image.release());
|
||||
// png is only supported format
|
||||
if (file.extension() != ".png")
|
||||
return false;
|
||||
std::string name = file.stem().string();
|
||||
// skip duplicates
|
||||
if (atlas.has(name)) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<ImageData> image(png::load_image(file.string()));
|
||||
if (image == nullptr) {
|
||||
std::cerr << "could not to load " << file.string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
image->fixAlphaColor();
|
||||
atlas.add(name, image.release());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::atlas(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name) {
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
Atlas* atlas = builder.build(2);
|
||||
assets->store(atlas, name);
|
||||
for (const auto& file : builder.getNames()) {
|
||||
assetload::animation(assets, paths, "textures", file, atlas);
|
||||
}
|
||||
return true;
|
||||
bool assetload::atlas(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
Atlas* atlas = builder.build(2);
|
||||
assets->store(atlas, name);
|
||||
for (const auto& file : builder.getNames()) {
|
||||
assetload::animation(assets, paths, "textures", file, atlas);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::font(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name) {
|
||||
std::vector<Texture*> pages;
|
||||
for (size_t i = 0; i <= 4; i++) {
|
||||
bool assetload::font(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
for (size_t i = 0; i <= 4; i++) {
|
||||
std::string name = filename + "_" + std::to_string(i) + ".png";
|
||||
name = paths->find(name).string();
|
||||
Texture* texture = png::load_texture(name);
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load bitmap font '" << name;
|
||||
std::unique_ptr<Texture> texture (png::load_texture(name));
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load bitmap font '" << name;
|
||||
std::cerr << "' (missing page " << std::to_string(i) << ")";
|
||||
std::cerr << std::endl;
|
||||
return false;
|
||||
}
|
||||
pages.push_back(texture);
|
||||
}
|
||||
Font* font = new Font(pages, pages[0]->height / 16);
|
||||
assets->store(font, name);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
pages.push_back(std::move(texture));
|
||||
}
|
||||
int res = pages[0]->height / 16;
|
||||
assets->store(new Font(std::move(pages), res, 4), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::layout(
|
||||
AssetsLoader& loader,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string file,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> config
|
||||
) {
|
||||
try {
|
||||
LayoutCfg* cfg = reinterpret_cast<LayoutCfg*>(config.get());
|
||||
auto document = UiDocument::read(loader, cfg->env, name, file);
|
||||
assets->store(document.release(), name);
|
||||
return true;
|
||||
} catch (const parsing_error& err) {
|
||||
std::cerr << "failed to parse layout XML '" << file << "'" << std::endl;
|
||||
std::cerr << err.errorLog() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool assetload::animation(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas) {
|
||||
std::string animsDir = directory + "/animations";
|
||||
std::string blocksDir = directory + "/blocks";
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas) {
|
||||
std::string animsDir = directory + "/animations";
|
||||
std::string blocksDir = directory + "/blocks";
|
||||
|
||||
for (const auto& folder : paths->listdir(animsDir)) {
|
||||
if (!fs::is_directory(folder)) continue;
|
||||
if (folder.filename().string() != name) continue;
|
||||
if (fs::is_empty(folder)) continue;
|
||||
|
||||
AtlasBuilder builder;
|
||||
for (const auto& folder : paths->listdir(animsDir)) {
|
||||
if (!fs::is_directory(folder)) continue;
|
||||
if (folder.filename().string() != name) continue;
|
||||
if (fs::is_empty(folder)) continue;
|
||||
|
||||
AtlasBuilder builder;
|
||||
appendAtlas(builder, paths->find(blocksDir + "/" + name + ".png"));
|
||||
|
||||
std::string animFile = folder.string() + "/animation.json";
|
||||
std::string animFile = folder.string() + "/animation.json";
|
||||
|
||||
std::vector<std::pair<std::string, float>> frameList;
|
||||
std::vector<std::pair<std::string, float>> frameList;
|
||||
|
||||
if (fs::exists(animFile)) {
|
||||
auto root = files::read_json(animFile);
|
||||
if (fs::exists(animFile)) {
|
||||
auto root = files::read_json(animFile);
|
||||
|
||||
auto frameArr = root->list("frames");
|
||||
auto frameArr = root->list("frames");
|
||||
|
||||
Frame temp;
|
||||
float frameDuration = DEFAULT_FRAME_DURATION;
|
||||
std::string frameName;
|
||||
float frameDuration = DEFAULT_FRAME_DURATION;
|
||||
std::string frameName;
|
||||
|
||||
if (frameArr) {
|
||||
for (size_t i = 0; i < frameArr->size(); i++) {
|
||||
auto currentFrame = frameArr->list(i);
|
||||
if (frameArr) {
|
||||
for (size_t i = 0; i < frameArr->size(); i++) {
|
||||
auto currentFrame = frameArr->list(i);
|
||||
|
||||
frameName = currentFrame->str(0);
|
||||
if (currentFrame->size() > 1) frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
|
||||
frameName = currentFrame->str(0);
|
||||
if (currentFrame->size() > 1)
|
||||
frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
|
||||
|
||||
frameList.emplace_back(frameName, frameDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
|
||||
if (!frameList.empty()) {
|
||||
bool contains = false;
|
||||
for (const auto& elem : frameList) {
|
||||
if (file.stem() == elem.first) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) continue;
|
||||
}
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
frameList.emplace_back(frameName, frameDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
|
||||
if (!frameList.empty()) {
|
||||
bool contains = false;
|
||||
for (const auto& elem : frameList) {
|
||||
if (file.stem() == elem.first) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) continue;
|
||||
}
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
|
||||
Atlas* srcAtlas = builder.build(2);
|
||||
std::unique_ptr<Atlas> srcAtlas (builder.build(2));
|
||||
|
||||
Texture* srcTex = srcAtlas->getTexture();
|
||||
Texture* dstTex = dstAtlas->getTexture();
|
||||
Texture* srcTex = srcAtlas->getTexture();
|
||||
Texture* dstTex = dstAtlas->getTexture();
|
||||
|
||||
TextureAnimation animation(srcTex, dstTex);
|
||||
Frame frame;
|
||||
UVRegion region = dstAtlas->get(name);
|
||||
TextureAnimation animation(srcTex, dstTex);
|
||||
Frame frame;
|
||||
UVRegion region = dstAtlas->get(name);
|
||||
|
||||
frame.dstPos = glm::ivec2(region.u1 * dstTex->width, region.v1 * dstTex->height);
|
||||
frame.size = glm::ivec2(region.u2 * dstTex->width, region.v2 * dstTex->height) - frame.dstPos;
|
||||
frame.dstPos = glm::ivec2(region.u1 * dstTex->width, region.v1 * dstTex->height);
|
||||
frame.size = glm::ivec2(region.u2 * dstTex->width, region.v2 * dstTex->height) - frame.dstPos;
|
||||
|
||||
if (frameList.empty()) {
|
||||
for (const auto& elem : builder.getNames()) {
|
||||
region = srcAtlas->get(elem);
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& elem : frameList) {
|
||||
if (!srcAtlas->has(elem.first)) {
|
||||
std::cerr << "Unknown frame name: " << elem.first << std::endl;
|
||||
continue;
|
||||
}
|
||||
region = srcAtlas->get(elem.first);
|
||||
frame.duration = elem.second;
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
if (frameList.empty()) {
|
||||
for (const auto& elem : builder.getNames()) {
|
||||
region = srcAtlas->get(elem);
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& elem : frameList) {
|
||||
if (!srcAtlas->has(elem.first)) {
|
||||
std::cerr << "Unknown frame name: " << elem.first << std::endl;
|
||||
continue;
|
||||
}
|
||||
region = srcAtlas->get(elem.first);
|
||||
frame.duration = elem.second;
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
assets->store(srcAtlas, name + "_animation");
|
||||
assets->store(animation);
|
||||
assets->store(srcAtlas.release(), name + "_animation");
|
||||
assets->store(animation);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2,33 +2,62 @@
|
||||
#define ASSETS_ASSET_LOADERS_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class ResPaths;
|
||||
class Assets;
|
||||
class AssetsLoader;
|
||||
class Atlas;
|
||||
|
||||
namespace assetload {
|
||||
bool texture(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name);
|
||||
bool shader(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name);
|
||||
bool atlas(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name);
|
||||
bool font(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name);
|
||||
bool animation(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas);
|
||||
bool texture(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool shader(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool atlas(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool font(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool layout(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string file,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
|
||||
bool animation(
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas
|
||||
);
|
||||
}
|
||||
|
||||
#endif // ASSETS_ASSET_LOADERS_H_
|
||||
@ -15,21 +15,23 @@
|
||||
#endif
|
||||
|
||||
bool is_big_endian(void){
|
||||
union {
|
||||
uint32_t i;
|
||||
char c[4];
|
||||
} bint = {0x01020304};
|
||||
uint32_t ui32_v = 0x01020304;
|
||||
char bytes[sizeof(uint32_t)];
|
||||
std::memcpy(bytes, &ui32_v, sizeof(uint32_t));
|
||||
|
||||
return bint.c[0] == 1;
|
||||
return bytes[0] == 1;
|
||||
}
|
||||
|
||||
std::int32_t convert_to_int(char* buffer, std::size_t len){
|
||||
std::int32_t a = 0;
|
||||
if(!is_big_endian())
|
||||
if (!is_big_endian()) {
|
||||
std::memcpy(&a, buffer, len);
|
||||
else
|
||||
for(std::size_t i = 0; i < len; ++i)
|
||||
}
|
||||
else {
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
reinterpret_cast<char*>(&a)[3 - i] = buffer[i];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,11 @@ static void to_binary(ByteBuilder& builder, const Value* value) {
|
||||
static List* array_from_binary(ByteReader& reader);
|
||||
static Map* object_from_binary(ByteReader& reader);
|
||||
|
||||
std::vector<ubyte> json::to_binary(const Map* obj) {
|
||||
std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
|
||||
if (compress) {
|
||||
auto bytes = to_binary(obj, false);
|
||||
return gzip::compress(bytes.data(), bytes.size());
|
||||
}
|
||||
ByteBuilder builder;
|
||||
// type byte
|
||||
builder.put(BJSON_TYPE_DOCUMENT);
|
||||
|
||||
@ -21,7 +21,7 @@ namespace json {
|
||||
const int BJSON_TYPE_NULL = 0xC;
|
||||
const int BJSON_TYPE_CDOCUMENT = 0x1F;
|
||||
|
||||
extern std::vector<ubyte> to_binary(const dynamic::Map* obj);
|
||||
extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false);
|
||||
extern std::unique_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);
|
||||
}
|
||||
|
||||
|
||||
@ -121,21 +121,21 @@ void ByteReader::checkMagic(const char* data, size_t size) {
|
||||
|
||||
ubyte ByteReader::get() {
|
||||
if (pos == size) {
|
||||
throw std::underflow_error("buffer underflow");
|
||||
throw std::runtime_error("buffer underflow");
|
||||
}
|
||||
return data[pos++];
|
||||
}
|
||||
|
||||
ubyte ByteReader::peek() {
|
||||
if (pos == size) {
|
||||
throw std::underflow_error("buffer underflow");
|
||||
throw std::runtime_error("buffer underflow");
|
||||
}
|
||||
return data[pos];
|
||||
}
|
||||
|
||||
int16_t ByteReader::getInt16() {
|
||||
if (pos+2 > size) {
|
||||
throw std::underflow_error("unexpected end");
|
||||
throw std::runtime_error("buffer underflow");
|
||||
}
|
||||
pos += 2;
|
||||
return (static_cast<int16_t>(data[pos - 1]) << 8) |
|
||||
@ -144,7 +144,7 @@ int16_t ByteReader::getInt16() {
|
||||
|
||||
int32_t ByteReader::getInt32() {
|
||||
if (pos+4 > size) {
|
||||
throw std::underflow_error("unexpected end");
|
||||
throw std::runtime_error("buffer underflow");
|
||||
}
|
||||
pos += 4;
|
||||
return (static_cast<int32_t>(data[pos - 1]) << 24) |
|
||||
@ -155,7 +155,7 @@ int32_t ByteReader::getInt32() {
|
||||
|
||||
int64_t ByteReader::getInt64() {
|
||||
if (pos+8 > size) {
|
||||
throw std::underflow_error("unexpected end");
|
||||
throw std::runtime_error("buffer underflow");
|
||||
}
|
||||
pos += 8;
|
||||
return (static_cast<int64_t>(data[pos - 1]) << 56) |
|
||||
@ -191,7 +191,7 @@ const char* ByteReader::getCString() {
|
||||
std::string ByteReader::getString() {
|
||||
uint32_t length = (uint32_t)getInt32();
|
||||
if (pos+length > size) {
|
||||
throw std::underflow_error("unexpected end");
|
||||
throw std::runtime_error("buffer underflow");
|
||||
}
|
||||
pos += length;
|
||||
return std::string(reinterpret_cast<const char*>(data+pos-length), length);
|
||||
@ -200,3 +200,11 @@ std::string ByteReader::getString() {
|
||||
bool ByteReader::hasNext() const {
|
||||
return pos < size;
|
||||
}
|
||||
|
||||
const ubyte* ByteReader::pointer() const {
|
||||
return data + pos;
|
||||
}
|
||||
|
||||
void ByteReader::skip(size_t n) {
|
||||
pos += n;
|
||||
}
|
||||
|
||||
@ -71,6 +71,9 @@ public:
|
||||
const char* getCString();
|
||||
std::string getString();
|
||||
bool hasNext() const;
|
||||
|
||||
const ubyte* pointer() const;
|
||||
void skip(size_t n);
|
||||
};
|
||||
|
||||
#endif // CODERS_BYTE_UTILS_H_
|
||||
|
||||
@ -4,19 +4,6 @@
|
||||
#include <stdexcept>
|
||||
#include <math.h>
|
||||
|
||||
inline int char2int(int c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return 10 + c - 'a';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return 10 + c - 'A';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline double power(double base, int64_t power) {
|
||||
double result = 1.0;
|
||||
for (int64_t i = 0; i < power; i++) {
|
||||
@ -98,6 +85,18 @@ void BasicParser::skipWhitespace() {
|
||||
}
|
||||
}
|
||||
|
||||
void BasicParser::skip(size_t n) {
|
||||
n = std::min(n, source.length()-pos);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char next = source[pos++];
|
||||
if (next == '\n') {
|
||||
line++;
|
||||
linestart = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BasicParser::skipLine() {
|
||||
while (hasNext()) {
|
||||
if (source[pos] == '\n') {
|
||||
@ -110,10 +109,28 @@ void BasicParser::skipLine() {
|
||||
}
|
||||
}
|
||||
|
||||
bool BasicParser::skipTo(const std::string& substring) {
|
||||
size_t idx = source.find(substring, pos);
|
||||
if (idx == std::string::npos) {
|
||||
skip(source.length()-pos);
|
||||
return false;
|
||||
} else {
|
||||
skip(idx-pos);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BasicParser::hasNext() {
|
||||
return pos < source.length();
|
||||
}
|
||||
|
||||
bool BasicParser::isNext(const std::string& substring) {
|
||||
if (source.length() - pos < substring.length()) {
|
||||
return false;
|
||||
}
|
||||
return source.substr(pos, substring.length()) == substring;
|
||||
}
|
||||
|
||||
char BasicParser::nextChar() {
|
||||
if (!hasNext()) {
|
||||
throw error("unexpected end");
|
||||
@ -129,6 +146,17 @@ void BasicParser::expect(char expected) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
void BasicParser::expect(const std::string& substring) {
|
||||
if (substring.empty())
|
||||
return;
|
||||
for (uint i = 0; i < substring.length(); i++) {
|
||||
if (source.length() <= pos + i || source[pos+i] != substring[i]) {
|
||||
throw error(escape_string(substring)+" expected");
|
||||
}
|
||||
}
|
||||
pos += substring.length();
|
||||
}
|
||||
|
||||
void BasicParser::expectNewLine() {
|
||||
while (hasNext()) {
|
||||
char next = source[pos];
|
||||
@ -145,6 +173,10 @@ void BasicParser::expectNewLine() {
|
||||
}
|
||||
}
|
||||
|
||||
void BasicParser::goBack() {
|
||||
if (pos) pos--;
|
||||
}
|
||||
|
||||
char BasicParser::peek() {
|
||||
skipWhitespace();
|
||||
if (pos >= source.length()) {
|
||||
@ -171,7 +203,7 @@ std::string BasicParser::parseName() {
|
||||
|
||||
int64_t BasicParser::parseSimpleInt(int base) {
|
||||
char c = peek();
|
||||
int index = char2int(c);
|
||||
int index = hexchar2int(c);
|
||||
if (index == -1 || index >= base) {
|
||||
throw error("invalid number literal");
|
||||
}
|
||||
@ -182,7 +214,7 @@ int64_t BasicParser::parseSimpleInt(int base) {
|
||||
while (c == '_') {
|
||||
c = source[++pos];
|
||||
}
|
||||
index = char2int(c);
|
||||
index = hexchar2int(c);
|
||||
if (index == -1 || index >= base) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -41,6 +41,19 @@ inline bool is_identifier_part(int c) {
|
||||
return is_identifier_start(c) || is_digit(c);
|
||||
}
|
||||
|
||||
inline int hexchar2int(int c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return 10 + c - 'a';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return 10 + c - 'A';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern std::string escape_string(std::string s);
|
||||
|
||||
class parsing_error : public std::runtime_error {
|
||||
@ -70,12 +83,17 @@ protected:
|
||||
uint linestart = 0;
|
||||
|
||||
virtual void skipWhitespace();
|
||||
void skip(size_t n);
|
||||
void skipLine();
|
||||
bool skipTo(const std::string& substring);
|
||||
void expect(char expected);
|
||||
void expect(const std::string& substring);
|
||||
char peek();
|
||||
char nextChar();
|
||||
bool hasNext();
|
||||
bool isNext(const std::string& substring);
|
||||
void expectNewLine();
|
||||
void goBack();
|
||||
|
||||
std::string parseName();
|
||||
int64_t parseSimpleInt(int base);
|
||||
|
||||
@ -34,7 +34,7 @@ std::vector<ubyte> gzip::compress(const ubyte* src, size_t size) {
|
||||
|
||||
std::vector<ubyte> gzip::decompress(const ubyte* src, size_t size) {
|
||||
// getting uncompressed data length from gzip footer
|
||||
size_t decompressed_size = *(uint32_t*)(src+size-4);
|
||||
size_t decompressed_size = *reinterpret_cast<const uint32_t*>(src+size-4);
|
||||
std::vector<ubyte> buffer;
|
||||
buffer.resize(decompressed_size);
|
||||
|
||||
|
||||
425
src/coders/xml.cpp
Normal file
@ -0,0 +1,425 @@
|
||||
#include "xml.h"
|
||||
|
||||
#include <charconv>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include "../util/stringutil.h"
|
||||
|
||||
using namespace xml;
|
||||
|
||||
Attribute::Attribute(std::string name, std::string text)
|
||||
: name(name),
|
||||
text(text) {
|
||||
}
|
||||
|
||||
const std::string& Attribute::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
const std::string& Attribute::getText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
int64_t Attribute::asInt() const {
|
||||
return std::stoll(text);
|
||||
}
|
||||
|
||||
double Attribute::asFloat() const {
|
||||
return util::parse_double(text);
|
||||
}
|
||||
|
||||
bool Attribute::asBool() const {
|
||||
return text == "true" || text == "1";
|
||||
}
|
||||
|
||||
/* Read 2d vector formatted `x,y`*/
|
||||
glm::vec2 Attribute::asVec2() const {
|
||||
size_t pos = text.find(',');
|
||||
if (pos == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec2 value "+escape_string(text));
|
||||
}
|
||||
return glm::vec2(
|
||||
util::parse_double(text, 0, pos),
|
||||
util::parse_double(text, pos+1, text.length()-pos-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read 3d vector formatted `x,y,z`*/
|
||||
glm::vec3 Attribute::asVec3() const {
|
||||
size_t pos1 = text.find(',');
|
||||
if (pos1 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec3 value "+escape_string(text));
|
||||
}
|
||||
size_t pos2 = text.find(',', pos1+1);
|
||||
if (pos2 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec3 value "+escape_string(text));
|
||||
}
|
||||
return glm::vec3(
|
||||
util::parse_double(text, 0, pos1),
|
||||
util::parse_double(text, pos1+1, pos2),
|
||||
util::parse_double(text, pos2+1, text.length()-pos2-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read 4d vector formatted `x,y,z,w`*/
|
||||
glm::vec4 Attribute::asVec4() const {
|
||||
size_t pos1 = text.find(',');
|
||||
if (pos1 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(text));
|
||||
}
|
||||
size_t pos2 = text.find(',', pos1+1);
|
||||
if (pos2 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(text));
|
||||
}
|
||||
size_t pos3 = text.find(',', pos2+1);
|
||||
if (pos3 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(text));
|
||||
}
|
||||
return glm::vec4(
|
||||
util::parse_double(text, 0, pos1),
|
||||
util::parse_double(text, pos1+1, pos2-pos1-1),
|
||||
util::parse_double(text, pos2+1, pos3-pos2-1),
|
||||
util::parse_double(text, pos3+1, text.length()-pos3-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read RGBA color. Supported formats:
|
||||
- "#RRGGBB" or "#RRGGBBAA" hex color */
|
||||
glm::vec4 Attribute::asColor() const {
|
||||
if (text[0] == '#') {
|
||||
if (text.length() != 7 && text.length() != 9) {
|
||||
throw std::runtime_error("#RRGGBB or #RRGGBBAA required");
|
||||
}
|
||||
int a = 255;
|
||||
int r = (hexchar2int(text[1]) << 4) | hexchar2int(text[2]);
|
||||
int g = (hexchar2int(text[3]) << 4) | hexchar2int(text[4]);
|
||||
int b = (hexchar2int(text[5]) << 4) | hexchar2int(text[6]);
|
||||
if (text.length() == 9) {
|
||||
a = (hexchar2int(text[7]) << 4) | hexchar2int(text[8]);
|
||||
}
|
||||
return glm::vec4(r / 255.f, g / 255.f, b / 255.f, a / 255.f);
|
||||
} else {
|
||||
throw std::runtime_error("hex colors are only supported");
|
||||
}
|
||||
}
|
||||
|
||||
Node::Node(std::string tag) : tag(tag) {
|
||||
}
|
||||
|
||||
void Node::add(xmlelement element) {
|
||||
elements.push_back(element);
|
||||
}
|
||||
|
||||
void Node::set(std::string name, std::string text) {
|
||||
attrs[name] = Attribute(name, text);
|
||||
}
|
||||
|
||||
const std::string& Node::getTag() const {
|
||||
return tag;
|
||||
}
|
||||
|
||||
const xmlattribute Node::attr(const std::string& name) const {
|
||||
auto found = attrs.find(name);
|
||||
if (found == attrs.end()) {
|
||||
throw std::runtime_error("element <"+tag+" ...> missing attribute "+name);
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
const xmlattribute Node::attr(const std::string& name, const std::string& def) const {
|
||||
auto found = attrs.find(name);
|
||||
if (found == attrs.end()) {
|
||||
return Attribute(name, def);
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
bool Node::has(const std::string& name) const {
|
||||
auto found = attrs.find(name);
|
||||
return found != attrs.end();
|
||||
}
|
||||
|
||||
xmlelement Node::sub(size_t index) {
|
||||
return elements.at(index);
|
||||
}
|
||||
|
||||
size_t Node::size() const {
|
||||
return elements.size();
|
||||
}
|
||||
|
||||
const std::vector<xmlelement>& Node::getElements() const {
|
||||
return elements;
|
||||
}
|
||||
|
||||
const xmlelements_map& Node::getAttributes() const {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
Document::Document(std::string version, std::string encoding)
|
||||
: version(version),
|
||||
encoding(encoding) {
|
||||
}
|
||||
|
||||
void Document::setRoot(xmlelement element) {
|
||||
this->root = element;
|
||||
}
|
||||
|
||||
xmlelement Document::getRoot() const {
|
||||
return root;
|
||||
}
|
||||
|
||||
const std::string& Document::getVersion() const {
|
||||
return version;
|
||||
}
|
||||
|
||||
const std::string& Document::getEncoding() const {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
Parser::Parser(std::string filename, std::string source)
|
||||
: BasicParser(filename, source) {
|
||||
}
|
||||
|
||||
xmlelement Parser::parseOpenTag() {
|
||||
std::string tag = parseXMLName();
|
||||
auto node = std::make_shared<Node>(tag);
|
||||
|
||||
char c;
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
c = peek();
|
||||
if (c == '/' || c == '>' || c == '?')
|
||||
break;
|
||||
std::string attrname = parseXMLName();
|
||||
std::string attrtext = "";
|
||||
skipWhitespace();
|
||||
if (peek() == '=') {
|
||||
nextChar();
|
||||
skipWhitespace();
|
||||
expect('"');
|
||||
attrtext = parseString('"');
|
||||
}
|
||||
node->set(attrname, attrtext);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void Parser::parseDeclaration() {
|
||||
std::string version = "1.0";
|
||||
std::string encoding = "UTF-8";
|
||||
expect('<');
|
||||
if (peek() == '?') {
|
||||
nextChar();
|
||||
xmlelement node = parseOpenTag();
|
||||
expect("?>");
|
||||
if (node->getTag() != "xml") {
|
||||
throw error("invalid declaration");
|
||||
}
|
||||
version = node->attr("version", version).getText();
|
||||
encoding = node->attr("encoding", encoding).getText();
|
||||
if (encoding != "utf-8" && encoding != "UTF-8") {
|
||||
throw error("UTF-8 encoding is only supported");
|
||||
}
|
||||
} else {
|
||||
goBack();
|
||||
}
|
||||
document = std::make_shared<Document>(version, encoding);
|
||||
}
|
||||
|
||||
void Parser::parseComment() {
|
||||
expect("!--");
|
||||
if (skipTo("-->")) {
|
||||
skip(3);
|
||||
} else {
|
||||
throw error("comment close missing");
|
||||
}
|
||||
}
|
||||
|
||||
std::string Parser::parseText() {
|
||||
size_t start = pos;
|
||||
while (hasNext()) {
|
||||
char c = peek();
|
||||
if (c == '<') {
|
||||
break;
|
||||
}
|
||||
nextChar();
|
||||
}
|
||||
return source.substr(start, pos-start);
|
||||
}
|
||||
|
||||
inline bool is_xml_identifier_start(char c) {
|
||||
return is_identifier_start(c) || c == ':';
|
||||
}
|
||||
|
||||
inline bool is_xml_identifier_part(char c) {
|
||||
return is_identifier_part(c) || c == '-' || c == '.' || c == ':';
|
||||
}
|
||||
|
||||
std::string Parser::parseXMLName() {
|
||||
char c = peek();
|
||||
if (!is_xml_identifier_start(c)) {
|
||||
throw error("identifier expected");
|
||||
}
|
||||
int start = pos;
|
||||
while (hasNext() && is_xml_identifier_part(source[pos])) {
|
||||
pos++;
|
||||
}
|
||||
return source.substr(start, pos-start);
|
||||
}
|
||||
|
||||
xmlelement Parser::parseElement() {
|
||||
// text element
|
||||
if (peek() != '<') {
|
||||
auto element = std::make_shared<Node>("#");
|
||||
auto text = parseText();
|
||||
util::replaceAll(text, """, "\"");
|
||||
util::replaceAll(text, "'", "'");
|
||||
util::replaceAll(text, "<", "<");
|
||||
util::replaceAll(text, ">", ">");
|
||||
util::replaceAll(text, "&", "&");
|
||||
element->set("#", text);
|
||||
return element;
|
||||
}
|
||||
nextChar();
|
||||
|
||||
// <!--element-->
|
||||
if (peek() == '!') {
|
||||
if (isNext("!DOCTYPE ")) {
|
||||
throw error("XML DTD is not supported yet");
|
||||
}
|
||||
parseComment();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto element = parseOpenTag();
|
||||
char c = nextChar();
|
||||
|
||||
// <element/>
|
||||
if (c == '/') {
|
||||
expect('>');
|
||||
}
|
||||
// <element>...</element>
|
||||
else if (c == '>') {
|
||||
skipWhitespace();
|
||||
while (!isNext("</")) {
|
||||
auto sub = parseElement();
|
||||
if (sub) {
|
||||
element->add(sub);
|
||||
}
|
||||
skipWhitespace();
|
||||
}
|
||||
skip(2);
|
||||
expect(element->getTag());
|
||||
expect('>');
|
||||
}
|
||||
// <element?>
|
||||
else {
|
||||
throw error("invalid syntax");
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
xmldocument Parser::parse() {
|
||||
parseDeclaration();
|
||||
|
||||
xmlelement root = nullptr;
|
||||
while (root == nullptr) {
|
||||
root = parseElement();
|
||||
}
|
||||
document->setRoot(root);
|
||||
return document;
|
||||
}
|
||||
|
||||
xmldocument xml::parse(std::string filename, std::string source) {
|
||||
Parser parser(filename, source);
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
inline void newline(
|
||||
std::stringstream& ss,
|
||||
bool nice,
|
||||
const std::string& indentStr,
|
||||
int indent
|
||||
) {
|
||||
if (!nice)
|
||||
return;
|
||||
ss << '\n';
|
||||
for (int i = 0; i < indent; i++) {
|
||||
ss << indentStr;
|
||||
}
|
||||
}
|
||||
|
||||
static void stringifyElement(
|
||||
std::stringstream& ss,
|
||||
const xmlelement element,
|
||||
bool nice,
|
||||
const std::string& indentStr,
|
||||
int indent
|
||||
) {
|
||||
if (element->isText()) {
|
||||
std::string text = element->attr("#").getText();
|
||||
util::replaceAll(text, "&", "&");
|
||||
util::replaceAll(text, "\"",""");
|
||||
util::replaceAll(text, "'", "'");
|
||||
util::replaceAll(text, "<", "<");
|
||||
util::replaceAll(text, ">", ">");
|
||||
ss << text;
|
||||
return;
|
||||
}
|
||||
const std::string& tag = element->getTag();
|
||||
|
||||
ss << '<' << tag;
|
||||
auto& attrs = element->getAttributes();
|
||||
if (!attrs.empty()) {
|
||||
ss << ' ';
|
||||
int count = 0;
|
||||
for (auto& entry : attrs) {
|
||||
auto attr = entry.second;
|
||||
ss << attr.getName();
|
||||
if (!attr.getText().empty()) {
|
||||
ss << "=" << escape_string(attr.getText());
|
||||
}
|
||||
if (count + 1 < int(attrs.size())) {
|
||||
ss << " ";
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
auto& elements = element->getElements();
|
||||
if (elements.size() == 1 && elements[0]->isText()) {
|
||||
ss << ">";
|
||||
stringifyElement(ss, elements[0], nice, indentStr, indent+1);
|
||||
ss << "</" << tag << ">";
|
||||
return;
|
||||
}
|
||||
if (!elements.empty()) {
|
||||
ss << '>';
|
||||
for (auto& sub : elements) {
|
||||
newline(ss, nice, indentStr, indent+1);
|
||||
stringifyElement(ss, sub, nice, indentStr, indent+1);
|
||||
}
|
||||
newline(ss, nice, indentStr, indent);
|
||||
ss << "</" << tag << ">";
|
||||
|
||||
} else {
|
||||
ss << "/>";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string xml::stringify(
|
||||
const xmldocument document,
|
||||
bool nice,
|
||||
const std::string& indentStr
|
||||
) {
|
||||
std::stringstream ss;
|
||||
|
||||
// XML declaration
|
||||
ss << "<?xml version=\"" << document->getVersion();
|
||||
ss << "\" encoding=\"UTF-8\" ?>";
|
||||
newline(ss, nice, indentStr, 0);
|
||||
|
||||
stringifyElement(ss, document->getRoot(), nice, indentStr, 0);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
139
src/coders/xml.h
Normal file
@ -0,0 +1,139 @@
|
||||
#ifndef CODERS_XML_H_
|
||||
#define CODERS_XML_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "commons.h"
|
||||
|
||||
namespace xml {
|
||||
class Node;
|
||||
class Attribute;
|
||||
class Document;
|
||||
|
||||
typedef Attribute xmlattribute;
|
||||
typedef std::shared_ptr<Node> xmlelement;
|
||||
typedef std::shared_ptr<Document> xmldocument;
|
||||
typedef std::unordered_map<std::string, xmlattribute> xmlelements_map;
|
||||
|
||||
class Attribute {
|
||||
std::string name;
|
||||
std::string text;
|
||||
public:
|
||||
Attribute() {};
|
||||
Attribute(std::string name, std::string text);
|
||||
|
||||
const std::string& getName() const;
|
||||
const std::string& getText() const;
|
||||
int64_t asInt() const;
|
||||
double asFloat() const;
|
||||
bool asBool() const;
|
||||
glm::vec2 asVec2() const;
|
||||
glm::vec3 asVec3() const;
|
||||
glm::vec4 asVec4() const;
|
||||
glm::vec4 asColor() const;
|
||||
};
|
||||
|
||||
/* XML element class. Text element has tag 'text' and attribute 'text' */
|
||||
class Node {
|
||||
std::string tag;
|
||||
std::unordered_map<std::string, xmlattribute> attrs;
|
||||
std::vector<xmlelement> elements;
|
||||
public:
|
||||
Node(std::string tag);
|
||||
|
||||
/* Add sub-element */
|
||||
void add(xmlelement element);
|
||||
|
||||
/* Set attribute value. Creates attribute if does not exists */
|
||||
void set(std::string name, std::string text);
|
||||
|
||||
/* Get element tag */
|
||||
const std::string& getTag() const;
|
||||
|
||||
inline bool isText() const {
|
||||
return getTag() == "#";
|
||||
}
|
||||
|
||||
inline const std::string& text() const {
|
||||
return attr("#").getText();
|
||||
}
|
||||
|
||||
/* Get attribute by name
|
||||
@param name attribute name
|
||||
@throws std::runtime_error if element has no attribute
|
||||
@return xmlattribute - {name, value} */
|
||||
const xmlattribute attr(const std::string& name) const;
|
||||
/* Get attribute by name
|
||||
@param name name
|
||||
@param def default value will be returned wrapped in xmlattribute
|
||||
if element has no attribute
|
||||
@return xmlattribute - {name, value} or {name, def} if not found*/
|
||||
const xmlattribute attr(const std::string& name, const std::string& def) const;
|
||||
|
||||
/* Check if element has attribute
|
||||
@param name attribute name */
|
||||
bool has(const std::string& name) const;
|
||||
|
||||
/* Get sub-element by index
|
||||
@throws std::out_of_range if an invalid index given */
|
||||
xmlelement sub(size_t index);
|
||||
|
||||
/* Get number of sub-elements */
|
||||
size_t size() const;
|
||||
|
||||
const std::vector<xmlelement>& getElements() const;
|
||||
const xmlelements_map& getAttributes() const;
|
||||
};
|
||||
|
||||
class Document {
|
||||
xmlelement root = nullptr;
|
||||
std::string version;
|
||||
std::string encoding;
|
||||
public:
|
||||
Document(std::string version, std::string encoding);
|
||||
|
||||
void setRoot(xmlelement element);
|
||||
xmlelement getRoot() const;
|
||||
|
||||
const std::string& getVersion() const;
|
||||
const std::string& getEncoding() const;
|
||||
};
|
||||
|
||||
class Parser : public BasicParser {
|
||||
xmldocument document;
|
||||
|
||||
xmlelement parseOpenTag();
|
||||
xmlelement parseElement();
|
||||
void parseDeclaration();
|
||||
void parseComment();
|
||||
std::string parseText();
|
||||
std::string parseXMLName();
|
||||
public:
|
||||
Parser(std::string filename, std::string source);
|
||||
|
||||
xmldocument parse();
|
||||
};
|
||||
|
||||
/* Serialize XML Document to string
|
||||
@param document serializing document
|
||||
@param nice use human readable format
|
||||
(with indents and line-separators)
|
||||
@param indentStr indentation characters sequence
|
||||
(default - 4 spaces)*/
|
||||
extern std::string stringify(
|
||||
const xmldocument document,
|
||||
bool nice=true,
|
||||
const std::string& indentStr=" "
|
||||
);
|
||||
|
||||
/* Read XML Document from string
|
||||
@param filename file name will be shown in error messages
|
||||
@param source xml source code string */
|
||||
extern xmldocument parse(std::string filename, std::string source);
|
||||
}
|
||||
|
||||
#endif // CODERS_XML_H_
|
||||
@ -5,7 +5,7 @@
|
||||
#include "typedefs.h"
|
||||
|
||||
const int ENGINE_VERSION_MAJOR = 0;
|
||||
const int ENGINE_VERSION_MINOR = 18;
|
||||
const int ENGINE_VERSION_MINOR = 19;
|
||||
|
||||
const int BLOCK_AIR = 0;
|
||||
const int ITEM_EMPTY = 0;
|
||||
@ -28,7 +28,7 @@ const itemid_t ITEM_VOID = std::numeric_limits<itemid_t>::max();
|
||||
|
||||
const blockid_t MAX_BLOCKS = BLOCK_VOID;
|
||||
|
||||
inline uint vox_index(int x, int y, int z, int w=CHUNK_W, int d=CHUNK_D) {
|
||||
constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=CHUNK_D) {
|
||||
return (y * d + z) * w + x;
|
||||
}
|
||||
|
||||
@ -36,5 +36,6 @@ inline uint vox_index(int x, int y, int z, int w=CHUNK_W, int d=CHUNK_D) {
|
||||
#define SHADERS_FOLDER "shaders"
|
||||
#define TEXTURES_FOLDER "textures"
|
||||
#define FONTS_FOLDER "fonts"
|
||||
#define LAYOUTS_FOLDER "layouts"
|
||||
|
||||
#endif // SRC_CONSTANTS_H_
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
#include "../voxels/Block.h"
|
||||
#include "../items/ItemDef.h"
|
||||
|
||||
#include "ContentPack.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
ContentBuilder::~ContentBuilder() {
|
||||
}
|
||||
|
||||
@ -22,7 +25,11 @@ void ContentBuilder::add(ItemDef* def) {
|
||||
itemIds.push_back(def->name);
|
||||
}
|
||||
|
||||
Block* ContentBuilder::createBlock(std::string id) {
|
||||
void ContentBuilder::add(ContentPackRuntime* pack) {
|
||||
packs.push_back(std::unique_ptr<ContentPackRuntime>(pack));
|
||||
}
|
||||
|
||||
Block& ContentBuilder::createBlock(std::string id) {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found != blockDefs.end()) {
|
||||
//return found->second;
|
||||
@ -30,20 +37,20 @@ Block* ContentBuilder::createBlock(std::string id) {
|
||||
}
|
||||
Block* block = new Block(id);
|
||||
add(block);
|
||||
return block;
|
||||
return *block;
|
||||
}
|
||||
|
||||
ItemDef* ContentBuilder::createItem(std::string id) {
|
||||
ItemDef& ContentBuilder::createItem(std::string id) {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found != itemDefs.end()) {
|
||||
if (found->second->generated) {
|
||||
return found->second;
|
||||
return *found->second;
|
||||
}
|
||||
throw namereuse_error("name "+id+" is already used", contenttype::item);
|
||||
}
|
||||
ItemDef* item = new ItemDef(id);
|
||||
add(item);
|
||||
return item;
|
||||
return *item;
|
||||
}
|
||||
|
||||
void ContentBuilder::checkIdentifier(std::string id) {
|
||||
@ -65,13 +72,13 @@ contenttype ContentBuilder::checkContentType(std::string id) {
|
||||
|
||||
Content* ContentBuilder::build() {
|
||||
std::vector<Block*> blockDefsIndices;
|
||||
DrawGroups* groups = new DrawGroups;
|
||||
auto groups = std::make_unique<DrawGroups>();
|
||||
for (const std::string& name : blockIds) {
|
||||
Block* def = blockDefs[name];
|
||||
|
||||
// Generating runtime info
|
||||
def->rt.id = blockDefsIndices.size();
|
||||
def->rt.emissive = *((uint32_t*)def->emission);
|
||||
def->rt.emissive = *reinterpret_cast<uint32_t*>(def->emission);
|
||||
def->rt.solid = def->model == BlockModel::block;
|
||||
|
||||
if (def->rotatable) {
|
||||
@ -95,20 +102,23 @@ Content* ContentBuilder::build() {
|
||||
|
||||
// Generating runtime info
|
||||
def->rt.id = itemDefsIndices.size();
|
||||
def->rt.emissive = *((uint32_t*)def->emission);
|
||||
def->rt.emissive = *reinterpret_cast<uint32_t*>(def->emission);
|
||||
itemDefsIndices.push_back(def);
|
||||
}
|
||||
|
||||
auto indices = new ContentIndices(blockDefsIndices, itemDefsIndices);
|
||||
std::unique_ptr<Content> content (new Content(indices, groups, blockDefs, itemDefs));
|
||||
|
||||
// Now, it's time to solve foreign keys
|
||||
auto content = std::make_unique<Content>(
|
||||
indices, std::move(groups), blockDefs, itemDefs, std::move(packs)
|
||||
);
|
||||
|
||||
// Now, it's time to resolve foreign keys
|
||||
for (Block* def : blockDefsIndices) {
|
||||
def->rt.pickingItem = content->requireItem(def->pickingItem)->rt.id;
|
||||
def->rt.pickingItem = content->requireItem(def->pickingItem).rt.id;
|
||||
}
|
||||
|
||||
for (ItemDef* def : itemDefsIndices) {
|
||||
def->rt.placingBlock = content->requireBlock(def->placingBlock)->rt.id;
|
||||
def->rt.placingBlock = content->requireBlock(def->placingBlock).rt.id;
|
||||
}
|
||||
|
||||
return content.release();
|
||||
@ -121,17 +131,19 @@ ContentIndices::ContentIndices(
|
||||
itemDefs(itemDefs) {
|
||||
}
|
||||
|
||||
Content::Content(ContentIndices* indices, DrawGroups* drawGroups,
|
||||
Content::Content(ContentIndices* indices,
|
||||
std::unique_ptr<DrawGroups> drawGroups,
|
||||
std::unordered_map<std::string, Block*> blockDefs,
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs)
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs,
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs)
|
||||
: blockDefs(blockDefs),
|
||||
itemDefs(itemDefs),
|
||||
indices(indices),
|
||||
drawGroups(drawGroups) {
|
||||
packs(std::move(packs)),
|
||||
drawGroups(std::move(drawGroups)) {
|
||||
}
|
||||
|
||||
Content::~Content() {
|
||||
delete drawGroups;
|
||||
}
|
||||
|
||||
Block* Content::findBlock(std::string id) const {
|
||||
@ -142,12 +154,12 @@ Block* Content::findBlock(std::string id) const {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
Block* Content::requireBlock(std::string id) const {
|
||||
Block& Content::requireBlock(std::string id) const {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found == blockDefs.end()) {
|
||||
throw std::runtime_error("missing block "+id);
|
||||
}
|
||||
return found->second;
|
||||
return *found->second;
|
||||
}
|
||||
|
||||
ItemDef* Content::findItem(std::string id) const {
|
||||
@ -158,10 +170,14 @@ ItemDef* Content::findItem(std::string id) const {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
ItemDef* Content::requireItem(std::string id) const {
|
||||
ItemDef& Content::requireItem(std::string id) const {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found == itemDefs.end()) {
|
||||
throw std::runtime_error("missing item "+id);
|
||||
}
|
||||
return found->second;
|
||||
return *found->second;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<ContentPackRuntime>>& Content::getPacks() const {
|
||||
return packs;
|
||||
}
|
||||
|
||||
@ -9,11 +9,12 @@
|
||||
#include <set>
|
||||
#include "../typedefs.h"
|
||||
|
||||
typedef std::set<unsigned char> DrawGroups;
|
||||
using DrawGroups = std::set<unsigned char>;
|
||||
|
||||
class Block;
|
||||
class ItemDef;
|
||||
class Content;
|
||||
class ContentPackRuntime;
|
||||
|
||||
enum class contenttype {
|
||||
none, block, item
|
||||
@ -46,14 +47,17 @@ class ContentBuilder {
|
||||
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs;
|
||||
std::vector<std::string> itemIds;
|
||||
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs;
|
||||
public:
|
||||
~ContentBuilder();
|
||||
|
||||
void add(Block* def);
|
||||
void add(ItemDef* def);
|
||||
void add(ContentPackRuntime* pack);
|
||||
|
||||
Block* createBlock(std::string id);
|
||||
ItemDef* createItem(std::string id);
|
||||
Block& createBlock(std::string id);
|
||||
ItemDef& createItem(std::string id);
|
||||
|
||||
void checkIdentifier(std::string id);
|
||||
contenttype checkContentType(std::string id);
|
||||
@ -104,12 +108,15 @@ class Content {
|
||||
std::unordered_map<std::string, Block*> blockDefs;
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs;
|
||||
std::unique_ptr<ContentIndices> indices;
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs;
|
||||
public:
|
||||
DrawGroups* const drawGroups;
|
||||
std::unique_ptr<DrawGroups> const drawGroups;
|
||||
|
||||
Content(ContentIndices* indices, DrawGroups* drawGroups,
|
||||
Content(ContentIndices* indices,
|
||||
std::unique_ptr<DrawGroups> drawGroups,
|
||||
std::unordered_map<std::string, Block*> blockDefs,
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs);
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs,
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs);
|
||||
~Content();
|
||||
|
||||
inline ContentIndices* getIndices() const {
|
||||
@ -117,10 +124,12 @@ public:
|
||||
}
|
||||
|
||||
Block* findBlock(std::string id) const;
|
||||
Block* requireBlock(std::string id) const;
|
||||
Block& requireBlock(std::string id) const;
|
||||
|
||||
ItemDef* findItem(std::string id) const;
|
||||
ItemDef* requireItem(std::string id) const;
|
||||
ItemDef& requireItem(std::string id) const;
|
||||
|
||||
const std::vector<std::unique_ptr<ContentPackRuntime>>& getPacks() const;
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_H_
|
||||
@ -74,7 +74,7 @@ void ContentLoader::fixPackIndices() {
|
||||
|
||||
std::unique_ptr<dynamic::Map> root;
|
||||
if (fs::is_regular_file(indexFile)) {
|
||||
root = std::move(files::read_json(indexFile));
|
||||
root = files::read_json(indexFile);
|
||||
} else {
|
||||
root.reset(new dynamic::Map());
|
||||
}
|
||||
@ -92,7 +92,7 @@ void ContentLoader::fixPackIndices() {
|
||||
}
|
||||
|
||||
// TODO: add basic validation and logging
|
||||
void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
void ContentLoader::loadBlock(Block& def, std::string name, fs::path file) {
|
||||
auto root = files::read_json(file);
|
||||
|
||||
// block texturing
|
||||
@ -100,22 +100,22 @@ void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
std::string texture;
|
||||
root->str("texture", texture);
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
def->textureFaces[i] = texture;
|
||||
def.textureFaces[i] = texture;
|
||||
}
|
||||
} else if (root->has("texture-faces")) {
|
||||
auto texarr = root->list("texture-faces");
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
def->textureFaces[i] = texarr->str(i);
|
||||
def.textureFaces[i] = texarr->str(i);
|
||||
}
|
||||
}
|
||||
|
||||
// block model
|
||||
std::string model = "block";
|
||||
root->str("model", model);
|
||||
if (model == "block") def->model = BlockModel::block;
|
||||
else if (model == "aabb") def->model = BlockModel::aabb;
|
||||
if (model == "block") def.model = BlockModel::block;
|
||||
else if (model == "aabb") def.model = BlockModel::aabb;
|
||||
else if (model == "custom") {
|
||||
def->model = BlockModel::custom;
|
||||
def.model = BlockModel::custom;
|
||||
if (root->has("model-primitives")) {
|
||||
loadCustomBlockModel(def, root->map("model-primitives"));
|
||||
}
|
||||
@ -124,30 +124,30 @@ void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
<< name << " parsed: no \"model-primitives\" found" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (model == "X") def->model = BlockModel::xsprite;
|
||||
else if (model == "none") def->model = BlockModel::none;
|
||||
else if (model == "X") def.model = BlockModel::xsprite;
|
||||
else if (model == "none") def.model = BlockModel::none;
|
||||
else {
|
||||
std::cerr << "unknown model " << model << std::endl;
|
||||
def->model = BlockModel::none;
|
||||
def.model = BlockModel::none;
|
||||
}
|
||||
|
||||
// rotation profile
|
||||
std::string profile = "none";
|
||||
root->str("rotation", profile);
|
||||
def->rotatable = profile != "none";
|
||||
def.rotatable = profile != "none";
|
||||
if (profile == "pipe") {
|
||||
def->rotations = BlockRotProfile::PIPE;
|
||||
def.rotations = BlockRotProfile::PIPE;
|
||||
} else if (profile == "pane") {
|
||||
def->rotations = BlockRotProfile::PANE;
|
||||
def.rotations = BlockRotProfile::PANE;
|
||||
} else if (profile != "none") {
|
||||
std::cerr << "unknown rotation profile " << profile << std::endl;
|
||||
def->rotatable = false;
|
||||
def.rotatable = false;
|
||||
}
|
||||
|
||||
// block hitbox AABB [x, y, z, width, height, depth]
|
||||
auto boxarr = root->list("hitbox");
|
||||
if (boxarr) {
|
||||
AABB& aabb = def->hitbox;
|
||||
AABB& aabb = def.hitbox;
|
||||
aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
aabb.b += aabb.a;
|
||||
@ -156,26 +156,28 @@ void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
// block light emission [r, g, b] where r,g,b in range [0..15]
|
||||
auto emissionarr = root->list("emission");
|
||||
if (emissionarr) {
|
||||
def->emission[0] = emissionarr->num(0);
|
||||
def->emission[1] = emissionarr->num(1);
|
||||
def->emission[2] = emissionarr->num(2);
|
||||
def.emission[0] = emissionarr->num(0);
|
||||
def.emission[1] = emissionarr->num(1);
|
||||
def.emission[2] = emissionarr->num(2);
|
||||
}
|
||||
|
||||
// primitive properties
|
||||
root->flag("obstacle", def->obstacle);
|
||||
root->flag("replaceable", def->replaceable);
|
||||
root->flag("light-passing", def->lightPassing);
|
||||
root->flag("breakable", def->breakable);
|
||||
root->flag("selectable", def->selectable);
|
||||
root->flag("grounded", def->grounded);
|
||||
root->flag("hidden", def->hidden);
|
||||
root->flag("sky-light-passing", def->skyLightPassing);
|
||||
root->num("draw-group", def->drawGroup);
|
||||
root->str("picking-item", def->pickingItem);
|
||||
root->str("script-name", def->scriptName);
|
||||
root->flag("obstacle", def.obstacle);
|
||||
root->flag("replaceable", def.replaceable);
|
||||
root->flag("light-passing", def.lightPassing);
|
||||
root->flag("breakable", def.breakable);
|
||||
root->flag("selectable", def.selectable);
|
||||
root->flag("grounded", def.grounded);
|
||||
root->flag("hidden", def.hidden);
|
||||
root->flag("sky-light-passing", def.skyLightPassing);
|
||||
root->num("draw-group", def.drawGroup);
|
||||
root->str("picking-item", def.pickingItem);
|
||||
root->str("script-name", def.scriptName);
|
||||
root->str("ui-layout", def.uiLayout);
|
||||
root->num("inventory-size", def.inventorySize);
|
||||
}
|
||||
|
||||
void ContentLoader::loadCustomBlockModel(Block* def, dynamic::Map* primitives) {
|
||||
void ContentLoader::loadCustomBlockModel(Block& def, dynamic::Map* primitives) {
|
||||
if (primitives->has("aabbs")) {
|
||||
auto modelboxes = primitives->list("aabbs");
|
||||
for (uint i = 0; i < modelboxes->size(); i++ ) {
|
||||
@ -185,19 +187,19 @@ void ContentLoader::loadCustomBlockModel(Block* def, dynamic::Map* primitives) {
|
||||
modelbox.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
modelbox.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
modelbox.b += modelbox.a;
|
||||
def->modelBoxes.push_back(modelbox);
|
||||
def.modelBoxes.push_back(modelbox);
|
||||
|
||||
if (boxarr->size() == 7)
|
||||
for (uint i = 6; i < 12; i++) {
|
||||
def->modelTextures.push_back(boxarr->str(6));
|
||||
def.modelTextures.push_back(boxarr->str(6));
|
||||
}
|
||||
else if (boxarr->size() == 12)
|
||||
for (uint i = 6; i < 12; i++) {
|
||||
def->modelTextures.push_back(boxarr->str(i));
|
||||
def.modelTextures.push_back(boxarr->str(i));
|
||||
}
|
||||
else
|
||||
for (uint i = 6; i < 12; i++) {
|
||||
def->modelTextures.push_back("notfound");
|
||||
def.modelTextures.push_back("notfound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,77 +211,81 @@ void ContentLoader::loadCustomBlockModel(Block* def, dynamic::Map* primitives) {
|
||||
glm::vec3 p1(tgonobj->num(0), tgonobj->num(1), tgonobj->num(2)),
|
||||
xw(tgonobj->num(3), tgonobj->num(4), tgonobj->num(5)),
|
||||
yh(tgonobj->num(6), tgonobj->num(7), tgonobj->num(8));
|
||||
def->modelExtraPoints.push_back(p1);
|
||||
def->modelExtraPoints.push_back(p1+xw);
|
||||
def->modelExtraPoints.push_back(p1+xw+yh);
|
||||
def->modelExtraPoints.push_back(p1+yh);
|
||||
def.modelExtraPoints.push_back(p1);
|
||||
def.modelExtraPoints.push_back(p1+xw);
|
||||
def.modelExtraPoints.push_back(p1+xw+yh);
|
||||
def.modelExtraPoints.push_back(p1+yh);
|
||||
|
||||
def->modelTextures.push_back(tgonobj->str(9));
|
||||
def.modelTextures.push_back(tgonobj->str(9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadItem(ItemDef* def, std::string name, fs::path file) {
|
||||
void ContentLoader::loadItem(ItemDef& def, std::string name, fs::path file) {
|
||||
auto root = files::read_json(file);
|
||||
std::string iconTypeStr = "";
|
||||
root->str("icon-type", iconTypeStr);
|
||||
if (iconTypeStr == "none") {
|
||||
def->iconType = item_icon_type::none;
|
||||
def.iconType = item_icon_type::none;
|
||||
} else if (iconTypeStr == "block") {
|
||||
def->iconType = item_icon_type::block;
|
||||
def.iconType = item_icon_type::block;
|
||||
} else if (iconTypeStr == "sprite") {
|
||||
def->iconType = item_icon_type::sprite;
|
||||
def.iconType = item_icon_type::sprite;
|
||||
} else if (iconTypeStr.length()){
|
||||
std::cerr << "unknown icon type" << iconTypeStr << std::endl;
|
||||
}
|
||||
root->str("icon", def->icon);
|
||||
root->str("placing-block", def->placingBlock);
|
||||
root->str("script-name", def->scriptName);
|
||||
root->num("stack-size", def->stackSize);
|
||||
root->str("icon", def.icon);
|
||||
root->str("placing-block", def.placingBlock);
|
||||
root->str("script-name", def.scriptName);
|
||||
root->num("stack-size", def.stackSize);
|
||||
|
||||
// item light emission [r, g, b] where r,g,b in range [0..15]
|
||||
auto emissionarr = root->list("emission");
|
||||
if (emissionarr) {
|
||||
def->emission[0] = emissionarr->num(0);
|
||||
def->emission[1] = emissionarr->num(1);
|
||||
def->emission[2] = emissionarr->num(2);
|
||||
def.emission[0] = emissionarr->num(0);
|
||||
def.emission[1] = emissionarr->num(1);
|
||||
def.emission[2] = emissionarr->num(2);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadBlock(Block* def, std::string full, std::string name) {
|
||||
void ContentLoader::loadBlock(Block& def, std::string full, std::string name) {
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path configFile = folder/fs::path("blocks/"+name+".json");
|
||||
loadBlock(def, full, configFile);
|
||||
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def->scriptName+".lua");
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_block_script(full, scriptfile, &def->rt.funcsset);
|
||||
scripting::load_block_script(env, full, scriptfile, def.rt.funcsset);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadItem(ItemDef* def, std::string full, std::string name) {
|
||||
void ContentLoader::loadItem(ItemDef& def, std::string full, std::string name) {
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path configFile = folder/fs::path("items/"+name+".json");
|
||||
loadItem(def, full, configFile);
|
||||
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def->scriptName+".lua");
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_item_script(full, scriptfile, &def->rt.funcsset);
|
||||
scripting::load_item_script(env, full, scriptfile, def.rt.funcsset);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::load(ContentBuilder* builder) {
|
||||
void ContentLoader::load(ContentBuilder& builder) {
|
||||
std::cout << "-- loading pack [" << pack->id << "]" << std::endl;
|
||||
|
||||
auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack));
|
||||
builder.add(runtime);
|
||||
env = runtime->getEnvironment()->getId();
|
||||
|
||||
fixPackIndices();
|
||||
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path scriptFile = folder/fs::path("scripts/world.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_world_script(pack->id, scriptFile);
|
||||
scripting::load_world_script(env, pack->id, scriptFile);
|
||||
}
|
||||
|
||||
if (!fs::is_regular_file(pack->getContentFile()))
|
||||
@ -290,17 +296,17 @@ void ContentLoader::load(ContentBuilder* builder) {
|
||||
for (uint i = 0; i < blocksarr->size(); i++) {
|
||||
std::string name = blocksarr->str(i);
|
||||
std::string full = pack->id+":"+name;
|
||||
auto def = builder->createBlock(full);
|
||||
auto& def = builder.createBlock(full);
|
||||
loadBlock(def, full, name);
|
||||
if (!def->hidden) {
|
||||
auto item = builder->createItem(full+BLOCK_ITEM_SUFFIX);
|
||||
item->generated = true;
|
||||
item->iconType = item_icon_type::block;
|
||||
item->icon = full;
|
||||
item->placingBlock = full;
|
||||
if (!def.hidden) {
|
||||
auto& item = builder.createItem(full+BLOCK_ITEM_SUFFIX);
|
||||
item.generated = true;
|
||||
item.iconType = item_icon_type::block;
|
||||
item.icon = full;
|
||||
item.placingBlock = full;
|
||||
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
item->emission[j] = def->emission[j];
|
||||
item.emission[j] = def.emission[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,7 +317,7 @@ void ContentLoader::load(ContentBuilder* builder) {
|
||||
for (uint i = 0; i < itemsarr->size(); i++) {
|
||||
std::string name = itemsarr->str(i);
|
||||
std::string full = pack->id+":"+name;
|
||||
loadItem(builder->createItem(full), full, name);
|
||||
loadItem(builder.createItem(full), full, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ namespace fs = std::filesystem;
|
||||
|
||||
class Block;
|
||||
class ItemDef;
|
||||
class ContentPack;
|
||||
struct ContentPack;
|
||||
class ContentBuilder;
|
||||
|
||||
namespace dynamic {
|
||||
@ -17,10 +17,11 @@ namespace dynamic {
|
||||
|
||||
class ContentLoader {
|
||||
const ContentPack* pack;
|
||||
int env = 0;
|
||||
|
||||
void loadBlock(Block* def, std::string full, std::string name);
|
||||
void loadCustomBlockModel(Block* def, dynamic::Map* primitives);
|
||||
void loadItem(ItemDef* def, std::string full, std::string name);
|
||||
void loadBlock(Block& def, std::string full, std::string name);
|
||||
void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
|
||||
void loadItem(ItemDef& def, std::string full, std::string name);
|
||||
public:
|
||||
ContentLoader(ContentPack* pack);
|
||||
|
||||
@ -28,9 +29,9 @@ public:
|
||||
dynamic::Map* indicesRoot,
|
||||
std::string contentSection);
|
||||
void fixPackIndices();
|
||||
void loadBlock(Block* def, std::string name, fs::path file);
|
||||
void loadItem(ItemDef* def, std::string name, fs::path file);
|
||||
void load(ContentBuilder* builder);
|
||||
void loadBlock(Block& def, std::string name, fs::path file);
|
||||
void loadItem(ItemDef& def, std::string name, fs::path file);
|
||||
void load(ContentBuilder& builder);
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_LOADER_H_
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "../files/files.h"
|
||||
#include "../files/engine_paths.h"
|
||||
#include "../data/dynamic.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -86,25 +87,43 @@ ContentPack ContentPack::read(fs::path folder) {
|
||||
return pack;
|
||||
}
|
||||
|
||||
void ContentPack::scan(fs::path rootfolder,
|
||||
std::vector<ContentPack>& packs) {
|
||||
if (!fs::is_directory(rootfolder)) {
|
||||
void ContentPack::scanFolder(
|
||||
fs::path folder,
|
||||
std::vector<ContentPack>& packs
|
||||
) {
|
||||
if (!fs::is_directory(folder)) {
|
||||
return;
|
||||
}
|
||||
for (auto entry : fs::directory_iterator(rootfolder)) {
|
||||
for (auto entry : fs::directory_iterator(folder)) {
|
||||
fs::path folder = entry.path();
|
||||
if (!fs::is_directory(folder))
|
||||
continue;
|
||||
if (!is_pack(folder))
|
||||
continue;
|
||||
packs.push_back(read(folder));
|
||||
try {
|
||||
packs.push_back(read(folder));
|
||||
} catch (const contentpack_error& err) {
|
||||
std::cerr << "package.json error at " << err.getFolder().u8string();
|
||||
std::cerr << ": " << err.what() << std::endl;
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContentPack::scan(
|
||||
fs::path rootfolder,
|
||||
EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs
|
||||
) {
|
||||
scanFolder(paths->getResources()/fs::path("content"), packs);
|
||||
scanFolder(paths->getUserfiles()/fs::path("content"), packs);
|
||||
scanFolder(rootfolder, packs);
|
||||
}
|
||||
|
||||
void ContentPack::scan(EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs) {
|
||||
scan(paths->getResources()/fs::path("content"), packs);
|
||||
scan(paths->getWorldFolder()/fs::path("content"), packs);
|
||||
scan(paths->getWorldFolder()/fs::path("content"), paths, packs);
|
||||
}
|
||||
|
||||
std::vector<std::string> ContentPack::worldPacksList(fs::path folder) {
|
||||
@ -122,6 +141,10 @@ fs::path ContentPack::findPack(const EnginePaths* paths, fs::path worldDir, std:
|
||||
if (fs::is_directory(folder)) {
|
||||
return folder;
|
||||
}
|
||||
folder = paths->getUserfiles() / fs::path("content") / fs::path(name);
|
||||
if (fs::is_directory(folder)) {
|
||||
return folder;
|
||||
}
|
||||
folder = paths->getResources() / fs::path("content") / fs::path(name);
|
||||
if (fs::is_directory(folder)) {
|
||||
return folder;
|
||||
@ -142,3 +165,10 @@ void ContentPack::readPacks(const EnginePaths* paths,
|
||||
packs.push_back(ContentPack::read(packfolder));
|
||||
}
|
||||
}
|
||||
|
||||
ContentPackRuntime::ContentPackRuntime(
|
||||
ContentPack info,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
) : info(info), env(std::move(env))
|
||||
{
|
||||
}
|
||||
|
||||
@ -8,16 +8,22 @@
|
||||
|
||||
class EnginePaths;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
class contentpack_error : public std::runtime_error {
|
||||
std::string packId;
|
||||
std::filesystem::path folder;
|
||||
fs::path folder;
|
||||
public:
|
||||
contentpack_error(std::string packId,
|
||||
std::filesystem::path folder,
|
||||
fs::path folder,
|
||||
std::string message);
|
||||
|
||||
std::string getPackId() const;
|
||||
std::filesystem::path getFolder() const;
|
||||
fs::path getFolder() const;
|
||||
};
|
||||
|
||||
struct ContentPack {
|
||||
@ -26,35 +32,71 @@ struct ContentPack {
|
||||
std::string version = "0.0";
|
||||
std::string creator = "";
|
||||
std::string description = "no description";
|
||||
std::filesystem::path folder;
|
||||
fs::path folder;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
std::filesystem::path getContentFile() const;
|
||||
fs::path getContentFile() const;
|
||||
|
||||
static const std::string PACKAGE_FILENAME;
|
||||
static const std::string CONTENT_FILENAME;
|
||||
static const std::filesystem::path BLOCKS_FOLDER;
|
||||
static const std::filesystem::path ITEMS_FOLDER;
|
||||
static const fs::path BLOCKS_FOLDER;
|
||||
static const fs::path ITEMS_FOLDER;
|
||||
static const std::vector<std::string> RESERVED_NAMES;
|
||||
|
||||
static bool is_pack(std::filesystem::path folder);
|
||||
static ContentPack read(std::filesystem::path folder);
|
||||
static bool is_pack(fs::path folder);
|
||||
static ContentPack read(fs::path folder);
|
||||
|
||||
static void scan(std::filesystem::path folder,
|
||||
std::vector<ContentPack>& packs);
|
||||
static void scan(EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs);
|
||||
static void scanFolder(
|
||||
fs::path folder,
|
||||
std::vector<ContentPack>& packs
|
||||
);
|
||||
|
||||
static void scan(
|
||||
fs::path folder,
|
||||
EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs
|
||||
);
|
||||
static void scan(
|
||||
EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs
|
||||
);
|
||||
|
||||
static std::vector<std::string> worldPacksList(std::filesystem::path folder);
|
||||
static std::vector<std::string> worldPacksList(fs::path folder);
|
||||
|
||||
static std::filesystem::path findPack(
|
||||
static fs::path findPack(
|
||||
const EnginePaths* paths,
|
||||
std::filesystem::path worldDir,
|
||||
std::string name);
|
||||
static void readPacks(const EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs,
|
||||
const std::vector<std::string>& names,
|
||||
std::filesystem::path worldDir);
|
||||
fs::path worldDir,
|
||||
std::string name
|
||||
);
|
||||
|
||||
static void readPacks(
|
||||
const EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs,
|
||||
const std::vector<std::string>& names,
|
||||
fs::path worldDir
|
||||
);
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
ContentPack info;
|
||||
std::unique_ptr<scripting::Environment> env;
|
||||
public:
|
||||
ContentPackRuntime(
|
||||
ContentPack info,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
);
|
||||
|
||||
inline const std::string& getId() {
|
||||
return info.id;
|
||||
}
|
||||
|
||||
inline const ContentPack& getInfo() const {
|
||||
return info;
|
||||
}
|
||||
|
||||
inline scripting::Environment* getEnvironment() const {
|
||||
return env.get();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_PACK_H_
|
||||
|
||||
@ -1,48 +1,43 @@
|
||||
#include "definitions.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "items/ItemDef.h"
|
||||
#include "content/Content.h"
|
||||
#include "window/Window.h"
|
||||
#include "window/Events.h"
|
||||
#include "window/input.h"
|
||||
#include "voxels/Block.h"
|
||||
|
||||
using glm::vec3;
|
||||
|
||||
// All in-game definitions (blocks, items, etc..)
|
||||
void setup_definitions(ContentBuilder* builder) { // Strange function, need to REDO ?
|
||||
Block* block = new Block("core:air", "air");
|
||||
block->replaceable = true;
|
||||
block->drawGroup = 1;
|
||||
block->lightPassing = true;
|
||||
block->skyLightPassing = true;
|
||||
block->obstacle = false;
|
||||
block->selectable = false;
|
||||
block->model = BlockModel::none;
|
||||
block->pickingItem = "core:empty";
|
||||
builder->add(block);
|
||||
|
||||
ItemDef* item = builder->createItem("core:empty");
|
||||
item->iconType = item_icon_type::none;
|
||||
}
|
||||
|
||||
void setup_bindings() {
|
||||
Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W);
|
||||
Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S);
|
||||
Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D);
|
||||
Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A);
|
||||
Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE);
|
||||
Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL);
|
||||
Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT);
|
||||
Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R);
|
||||
Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C);
|
||||
Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4);
|
||||
Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N);
|
||||
Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F);
|
||||
Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1);
|
||||
Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2);
|
||||
Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3);
|
||||
Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB);
|
||||
#include "core_defs.h"
|
||||
|
||||
#include "items/ItemDef.h"
|
||||
#include "content/Content.h"
|
||||
#include "window/Window.h"
|
||||
#include "window/Events.h"
|
||||
#include "window/input.h"
|
||||
#include "voxels/Block.h"
|
||||
|
||||
// All in-game definitions (blocks, items, etc..)
|
||||
void corecontent::setup(ContentBuilder* builder) {
|
||||
Block& block = builder->createBlock("core:air");
|
||||
block.replaceable = true;
|
||||
block.drawGroup = 1;
|
||||
block.lightPassing = true;
|
||||
block.skyLightPassing = true;
|
||||
block.obstacle = false;
|
||||
block.selectable = false;
|
||||
block.model = BlockModel::none;
|
||||
block.pickingItem = "core:empty";
|
||||
|
||||
ItemDef& item = builder->createItem("core:empty");
|
||||
item.iconType = item_icon_type::none;
|
||||
}
|
||||
|
||||
void corecontent::setup_bindings() {
|
||||
Events::bind(BIND_MOVE_FORWARD, inputtype::keyboard, keycode::W);
|
||||
Events::bind(BIND_MOVE_BACK, inputtype::keyboard, keycode::S);
|
||||
Events::bind(BIND_MOVE_RIGHT, inputtype::keyboard, keycode::D);
|
||||
Events::bind(BIND_MOVE_LEFT, inputtype::keyboard, keycode::A);
|
||||
Events::bind(BIND_MOVE_JUMP, inputtype::keyboard, keycode::SPACE);
|
||||
Events::bind(BIND_MOVE_SPRINT, inputtype::keyboard, keycode::LEFT_CONTROL);
|
||||
Events::bind(BIND_MOVE_CROUCH, inputtype::keyboard, keycode::LEFT_SHIFT);
|
||||
Events::bind(BIND_MOVE_CHEAT, inputtype::keyboard, keycode::R);
|
||||
Events::bind(BIND_CAM_ZOOM, inputtype::keyboard, keycode::C);
|
||||
Events::bind(BIND_CAM_MODE, inputtype::keyboard, keycode::F4);
|
||||
Events::bind(BIND_PLAYER_NOCLIP, inputtype::keyboard, keycode::N);
|
||||
Events::bind(BIND_PLAYER_FLIGHT, inputtype::keyboard, keycode::F);
|
||||
Events::bind(BIND_PLAYER_ATTACK, inputtype::mouse, mousecode::BUTTON_1);
|
||||
Events::bind(BIND_PLAYER_BUILD, inputtype::mouse, mousecode::BUTTON_2);
|
||||
Events::bind(BIND_PLAYER_PICK, inputtype::mouse, mousecode::BUTTON_3);
|
||||
Events::bind(BIND_HUD_INVENTORY, inputtype::keyboard, keycode::TAB);
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
const std::string TEXTURE_NOTFOUND = "notfound";
|
||||
|
||||
/* bindings used in engine code */
|
||||
@ -24,4 +23,11 @@ const std::string BIND_PLAYER_BUILD = "player.build";
|
||||
const std::string BIND_PLAYER_PICK = "player.pick";
|
||||
const std::string BIND_HUD_INVENTORY = "hud.inventory";
|
||||
|
||||
class ContentBuilder;
|
||||
|
||||
namespace corecontent {
|
||||
extern void setup_bindings();
|
||||
extern void setup(ContentBuilder* builder);
|
||||
}
|
||||
|
||||
#endif // SRC_CORE_DEFS_H_
|
||||
@ -1,13 +0,0 @@
|
||||
#ifndef DECLARATIONS_H
|
||||
#define DECLARATIONS_H
|
||||
|
||||
#include <iostream>
|
||||
#include "core_defs.h"
|
||||
|
||||
class ContentBuilder;
|
||||
|
||||
extern void setup_bindings();
|
||||
extern void setup_definitions(ContentBuilder* builder);
|
||||
|
||||
#endif // DECLARATIONS_H
|
||||
|
||||
23
src/delegates.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef DELEGATES_H_
|
||||
#define DELEGATES_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
using runnable = std::function<void()>;
|
||||
|
||||
// data sources
|
||||
using wstringsupplier = std::function<std::wstring()>;
|
||||
using doublesupplier = std::function<double()>;
|
||||
using boolsupplier = std::function<bool()>;
|
||||
using vec2supplier = std::function<glm::vec2()>;
|
||||
|
||||
using stringconsumer = std::function<void(const std::string&)>;
|
||||
using wstringconsumer = std::function<void(const std::wstring&)>;
|
||||
using doubleconsumer = std::function<void(double)>;
|
||||
using boolconsumer = std::function<void(bool)>;
|
||||
using int_array_consumer = std::function<void(const int[], size_t)>;
|
||||
using wstringchecker = std::function<bool(const std::wstring&)>;
|
||||
|
||||
#endif // DELEGATES_H_
|
||||
@ -17,6 +17,7 @@
|
||||
#include "window/Camera.h"
|
||||
#include "window/input.h"
|
||||
#include "graphics/Batch2D.h"
|
||||
#include "graphics/GfxContext.h"
|
||||
#include "graphics/Shader.h"
|
||||
#include "graphics/ImageData.h"
|
||||
#include "frontend/gui/GUI.h"
|
||||
@ -36,12 +37,13 @@
|
||||
#include "frontend/locale/langs.h"
|
||||
#include "logic/scripting/scripting.h"
|
||||
|
||||
#include "definitions.h"
|
||||
#include "core_defs.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
Engine::Engine(EngineSettings& settings, EnginePaths* paths)
|
||||
: settings(settings), paths(paths) {
|
||||
: settings(settings), paths(paths)
|
||||
{
|
||||
if (Window::initialize(settings.display)){
|
||||
throw initialize_error("could not initialize window");
|
||||
}
|
||||
@ -51,18 +53,21 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths)
|
||||
|
||||
std::cout << "-- loading assets" << std::endl;
|
||||
std::vector<fs::path> roots {resdir};
|
||||
resPaths.reset(new ResPaths(resdir, roots));
|
||||
assets.reset(new Assets());
|
||||
|
||||
resPaths = std::make_unique<ResPaths>(resdir, roots);
|
||||
assets = std::make_unique<Assets>();
|
||||
|
||||
|
||||
AssetsLoader loader(assets.get(), resPaths.get());
|
||||
AssetsLoader::createDefaults(loader);
|
||||
AssetsLoader::addDefaults(loader, true);
|
||||
AssetsLoader::addDefaults(loader, nullptr);
|
||||
|
||||
Shader::preprocessor->setPaths(resPaths.get());
|
||||
while (loader.hasNext()) {
|
||||
if (!loader.loadNext()) {
|
||||
assets.reset();
|
||||
scripting::close();
|
||||
Window::terminate();
|
||||
throw initialize_error("could not to initialize assets");
|
||||
throw initialize_error("could not to load assets");
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +77,6 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths)
|
||||
settings.ui.language = langs::locale_by_envlocale(platform::detect_locale(), paths->getResources());
|
||||
}
|
||||
setLanguage(settings.ui.language);
|
||||
std::cout << "-- initializing finished" << std::endl;
|
||||
}
|
||||
|
||||
void Engine::updateTimers() {
|
||||
@ -97,12 +101,11 @@ void Engine::updateHotkeys() {
|
||||
|
||||
void Engine::mainloop() {
|
||||
setScreen(std::make_shared<MenuScreen>(this));
|
||||
|
||||
std::cout << "-- preparing systems" << std::endl;
|
||||
|
||||
Batch2D batch(1024);
|
||||
lastTime = Window::time();
|
||||
|
||||
std::cout << "-- initialized" << std::endl;
|
||||
while (!Window::isShouldClose()){
|
||||
assert(screen != nullptr);
|
||||
updateTimers();
|
||||
@ -113,7 +116,11 @@ void Engine::mainloop() {
|
||||
|
||||
if (!Window::isIconified()) {
|
||||
screen->draw(delta);
|
||||
gui->draw(&batch, assets.get());
|
||||
|
||||
Viewport viewport(Window::width, Window::height);
|
||||
GfxContext ctx(nullptr, viewport, &batch);
|
||||
gui->draw(&ctx, assets.get());
|
||||
|
||||
Window::swapInterval(settings.display.swapInterval);
|
||||
} else {
|
||||
Window::swapInterval(1);
|
||||
@ -124,13 +131,13 @@ void Engine::mainloop() {
|
||||
}
|
||||
|
||||
Engine::~Engine() {
|
||||
screen = nullptr;
|
||||
scripting::close();
|
||||
std::cout << "-- shutting down" << std::endl;
|
||||
screen.reset();
|
||||
content.reset();
|
||||
|
||||
Audio::finalize();
|
||||
|
||||
std::cout << "-- shutting down" << std::endl;
|
||||
assets.reset();
|
||||
scripting::close();
|
||||
Window::terminate();
|
||||
std::cout << "-- engine finished" << std::endl;
|
||||
}
|
||||
@ -143,7 +150,7 @@ inline const std::string checkPacks(const std::unordered_set<std::string>& packs
|
||||
void Engine::loadContent() {
|
||||
auto resdir = paths->getResources();
|
||||
ContentBuilder contentBuilder;
|
||||
setup_definitions(&contentBuilder);
|
||||
corecontent::setup(&contentBuilder);
|
||||
paths->setContentPacks(&contentPacks);
|
||||
|
||||
std::vector<fs::path> resRoots;
|
||||
@ -158,13 +165,14 @@ void Engine::loadContent() {
|
||||
for (auto& pack : srcPacks) {
|
||||
if(loadedPacks.find(pack.id) != loadedPacks.end()) continue;
|
||||
missingDependency = checkPacks(existingPacks, pack.dependencies);
|
||||
if(!missingDependency.empty()) throw contentpack_error(pack.id, pack.folder, "missing dependency '"+missingDependency+"'");
|
||||
if(!missingDependency.empty())
|
||||
throw contentpack_error(pack.id, pack.folder, "missing dependency '"+missingDependency+"'");
|
||||
if(pack.dependencies.empty() || checkPacks(loadedPacks, pack.dependencies).empty()) {
|
||||
loadedPacks.insert(pack.id);
|
||||
resRoots.push_back(pack.folder);
|
||||
contentPacks.push_back(pack);
|
||||
ContentLoader loader(&pack);
|
||||
loader.load(&contentBuilder);
|
||||
loader.load(contentBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,8 +185,7 @@ void Engine::loadContent() {
|
||||
std::unique_ptr<Assets> new_assets(new Assets());
|
||||
std::cout << "-- loading assets" << std::endl;
|
||||
AssetsLoader loader(new_assets.get(), resPaths.get());
|
||||
AssetsLoader::createDefaults(loader);
|
||||
AssetsLoader::addDefaults(loader, false);
|
||||
AssetsLoader::addDefaults(loader, content.get());
|
||||
while (loader.hasNext()) {
|
||||
if (!loader.loadNext()) {
|
||||
new_assets.reset();
|
||||
@ -191,7 +198,6 @@ void Engine::loadContent() {
|
||||
void Engine::loadWorldContent(const fs::path& folder) {
|
||||
contentPacks.clear();
|
||||
auto packNames = ContentPack::worldPacksList(folder);
|
||||
std::cout << folder << " " << packNames.size() << std::endl;
|
||||
ContentPack::readPacks(paths, contentPacks, packNames, folder);
|
||||
loadContent();
|
||||
}
|
||||
@ -202,6 +208,10 @@ void Engine::loadAllPacks() {
|
||||
ContentPack::scan(paths, contentPacks);
|
||||
}
|
||||
|
||||
double Engine::getDelta() const {
|
||||
return delta;
|
||||
}
|
||||
|
||||
void Engine::setScreen(std::shared_ptr<Screen> screen) {
|
||||
this->screen = screen;
|
||||
}
|
||||
@ -209,7 +219,7 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
|
||||
void Engine::setLanguage(std::string locale) {
|
||||
settings.ui.language = locale;
|
||||
langs::setup(paths->getResources(), locale, contentPacks);
|
||||
menus::create_menus(this, gui->getMenu());
|
||||
menus::create_menus(this);
|
||||
}
|
||||
|
||||
gui::GUI* Engine::getGUI() {
|
||||
@ -238,4 +248,4 @@ EnginePaths* Engine::getPaths() {
|
||||
|
||||
std::shared_ptr<Screen> Engine::getScreen() {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
|
||||
69
src/engine.h
@ -44,27 +44,76 @@ class Engine {
|
||||
double delta = 0.0;
|
||||
|
||||
std::unique_ptr<gui::GUI> gui;
|
||||
|
||||
void updateTimers();
|
||||
void updateHotkeys();
|
||||
public:
|
||||
Engine(EngineSettings& settings, EnginePaths* paths);
|
||||
~Engine();
|
||||
|
||||
void updateTimers();
|
||||
void updateHotkeys();
|
||||
/**
|
||||
* Start main engine input/update/render loop
|
||||
* Automatically sets MenuScreen
|
||||
*/
|
||||
void mainloop();
|
||||
|
||||
Assets* getAssets();
|
||||
gui::GUI* getGUI();
|
||||
EngineSettings& getSettings();
|
||||
void setScreen(std::shared_ptr<Screen> screen);
|
||||
EnginePaths* getPaths();
|
||||
const Content* getContent() const;
|
||||
std::vector<ContentPack>& getContentPacks();
|
||||
/**
|
||||
* Set screen (scene).
|
||||
* nullptr may be used to delete previous screen before creating new one
|
||||
* example:
|
||||
*
|
||||
* engine->setScreen(nullptr);
|
||||
* engine->setScreen(std::make_shared<...>(...));
|
||||
*
|
||||
* not-null value must be set before next frame
|
||||
*/
|
||||
void setScreen(std::shared_ptr<Screen> screen);
|
||||
|
||||
/**
|
||||
* Change locale to specified
|
||||
* @param locale isolanguage_ISOCOUNTRY (example: en_US)
|
||||
*/
|
||||
void setLanguage(std::string locale);
|
||||
|
||||
/**
|
||||
* Load all selected content-packs and reload assets
|
||||
*/
|
||||
void loadContent();
|
||||
/**
|
||||
* Collect world content-packs and load content
|
||||
* @see loadContent
|
||||
* @param folder world folder
|
||||
*/
|
||||
void loadWorldContent(const fs::path& folder);
|
||||
|
||||
/**
|
||||
* Collect all available content-packs from res/content
|
||||
*/
|
||||
void loadAllPacks();
|
||||
|
||||
/** Get current frame delta-time */
|
||||
double getDelta() const;
|
||||
|
||||
/** Get active assets storage instance */
|
||||
Assets* getAssets();
|
||||
|
||||
/** Get main UI controller */
|
||||
gui::GUI* getGUI();
|
||||
|
||||
/** Get writeable engine settings structure instance */
|
||||
EngineSettings& getSettings();
|
||||
|
||||
/** Get engine filesystem paths source */
|
||||
EnginePaths* getPaths();
|
||||
|
||||
/** Get current Content instance */
|
||||
const Content* getContent() const;
|
||||
|
||||
/** Get selected content packs */
|
||||
std::vector<ContentPack>& getContentPacks();
|
||||
|
||||
/** Get current screen */
|
||||
std::shared_ptr<Screen> getScreen();
|
||||
};
|
||||
|
||||
#endif // SRC_ENGINE_H_
|
||||
#endif // SRC_ENGINE_H_
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "../world/World.h"
|
||||
#include "../lighting/Lightmap.h"
|
||||
|
||||
#include "../coders/byte_utils.h"
|
||||
#include "../util/data_io.h"
|
||||
#include "../coders/json.h"
|
||||
#include "../constants.h"
|
||||
@ -28,6 +29,8 @@
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
const size_t BUFFER_SIZE_UNKNOWN = -1;
|
||||
|
||||
regfile::regfile(fs::path filename) : file(filename) {
|
||||
if (file.length() < REGION_HEADER_SIZE)
|
||||
throw std::runtime_error("incomplete region file header");
|
||||
@ -189,7 +192,7 @@ void WorldFiles::put(Chunk* chunk){
|
||||
int localX = chunk->x - (regionX * REGION_SIZE);
|
||||
int localZ = chunk->z - (regionZ * REGION_SIZE);
|
||||
|
||||
/* Writing Voxels */ {
|
||||
/* Writing voxels */ {
|
||||
size_t compressedSize;
|
||||
std::unique_ptr<ubyte[]> chunk_data (chunk->encode());
|
||||
ubyte* data = compress(chunk_data.get(), CHUNK_DATA_LEN, compressedSize);
|
||||
@ -198,15 +201,39 @@ void WorldFiles::put(Chunk* chunk){
|
||||
region->setUnsaved(true);
|
||||
region->put(localX, localZ, data, compressedSize);
|
||||
}
|
||||
/* Writing lights cache */
|
||||
if (doWriteLights && chunk->isLighted()) {
|
||||
size_t compressedSize;
|
||||
std::unique_ptr<ubyte[]> light_data (chunk->lightmap->encode());
|
||||
std::unique_ptr<ubyte[]> light_data (chunk->lightmap.encode());
|
||||
ubyte* data = compress(light_data.get(), LIGHTMAP_DATA_LEN, compressedSize);
|
||||
|
||||
WorldRegion* region = getOrCreateRegion(lights, regionX, regionZ);
|
||||
region->setUnsaved(true);
|
||||
region->put(localX, localZ, data, compressedSize);
|
||||
}
|
||||
/* Writing block inventories */
|
||||
if (!chunk->inventories.empty()){
|
||||
auto& inventories = chunk->inventories;
|
||||
ByteBuilder builder;
|
||||
builder.putInt32(inventories.size());
|
||||
for (auto& entry : inventories) {
|
||||
builder.putInt32(entry.first);
|
||||
auto map = entry.second->serialize();
|
||||
auto bytes = json::to_binary(map.get(), true);
|
||||
builder.putInt32(bytes.size());
|
||||
builder.put(bytes.data(), bytes.size());
|
||||
}
|
||||
WorldRegion* region = getOrCreateRegion(storages, regionX, regionZ);
|
||||
region->setUnsaved(true);
|
||||
|
||||
auto datavec = builder.data();
|
||||
uint datasize = builder.size();
|
||||
auto data = std::make_unique<ubyte[]>(datasize);
|
||||
for (uint i = 0; i < datasize; i++) {
|
||||
data[i] = datavec[i];
|
||||
}
|
||||
region->put(localX, localZ, data.release(), datasize);
|
||||
}
|
||||
}
|
||||
|
||||
fs::path WorldFiles::getRegionsFolder() const {
|
||||
@ -217,6 +244,10 @@ fs::path WorldFiles::getLightsFolder() const {
|
||||
return directory/fs::path("lights");
|
||||
}
|
||||
|
||||
fs::path WorldFiles::getInventoriesFolder() const {
|
||||
return directory/fs::path("inventories");
|
||||
}
|
||||
|
||||
fs::path WorldFiles::getRegionFilename(int x, int z) const {
|
||||
return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin");
|
||||
}
|
||||
@ -260,20 +291,39 @@ fs::path WorldFiles::getPacksFile() const {
|
||||
}
|
||||
|
||||
ubyte* WorldFiles::getChunk(int x, int z){
|
||||
return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS);
|
||||
return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS, true);
|
||||
}
|
||||
|
||||
/* Get cached lights for chunk at x,z
|
||||
* @return lights data or nullptr */
|
||||
light_t* WorldFiles::getLights(int x, int z) {
|
||||
std::unique_ptr<ubyte> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS));
|
||||
std::unique_ptr<ubyte[]> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS, true));
|
||||
if (data == nullptr)
|
||||
return nullptr;
|
||||
return Lightmap::decode(data.get());
|
||||
}
|
||||
|
||||
chunk_inventories_map WorldFiles::fetchInventories(int x, int z) {
|
||||
chunk_inventories_map inventories;
|
||||
const ubyte* data = getData(storages, getInventoriesFolder(), x, z, REGION_LAYER_INVENTORIES, false);
|
||||
if (data == nullptr)
|
||||
return inventories;
|
||||
ByteReader reader(data, BUFFER_SIZE_UNKNOWN);
|
||||
int count = reader.getInt32();
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint index = reader.getInt32();
|
||||
uint size = reader.getInt32();
|
||||
auto map = json::from_binary(reader.pointer(), size);
|
||||
reader.skip(size);
|
||||
auto inv = std::make_shared<Inventory>(0, 0);
|
||||
inv->deserialize(map.get());
|
||||
inventories[index] = inv;
|
||||
}
|
||||
return inventories;
|
||||
}
|
||||
|
||||
ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
|
||||
int x, int z, int layer) {
|
||||
int x, int z, int layer, bool compression) {
|
||||
int regionX = floordiv(x, REGION_SIZE);
|
||||
int regionZ = floordiv(z, REGION_SIZE);
|
||||
|
||||
@ -291,7 +341,10 @@ ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
|
||||
}
|
||||
if (data != nullptr) {
|
||||
size_t size = region->getChunkDataSize(localX, localZ);
|
||||
return decompress(data, size, CHUNK_DATA_LEN);
|
||||
if (compression) {
|
||||
return decompress(data, size, CHUNK_DATA_LEN);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -343,17 +396,16 @@ ubyte* WorldFiles::readChunkData(int x,
|
||||
file.seekg(table_offset + chunkIndex * 4);
|
||||
file.read((char*)(&offset), 4);
|
||||
offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
|
||||
|
||||
if (offset == 0){
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.seekg(offset);
|
||||
file.read((char*)(&offset), 4);
|
||||
length = dataio::read_int32_big((const ubyte*)(&offset), 0);
|
||||
ubyte* data = new ubyte[length];
|
||||
ubyte* data = new ubyte[length]{};
|
||||
file.read((char*)data, length);
|
||||
if (data == nullptr) {
|
||||
std::cerr << "ERROR: failed to read data of chunk x("<< x <<"), z("<< z <<")" << std::endl;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -436,20 +488,24 @@ void WorldFiles::writeRegions(regionsmap& regions, const fs::path& folder, int l
|
||||
void WorldFiles::write(const World* world, const Content* content) {
|
||||
fs::path regionsFolder = getRegionsFolder();
|
||||
fs::path lightsFolder = getLightsFolder();
|
||||
fs::path inventoriesFolder = getInventoriesFolder();
|
||||
|
||||
fs::create_directories(regionsFolder);
|
||||
fs::create_directories(inventoriesFolder);
|
||||
fs::create_directories(lightsFolder);
|
||||
|
||||
if (world) {
|
||||
writeWorldInfo(world);
|
||||
writePacks(world);
|
||||
}
|
||||
if (generatorTestMode)
|
||||
if (generatorTestMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
writeIndices(content->getIndices());
|
||||
writeRegions(regions, regionsFolder, REGION_LAYER_VOXELS);
|
||||
writeRegions(lights, lightsFolder, REGION_LAYER_LIGHTS);
|
||||
writeRegions(storages, inventoriesFolder, REGION_LAYER_INVENTORIES);
|
||||
}
|
||||
|
||||
void WorldFiles::writePacks(const World* world) {
|
||||
@ -488,21 +544,7 @@ void WorldFiles::writeIndices(const ContentIndices* indices) {
|
||||
}
|
||||
|
||||
void WorldFiles::writeWorldInfo(const World* world) {
|
||||
dynamic::Map root;
|
||||
|
||||
auto& versionobj = root.putMap("version");
|
||||
versionobj.put("major", ENGINE_VERSION_MAJOR);
|
||||
versionobj.put("minor", ENGINE_VERSION_MINOR);
|
||||
|
||||
root.put("name", world->getName());
|
||||
root.put("seed", world->getSeed());
|
||||
|
||||
auto& timeobj = root.putMap("time");
|
||||
timeobj.put("day-time", world->daytime);
|
||||
timeobj.put("day-time-speed", world->daytimeSpeed);
|
||||
timeobj.put("total-time", world->totalTime);
|
||||
|
||||
files::write_json(getWorldFile(), &root);
|
||||
files::write_json(getWorldFile(), world->serialize().get());
|
||||
}
|
||||
|
||||
bool WorldFiles::readWorldInfo(World* world) {
|
||||
@ -513,25 +555,7 @@ bool WorldFiles::readWorldInfo(World* world) {
|
||||
}
|
||||
|
||||
auto root = files::read_json(file);
|
||||
|
||||
world->setName(root->getStr("name", world->getName()));
|
||||
world->setSeed(root->getInt("seed", world->getSeed()));
|
||||
|
||||
auto verobj = root->map("version");
|
||||
if (verobj) {
|
||||
int major=0, minor=-1;
|
||||
verobj->num("major", major);
|
||||
verobj->num("minor", minor);
|
||||
std::cout << "world version: " << major << "." << minor << std::endl;
|
||||
}
|
||||
|
||||
auto timeobj = root->map("time");
|
||||
if (timeobj) {
|
||||
timeobj->num("day-time", world->daytime);
|
||||
timeobj->num("day-time-speed", world->daytimeSpeed);
|
||||
timeobj->num("total-time", world->totalTime);
|
||||
}
|
||||
|
||||
world->deserialize(root.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -550,8 +574,15 @@ bool WorldFiles::readPlayer(Player* player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldFiles::addPack(const std::string& id) {
|
||||
auto packs = files::read_list(getPacksFile());
|
||||
void WorldFiles::addPack(const World* world, const std::string& id) {
|
||||
fs::path file = getPacksFile();
|
||||
if (!fs::is_regular_file(file)) {
|
||||
if (!fs::is_directory(directory)) {
|
||||
fs::create_directories(directory);
|
||||
}
|
||||
writePacks(world);
|
||||
}
|
||||
auto packs = files::read_list(file);
|
||||
packs.push_back(id);
|
||||
|
||||
std::stringstream ss;
|
||||
@ -559,5 +590,5 @@ void WorldFiles::addPack(const std::string& id) {
|
||||
for (const auto& pack : packs) {
|
||||
ss << pack << "\n";
|
||||
}
|
||||
files::write_string(getPacksFile(), ss.str());
|
||||
files::write_string(file, ss.str());
|
||||
}
|
||||
|
||||
@ -15,9 +15,14 @@
|
||||
#include "../typedefs.h"
|
||||
#include "../settings.h"
|
||||
|
||||
#include "../voxels/Chunk.h"
|
||||
|
||||
const uint REGION_HEADER_SIZE = 10;
|
||||
|
||||
const uint REGION_LAYER_VOXELS = 0;
|
||||
const uint REGION_LAYER_LIGHTS = 1;
|
||||
const uint REGION_LAYER_INVENTORIES = 2;
|
||||
|
||||
const uint REGION_SIZE_BIT = 5;
|
||||
const uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
|
||||
const uint REGION_CHUNKS_COUNT = ((REGION_SIZE) * (REGION_SIZE));
|
||||
@ -29,7 +34,6 @@ const uint MAX_OPEN_REGION_FILES = 16;
|
||||
#define WORLD_FORMAT_MAGIC ".VOXWLD"
|
||||
|
||||
class Player;
|
||||
class Chunk;
|
||||
class Content;
|
||||
class ContentIndices;
|
||||
class World;
|
||||
@ -74,8 +78,7 @@ class WorldFiles {
|
||||
std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles;
|
||||
|
||||
void writeWorldInfo(const World* world);
|
||||
fs::path getLightsFolder() const;
|
||||
fs::path getRegionFilename(int x, int y) const;
|
||||
fs::path getRegionFilename(int x, int y) const;
|
||||
fs::path getWorldFile() const;
|
||||
fs::path getIndicesFile() const;
|
||||
fs::path getPacksFile() const;
|
||||
@ -107,15 +110,19 @@ class WorldFiles {
|
||||
|
||||
ubyte* getData(regionsmap& regions,
|
||||
const fs::path& folder,
|
||||
int x, int z, int layer);
|
||||
int x, int z, int layer, bool compression);
|
||||
|
||||
regfile* getRegFile(glm::ivec3 coord, const fs::path& folder);
|
||||
|
||||
fs::path getLightsFolder() const;
|
||||
fs::path getInventoriesFolder() const;
|
||||
public:
|
||||
static bool parseRegionFilename(const std::string& name, int& x, int& y);
|
||||
fs::path getRegionsFolder() const;
|
||||
fs::path getPlayerFile() const;
|
||||
|
||||
regionsmap regions;
|
||||
regionsmap storages;
|
||||
regionsmap lights;
|
||||
fs::path directory;
|
||||
std::unique_ptr<ubyte[]> compressionBuffer;
|
||||
@ -133,6 +140,7 @@ public:
|
||||
|
||||
ubyte* getChunk(int x, int z);
|
||||
light_t* getLights(int x, int z);
|
||||
chunk_inventories_map fetchInventories(int x, int z);
|
||||
|
||||
bool readWorldInfo(World* world);
|
||||
bool readPlayer(Player* player);
|
||||
@ -147,7 +155,7 @@ public:
|
||||
void writePacks(const World* world);
|
||||
void writeIndices(const ContentIndices* indices);
|
||||
/* Append pack to packs.list without duplicate check */
|
||||
void addPack(const std::string& id);
|
||||
void addPack(const World* world, const std::string& id);
|
||||
|
||||
static const char* WORLD_FILE;
|
||||
};
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
#include "engine_paths.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "WorldFiles.h"
|
||||
|
||||
#define SCREENSHOTS_FOLDER "screenshots"
|
||||
const fs::path SCREENSHOTS_FOLDER {"screenshots"};
|
||||
|
||||
fs::path EnginePaths::getUserfiles() const {
|
||||
return userfiles;
|
||||
@ -30,10 +31,10 @@ fs::path EnginePaths::getScreenshotFile(std::string ext) {
|
||||
ss << std::put_time(&tm, format);
|
||||
std::string datetimestr = ss.str();
|
||||
|
||||
fs::path filename = folder/fs::path("screenshot-"+datetimestr+"."+ext);
|
||||
fs::path filename = folder/fs::u8path("screenshot-"+datetimestr+"."+ext);
|
||||
uint index = 0;
|
||||
while (fs::exists(filename)) {
|
||||
filename = folder/fs::path("screenshot-"+datetimestr+"-"+std::to_string(index)+"."+ext);
|
||||
filename = folder/fs::u8path("screenshot-"+datetimestr+"-"+std::to_string(index)+"."+ext);
|
||||
index++;
|
||||
}
|
||||
return filename;
|
||||
@ -59,12 +60,17 @@ std::vector<fs::path> EnginePaths::scanForWorlds() {
|
||||
continue;
|
||||
}
|
||||
fs::path worldFolder = entry.path();
|
||||
fs::path worldFile = worldFolder/fs::path(WorldFiles::WORLD_FILE);
|
||||
fs::path worldFile = worldFolder/fs::u8path(WorldFiles::WORLD_FILE);
|
||||
if (!fs::is_regular_file(worldFile)) {
|
||||
continue;
|
||||
}
|
||||
folders.push_back(worldFolder);
|
||||
}
|
||||
std::sort(folders.begin(), folders.end(), [](fs::path a, fs::path b) {
|
||||
a = a/fs::u8path(WorldFiles::WORLD_FILE);
|
||||
b = b/fs::u8path(WorldFiles::WORLD_FILE);
|
||||
return fs::last_write_time(a) > fs::last_write_time(b);
|
||||
});
|
||||
return folders;
|
||||
}
|
||||
|
||||
@ -88,34 +94,58 @@ void EnginePaths::setContentPacks(std::vector<ContentPack>* contentPacks) {
|
||||
this->contentPacks = contentPacks;
|
||||
}
|
||||
|
||||
static fs::path toCanonic(fs::path path) {
|
||||
std::stack<std::string> parts;
|
||||
path = path.lexically_normal();
|
||||
while (true) {
|
||||
parts.push(path.filename().u8string());
|
||||
path = path.parent_path();
|
||||
if (path.empty())
|
||||
break;
|
||||
}
|
||||
path = fs::u8path("");
|
||||
while (!parts.empty()) {
|
||||
const std::string part = parts.top();
|
||||
parts.pop();
|
||||
if (part == ".") {
|
||||
continue;
|
||||
}
|
||||
if (part == "..") {
|
||||
throw files_access_error("entry point reached");
|
||||
}
|
||||
|
||||
path = path / fs::path(part);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
fs::path EnginePaths::resolve(std::string path) {
|
||||
size_t separator = path.find(':');
|
||||
if (separator == std::string::npos) {
|
||||
return fs::path(path);
|
||||
throw files_access_error("no entry point specified");
|
||||
}
|
||||
std::string prefix = path.substr(0, separator);
|
||||
std::string filename = path.substr(separator+1);
|
||||
filename = toCanonic(fs::u8path(filename)).u8string();
|
||||
|
||||
if (prefix == "res" || prefix == "core") {
|
||||
return resources/fs::path(filename);
|
||||
return resources/fs::u8path(filename);
|
||||
}
|
||||
|
||||
if (prefix == "user") {
|
||||
return userfiles/fs::path(filename);
|
||||
return userfiles/fs::u8path(filename);
|
||||
}
|
||||
|
||||
if (prefix == "world") {
|
||||
return worldFolder/fs::path(filename);
|
||||
return worldFolder/fs::u8path(filename);
|
||||
}
|
||||
|
||||
if (contentPacks) {
|
||||
for (auto& pack : *contentPacks) {
|
||||
if (pack.id == prefix) {
|
||||
return pack.folder/fs::path(filename);
|
||||
return pack.folder/fs::u8path(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fs::path("./"+filename);
|
||||
throw files_access_error("unknown entry point '"+prefix+"'");
|
||||
}
|
||||
|
||||
ResPaths::ResPaths(fs::path mainRoot, std::vector<fs::path> roots)
|
||||
@ -125,19 +155,19 @@ ResPaths::ResPaths(fs::path mainRoot, std::vector<fs::path> roots)
|
||||
fs::path ResPaths::find(const std::string& filename) const {
|
||||
for (int i = roots.size()-1; i >= 0; i--) {
|
||||
auto& root = roots[i];
|
||||
fs::path file = root / fs::path(filename);
|
||||
fs::path file = root / fs::u8path(filename);
|
||||
if (fs::exists(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return mainRoot / fs::path(filename);
|
||||
return mainRoot / fs::u8path(filename);
|
||||
}
|
||||
|
||||
std::vector<fs::path> ResPaths::listdir(const std::string& folderName) const {
|
||||
std::vector<fs::path> entries;
|
||||
for (int i = roots.size()-1; i >= 0; i--) {
|
||||
auto& root = roots[i];
|
||||
fs::path folder = root / fs::path(folderName);
|
||||
fs::path folder = root / fs::u8path(folderName);
|
||||
if (!fs::is_directory(folder))
|
||||
continue;
|
||||
for (const auto& entry : fs::directory_iterator(folder)) {
|
||||
@ -145,7 +175,7 @@ std::vector<fs::path> ResPaths::listdir(const std::string& folderName) const {
|
||||
}
|
||||
}
|
||||
{
|
||||
fs::path folder = mainRoot / fs::path(folderName);
|
||||
fs::path folder = mainRoot / fs::u8path(folderName);
|
||||
if (!fs::is_directory(folder))
|
||||
return entries;
|
||||
for (const auto& entry : fs::directory_iterator(folder)) {
|
||||
@ -154,3 +184,7 @@ std::vector<fs::path> ResPaths::listdir(const std::string& folderName) const {
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
const fs::path& ResPaths::getMainRoot() const {
|
||||
return mainRoot;
|
||||
}
|
||||
|
||||
@ -3,12 +3,18 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../content/ContentPack.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class files_access_error : public std::runtime_error {
|
||||
public:
|
||||
files_access_error(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class EnginePaths {
|
||||
fs::path userfiles {"."};
|
||||
fs::path resources {"res"};
|
||||
@ -42,6 +48,8 @@ public:
|
||||
|
||||
fs::path find(const std::string& filename) const;
|
||||
std::vector<fs::path> listdir(const std::string& folder) const;
|
||||
|
||||
const fs::path& getMainRoot() const;
|
||||
};
|
||||
|
||||
#endif // FILES_ENGINE_PATHS_H_
|
||||
#endif // FILES_ENGINE_PATHS_H_
|
||||
|
||||
@ -99,10 +99,7 @@ bool files::write_json(fs::path filename, const dynamic::Map* obj, bool nice) {
|
||||
}
|
||||
|
||||
bool files::write_binary_json(fs::path filename, const dynamic::Map* obj, bool compression) {
|
||||
auto bytes = json::to_binary(obj);
|
||||
if (compression) {
|
||||
bytes = gzip::compress(bytes.data(), bytes.size());
|
||||
}
|
||||
auto bytes = json::to_binary(obj, compression);
|
||||
return files::write_bytes(filename, bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ toml::Wrapper* create_wrapper(EngineSettings& settings) {
|
||||
camera.add("sensitivity", &settings.camera.sensitivity);
|
||||
|
||||
toml::Section& graphics = wrapper->add("graphics");
|
||||
graphics.add("gamma", &settings.graphics.gamma);
|
||||
graphics.add("fog-curve", &settings.graphics.fogCurve);
|
||||
graphics.add("backlight", &settings.graphics.backlight);
|
||||
graphics.add("frustum-culling", &settings.graphics.frustumCulling);
|
||||
|
||||
@ -4,38 +4,49 @@
|
||||
|
||||
#include "../assets/Assets.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../content/ContentPack.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../core_defs.h"
|
||||
#include "UiDocument.h"
|
||||
|
||||
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) {
|
||||
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : content(content) {
|
||||
auto indices = content->getIndices();
|
||||
sideregions = new UVRegion[indices->countBlockDefs() * 6];
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
for (uint i = 0; i < indices->countBlockDefs(); i++) {
|
||||
Block* def = indices->getBlockDef(i);
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
std::string tex = def->textureFaces[side];
|
||||
if (atlas->has(tex)) {
|
||||
sideregions[i * 6 + side] = atlas->get(tex);
|
||||
} else {
|
||||
if (atlas->has("notfound"))
|
||||
sideregions[i * 6 + side] = atlas->get("notfound");
|
||||
}
|
||||
}
|
||||
for (uint side = 0; side < def->modelTextures.size(); side++)
|
||||
{
|
||||
std::string tex = def->modelTextures[side];
|
||||
if (atlas->has(tex)) {
|
||||
def->modelUVs.push_back(atlas->get(tex));
|
||||
} else {
|
||||
if (atlas->has("notfound"))
|
||||
def->modelUVs.push_back(atlas->get("notfound"));
|
||||
}
|
||||
}
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->countBlockDefs() * 6);
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
for (uint i = 0; i < indices->countBlockDefs(); i++) {
|
||||
Block* def = indices->getBlockDef(i);
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
const std::string& tex = def->textureFaces[side];
|
||||
if (atlas->has(tex)) {
|
||||
sideregions[i * 6 + side] = atlas->get(tex);
|
||||
} else if (atlas->has(TEXTURE_NOTFOUND)) {
|
||||
sideregions[i * 6 + side] = atlas->get(TEXTURE_NOTFOUND);
|
||||
}
|
||||
}
|
||||
for (uint side = 0; side < def->modelTextures.size(); side++) {
|
||||
const std::string& tex = def->modelTextures[side];
|
||||
if (atlas->has(tex)) {
|
||||
def->modelUVs.push_back(atlas->get(tex));
|
||||
} else if (atlas->has(TEXTURE_NOTFOUND)) {
|
||||
def->modelUVs.push_back(atlas->get(TEXTURE_NOTFOUND));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentGfxCache::~ContentGfxCache() {
|
||||
delete[] sideregions;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiDocument> ContentGfxCache::getLayout(const std::string& id) {
|
||||
auto found = layouts.find(id);
|
||||
if (found == layouts.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
const Content* ContentGfxCache::getContent() const {
|
||||
return content;
|
||||
}
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
#ifndef FRONTEND_BLOCKS_GFX_CACHE_H_
|
||||
#define FRONTEND_BLOCKS_GFX_CACHE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "../graphics/UVRegion.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
class Content;
|
||||
class Assets;
|
||||
class UiDocument;
|
||||
|
||||
using uidocuments_map = std::unordered_map<std::string, std::shared_ptr<UiDocument>>;
|
||||
|
||||
class ContentGfxCache {
|
||||
const Content* content;
|
||||
// array of block sides uv regions (6 per block)
|
||||
UVRegion* sideregions;
|
||||
std::unique_ptr<UVRegion[]> sideregions;
|
||||
// all loaded layouts
|
||||
uidocuments_map layouts;
|
||||
public:
|
||||
ContentGfxCache(const Content* content, Assets* assets);
|
||||
~ContentGfxCache();
|
||||
@ -17,6 +26,10 @@ public:
|
||||
inline const UVRegion& getRegion(blockid_t id, int side) const {
|
||||
return sideregions[id * 6 + side];
|
||||
}
|
||||
|
||||
std::shared_ptr<UiDocument> getLayout(const std::string& id);
|
||||
|
||||
const Content* getContent() const;
|
||||
};
|
||||
|
||||
#endif // FRONTEND_BLOCKS_GFX_CACHE_H_
|
||||
|
||||
@ -19,66 +19,40 @@
|
||||
#include "../maths/voxmaths.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../frontend/gui/containers.h"
|
||||
#include "../frontend/gui/controls.h"
|
||||
#include "../util/stringutil.h"
|
||||
|
||||
InventoryLayout::InventoryLayout(glm::vec2 size) : size(size) {}
|
||||
|
||||
void InventoryLayout::add(SlotLayout slot) {
|
||||
slots.push_back(slot);
|
||||
}
|
||||
|
||||
void InventoryLayout::add(InventoryPanel panel) {
|
||||
panels.push_back(panel);
|
||||
}
|
||||
|
||||
void InventoryLayout::setSize(glm::vec2 size) {
|
||||
this->size = size;
|
||||
}
|
||||
|
||||
void InventoryLayout::setOrigin(glm::vec2 origin) {
|
||||
this->origin = origin;
|
||||
}
|
||||
|
||||
glm::vec2 InventoryLayout::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
glm::vec2 InventoryLayout::getOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
std::vector<SlotLayout>& InventoryLayout::getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
std::vector<InventoryPanel>& InventoryLayout::getPanels() {
|
||||
return panels;
|
||||
}
|
||||
#include "../world/Level.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
SlotLayout::SlotLayout(
|
||||
int index,
|
||||
glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
itemsharefunc shareFunc,
|
||||
slotcallback shareFunc,
|
||||
slotcallback rightClick
|
||||
)
|
||||
: position(position),
|
||||
: index(index),
|
||||
position(position),
|
||||
background(background),
|
||||
itemSource(itemSource),
|
||||
shareFunc(shareFunc),
|
||||
rightClick(rightClick) {}
|
||||
|
||||
InventoryPanel::InventoryPanel(
|
||||
glm::vec2 position,
|
||||
glm::vec2 size,
|
||||
glm::vec4 color)
|
||||
: position(position), size(size), color(color) {}
|
||||
|
||||
InventoryBuilder::InventoryBuilder()
|
||||
: layout(std::make_unique<InventoryLayout>(glm::vec2()))
|
||||
{}
|
||||
InventoryBuilder::InventoryBuilder() {
|
||||
view = std::make_shared<InventoryView>();
|
||||
}
|
||||
|
||||
/** Add slots grid to inventory view
|
||||
* @param cols grid columns
|
||||
* @param count total number of grid slots
|
||||
* @param coord position of the first slot of the grid
|
||||
* @param padding additional space around the grid
|
||||
* @param addpanel automatically create panel behind the grid
|
||||
* with size including padding
|
||||
* @param slotLayout slot settings (index and position are ignored)
|
||||
*/
|
||||
void InventoryBuilder::addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 coord,
|
||||
@ -94,14 +68,20 @@ void InventoryBuilder::addGrid(
|
||||
uint width = cols * (slotSize + interval) - interval + padding*2;
|
||||
uint height = rows * (slotSize + interval) - interval + padding*2;
|
||||
|
||||
auto lsize = layout->getSize();
|
||||
if (coord.x + width > lsize.x) {
|
||||
lsize.x = coord.x + width;
|
||||
glm::vec2 vsize = view->getSize();
|
||||
if (coord.x + width > vsize.x) {
|
||||
vsize.x = coord.x + width;
|
||||
}
|
||||
if (coord.y + height > lsize.y) {
|
||||
lsize.y = coord.y + height;
|
||||
if (coord.y + height > vsize.y) {
|
||||
vsize.y = coord.y + height;
|
||||
}
|
||||
view->setSize(vsize);
|
||||
|
||||
if (addpanel) {
|
||||
auto panel = std::make_shared<gui::Container>(coord, glm::vec2(width, height));
|
||||
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f));
|
||||
view->add(panel);
|
||||
}
|
||||
layout->setSize(lsize);
|
||||
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
@ -112,70 +92,47 @@ void InventoryBuilder::addGrid(
|
||||
row * (slotSize + interval) + padding
|
||||
);
|
||||
auto builtSlot = slotLayout;
|
||||
builtSlot.index = row * cols + col;
|
||||
builtSlot.position = position;
|
||||
layout->add(builtSlot);
|
||||
add(builtSlot);
|
||||
}
|
||||
}
|
||||
|
||||
if (addpanel) {
|
||||
add(InventoryPanel(
|
||||
coord,
|
||||
glm::vec2(width, height),
|
||||
glm::vec4(0, 0, 0, 0.5f)));
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryBuilder::add(SlotLayout slotLayout) {
|
||||
uint width = InventoryView::SLOT_SIZE;
|
||||
uint height = InventoryView::SLOT_SIZE;
|
||||
|
||||
auto coord = slotLayout.position;
|
||||
auto lsize = layout->getSize();
|
||||
if (coord.x + width > lsize.x) {
|
||||
lsize.x = coord.x + width;
|
||||
}
|
||||
if (coord.y + height > lsize.y) {
|
||||
lsize.y = coord.y + height;
|
||||
}
|
||||
layout->add(slotLayout);
|
||||
void InventoryBuilder::add(SlotLayout layout) {
|
||||
view->add(view->addSlot(layout), layout.position);
|
||||
}
|
||||
|
||||
void InventoryBuilder::add(InventoryPanel panel) {
|
||||
layout->add(panel);
|
||||
}
|
||||
|
||||
std::unique_ptr<InventoryLayout> InventoryBuilder::build() {
|
||||
return std::unique_ptr<InventoryLayout>(layout.release());
|
||||
std::shared_ptr<InventoryView> InventoryBuilder::build() {
|
||||
return view;
|
||||
}
|
||||
|
||||
SlotView::SlotView(
|
||||
ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
const Content* content,
|
||||
SlotLayout layout)
|
||||
: UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
frontend(frontend),
|
||||
interaction(interaction),
|
||||
content(content),
|
||||
stack(stack),
|
||||
layout(layout) {
|
||||
color(glm::vec4(0, 0, 0, 0.2f));
|
||||
SlotLayout layout
|
||||
) : UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
layout(layout)
|
||||
{
|
||||
setColor(glm::vec4(0, 0, 0, 0.2f));
|
||||
}
|
||||
|
||||
// performance disaster
|
||||
void SlotView::draw(Batch2D* batch, Assets* assets) {
|
||||
void SlotView::draw(const GfxContext* pctx, Assets* assets) {
|
||||
if (bound == nullptr)
|
||||
return;
|
||||
|
||||
ItemStack& stack = *bound;
|
||||
|
||||
glm::vec2 coord = calcCoord();
|
||||
|
||||
int slotSize = InventoryView::SLOT_SIZE;
|
||||
|
||||
glm::vec4 tint(1.0f);
|
||||
glm::vec4 color = color_;
|
||||
if (hover_ || highlighted) {
|
||||
glm::vec4 color = getColor();
|
||||
if (hover || highlighted) {
|
||||
tint *= 1.333f;
|
||||
color = glm::vec4(1, 1, 1, 0.2f);
|
||||
}
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->color = color;
|
||||
if (color.a > 0.0) {
|
||||
batch->texture(nullptr);
|
||||
@ -196,10 +153,10 @@ void SlotView::draw(Batch2D* batch, Assets* assets) {
|
||||
case item_icon_type::none:
|
||||
break;
|
||||
case item_icon_type::block: {
|
||||
Block* cblock = content->requireBlock(item->icon);
|
||||
const Block& cblock = content->requireBlock(item->icon);
|
||||
batch->texture(previews->getTexture());
|
||||
|
||||
UVRegion region = previews->get(cblock->name);
|
||||
UVRegion region = previews->get(cblock.name);
|
||||
batch->rect(
|
||||
coord.x, coord.y, slotSize, slotSize,
|
||||
0, 0, 0, region, false, true, tint);
|
||||
@ -249,11 +206,16 @@ bool SlotView::isHighlighted() const {
|
||||
}
|
||||
|
||||
void SlotView::clicked(gui::GUI* gui, int button) {
|
||||
if (bound == nullptr)
|
||||
return;
|
||||
|
||||
ItemStack& grabbed = interaction->getGrabbedItem();
|
||||
ItemStack& stack = *bound;
|
||||
|
||||
if (button == mousecode::BUTTON_1) {
|
||||
if (Events::pressed(keycode::LEFT_SHIFT)) {
|
||||
if (layout.shareFunc) {
|
||||
layout.shareFunc(stack);
|
||||
layout.shareFunc(layout.index, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -272,7 +234,7 @@ void SlotView::clicked(gui::GUI* gui, int button) {
|
||||
}
|
||||
} else if (button == mousecode::BUTTON_2) {
|
||||
if (layout.rightClick) {
|
||||
layout.rightClick(stack, grabbed);
|
||||
layout.rightClick(inventoryid, stack);
|
||||
return;
|
||||
}
|
||||
if (layout.itemSource)
|
||||
@ -288,50 +250,90 @@ void SlotView::clicked(gui::GUI* gui, int button) {
|
||||
if (stack.isEmpty()) {
|
||||
stack.set(grabbed);
|
||||
stack.setCount(1);
|
||||
} else {
|
||||
grabbed.setCount(grabbed.getCount()-1);
|
||||
} else if (stack.accepts(grabbed)){
|
||||
stack.setCount(stack.getCount()+1);
|
||||
grabbed.setCount(grabbed.getCount()-1);
|
||||
}
|
||||
grabbed.setCount(grabbed.getCount()-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InventoryView::InventoryView(
|
||||
const Content* content,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
std::unique_ptr<InventoryLayout> layout)
|
||||
: Container(glm::vec2(), glm::vec2()),
|
||||
content(content),
|
||||
indices(content->getIndices()),
|
||||
inventory(inventory),
|
||||
layout(std::move(layout)),
|
||||
frontend(frontend),
|
||||
interaction(interaction) {
|
||||
size(this->layout->getSize());
|
||||
color(glm::vec4(0, 0, 0, 0.0f));
|
||||
void SlotView::focus(gui::GUI* gui) {
|
||||
clicked(gui, 0);
|
||||
}
|
||||
|
||||
void SlotView::bind(
|
||||
int64_t inventoryid,
|
||||
ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
) {
|
||||
this->inventoryid = inventoryid;
|
||||
bound = &stack;
|
||||
content = frontend->getLevel()->content;
|
||||
this->frontend = frontend;
|
||||
this->interaction = interaction;
|
||||
}
|
||||
|
||||
const SlotLayout& SlotView::getLayout() const {
|
||||
return layout;
|
||||
}
|
||||
|
||||
InventoryView::InventoryView() : Container(glm::vec2(), glm::vec2()) {
|
||||
setColor(glm::vec4(0, 0, 0, 0.0f));
|
||||
}
|
||||
|
||||
InventoryView::~InventoryView() {}
|
||||
|
||||
void InventoryView::build() {
|
||||
size_t index = 0;
|
||||
for (auto& slot : layout->getSlots()) {
|
||||
if (index >= inventory->size())
|
||||
break;
|
||||
|
||||
ItemStack& item = inventory->getSlot(index);
|
||||
std::shared_ptr<SlotView> InventoryView::addSlot(SlotLayout layout) {
|
||||
uint width = InventoryView::SLOT_SIZE + layout.padding;
|
||||
uint height = InventoryView::SLOT_SIZE + layout.padding;
|
||||
|
||||
auto view = std::make_shared<SlotView>(
|
||||
item, frontend, interaction, content, slot
|
||||
auto coord = layout.position;
|
||||
auto vsize = getSize();
|
||||
if (coord.x + width > vsize.x) {
|
||||
vsize.x = coord.x + width;
|
||||
}
|
||||
if (coord.y + height > vsize.y) {
|
||||
vsize.y = coord.y + height;
|
||||
}
|
||||
setSize(vsize);
|
||||
|
||||
auto slot = std::make_shared<SlotView>(layout);
|
||||
if (!layout.background) {
|
||||
slot->setColor(glm::vec4());
|
||||
}
|
||||
slots.push_back(slot.get());
|
||||
return slot;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> InventoryView::getInventory() const {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
|
||||
size_t InventoryView::getSlotsCount() const {
|
||||
return slots.size();
|
||||
}
|
||||
|
||||
void InventoryView::bind(
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
) {
|
||||
this->frontend = frontend;
|
||||
this->interaction = interaction;
|
||||
this->inventory = inventory;
|
||||
content = frontend->getLevel()->content;
|
||||
indices = content->getIndices();
|
||||
for (auto slot : slots) {
|
||||
slot->bind(
|
||||
inventory->getId(),
|
||||
inventory->getSlot(slot->getLayout().index),
|
||||
frontend, interaction
|
||||
);
|
||||
if (!slot.background) {
|
||||
view->color(glm::vec4());
|
||||
}
|
||||
slots.push_back(view.get());
|
||||
add(view, slot.position);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,26 +345,119 @@ void InventoryView::setSelected(int index) {
|
||||
}
|
||||
|
||||
void InventoryView::setCoord(glm::vec2 coord) {
|
||||
Container::setCoord(coord - layout->getOrigin());
|
||||
Container::setCoord(coord - origin);
|
||||
}
|
||||
|
||||
void InventoryView::setOrigin(glm::vec2 origin) {
|
||||
this->origin = origin;
|
||||
}
|
||||
|
||||
glm::vec2 InventoryView::getOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
void InventoryView::setInventory(std::shared_ptr<Inventory> inventory) {
|
||||
this->inventory = inventory;
|
||||
}
|
||||
|
||||
InventoryLayout* InventoryView::getLayout() const {
|
||||
return layout.get();
|
||||
#include "../coders/xml.h"
|
||||
#include "gui/gui_xml.h"
|
||||
|
||||
static slotcallback readSlotFunc(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelement& element, const std::string& attr) {
|
||||
auto consumer = scripting::create_int_array_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr(attr).getText()
|
||||
);
|
||||
return [=](uint slot, ItemStack& stack) {
|
||||
int args[] {int(view->getInventory()->getId()), int(slot)};
|
||||
consumer(args, 2);
|
||||
};
|
||||
}
|
||||
|
||||
void InventoryView::drawBackground(Batch2D* batch, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
static void readSlot(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelement element) {
|
||||
int index = element->attr("index", "0").asInt();
|
||||
bool itemSource = element->attr("item-source", "false").asBool();
|
||||
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr);
|
||||
if (element->has("coord")) {
|
||||
layout.position = element->attr("coord").asVec2();
|
||||
}
|
||||
if (element->has("sharefunc")) {
|
||||
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
|
||||
}
|
||||
if (element->has("onrightclick")) {
|
||||
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
|
||||
}
|
||||
auto slot = view->addSlot(layout);
|
||||
reader.readUINode(reader, element, *slot);
|
||||
view->add(slot);
|
||||
}
|
||||
|
||||
batch->texture(nullptr);
|
||||
static void readSlotsGrid(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelement element) {
|
||||
int startIndex = element->attr("start-index", "0").asInt();
|
||||
int rows = element->attr("rows", "0").asInt();
|
||||
int cols = element->attr("cols", "0").asInt();
|
||||
int count = element->attr("count", "0").asInt();
|
||||
const int slotSize = InventoryView::SLOT_SIZE;
|
||||
int interval = element->attr("interval", "-1").asInt();
|
||||
if (interval < 0) {
|
||||
interval = InventoryView::SLOT_INTERVAL;
|
||||
}
|
||||
int padding = element->attr("padding", "-1").asInt();
|
||||
if (padding < 0) {
|
||||
padding = interval;
|
||||
}
|
||||
if (rows == 0) {
|
||||
rows = ceildiv(count, cols);
|
||||
} else if (cols == 0) {
|
||||
cols = ceildiv(count, rows);
|
||||
} else if (count == 0) {
|
||||
count = rows * cols;
|
||||
}
|
||||
bool itemSource = element->attr("item-source", "false").asBool();
|
||||
SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr);
|
||||
if (element->has("pos")) {
|
||||
layout.position = element->attr("pos").asVec2();
|
||||
}
|
||||
if (element->has("sharefunc")) {
|
||||
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
|
||||
}
|
||||
if (element->has("onrightclick")) {
|
||||
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
|
||||
}
|
||||
layout.padding = padding;
|
||||
|
||||
for (auto& panel : layout->getPanels()) {
|
||||
glm::vec2 size = panel.size;
|
||||
glm::vec2 pos = coord + panel.position;
|
||||
batch->color = panel.color;
|
||||
batch->rect(pos.x-1, pos.y-1, size.x+2, size.y+2);
|
||||
int idx = 0;
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++, idx++) {
|
||||
if (idx >= count) {
|
||||
return;
|
||||
}
|
||||
SlotLayout slotLayout = layout;
|
||||
slotLayout.index = startIndex + idx;
|
||||
slotLayout.position += glm::vec2(
|
||||
padding + col * (slotSize + interval),
|
||||
padding + (rows-row-1) * (slotSize + interval)
|
||||
);
|
||||
auto slot = view->addSlot(slotLayout);
|
||||
view->add(slot, slotLayout.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryView::createReaders(gui::UiXmlReader& reader) {
|
||||
reader.add("inventory", [=](gui::UiXmlReader& reader, xml::xmlelement element) {
|
||||
auto view = std::make_shared<InventoryView>();
|
||||
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f)); // todo: fixme
|
||||
reader.addIgnore("slots-grid");
|
||||
reader.readUINode(reader, element, *view);
|
||||
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->getTag() == "slot") {
|
||||
readSlot(view.get(), reader, sub);
|
||||
} else if (sub->getTag() == "slots-grid") {
|
||||
readSlotsGrid(view.get(), reader, sub);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
});
|
||||
}
|
||||
|
||||
@ -6,12 +6,11 @@
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../frontend/gui/UINode.h"
|
||||
#include "../frontend/gui/panels.h"
|
||||
#include "../frontend/gui/containers.h"
|
||||
#include "../frontend/gui/controls.h"
|
||||
#include "../items/ItemStack.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
class Batch2D;
|
||||
class Assets;
|
||||
class GfxContext;
|
||||
class Content;
|
||||
@ -19,134 +18,15 @@ class ContentIndices;
|
||||
class LevelFrontend;
|
||||
class Inventory;
|
||||
|
||||
typedef std::function<void(ItemStack&)> itemsharefunc;
|
||||
typedef std::function<void(ItemStack&, ItemStack&)> slotcallback;
|
||||
namespace gui {
|
||||
class UiXmlReader;
|
||||
}
|
||||
|
||||
class InventoryInteraction;
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
struct SlotLayout {
|
||||
glm::vec2 position;
|
||||
bool background;
|
||||
bool itemSource;
|
||||
itemsharefunc shareFunc;
|
||||
slotcallback rightClick;
|
||||
|
||||
SlotLayout(glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
itemsharefunc shareFunc,
|
||||
slotcallback rightClick);
|
||||
};
|
||||
|
||||
// temporary unused
|
||||
struct InventoryPanel {
|
||||
glm::vec2 position;
|
||||
glm::vec2 size;
|
||||
glm::vec4 color;
|
||||
|
||||
InventoryPanel(glm::vec2 position,
|
||||
glm::vec2 size,
|
||||
glm::vec4 color);
|
||||
};
|
||||
|
||||
class InventoryLayout {
|
||||
glm::vec2 size {};
|
||||
glm::vec2 origin {};
|
||||
std::vector<SlotLayout> slots;
|
||||
std::vector<InventoryPanel> panels;
|
||||
public:
|
||||
InventoryLayout(glm::vec2 size);
|
||||
|
||||
void add(SlotLayout slot);
|
||||
void add(InventoryPanel panel);
|
||||
void setSize(glm::vec2 size);
|
||||
void setOrigin(glm::vec2 origin);
|
||||
|
||||
glm::vec2 getSize() const;
|
||||
glm::vec2 getOrigin() const;
|
||||
|
||||
std::vector<SlotLayout>& getSlots();
|
||||
std::vector<InventoryPanel>& getPanels();
|
||||
};
|
||||
|
||||
class InventoryBuilder {
|
||||
std::unique_ptr<InventoryLayout> layout;
|
||||
public:
|
||||
InventoryBuilder();
|
||||
|
||||
void addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 coord,
|
||||
int padding,
|
||||
bool addpanel,
|
||||
SlotLayout slotLayout);
|
||||
|
||||
void add(SlotLayout slotLayout);
|
||||
void add(InventoryPanel panel);
|
||||
|
||||
std::unique_ptr<InventoryLayout> build();
|
||||
};
|
||||
|
||||
class SlotView : public gui::UINode {
|
||||
LevelFrontend* frontend;
|
||||
InventoryInteraction* interaction;
|
||||
const Content* const content;
|
||||
ItemStack& stack;
|
||||
bool highlighted = false;
|
||||
|
||||
SlotLayout layout;
|
||||
public:
|
||||
SlotView(ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
const Content* content,
|
||||
SlotLayout layout);
|
||||
|
||||
virtual void draw(Batch2D* batch, Assets* assets) override;
|
||||
|
||||
void setHighlighted(bool flag);
|
||||
bool isHighlighted() const;
|
||||
|
||||
virtual void clicked(gui::GUI*, int) override;
|
||||
};
|
||||
|
||||
class InventoryView : public gui::Container {
|
||||
const Content* content;
|
||||
const ContentIndices* indices;
|
||||
|
||||
std::shared_ptr<Inventory> inventory;
|
||||
std::unique_ptr<InventoryLayout> layout;
|
||||
LevelFrontend* frontend;
|
||||
InventoryInteraction* interaction;
|
||||
|
||||
std::vector<SlotView*> slots;
|
||||
|
||||
int scroll = 0;
|
||||
public:
|
||||
InventoryView(
|
||||
const Content* content,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
std::unique_ptr<InventoryLayout> layout);
|
||||
|
||||
virtual ~InventoryView();
|
||||
|
||||
void build();
|
||||
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
|
||||
|
||||
void setInventory(std::shared_ptr<Inventory> inventory);
|
||||
|
||||
virtual void setCoord(glm::vec2 coord) override;
|
||||
|
||||
InventoryLayout* getLayout() const;
|
||||
|
||||
void setSelected(int index);
|
||||
|
||||
static const int SLOT_INTERVAL = 4;
|
||||
static const int SLOT_SIZE = ITEM_ICON_SIZE;
|
||||
};
|
||||
using slotcallback = std::function<void(uint, ItemStack&)>;
|
||||
|
||||
class InventoryInteraction {
|
||||
ItemStack grabbedItem;
|
||||
@ -158,4 +38,109 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct SlotLayout {
|
||||
int index;
|
||||
glm::vec2 position;
|
||||
bool background;
|
||||
bool itemSource;
|
||||
slotcallback shareFunc;
|
||||
slotcallback rightClick;
|
||||
int padding = 0;
|
||||
|
||||
SlotLayout(int index,
|
||||
glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
slotcallback shareFunc,
|
||||
slotcallback rightClick);
|
||||
};
|
||||
|
||||
class SlotView : public gui::UINode {
|
||||
LevelFrontend* frontend = nullptr;
|
||||
InventoryInteraction* interaction = nullptr;
|
||||
const Content* content;
|
||||
SlotLayout layout;
|
||||
bool highlighted = false;
|
||||
|
||||
int64_t inventoryid = 0;
|
||||
ItemStack* bound = nullptr;
|
||||
public:
|
||||
SlotView(SlotLayout layout);
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
void setHighlighted(bool flag);
|
||||
bool isHighlighted() const;
|
||||
|
||||
virtual void clicked(gui::GUI*, int) override;
|
||||
virtual void focus(gui::GUI*) override;
|
||||
|
||||
void bind(
|
||||
int64_t inventoryid,
|
||||
ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
);
|
||||
|
||||
const SlotLayout& getLayout() const;
|
||||
};
|
||||
|
||||
class InventoryView : public gui::Container {
|
||||
const Content* content;
|
||||
const ContentIndices* indices;
|
||||
|
||||
std::shared_ptr<Inventory> inventory;
|
||||
LevelFrontend* frontend = nullptr;
|
||||
InventoryInteraction* interaction = nullptr;
|
||||
|
||||
std::vector<SlotView*> slots;
|
||||
glm::vec2 origin {};
|
||||
public:
|
||||
InventoryView();
|
||||
virtual ~InventoryView();
|
||||
|
||||
void setInventory(std::shared_ptr<Inventory> inventory);
|
||||
|
||||
virtual void setCoord(glm::vec2 coord) override;
|
||||
|
||||
void setOrigin(glm::vec2 origin);
|
||||
glm::vec2 getOrigin() const;
|
||||
|
||||
void setSelected(int index);
|
||||
|
||||
void bind(
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
);
|
||||
|
||||
std::shared_ptr<SlotView> addSlot(SlotLayout layout);
|
||||
|
||||
std::shared_ptr<Inventory> getInventory() const;
|
||||
|
||||
size_t getSlotsCount() const;
|
||||
|
||||
static void createReaders(gui::UiXmlReader& reader);
|
||||
|
||||
static const int SLOT_INTERVAL = 4;
|
||||
static const int SLOT_SIZE = ITEM_ICON_SIZE;
|
||||
};
|
||||
|
||||
class InventoryBuilder {
|
||||
std::shared_ptr<InventoryView> view;
|
||||
public:
|
||||
InventoryBuilder();
|
||||
|
||||
void addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 coord,
|
||||
int padding,
|
||||
bool addpanel,
|
||||
SlotLayout slotLayout
|
||||
);
|
||||
|
||||
void add(SlotLayout slotLayout);
|
||||
std::shared_ptr<InventoryView> build();
|
||||
};
|
||||
|
||||
#endif // FRONTEND_INVENTORY_VIEW_H_
|
||||
|
||||
@ -24,5 +24,4 @@ public:
|
||||
Atlas* getBlocksAtlas() const;
|
||||
};
|
||||
|
||||
|
||||
#endif // FRONTEND_LEVEL_FRONTEND_H_
|
||||
|
||||
79
src/frontend/UiDocument.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "UiDocument.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "gui/UINode.h"
|
||||
#include "gui/containers.h"
|
||||
#include "InventoryView.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
#include "../files/files.h"
|
||||
#include "../frontend/gui/gui_xml.h"
|
||||
|
||||
UiDocument::UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
std::shared_ptr<gui::UINode> root,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
) : id(id), script(script), root(root), env(std::move(env)) {
|
||||
collect(map, root);
|
||||
}
|
||||
|
||||
|
||||
const uinodes_map& UiDocument::getMap() const {
|
||||
return map;
|
||||
}
|
||||
|
||||
const std::string& UiDocument::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
const std::shared_ptr<gui::UINode> UiDocument::getRoot() const {
|
||||
return root;
|
||||
}
|
||||
|
||||
const std::shared_ptr<gui::UINode> UiDocument::get(const std::string& id) const {
|
||||
auto found = map.find(id);
|
||||
if (found == map.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
const uidocscript& UiDocument::getScript() const {
|
||||
return script;
|
||||
}
|
||||
|
||||
int UiDocument::getEnvironment() const {
|
||||
return env->getId();
|
||||
}
|
||||
|
||||
void UiDocument::collect(uinodes_map& map, std::shared_ptr<gui::UINode> node) {
|
||||
const std::string& id = node->getId();
|
||||
if (!id.empty()) {
|
||||
map[id] = node;
|
||||
}
|
||||
auto container = dynamic_cast<gui::Container*>(node.get());
|
||||
if (container) {
|
||||
for (auto subnode : container->getNodes()) {
|
||||
collect(map, subnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<UiDocument> UiDocument::read(AssetsLoader& loader, int penv, std::string namesp, fs::path file) {
|
||||
const std::string text = files::read_string(file);
|
||||
auto xmldoc = xml::parse(file.u8string(), text);
|
||||
|
||||
auto env = scripting::create_doc_environment(penv, namesp);
|
||||
gui::UiXmlReader reader(*env, loader);
|
||||
InventoryView::createReaders(reader);
|
||||
auto view = reader.readXML(
|
||||
file.u8string(), xmldoc->getRoot()
|
||||
);
|
||||
view->setId("root");
|
||||
uidocscript script {};
|
||||
auto scriptFile = fs::path(file.u8string()+".lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_layout_script(env->getId(), namesp, scriptFile, script);
|
||||
}
|
||||
return std::make_unique<UiDocument>(namesp, script, view, std::move(env));
|
||||
}
|
||||
55
src/frontend/UiDocument.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef FRONTEND_UI_DOCUMENT_H_
|
||||
#define FRONTEND_UI_DOCUMENT_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace gui {
|
||||
class UINode;
|
||||
}
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
struct uidocscript {
|
||||
int environment;
|
||||
bool onopen : 1;
|
||||
bool onclose : 1;
|
||||
};
|
||||
|
||||
using uinodes_map = std::unordered_map<std::string, std::shared_ptr<gui::UINode>>;
|
||||
|
||||
class AssetsLoader;
|
||||
|
||||
class UiDocument {
|
||||
std::string id;
|
||||
uidocscript script;
|
||||
uinodes_map map;
|
||||
std::shared_ptr<gui::UINode> root;
|
||||
std::unique_ptr<scripting::Environment> env;
|
||||
public:
|
||||
UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
std::shared_ptr<gui::UINode> root,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
);
|
||||
|
||||
const std::string& getId() const;
|
||||
const uinodes_map& getMap() const;
|
||||
const std::shared_ptr<gui::UINode> getRoot() const;
|
||||
const std::shared_ptr<gui::UINode> get(const std::string& id) const;
|
||||
const uidocscript& getScript() const;
|
||||
int getEnvironment() const;
|
||||
/* Collect map of all uinodes having identifiers */
|
||||
static void collect(uinodes_map& map, std::shared_ptr<gui::UINode> node);
|
||||
|
||||
static std::unique_ptr<UiDocument> read(AssetsLoader& loader, int env, std::string namesp, fs::path file);
|
||||
};
|
||||
|
||||
#endif // FRONTEND_UI_DOCUMENT_H_
|
||||
@ -106,7 +106,7 @@ void WorldRenderer::drawChunks(Chunks* chunks,
|
||||
}
|
||||
float px = camera->position.x / (float)CHUNK_W;
|
||||
float pz = camera->position.z / (float)CHUNK_D;
|
||||
std::sort(indices.begin(), indices.end(), [this, chunks, px, pz](size_t i, size_t j) {
|
||||
std::sort(indices.begin(), indices.end(), [chunks, px, pz](size_t i, size_t j) {
|
||||
auto a = chunks->chunks[i];
|
||||
auto b = chunks->chunks[j];
|
||||
return ((a->x + 0.5f - px)*(a->x + 0.5f - px) +
|
||||
@ -157,7 +157,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool hudVisible
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_proj", camera->getProjection());
|
||||
shader->uniformMatrix("u_view", camera->getView());
|
||||
shader->uniform1f("u_gamma", 1.0f);
|
||||
shader->uniform1f("u_gamma", settings.graphics.gamma);
|
||||
shader->uniform1f("u_fogFactor", fogFactor);
|
||||
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve);
|
||||
shader->uniform3f("u_cameraPos", camera->position);
|
||||
|
||||
@ -39,7 +39,7 @@ Skybox::Skybox(uint size, Shader* shader)
|
||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||
};
|
||||
vattr attrs[] {2, 0};
|
||||
vattr attrs[] {{2}, {0}};
|
||||
mesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||
|
||||
sprites.push_back(skysprite {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "GUI.h"
|
||||
#include "UINode.h"
|
||||
#include "panels.h"
|
||||
#include "containers.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
@ -8,44 +8,41 @@
|
||||
#include "../../assets/Assets.h"
|
||||
#include "../../graphics/Batch2D.h"
|
||||
#include "../../graphics/Shader.h"
|
||||
#include "../../graphics/GfxContext.h"
|
||||
#include "../../window/Events.h"
|
||||
#include "../../window/input.h"
|
||||
#include "../../window/Camera.h"
|
||||
|
||||
using glm::vec2;
|
||||
using glm::vec3;
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
using namespace gui;
|
||||
|
||||
GUI::GUI() {
|
||||
container = new Container(vec2(0, 0), vec2(1000));
|
||||
|
||||
uicamera = new Camera(vec3(), Window::height);
|
||||
container = std::make_shared<Container>(glm::vec2(0, 0), glm::vec2(1000));
|
||||
uicamera = std::make_unique<Camera>(glm::vec3(), Window::height);
|
||||
uicamera->perspective = false;
|
||||
uicamera->flipped = true;
|
||||
|
||||
menu = new PagesControl();
|
||||
menu = std::make_shared<PagesControl>();
|
||||
container->add(menu);
|
||||
container->scrollable(false);
|
||||
container->setScrollable(false);
|
||||
}
|
||||
|
||||
GUI::~GUI() {
|
||||
delete uicamera;
|
||||
delete container;
|
||||
}
|
||||
|
||||
PagesControl* GUI::getMenu() {
|
||||
std::shared_ptr<PagesControl> GUI::getMenu() {
|
||||
return menu;
|
||||
}
|
||||
|
||||
/** Mouse related input and logic handling
|
||||
* @param delta delta time
|
||||
*/
|
||||
void GUI::actMouse(float delta) {
|
||||
auto hover = container->getAt(Events::cursor, nullptr);
|
||||
if (this->hover && this->hover != hover) {
|
||||
this->hover->hover(false);
|
||||
this->hover->setHover(false);
|
||||
}
|
||||
if (hover) {
|
||||
hover->hover(true);
|
||||
hover->setHover(true);
|
||||
if (Events::scroll) {
|
||||
hover->scrolled(Events::scroll);
|
||||
}
|
||||
@ -83,8 +80,11 @@ void GUI::actMouse(float delta) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Processing user input and UI logic
|
||||
* @param delta delta time
|
||||
*/
|
||||
void GUI::act(float delta) {
|
||||
container->size(vec2(Window::width, Window::height));
|
||||
container->setSize(glm::vec2(Window::width, Window::height));
|
||||
container->act(delta);
|
||||
auto prevfocus = focus;
|
||||
|
||||
@ -111,48 +111,47 @@ void GUI::act(float delta) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (focus && !focus->isfocused()) {
|
||||
if (focus && !focus->isFocused()) {
|
||||
focus = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::draw(Batch2D* batch, Assets* assets) {
|
||||
menu->setCoord((Window::size() - menu->size()) / 2.0f);
|
||||
uicamera->setFov(Window::height);
|
||||
void GUI::draw(const GfxContext* pctx, Assets* assets) {
|
||||
auto& viewport = pctx->getViewport();
|
||||
glm::vec2 wsize = viewport.size();
|
||||
|
||||
menu->setCoord((wsize - menu->getSize()) / 2.0f);
|
||||
uicamera->setFov(wsize.y);
|
||||
|
||||
Shader* uishader = assets->getShader("ui");
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjection()*uicamera->getView());
|
||||
|
||||
batch->begin();
|
||||
container->draw(batch, assets);
|
||||
pctx->getBatch2D()->begin();
|
||||
container->draw(pctx, assets);
|
||||
}
|
||||
|
||||
shared_ptr<UINode> GUI::getFocused() const {
|
||||
std::shared_ptr<UINode> GUI::getFocused() const {
|
||||
return focus;
|
||||
}
|
||||
|
||||
bool GUI::isFocusCaught() const {
|
||||
return focus && focus->isfocuskeeper();
|
||||
return focus && focus->isFocuskeeper();
|
||||
}
|
||||
|
||||
void GUI::addBack(std::shared_ptr<UINode> panel) {
|
||||
container->addBack(panel);
|
||||
void GUI::add(std::shared_ptr<UINode> node) {
|
||||
container->add(node);
|
||||
}
|
||||
|
||||
void GUI::add(shared_ptr<UINode> panel) {
|
||||
container->add(panel);
|
||||
void GUI::remove(std::shared_ptr<UINode> node) noexcept {
|
||||
container->remove(node);
|
||||
}
|
||||
|
||||
void GUI::remove(shared_ptr<UINode> panel) {
|
||||
container->remove(panel);
|
||||
}
|
||||
|
||||
void GUI::store(string name, shared_ptr<UINode> node) {
|
||||
void GUI::store(std::string name, std::shared_ptr<UINode> node) {
|
||||
storage[name] = node;
|
||||
}
|
||||
|
||||
shared_ptr<UINode> GUI::get(string name) {
|
||||
std::shared_ptr<UINode> GUI::get(std::string name) noexcept {
|
||||
auto found = storage.find(name);
|
||||
if (found == storage.end()) {
|
||||
return nullptr;
|
||||
@ -160,11 +159,11 @@ shared_ptr<UINode> GUI::get(string name) {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
void GUI::remove(string name) {
|
||||
void GUI::remove(std::string name) noexcept {
|
||||
storage.erase(name);
|
||||
}
|
||||
|
||||
void GUI::setFocus(shared_ptr<UINode> node) {
|
||||
void GUI::setFocus(std::shared_ptr<UINode> node) {
|
||||
if (focus) {
|
||||
focus->defocus();
|
||||
}
|
||||
@ -173,3 +172,7 @@ void GUI::setFocus(shared_ptr<UINode> node) {
|
||||
focus->focus(this);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Container> GUI::getContainer() const {
|
||||
return container;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
class Batch2D;
|
||||
class GfxContext;
|
||||
class Assets;
|
||||
class Camera;
|
||||
|
||||
@ -44,42 +44,76 @@ class Camera;
|
||||
*/
|
||||
|
||||
namespace gui {
|
||||
typedef std::function<void()> runnable;
|
||||
typedef std::function<void(const std::string&)> stringconsumer;
|
||||
|
||||
class UINode;
|
||||
class Container;
|
||||
class PagesControl;
|
||||
|
||||
/** The main UI controller */
|
||||
class GUI {
|
||||
Container* container;
|
||||
std::shared_ptr<Container> container;
|
||||
std::shared_ptr<UINode> hover = nullptr;
|
||||
std::shared_ptr<UINode> pressed = nullptr;
|
||||
std::shared_ptr<UINode> focus = nullptr;
|
||||
std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
|
||||
|
||||
Camera* uicamera;
|
||||
PagesControl* menu;
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
std::shared_ptr<PagesControl> menu;
|
||||
void actMouse(float delta);
|
||||
public:
|
||||
GUI();
|
||||
~GUI();
|
||||
|
||||
PagesControl* getMenu();
|
||||
/** Get the main menu (PagesControl) node */
|
||||
std::shared_ptr<PagesControl> getMenu();
|
||||
|
||||
/** Get current focused node
|
||||
* @return focused node or nullptr */
|
||||
std::shared_ptr<UINode> getFocused() const;
|
||||
|
||||
/** Check if all user input is caught by some element like TextBox */
|
||||
bool isFocusCaught() const;
|
||||
|
||||
/** Main input handling and logic update method
|
||||
* @param delta delta time */
|
||||
void act(float delta);
|
||||
void draw(Batch2D* batch, Assets* assets);
|
||||
void addBack(std::shared_ptr<UINode> panel);
|
||||
void add(std::shared_ptr<UINode> panel);
|
||||
void remove(std::shared_ptr<UINode> panel);
|
||||
|
||||
/** Draw all visible elements on main container
|
||||
* @param pctx parent graphics context
|
||||
* @param assets active assets storage */
|
||||
void draw(const GfxContext* pctx, Assets* assets);
|
||||
|
||||
/** Add node to the main container */
|
||||
void add(std::shared_ptr<UINode> node);
|
||||
|
||||
/** Remove node from the main container */
|
||||
void remove(std::shared_ptr<UINode> node) noexcept;
|
||||
|
||||
/** Store node in the GUI nodes dictionary
|
||||
* (does not add node to the main container)
|
||||
* @param name node key
|
||||
* @param node target node
|
||||
*/
|
||||
void store(std::string name, std::shared_ptr<UINode> node);
|
||||
std::shared_ptr<UINode> get(std::string name);
|
||||
void remove(std::string name);
|
||||
|
||||
/** Get node from the GUI nodes dictionary
|
||||
* @param name node key
|
||||
* @return stored node or nullptr
|
||||
*/
|
||||
std::shared_ptr<UINode> get(std::string name) noexcept;
|
||||
|
||||
/** Remove node from the GUI nodes dictionary
|
||||
* @param name node key
|
||||
*/
|
||||
void remove(std::string name) noexcept;
|
||||
|
||||
/** Set node as focused
|
||||
* @param node new focused node or nullptr to remove focus
|
||||
*/
|
||||
void setFocus(std::shared_ptr<UINode> node);
|
||||
|
||||
/** Get the main container */
|
||||
std::shared_ptr<Container> getContainer() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_GUI_H_
|
||||
#endif // FRONTEND_GUI_GUI_H_
|
||||
|
||||
@ -2,42 +2,37 @@
|
||||
|
||||
#include "../../graphics/Batch2D.h"
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
using gui::UINode;
|
||||
using gui::Align;
|
||||
|
||||
using glm::vec2;
|
||||
using glm::vec4;
|
||||
|
||||
UINode::UINode(vec2 coord, vec2 size) : coord(coord), size_(size) {
|
||||
UINode::UINode(glm::vec2 coord, glm::vec2 size) : coord(coord), size(size) {
|
||||
}
|
||||
|
||||
UINode::~UINode() {
|
||||
}
|
||||
|
||||
bool UINode::visible() const {
|
||||
return isvisible;
|
||||
bool UINode::isVisible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
void UINode::visible(bool flag) {
|
||||
isvisible = flag;
|
||||
void UINode::setVisible(bool flag) {
|
||||
visible = flag;
|
||||
}
|
||||
|
||||
Align UINode::align() const {
|
||||
return align_;
|
||||
Align UINode::getAlign() const {
|
||||
return align;
|
||||
}
|
||||
|
||||
void UINode::align(Align align) {
|
||||
align_ = align;
|
||||
void UINode::setAlign(Align align) {
|
||||
this->align = align;
|
||||
}
|
||||
|
||||
void UINode::hover(bool flag) {
|
||||
hover_ = flag;
|
||||
void UINode::setHover(bool flag) {
|
||||
hover = flag;
|
||||
}
|
||||
|
||||
bool UINode::hover() const {
|
||||
return hover_;
|
||||
bool UINode::isHover() const {
|
||||
return hover;
|
||||
}
|
||||
|
||||
void UINode::setParent(UINode* node) {
|
||||
@ -49,33 +44,33 @@ UINode* UINode::getParent() const {
|
||||
}
|
||||
|
||||
void UINode::click(GUI*, int x, int y) {
|
||||
pressed_ = true;
|
||||
pressed = true;
|
||||
}
|
||||
|
||||
void UINode::mouseRelease(GUI*, int x, int y) {
|
||||
pressed_ = false;
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
bool UINode::ispressed() const {
|
||||
return pressed_;
|
||||
bool UINode::isPressed() const {
|
||||
return pressed;
|
||||
}
|
||||
|
||||
void UINode::defocus() {
|
||||
focused_ = false;
|
||||
focused = false;
|
||||
}
|
||||
|
||||
bool UINode::isfocused() const {
|
||||
return focused_;
|
||||
bool UINode::isFocused() const {
|
||||
return focused;
|
||||
}
|
||||
|
||||
bool UINode::isInside(glm::vec2 pos) {
|
||||
vec2 coord = calcCoord();
|
||||
vec2 size = this->size();
|
||||
glm::vec2 coord = calcCoord();
|
||||
glm::vec2 size = getSize();
|
||||
return (pos.x >= coord.x && pos.y >= coord.y &&
|
||||
pos.x < coord.x + size.x && pos.y < coord.y + size.y);
|
||||
}
|
||||
|
||||
shared_ptr<UINode> UINode::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
std::shared_ptr<UINode> UINode::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -83,14 +78,22 @@ shared_ptr<UINode> UINode::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
}
|
||||
|
||||
bool UINode::isInteractive() const {
|
||||
return interactive && visible();
|
||||
return interactive && isVisible();
|
||||
}
|
||||
|
||||
void UINode::setInteractive(bool flag) {
|
||||
interactive = flag;
|
||||
}
|
||||
|
||||
vec2 UINode::calcCoord() const {
|
||||
void UINode::setResizing(bool flag) {
|
||||
resizing = flag;
|
||||
}
|
||||
|
||||
bool UINode::isResizing() const {
|
||||
return resizing;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::calcCoord() const {
|
||||
if (parent) {
|
||||
return coord + parent->calcCoord() + parent->contentOffset();
|
||||
}
|
||||
@ -103,41 +106,87 @@ void UINode::scrolled(int value) {
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setCoord(vec2 coord) {
|
||||
void UINode::setCoord(glm::vec2 coord) {
|
||||
this->coord = coord;
|
||||
}
|
||||
|
||||
vec2 UINode::size() const {
|
||||
return size_;
|
||||
glm::vec2 UINode::getCoord() const {
|
||||
return coord;
|
||||
}
|
||||
|
||||
void UINode::size(vec2 size) {
|
||||
if (sizelock)
|
||||
return;
|
||||
this->size_ = size;
|
||||
glm::vec2 UINode::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void UINode::_size(vec2 size) {
|
||||
if (sizelock)
|
||||
return;
|
||||
this->size_ = size;
|
||||
void UINode::setSize(glm::vec2 size) {
|
||||
this->size = glm::vec2(
|
||||
glm::max(minSize.x, size.x), glm::max(minSize.y, size.y)
|
||||
);
|
||||
}
|
||||
|
||||
void UINode::color(vec4 color) {
|
||||
this->color_ = color;
|
||||
glm::vec2 UINode::getMinSize() const {
|
||||
return minSize;
|
||||
}
|
||||
|
||||
vec4 UINode::color() const {
|
||||
return color_;
|
||||
void UINode::setMinSize(glm::vec2 minSize) {
|
||||
this->minSize = minSize;
|
||||
setSize(getSize());
|
||||
}
|
||||
|
||||
void UINode::margin(vec4 margin) {
|
||||
this->margin_ = margin;
|
||||
void UINode::setColor(glm::vec4 color) {
|
||||
this->color = color;
|
||||
this->hoverColor = color;
|
||||
}
|
||||
|
||||
vec4 UINode::margin() const {
|
||||
return margin_;
|
||||
void UINode::setHoverColor(glm::vec4 newColor) {
|
||||
this->hoverColor = newColor;
|
||||
}
|
||||
|
||||
glm::vec4 UINode::getHoverColor() const {
|
||||
return hoverColor;
|
||||
}
|
||||
|
||||
glm::vec4 UINode::getColor() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
void UINode::setMargin(glm::vec4 margin) {
|
||||
this->margin = margin;
|
||||
}
|
||||
|
||||
glm::vec4 UINode::getMargin() const {
|
||||
return margin;
|
||||
}
|
||||
|
||||
void UINode::setZIndex(int zindex) {
|
||||
this->zindex = zindex;
|
||||
}
|
||||
|
||||
int UINode::getZIndex() const {
|
||||
return zindex;
|
||||
}
|
||||
|
||||
void UINode::lock() {
|
||||
}
|
||||
}
|
||||
|
||||
vec2supplier UINode::getPositionFunc() const {
|
||||
return positionfunc;
|
||||
}
|
||||
|
||||
void UINode::setPositionFunc(vec2supplier func) {
|
||||
positionfunc = func;
|
||||
}
|
||||
|
||||
void UINode::setId(const std::string& id) {
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
const std::string& UINode::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
void UINode::reposition() {
|
||||
if (positionfunc) {
|
||||
setCoord(positionfunc());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,88 +4,142 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "../../delegates.h"
|
||||
|
||||
class Batch2D;
|
||||
class GfxContext;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
class UINode;
|
||||
class GUI;
|
||||
|
||||
typedef std::function<void(GUI*)> onaction;
|
||||
typedef std::function<void(GUI*, double)> onnumberchange;
|
||||
using onaction = std::function<void(GUI*)>;
|
||||
using onnumberchange = std::function<void(GUI*, double)>;
|
||||
|
||||
enum class Align {
|
||||
left, center, right
|
||||
};
|
||||
|
||||
class UINode {
|
||||
/**
|
||||
* element identifier used for direct access in UiDocument
|
||||
*/
|
||||
std::string id = "";
|
||||
protected:
|
||||
glm::vec2 coord;
|
||||
glm::vec2 size_;
|
||||
glm::vec4 color_ {1.0f};
|
||||
glm::vec4 margin_ {1.0f};
|
||||
bool isvisible = true;
|
||||
bool sizelock = false;
|
||||
bool hover_ = false;
|
||||
bool pressed_ = false;
|
||||
bool focused_ = false;
|
||||
glm::vec2 size;
|
||||
glm::vec2 minSize {1.0f};
|
||||
glm::vec4 color {1.0f};
|
||||
glm::vec4 hoverColor {1.0f};
|
||||
glm::vec4 margin {1.0f};
|
||||
bool visible = true;
|
||||
bool hover = false;
|
||||
bool pressed = false;
|
||||
bool focused = false;
|
||||
bool interactive = true;
|
||||
Align align_ = Align::left;
|
||||
bool resizing = true;
|
||||
int zindex = 0;
|
||||
Align align = Align::left;
|
||||
UINode* parent = nullptr;
|
||||
vec2supplier positionfunc = nullptr;
|
||||
UINode(glm::vec2 coord, glm::vec2 size);
|
||||
public:
|
||||
virtual ~UINode();
|
||||
/** Called every frame for all visible elements
|
||||
* @param delta delta time
|
||||
*/
|
||||
virtual void act(float delta) {};
|
||||
virtual void draw(Batch2D* batch, Assets* assets) = 0;
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) = 0;
|
||||
|
||||
virtual void visible(bool flag);
|
||||
bool visible() const;
|
||||
virtual void setVisible(bool flag);
|
||||
bool isVisible() const;
|
||||
|
||||
virtual void align(Align align);
|
||||
Align align() const;
|
||||
virtual void setAlign(Align align);
|
||||
Align getAlign() const;
|
||||
|
||||
virtual void hover(bool flag);
|
||||
bool hover() const;
|
||||
virtual void setHover(bool flag);
|
||||
bool isHover() const;
|
||||
|
||||
virtual void setParent(UINode* node);
|
||||
UINode* getParent() const;
|
||||
|
||||
virtual void color(glm::vec4 newColor);
|
||||
glm::vec4 color() const;
|
||||
/** Set element color (doesn't affect inner elements).
|
||||
Also replaces hover color to avoid adding extra properties. */
|
||||
virtual void setColor(glm::vec4 newColor);
|
||||
|
||||
virtual void margin(glm::vec4 margin);
|
||||
glm::vec4 margin() const;
|
||||
/** Get element color (float R,G,B,A in range [0.0, 1.0])*/
|
||||
glm::vec4 getColor() const;
|
||||
|
||||
virtual void focus(GUI*) {focused_ = true;}
|
||||
virtual void setHoverColor(glm::vec4 newColor);
|
||||
glm::vec4 getHoverColor() const;
|
||||
|
||||
virtual void setMargin(glm::vec4 margin);
|
||||
glm::vec4 getMargin() const;
|
||||
|
||||
/** Influences container elements sort order
|
||||
Doesn't work in Panel */
|
||||
virtual void setZIndex(int idx);
|
||||
int getZIndex() const;
|
||||
|
||||
virtual void focus(GUI*) {focused = true;}
|
||||
virtual void click(GUI*, int x, int y);
|
||||
virtual void clicked(GUI*, int button) {}
|
||||
virtual void mouseMove(GUI*, int x, int y) {};
|
||||
virtual void mouseRelease(GUI*, int x, int y);
|
||||
virtual void scrolled(int value);
|
||||
|
||||
bool ispressed() const;
|
||||
bool isPressed() const;
|
||||
void defocus();
|
||||
bool isfocused() const;
|
||||
virtual bool isfocuskeeper() const {return false;}
|
||||
bool isFocused() const;
|
||||
|
||||
/** Check if element catches all user input when focused */
|
||||
virtual bool isFocuskeeper() const {return false;}
|
||||
|
||||
virtual void typed(unsigned int codepoint) {};
|
||||
virtual void keyPressed(int key) {};
|
||||
|
||||
/** Check if screen position is inside of the element
|
||||
* @param pos screen position */
|
||||
virtual bool isInside(glm::vec2 pos);
|
||||
|
||||
/** Get element under the cursor.
|
||||
* @param pos cursor screen position
|
||||
* @param self shared pointer to element
|
||||
* @return self, sub-element or nullptr if element is not interractive */
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self);
|
||||
|
||||
/* Check if element is opaque for cursor */
|
||||
virtual bool isInteractive() const;
|
||||
/* Make the element opaque (true) or transparent (false) for cursor */
|
||||
virtual void setInteractive(bool flag);
|
||||
|
||||
virtual void setResizing(bool flag);
|
||||
virtual bool isResizing() const;
|
||||
|
||||
/* Get inner content offset. Used for scroll */
|
||||
virtual glm::vec2 contentOffset() {return glm::vec2(0.0f);};
|
||||
glm::vec2 calcCoord() const;
|
||||
/* Calculate screen position of the element */
|
||||
virtual glm::vec2 calcCoord() const;
|
||||
virtual void setCoord(glm::vec2 coord);
|
||||
virtual glm::vec2 size() const;
|
||||
virtual void size(glm::vec2 size);
|
||||
void _size(glm::vec2 size);
|
||||
virtual glm::vec2 getCoord() const;
|
||||
virtual glm::vec2 getSize() const;
|
||||
virtual void setSize(glm::vec2 size);
|
||||
virtual glm::vec2 getMinSize() const;
|
||||
virtual void setMinSize(glm::vec2 size);
|
||||
/* Called in containers when new element added */
|
||||
virtual void refresh() {};
|
||||
virtual void lock();
|
||||
|
||||
virtual vec2supplier getPositionFunc() const;
|
||||
virtual void setPositionFunc(vec2supplier);
|
||||
|
||||
void setId(const std::string& id);
|
||||
const std::string& getId() const;
|
||||
|
||||
/* Fetch coord from positionfunc if assigned */
|
||||
void reposition();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
304
src/frontend/gui/containers.cpp
Normal file
@ -0,0 +1,304 @@
|
||||
#include "containers.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../window/Window.h"
|
||||
#include "../../assets/Assets.h"
|
||||
#include "../../graphics/Batch2D.h"
|
||||
#include "../../graphics/GfxContext.h"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
Container::Container(glm::vec2 coord, glm::vec2 size) : UINode(coord, size) {
|
||||
actualLength = size.y;
|
||||
setColor(glm::vec4());
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isInside(pos)) return nullptr;
|
||||
|
||||
for (int i = nodes.size()-1; i >= 0; i--) {
|
||||
auto& node = nodes[i];
|
||||
if (!node->isVisible())
|
||||
continue;
|
||||
auto hover = node->getAt(pos, node);
|
||||
if (hover != nullptr) {
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
|
||||
void Container::act(float delta) {
|
||||
for (IntervalEvent& event : intervalEvents) {
|
||||
event.timer += delta;
|
||||
if (event.timer > event.interval) {
|
||||
event.callback();
|
||||
event.timer = fmod(event.timer, event.interval);
|
||||
if (event.repeat > 0) {
|
||||
event.repeat--;
|
||||
}
|
||||
}
|
||||
}
|
||||
intervalEvents.erase(std::remove_if(
|
||||
intervalEvents.begin(), intervalEvents.end(),
|
||||
[](const IntervalEvent& event) {
|
||||
return event.repeat == 0;
|
||||
}
|
||||
), intervalEvents.end());
|
||||
|
||||
for (auto node : nodes) {
|
||||
if (node->isVisible()) {
|
||||
node->act(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Container::scrolled(int value) {
|
||||
int diff = (actualLength-getSize().y);
|
||||
if (diff > 0 && scrollable) {
|
||||
scroll += value * 40;
|
||||
if (scroll > 0)
|
||||
scroll = 0;
|
||||
if (-scroll > diff) {
|
||||
scroll = -diff;
|
||||
}
|
||||
} else if (parent) {
|
||||
parent->scrolled(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::setScrollable(bool flag) {
|
||||
scrollable = flag;
|
||||
}
|
||||
|
||||
void Container::draw(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
glm::vec2 size = getSize();
|
||||
drawBackground(pctx, assets);
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->render();
|
||||
{
|
||||
GfxContext ctx = pctx->sub();
|
||||
ctx.scissors(glm::vec4(coord.x, coord.y, size.x, size.y));
|
||||
for (auto node : nodes) {
|
||||
if (node->isVisible())
|
||||
node->draw(pctx, assets);
|
||||
}
|
||||
batch->render();
|
||||
}
|
||||
}
|
||||
|
||||
void Container::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
if (color.a <= 0.0f)
|
||||
return;
|
||||
glm::vec2 coord = calcCoord();
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->color = color;
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
}
|
||||
|
||||
void Container::addBack(std::shared_ptr<UINode> node) {
|
||||
nodes.insert(nodes.begin(), node);
|
||||
node->setParent(this);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::add(std::shared_ptr<UINode> node) {
|
||||
nodes.push_back(node);
|
||||
node->setParent(this);
|
||||
node->reposition();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::add(std::shared_ptr<UINode> node, glm::vec2 coord) {
|
||||
node->setCoord(coord);
|
||||
add(node);
|
||||
}
|
||||
|
||||
void Container::remove(std::shared_ptr<UINode> selected) {
|
||||
selected->setParent(nullptr);
|
||||
nodes.erase(std::remove_if(nodes.begin(), nodes.end(),
|
||||
[selected](const std::shared_ptr<UINode> node) {
|
||||
return node == selected;
|
||||
}
|
||||
), nodes.end());
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::listenInterval(float interval, ontimeout callback, int repeat) {
|
||||
intervalEvents.push_back({callback, interval, 0.0f, repeat});
|
||||
}
|
||||
|
||||
void Container::setSize(glm::vec2 size) {
|
||||
if (size == getSize()) {
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
UINode::setSize(size);
|
||||
refresh();
|
||||
for (auto& node : nodes) {
|
||||
node->reposition();
|
||||
}
|
||||
}
|
||||
|
||||
void Container::refresh() {
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
||||
return a->getZIndex() < b->getZIndex();
|
||||
});
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& Container::getNodes() const {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Panel::Panel(glm::vec2 size, glm::vec4 padding, float interval)
|
||||
: Container(glm::vec2(), size),
|
||||
padding(padding),
|
||||
interval(interval) {
|
||||
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
|
||||
}
|
||||
|
||||
Panel::~Panel() {
|
||||
}
|
||||
|
||||
void Panel::setMaxLength(int value) {
|
||||
maxLength = value;
|
||||
}
|
||||
|
||||
int Panel::getMaxLength() const {
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
void Panel::setPadding(glm::vec4 padding) {
|
||||
this->padding = padding;
|
||||
refresh();
|
||||
}
|
||||
|
||||
glm::vec4 Panel::getPadding() const {
|
||||
return padding;
|
||||
}
|
||||
|
||||
void Panel::cropToContent() {
|
||||
if (maxLength > 0.0f) {
|
||||
setSize(glm::vec2(getSize().x, glm::min(maxLength, actualLength)));
|
||||
} else {
|
||||
setSize(glm::vec2(getSize().x, actualLength));
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::add(std::shared_ptr<UINode> node) {
|
||||
Container::add(node);
|
||||
refresh();
|
||||
cropToContent();
|
||||
}
|
||||
|
||||
void Panel::refresh() {
|
||||
UINode::refresh();
|
||||
float x = padding.x;
|
||||
float y = padding.y;
|
||||
glm::vec2 size = getSize();
|
||||
if (orientation == Orientation::vertical) {
|
||||
float maxw = size.x;
|
||||
for (auto& node : nodes) {
|
||||
glm::vec2 nodesize = node->getSize();
|
||||
const glm::vec4 margin = node->getMargin();
|
||||
y += margin.y;
|
||||
|
||||
float ex = x + margin.x;
|
||||
node->setCoord(glm::vec2(ex, y));
|
||||
y += nodesize.y + margin.w + interval;
|
||||
|
||||
float width = size.x - padding.x - padding.z - margin.x - margin.z;
|
||||
if (node->isResizing()) {
|
||||
node->setSize(glm::vec2(width, nodesize.y));
|
||||
}
|
||||
node->refresh();
|
||||
maxw = fmax(maxw, ex+node->getSize().x+margin.z+padding.z);
|
||||
}
|
||||
actualLength = y + padding.w;
|
||||
} else {
|
||||
float maxh = size.y;
|
||||
for (auto& node : nodes) {
|
||||
glm::vec2 nodesize = node->getSize();
|
||||
const glm::vec4 margin = node->getMargin();
|
||||
x += margin.x;
|
||||
node->setCoord(glm::vec2(x, y+margin.y));
|
||||
x += nodesize.x + margin.z + interval;
|
||||
|
||||
node->refresh();
|
||||
maxh = fmax(maxh, y+margin.y+node->getSize().y+margin.w+padding.w);
|
||||
}
|
||||
actualLength = size.y;
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::setOrientation(Orientation orientation) {
|
||||
this->orientation = orientation;
|
||||
}
|
||||
|
||||
Orientation Panel::getOrientation() const {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
PagesControl::PagesControl() : Container(glm::vec2(), glm::vec2(1)){
|
||||
}
|
||||
|
||||
bool PagesControl::has(std::string name) {
|
||||
return pages.find(name) != pages.end();
|
||||
}
|
||||
|
||||
void PagesControl::addPage(std::string name, std::shared_ptr<UINode> panel) {
|
||||
pages[name] = Page{panel};
|
||||
}
|
||||
|
||||
void PagesControl::setPage(std::string name, bool history) {
|
||||
auto found = pages.find(name);
|
||||
if (found == pages.end()) {
|
||||
throw std::runtime_error("no page found");
|
||||
}
|
||||
if (current.panel) {
|
||||
Container::remove(current.panel);
|
||||
}
|
||||
if (history) {
|
||||
pageStack.push(curname);
|
||||
}
|
||||
curname = name;
|
||||
current = found->second;
|
||||
Container::add(current.panel);
|
||||
setSize(current.panel->getSize());
|
||||
}
|
||||
|
||||
void PagesControl::back() {
|
||||
if (pageStack.empty())
|
||||
return;
|
||||
std::string name = pageStack.top();
|
||||
pageStack.pop();
|
||||
setPage(name, false);
|
||||
}
|
||||
|
||||
Page& PagesControl::getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
void PagesControl::clearHistory() {
|
||||
pageStack = std::stack<std::string>();
|
||||
}
|
||||
|
||||
void PagesControl::reset() {
|
||||
clearHistory();
|
||||
if (current.panel) {
|
||||
curname = "";
|
||||
Container::remove(current.panel);
|
||||
current = Page{nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#ifndef FRONTEND_GUI_PANELS_H_
|
||||
#define FRONTEND_GUI_PANELS_H_
|
||||
#ifndef FRONTEND_GUI_CONTAINERS_H_
|
||||
#define FRONTEND_GUI_CONTAINERS_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
@ -12,7 +12,8 @@ class Batch2D;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
typedef std::function<void()> ontimeout;
|
||||
using ontimeout = std::function<void()>;
|
||||
|
||||
struct IntervalEvent {
|
||||
ontimeout callback;
|
||||
float interval;
|
||||
@ -29,46 +30,56 @@ namespace gui {
|
||||
std::vector<IntervalEvent> intervalEvents;
|
||||
int scroll = 0;
|
||||
int actualLength = 0;
|
||||
bool scrollable_ = true;
|
||||
bool scrollable = true;
|
||||
public:
|
||||
Container(glm::vec2 coord, glm::vec2 size);
|
||||
|
||||
virtual void act(float delta) override;
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) {};
|
||||
virtual void draw(Batch2D* batch, Assets* assets) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets);
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
|
||||
virtual void addBack(std::shared_ptr<UINode> node);
|
||||
virtual void add(std::shared_ptr<UINode> node);
|
||||
virtual void add(UINode* node);
|
||||
virtual void add(std::shared_ptr<UINode> node, glm::vec2 coord);
|
||||
virtual void remove(std::shared_ptr<UINode> node);
|
||||
virtual void scrolled(int value) override;
|
||||
virtual void scrollable(bool flag);
|
||||
virtual void setScrollable(bool flag);
|
||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||
virtual glm::vec2 contentOffset() override {return glm::vec2(0.0f, scroll);};
|
||||
virtual void setSize(glm::vec2 size) override;
|
||||
virtual void refresh() override;
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
||||
};
|
||||
|
||||
class Panel : public Container {
|
||||
protected:
|
||||
Orientation orientation_ = Orientation::vertical;
|
||||
Orientation orientation = Orientation::vertical;
|
||||
glm::vec4 padding {2.0f};
|
||||
float interval = 2.0f;
|
||||
bool resizing_;
|
||||
int maxLength_ = 0;
|
||||
int maxLength = 0;
|
||||
public:
|
||||
Panel(glm::vec2 size, glm::vec4 padding=glm::vec4(2.0f), float interval=2.0f, bool resizing=true);
|
||||
Panel(
|
||||
glm::vec2 size,
|
||||
glm::vec4 padding=glm::vec4(2.0f),
|
||||
float interval=2.0f
|
||||
);
|
||||
virtual ~Panel();
|
||||
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
|
||||
virtual void cropToContent();
|
||||
|
||||
virtual void orientation(Orientation orientation);
|
||||
Orientation orientation() const;
|
||||
virtual void setOrientation(Orientation orientation);
|
||||
Orientation getOrientation() const;
|
||||
|
||||
virtual void add(std::shared_ptr<UINode> node) override;
|
||||
|
||||
virtual void refresh() override;
|
||||
virtual void lock() override;
|
||||
|
||||
virtual void maxLength(int value);
|
||||
int maxLength() const;
|
||||
virtual void setMaxLength(int value);
|
||||
int getMaxLength() const;
|
||||
|
||||
virtual void setPadding(glm::vec4 padding);
|
||||
glm::vec4 getPadding() const;
|
||||
};
|
||||
|
||||
struct Page {
|
||||
@ -83,20 +94,19 @@ namespace gui {
|
||||
protected:
|
||||
std::unordered_map<std::string, Page> pages;
|
||||
std::stack<std::string> pageStack;
|
||||
Page current_;
|
||||
std::string curname_ = "";
|
||||
Page current;
|
||||
std::string curname = "";
|
||||
public:
|
||||
PagesControl();
|
||||
|
||||
bool has(std::string name);
|
||||
void set(std::string name, bool history=true);
|
||||
void add(std::string name, std::shared_ptr<UINode> panel);
|
||||
void add(std::string name, UINode* panel);
|
||||
void setPage(std::string name, bool history=true);
|
||||
void addPage(std::string name, std::shared_ptr<UINode> panel);
|
||||
void back();
|
||||
void clearHistory();
|
||||
void reset();
|
||||
|
||||
Page& current();
|
||||
Page& getCurrent();
|
||||
};
|
||||
}
|
||||
#endif // FRONTEND_GUI_PANELS_H_
|
||||
#endif // FRONTEND_GUI_CONTAINERS_H_
|
||||
@ -6,140 +6,175 @@
|
||||
#include "../../assets/Assets.h"
|
||||
#include "../../graphics/Batch2D.h"
|
||||
#include "../../graphics/Font.h"
|
||||
#include "../../graphics/Texture.h"
|
||||
#include "../../graphics/GfxContext.h"
|
||||
#include "../../util/stringutil.h"
|
||||
#include "GUI.h"
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::shared_ptr;
|
||||
using glm::vec2;
|
||||
using glm::vec3;
|
||||
using glm::vec4;
|
||||
|
||||
const uint KEY_ESCAPE = 256;
|
||||
const uint KEY_ENTER = 257;
|
||||
const uint KEY_BACKSPACE = 259;
|
||||
|
||||
using namespace gui;
|
||||
|
||||
Label::Label(string text, string fontName)
|
||||
: UINode(vec2(), vec2(text.length() * 8, 15)),
|
||||
text_(util::str2wstr_utf8(text)),
|
||||
fontName_(fontName) {
|
||||
}
|
||||
|
||||
|
||||
Label::Label(wstring text, string fontName)
|
||||
: UINode(vec2(), vec2(text.length() * 8, 15)),
|
||||
text_(text),
|
||||
fontName_(fontName) {
|
||||
}
|
||||
|
||||
Label& Label::text(wstring text) {
|
||||
this->text_ = text;
|
||||
return *this;
|
||||
}
|
||||
|
||||
wstring Label::text() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void Label::draw(Batch2D* batch, Assets* assets) {
|
||||
if (supplier) {
|
||||
text(supplier());
|
||||
}
|
||||
batch->color = color_;
|
||||
Font* font = assets->getFont(fontName_);
|
||||
vec2 size = UINode::size();
|
||||
vec2 newsize = vec2(font->calcWidth(text_), font->lineHeight());
|
||||
if (newsize.x > size.x) {
|
||||
this->size(newsize);
|
||||
size = newsize;
|
||||
}
|
||||
vec2 coord = calcCoord();
|
||||
font->draw(batch, text_, coord.x, coord.y);
|
||||
}
|
||||
|
||||
Label* Label::textSupplier(wstringsupplier supplier) {
|
||||
this->supplier = supplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
void Label::size(vec2 sizenew) {
|
||||
UINode::size(vec2(UINode::size().x, sizenew.y));
|
||||
}
|
||||
|
||||
// ================================= Image ====================================
|
||||
Image::Image(string texture, vec2 size) : UINode(vec2(), size), texture(texture) {
|
||||
Label::Label(std::string text, std::string fontName)
|
||||
: UINode(glm::vec2(), glm::vec2(text.length() * 8, 15)),
|
||||
text(util::str2wstr_utf8(text)),
|
||||
fontName(fontName) {
|
||||
setInteractive(false);
|
||||
}
|
||||
|
||||
void Image::draw(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
batch->texture(assets->getTexture(texture));
|
||||
batch->color = color_;
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y, 0, 0, 0, UVRegion(), false, true, color_);
|
||||
|
||||
Label::Label(std::wstring text, std::string fontName)
|
||||
: UINode(glm::vec2(), glm::vec2(text.length() * 8, 15)),
|
||||
text(text),
|
||||
fontName(fontName) {
|
||||
setInteractive(false);
|
||||
}
|
||||
|
||||
void Label::setText(std::wstring text) {
|
||||
this->text = text;
|
||||
}
|
||||
|
||||
std::wstring Label::getText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
void Label::draw(const GfxContext* pctx, Assets* assets) {
|
||||
if (supplier) {
|
||||
setText(supplier());
|
||||
}
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->color = getColor();
|
||||
Font* font = assets->getFont(fontName);
|
||||
glm::vec2 size = getSize();
|
||||
glm::vec2 newsize (
|
||||
font->calcWidth(text),
|
||||
font->getLineHeight()+font->getYOffset()
|
||||
);
|
||||
|
||||
glm::vec2 coord = calcCoord();
|
||||
switch (align) {
|
||||
case Align::left:
|
||||
break;
|
||||
case Align::center:
|
||||
coord.x += (size.x-newsize.x)*0.5f;
|
||||
break;
|
||||
case Align::right:
|
||||
coord.x += size.x-newsize.x;
|
||||
break;
|
||||
}
|
||||
coord.y += (size.y-newsize.y)*0.5f;
|
||||
font->draw(batch, text, coord.x, coord.y);
|
||||
}
|
||||
|
||||
void Label::textSupplier(wstringsupplier supplier) {
|
||||
this->supplier = supplier;
|
||||
}
|
||||
|
||||
// ================================= Image ====================================
|
||||
Image::Image(std::string texture, glm::vec2 size) : UINode(glm::vec2(), size), texture(texture) {
|
||||
setInteractive(false);
|
||||
}
|
||||
|
||||
void Image::draw(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
glm::vec4 color = getColor();
|
||||
auto batch = pctx->getBatch2D();
|
||||
|
||||
auto texture = assets->getTexture(this->texture);
|
||||
if (texture && autoresize) {
|
||||
setSize(glm::vec2(texture->width, texture->height));
|
||||
}
|
||||
batch->texture(texture);
|
||||
batch->color = color;
|
||||
batch->rect(coord.x, coord.y, size.x, size.y,
|
||||
0, 0, 0, UVRegion(), false, true, color);
|
||||
}
|
||||
|
||||
void Image::setAutoResize(bool flag) {
|
||||
autoresize = flag;
|
||||
}
|
||||
bool Image::isAutoResize() const {
|
||||
return autoresize;
|
||||
}
|
||||
|
||||
// ================================= Button ===================================
|
||||
Button::Button(shared_ptr<UINode> content, glm::vec4 padding)
|
||||
: Panel(content->size()+vec2(padding[0]+padding[2]+content->margin()[0]+content->margin()[2],
|
||||
padding[1]+padding[3]+content->margin()[1]+content->margin()[3]), padding, 0) {
|
||||
Button::Button(std::shared_ptr<UINode> content, glm::vec4 padding)
|
||||
: Panel(glm::vec2(), padding, 0) {
|
||||
glm::vec4 margin = getMargin();
|
||||
setSize(content->getSize()+
|
||||
glm::vec2(padding[0]+padding[2]+margin[0]+margin[2],
|
||||
padding[1]+padding[3]+margin[1]+margin[3]));
|
||||
add(content);
|
||||
scrollable(false);
|
||||
setScrollable(false);
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
|
||||
content->setInteractive(false);
|
||||
}
|
||||
|
||||
Button::Button(wstring text, glm::vec4 padding, glm::vec4 margin)
|
||||
: Panel(vec2(32,32), padding, 0) {
|
||||
this->margin(margin);
|
||||
Label* label = new Label(text);
|
||||
label->align(Align::center);
|
||||
this->label = shared_ptr<UINode>(label);
|
||||
add(this->label);
|
||||
scrollable(false);
|
||||
Button::Button(
|
||||
std::wstring text,
|
||||
glm::vec4 padding,
|
||||
onaction action,
|
||||
glm::vec2 size
|
||||
) : Panel(size, padding, 0)
|
||||
{
|
||||
if (size.y < 0.0f) {
|
||||
size = glm::vec2(
|
||||
glm::max(padding.x + padding.z + text.length()*8, size.x),
|
||||
glm::max(padding.y + padding.w + 16, size.y)
|
||||
);
|
||||
}
|
||||
setSize(size);
|
||||
|
||||
if (action) {
|
||||
listenAction(action);
|
||||
}
|
||||
setScrollable(false);
|
||||
|
||||
label = std::make_shared<Label>(text);
|
||||
label->setAlign(Align::center);
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
label->setInteractive(false);
|
||||
add(label);
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
|
||||
}
|
||||
|
||||
void Button::text(std::wstring text) {
|
||||
void Button::setText(std::wstring text) {
|
||||
if (label) {
|
||||
Label* label = (Label*)(this->label.get());
|
||||
label->text(text);
|
||||
label->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
wstring Button::text() const {
|
||||
std::wstring Button::getText() const {
|
||||
if (label) {
|
||||
Label* label = (Label*)(this->label.get());
|
||||
return label->text();
|
||||
return label->getText();
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
Button* Button::textSupplier(wstringsupplier supplier) {
|
||||
if (label) {
|
||||
Label* label = (Label*)(this->label.get());
|
||||
label->textSupplier(supplier);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void Button::setHoverColor(glm::vec4 color) {
|
||||
hoverColor = color;
|
||||
void Button::refresh() {
|
||||
Panel::refresh();
|
||||
if (label) {
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
}
|
||||
}
|
||||
|
||||
void Button::drawBackground(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
void Button::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->color = (ispressed() ? pressedColor : (hover_ ? hoverColor : color_));
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
}
|
||||
|
||||
shared_ptr<UINode> Button::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
return UINode::getAt(pos, self);
|
||||
batch->color = (isPressed() ? pressedColor : (hover ? hoverColor : color));
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
}
|
||||
|
||||
void Button::mouseRelease(GUI* gui, int x, int y) {
|
||||
UINode::mouseRelease(gui, x, y);
|
||||
if (isInside(vec2(x, y))) {
|
||||
if (isInside(glm::vec2(x, y))) {
|
||||
for (auto callback : actions) {
|
||||
callback(gui);
|
||||
}
|
||||
@ -151,21 +186,28 @@ Button* Button::listenAction(onaction action) {
|
||||
return this;
|
||||
}
|
||||
|
||||
void Button::textAlign(Align align) {
|
||||
void Button::setTextAlign(Align align) {
|
||||
if (label) {
|
||||
Label* label = (Label*)(this->label.get());
|
||||
label->align(align);
|
||||
label->setAlign(align);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Align Button::getTextAlign() const {
|
||||
if (label) {
|
||||
return label->getAlign();
|
||||
}
|
||||
return Align::left;
|
||||
}
|
||||
|
||||
// ============================== RichButton ==================================
|
||||
RichButton::RichButton(vec2 size) : Container(vec2(), size) {
|
||||
RichButton::RichButton(glm::vec2 size) : Container(glm::vec2(), size) {
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
|
||||
}
|
||||
|
||||
void RichButton::mouseRelease(GUI* gui, int x, int y) {
|
||||
UINode::mouseRelease(gui, x, y);
|
||||
if (isInside(vec2(x, y))) {
|
||||
if (isInside(glm::vec2(x, y))) {
|
||||
for (auto callback : actions) {
|
||||
callback(gui);
|
||||
}
|
||||
@ -177,65 +219,66 @@ RichButton* RichButton::listenAction(onaction action) {
|
||||
return this;
|
||||
}
|
||||
|
||||
void RichButton::setHoverColor(glm::vec4 color) {
|
||||
hoverColor = color;
|
||||
}
|
||||
|
||||
void RichButton::drawBackground(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
void RichButton::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->color = (ispressed() ? pressedColor : (hover_ ? hoverColor : color_));
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
batch->color = (isPressed() ? pressedColor : (hover ? hoverColor : color));
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
}
|
||||
|
||||
// ================================ TextBox ===================================
|
||||
TextBox::TextBox(wstring placeholder, vec4 padding)
|
||||
: Panel(vec2(200,32), padding, 0, false),
|
||||
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
|
||||
: Panel(glm::vec2(200,32), padding, 0),
|
||||
input(L""),
|
||||
placeholder(placeholder) {
|
||||
label = new Label(L"");
|
||||
add(shared_ptr<UINode>(label));
|
||||
label = std::make_shared<Label>(L"");
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
add(label);
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
|
||||
}
|
||||
|
||||
void TextBox::drawBackground(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
void TextBox::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
|
||||
|
||||
if (valid) {
|
||||
if (isfocused()) {
|
||||
if (isFocused()) {
|
||||
batch->color = focusedColor;
|
||||
} else if (hover_) {
|
||||
} else if (hover) {
|
||||
batch->color = hoverColor;
|
||||
} else {
|
||||
batch->color = color_;
|
||||
batch->color = color;
|
||||
}
|
||||
} else {
|
||||
batch->color = invalidColor;
|
||||
}
|
||||
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
if (!focused_ && supplier) {
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
if (!isFocused() && supplier) {
|
||||
input = supplier();
|
||||
}
|
||||
|
||||
if (input.empty()) {
|
||||
label->color(vec4(0.5f));
|
||||
label->text(placeholder);
|
||||
label->setColor(glm::vec4(0.5f));
|
||||
label->setText(placeholder);
|
||||
} else {
|
||||
label->color(vec4(1.0f));
|
||||
label->text(input);
|
||||
label->setColor(glm::vec4(1.0f));
|
||||
label->setText(input);
|
||||
}
|
||||
scrollable(false);
|
||||
setScrollable(false);
|
||||
}
|
||||
|
||||
void TextBox::typed(unsigned int codepoint) {
|
||||
input += wstring({(wchar_t)codepoint});
|
||||
input += std::wstring({(wchar_t)codepoint});
|
||||
validate();
|
||||
}
|
||||
|
||||
bool TextBox::validate() {
|
||||
if (validator) {
|
||||
valid = validator(input);
|
||||
valid = validator(getText());
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
@ -250,7 +293,7 @@ bool TextBox::isValid() const {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void TextBox::setOnEditStart(gui::runnable oneditstart) {
|
||||
void TextBox::setOnEditStart(runnable oneditstart) {
|
||||
onEditStart = oneditstart;
|
||||
}
|
||||
|
||||
@ -261,20 +304,22 @@ void TextBox::focus(GUI* gui) {
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::refresh() {
|
||||
Panel::refresh();
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
}
|
||||
|
||||
void TextBox::keyPressed(int key) {
|
||||
switch (key) {
|
||||
case KEY_BACKSPACE:
|
||||
if (!input.empty()){
|
||||
input = input.substr(0, input.length()-1);
|
||||
validate();
|
||||
}
|
||||
break;
|
||||
case KEY_ENTER:
|
||||
if (validate() && consumer) {
|
||||
consumer(label->text());
|
||||
}
|
||||
defocus();
|
||||
break;
|
||||
if (key == keycode::BACKSPACE) {
|
||||
if (!input.empty()){
|
||||
input = input.substr(0, input.length()-1);
|
||||
validate();
|
||||
}
|
||||
} else if (key == keycode::ENTER) {
|
||||
if (validate() && consumer) {
|
||||
consumer(label->getText());
|
||||
}
|
||||
defocus();
|
||||
}
|
||||
// Pasting text from clipboard
|
||||
if (key == keycode::V && Events::pressed(keycode::LEFT_CONTROL)) {
|
||||
@ -286,51 +331,48 @@ void TextBox::keyPressed(int key) {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<UINode> TextBox::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
|
||||
void TextBox::textSupplier(wstringsupplier supplier) {
|
||||
void TextBox::setTextSupplier(wstringsupplier supplier) {
|
||||
this->supplier = supplier;
|
||||
}
|
||||
|
||||
void TextBox::textConsumer(wstringconsumer consumer) {
|
||||
void TextBox::setTextConsumer(wstringconsumer consumer) {
|
||||
this->consumer = consumer;
|
||||
}
|
||||
|
||||
void TextBox::textValidator(wstringchecker validator) {
|
||||
void TextBox::setTextValidator(wstringchecker validator) {
|
||||
this->validator = validator;
|
||||
}
|
||||
|
||||
wstring TextBox::text() const {
|
||||
std::wstring TextBox::getText() const {
|
||||
if (input.empty())
|
||||
return placeholder;
|
||||
return input;
|
||||
}
|
||||
|
||||
void TextBox::text(std::wstring value) {
|
||||
void TextBox::setText(std::wstring value) {
|
||||
this->input = value;
|
||||
}
|
||||
|
||||
// ============================== InputBindBox ================================
|
||||
InputBindBox::InputBindBox(Binding& binding, vec4 padding)
|
||||
: Panel(vec2(100,32), padding, 0, false),
|
||||
InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding)
|
||||
: Panel(glm::vec2(100,32), padding, 0),
|
||||
binding(binding) {
|
||||
label = new Label(L"");
|
||||
label = std::make_shared<Label>(L"");
|
||||
add(label);
|
||||
scrollable(false);
|
||||
setScrollable(false);
|
||||
}
|
||||
|
||||
shared_ptr<UINode> InputBindBox::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
|
||||
void InputBindBox::drawBackground(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
void InputBindBox::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->color = (isfocused() ? focusedColor : (hover_ ? hoverColor : color_));
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
label->text(util::str2wstr_utf8(binding.text()));
|
||||
batch->color = (isFocused() ? focusedColor : (hover ? hoverColor : color));
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
label->setText(util::str2wstr_utf8(binding.text()));
|
||||
}
|
||||
|
||||
void InputBindBox::clicked(GUI*, int button) {
|
||||
@ -353,98 +395,100 @@ TrackBar::TrackBar(double min,
|
||||
double value,
|
||||
double step,
|
||||
int trackWidth)
|
||||
: UINode(vec2(), vec2(26)),
|
||||
: UINode(glm::vec2(), glm::vec2(26)),
|
||||
min(min),
|
||||
max(max),
|
||||
value(value),
|
||||
step(step),
|
||||
trackWidth(trackWidth) {
|
||||
color(vec4(0.f, 0.f, 0.f, 0.4f));
|
||||
setColor(glm::vec4(0.f, 0.f, 0.f, 0.4f));
|
||||
}
|
||||
|
||||
void TrackBar::draw(Batch2D* batch, Assets* assets) {
|
||||
if (supplier_) {
|
||||
value = supplier_();
|
||||
void TrackBar::draw(const GfxContext* pctx, Assets* assets) {
|
||||
if (supplier) {
|
||||
value = supplier();
|
||||
}
|
||||
vec2 coord = calcCoord();
|
||||
glm::vec2 coord = calcCoord();
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->color = (hover_ ? hoverColor : color_);
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
batch->color = (hover ? hoverColor : color);
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
|
||||
float width = size_.x;
|
||||
float width = size.x;
|
||||
float t = (value - min) / (max-min+trackWidth*step);
|
||||
|
||||
batch->color = trackColor;
|
||||
int actualWidth = size_.x * (trackWidth / (max-min+trackWidth*step) * step);
|
||||
batch->rect(coord.x + width * t, coord.y, actualWidth, size_.y);
|
||||
int actualWidth = size.x * (trackWidth / (max-min+trackWidth*step) * step);
|
||||
batch->rect(coord.x + width * t, coord.y, actualWidth, size.y);
|
||||
}
|
||||
|
||||
void TrackBar::supplier(doublesupplier supplier) {
|
||||
this->supplier_ = supplier;
|
||||
void TrackBar::setSupplier(doublesupplier supplier) {
|
||||
this->supplier = supplier;
|
||||
}
|
||||
|
||||
void TrackBar::consumer(doubleconsumer consumer) {
|
||||
this->consumer_ = consumer;
|
||||
void TrackBar::setConsumer(doubleconsumer consumer) {
|
||||
this->consumer = consumer;
|
||||
}
|
||||
|
||||
void TrackBar::mouseMove(GUI*, int x, int y) {
|
||||
vec2 coord = calcCoord();
|
||||
glm::vec2 coord = calcCoord();
|
||||
value = x;
|
||||
value -= coord.x;
|
||||
value = (value)/size_.x * (max-min+trackWidth*step);
|
||||
value = (value)/size.x * (max-min+trackWidth*step);
|
||||
value += min;
|
||||
value = (value > max) ? max : value;
|
||||
value = (value < min) ? min : value;
|
||||
value = (int)(value / step) * step;
|
||||
if (consumer_) {
|
||||
consumer_(value);
|
||||
if (consumer) {
|
||||
consumer(value);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================ CheckBox ==================================
|
||||
CheckBox::CheckBox(bool checked) : UINode(vec2(), vec2(32.0f)), checked_(checked) {
|
||||
color(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
CheckBox::CheckBox(bool checked) : UINode(glm::vec2(), glm::vec2(32.0f)), checked(checked) {
|
||||
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
}
|
||||
|
||||
void CheckBox::draw(Batch2D* batch, Assets* assets) {
|
||||
if (supplier_) {
|
||||
checked_ = supplier_();
|
||||
void CheckBox::draw(const GfxContext* pctx, Assets* assets) {
|
||||
if (supplier) {
|
||||
checked = supplier();
|
||||
}
|
||||
vec2 coord = calcCoord();
|
||||
glm::vec2 coord = calcCoord();
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->color = checked_ ? checkColor : (hover_ ? hoverColor : color_);
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
batch->color = checked ? checkColor : (hover ? hoverColor : color);
|
||||
batch->rect(coord.x, coord.y, size.x, size.y);
|
||||
}
|
||||
|
||||
void CheckBox::mouseRelease(GUI*, int x, int y) {
|
||||
checked_ = !checked_;
|
||||
if (consumer_) {
|
||||
consumer_(checked_);
|
||||
checked = !checked;
|
||||
if (consumer) {
|
||||
consumer(checked);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckBox::supplier(boolsupplier supplier) {
|
||||
supplier_ = supplier;
|
||||
void CheckBox::setSupplier(boolsupplier supplier) {
|
||||
this->supplier = supplier;
|
||||
}
|
||||
|
||||
void CheckBox::consumer(boolconsumer consumer) {
|
||||
consumer_ = consumer;
|
||||
void CheckBox::setConsumer(boolconsumer consumer) {
|
||||
this->consumer = consumer;
|
||||
}
|
||||
|
||||
CheckBox* CheckBox::checked(bool flag) {
|
||||
checked_ = flag;
|
||||
CheckBox* CheckBox::setChecked(bool flag) {
|
||||
checked = flag;
|
||||
return this;
|
||||
}
|
||||
|
||||
FullCheckBox::FullCheckBox(std::wstring text, glm::vec2 size, bool checked)
|
||||
: Panel(size),
|
||||
checkbox(std::make_shared<CheckBox>(checked)){
|
||||
color(vec4(0.0f));
|
||||
orientation(Orientation::horizontal);
|
||||
setColor(glm::vec4(0.0f));
|
||||
setOrientation(Orientation::horizontal);
|
||||
|
||||
add(checkbox);
|
||||
|
||||
auto label = std::make_shared<Label>(text);
|
||||
label->margin(vec4(5.0f, 5.0f, 0.0f, 0.0f));
|
||||
label->setMargin(glm::vec4(5.f, 5.f, 0.f, 0.f));
|
||||
add(label);
|
||||
}
|
||||
|
||||
@ -6,107 +6,95 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "GUI.h"
|
||||
#include "UINode.h"
|
||||
#include "panels.h"
|
||||
#include "containers.h"
|
||||
#include "../../window/input.h"
|
||||
#include "../../delegates.h"
|
||||
|
||||
class Batch2D;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
typedef std::function<std::wstring()> wstringsupplier;
|
||||
typedef std::function<void(std::wstring)> wstringconsumer;
|
||||
|
||||
typedef std::function<double()> doublesupplier;
|
||||
typedef std::function<void(double)> doubleconsumer;
|
||||
|
||||
typedef std::function<bool()> boolsupplier;
|
||||
typedef std::function<void(bool)> boolconsumer;
|
||||
|
||||
typedef std::function<bool(const std::wstring&)> wstringchecker;
|
||||
|
||||
class Label : public UINode {
|
||||
protected:
|
||||
std::wstring text_;
|
||||
std::string fontName_;
|
||||
std::wstring text;
|
||||
std::string fontName;
|
||||
wstringsupplier supplier = nullptr;
|
||||
public:
|
||||
Label(std::string text, std::string fontName="normal");
|
||||
Label(std::wstring text, std::string fontName="normal");
|
||||
|
||||
virtual Label& text(std::wstring text);
|
||||
std::wstring text() const;
|
||||
virtual void setText(std::wstring text);
|
||||
std::wstring getText() const;
|
||||
|
||||
virtual void draw(Batch2D* batch, Assets* assets) override;
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual Label* textSupplier(wstringsupplier supplier);
|
||||
virtual glm::vec2 size() const override {
|
||||
return UINode::size();
|
||||
}
|
||||
virtual void size(glm::vec2 size) override;
|
||||
virtual void textSupplier(wstringsupplier supplier);
|
||||
};
|
||||
|
||||
class Image : public UINode {
|
||||
protected:
|
||||
std::string texture;
|
||||
bool autoresize = false;
|
||||
public:
|
||||
Image(std::string texture, glm::vec2 size);
|
||||
Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
|
||||
|
||||
virtual void draw(Batch2D* batch, Assets* assets) override;
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void setAutoResize(bool flag);
|
||||
virtual bool isAutoResize() const;
|
||||
};
|
||||
|
||||
class Button : public Panel {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.15f, 0.75f};
|
||||
glm::vec4 pressedColor {0.0f, 0.0f, 0.0f, 0.95f};
|
||||
std::vector<onaction> actions;
|
||||
std::shared_ptr<UINode> label = nullptr;
|
||||
std::shared_ptr<Label> label = nullptr;
|
||||
public:
|
||||
Button(std::shared_ptr<UINode> content, glm::vec4 padding=glm::vec4(2.0f));
|
||||
Button(std::shared_ptr<UINode> content,
|
||||
glm::vec4 padding=glm::vec4(2.0f));
|
||||
|
||||
Button(std::wstring text,
|
||||
glm::vec4 padding=glm::vec4(2.0f),
|
||||
glm::vec4 margin=glm::vec4(1.0f));
|
||||
glm::vec4 padding,
|
||||
onaction action,
|
||||
glm::vec2 size=glm::vec2(-1));
|
||||
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
|
||||
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual Button* listenAction(onaction action);
|
||||
|
||||
virtual void textAlign(Align align);
|
||||
virtual Align getTextAlign() const;
|
||||
virtual void setTextAlign(Align align);
|
||||
|
||||
virtual void text(std::wstring text);
|
||||
virtual std::wstring text() const;
|
||||
virtual void setText(std::wstring text);
|
||||
virtual std::wstring getText() const;
|
||||
|
||||
virtual Button* textSupplier(wstringsupplier supplier);
|
||||
|
||||
virtual void setHoverColor(glm::vec4 color);
|
||||
virtual void refresh() override;
|
||||
};
|
||||
|
||||
class RichButton : public Container {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.15f, 0.75f};
|
||||
glm::vec4 pressedColor {0.0f, 0.0f, 0.0f, 0.95f};
|
||||
std::vector<onaction> actions;
|
||||
public:
|
||||
RichButton(glm::vec2 size);
|
||||
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual RichButton* listenAction(onaction action);
|
||||
|
||||
virtual void setHoverColor(glm::vec4 color);
|
||||
};
|
||||
|
||||
class TextBox : public Panel {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f};
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||
Label* label;
|
||||
std::shared_ptr<Label> label;
|
||||
std::wstring input;
|
||||
std::wstring placeholder;
|
||||
wstringsupplier supplier = nullptr;
|
||||
@ -116,48 +104,50 @@ namespace gui {
|
||||
bool valid = true;
|
||||
public:
|
||||
TextBox(std::wstring placeholder,
|
||||
glm::vec4 padding=glm::vec4(2.0f));
|
||||
glm::vec4 padding=glm::vec4(4.0f));
|
||||
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
|
||||
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual void typed(unsigned int codepoint) override;
|
||||
virtual void keyPressed(int key) override;
|
||||
virtual void textSupplier(wstringsupplier supplier);
|
||||
virtual void textConsumer(wstringconsumer consumer);
|
||||
virtual void textValidator(wstringchecker validator);
|
||||
virtual bool isfocuskeeper() const override {return true;}
|
||||
virtual std::wstring text() const;
|
||||
virtual void text(std::wstring value);
|
||||
virtual void setTextSupplier(wstringsupplier supplier);
|
||||
virtual void setTextConsumer(wstringconsumer consumer);
|
||||
virtual void setTextValidator(wstringchecker validator);
|
||||
virtual bool isFocuskeeper() const override {return true;}
|
||||
/* Get TextBox content text or placeholder if empty */
|
||||
virtual std::wstring getText() const;
|
||||
/* Set TextBox content text */
|
||||
virtual void setText(std::wstring value);
|
||||
virtual bool validate();
|
||||
virtual void setValid(bool valid);
|
||||
virtual bool isValid() const;
|
||||
virtual void setOnEditStart(runnable oneditstart);
|
||||
virtual void focus(GUI*) override;
|
||||
virtual void refresh() override;
|
||||
};
|
||||
|
||||
class InputBindBox : public Panel {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f};
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
Label* label;
|
||||
std::shared_ptr<Label> label;
|
||||
Binding& binding;
|
||||
public:
|
||||
InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f));
|
||||
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void clicked(GUI*, int button) override;
|
||||
virtual void keyPressed(int key) override;
|
||||
virtual bool isfocuskeeper() const override {return true;}
|
||||
virtual bool isFocuskeeper() const override {return true;}
|
||||
};
|
||||
|
||||
class TrackBar : public UINode {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.01f, 0.02f, 0.03f, 0.5f};
|
||||
glm::vec4 trackColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
doublesupplier supplier_ = nullptr;
|
||||
doubleconsumer consumer_ = nullptr;
|
||||
doublesupplier supplier = nullptr;
|
||||
doubleconsumer consumer = nullptr;
|
||||
double min;
|
||||
double max;
|
||||
double value;
|
||||
@ -169,10 +159,10 @@ namespace gui {
|
||||
double value,
|
||||
double step=1.0,
|
||||
int trackWidth=1);
|
||||
virtual void draw(Batch2D* batch, Assets* assets) override;
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void supplier(doublesupplier supplier);
|
||||
virtual void consumer(doubleconsumer consumer);
|
||||
virtual void setSupplier(doublesupplier supplier);
|
||||
virtual void setConsumer(doubleconsumer consumer);
|
||||
|
||||
virtual void mouseMove(GUI*, int x, int y) override;
|
||||
};
|
||||
@ -181,25 +171,25 @@ namespace gui {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f};
|
||||
glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
boolsupplier supplier_ = nullptr;
|
||||
boolconsumer consumer_ = nullptr;
|
||||
bool checked_ = false;
|
||||
boolsupplier supplier = nullptr;
|
||||
boolconsumer consumer = nullptr;
|
||||
bool checked = false;
|
||||
public:
|
||||
CheckBox(bool checked=false);
|
||||
|
||||
virtual void draw(Batch2D* batch, Assets* assets) override;
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
|
||||
virtual void supplier(boolsupplier supplier);
|
||||
virtual void consumer(boolconsumer consumer);
|
||||
virtual void setSupplier(boolsupplier supplier);
|
||||
virtual void setConsumer(boolconsumer consumer);
|
||||
|
||||
virtual CheckBox* checked(bool flag);
|
||||
virtual CheckBox* setChecked(bool flag);
|
||||
|
||||
virtual bool checked() const {
|
||||
if (supplier_)
|
||||
return supplier_();
|
||||
return checked_;
|
||||
virtual bool isChecked() const {
|
||||
if (supplier)
|
||||
return supplier();
|
||||
return checked;
|
||||
}
|
||||
};
|
||||
|
||||
@ -209,22 +199,22 @@ namespace gui {
|
||||
public:
|
||||
FullCheckBox(std::wstring text, glm::vec2 size, bool checked=false);
|
||||
|
||||
virtual void supplier(boolsupplier supplier) {
|
||||
checkbox->supplier(supplier);
|
||||
virtual void setSupplier(boolsupplier supplier) {
|
||||
checkbox->setSupplier(supplier);
|
||||
}
|
||||
|
||||
virtual void consumer(boolconsumer consumer) {
|
||||
checkbox->consumer(consumer);
|
||||
virtual void setConsumer(boolconsumer consumer) {
|
||||
checkbox->setConsumer(consumer);
|
||||
}
|
||||
|
||||
virtual void checked(bool flag) {
|
||||
checkbox->checked(flag);
|
||||
virtual void setChecked(bool flag) {
|
||||
checkbox->setChecked(flag);
|
||||
}
|
||||
|
||||
virtual bool checked() const {
|
||||
return checkbox->checked();
|
||||
virtual bool isChecked() const {
|
||||
return checkbox->isChecked();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_CONTROLS_H_
|
||||
#endif // FRONTEND_GUI_CONTROLS_H_
|
||||
|
||||
@ -1,35 +1,39 @@
|
||||
#include "gui_util.h"
|
||||
#include "controls.h"
|
||||
#include "panels.h"
|
||||
#include "containers.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../locale/langs.h"
|
||||
#include "../../delegates.h"
|
||||
|
||||
using namespace gui;
|
||||
using glm::vec2;
|
||||
using glm::vec4;
|
||||
|
||||
Button* guiutil::backButton(PagesControl* menu) {
|
||||
return (new Button(langs::get(L"Back"), vec4(10.f)))->listenAction([=](GUI* gui) {
|
||||
menu->back();
|
||||
});
|
||||
std::shared_ptr<Button> guiutil::backButton(std::shared_ptr<PagesControl> menu) {
|
||||
return std::make_shared<Button>(
|
||||
langs::get(L"Back"), vec4(10.f), [=](GUI*) {
|
||||
menu->back();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Button* guiutil::gotoButton(
|
||||
std::wstring text,
|
||||
const std::string& page,
|
||||
PagesControl* menu) {
|
||||
std::shared_ptr<Button> guiutil::gotoButton(
|
||||
std::wstring text,
|
||||
const std::string& page,
|
||||
std::shared_ptr<PagesControl> menu
|
||||
) {
|
||||
text = langs::get(text, L"menu");
|
||||
return (new Button(text, vec4(10.f)))->listenAction([=](GUI* gui) {
|
||||
menu->set(page);
|
||||
return std::make_shared<Button>(text, vec4(10.f), [=](GUI* gui) {
|
||||
menu->setPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
void guiutil::alert(GUI* gui, const std::wstring& text, gui::runnable on_hidden) {
|
||||
PagesControl* menu = gui->getMenu();
|
||||
Panel* panel = new Panel(vec2(500, 200), vec4(8.0f), 8.0f);
|
||||
panel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
void guiutil::alert(GUI* gui, const std::wstring& text, runnable on_hidden) {
|
||||
auto menu = gui->getMenu();
|
||||
auto panel = std::make_shared<Panel>(vec2(500, 200), vec4(8.0f), 8.0f);
|
||||
panel->setColor(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
|
||||
// TODO: implement built-in text wrapping
|
||||
const int wrap_length = 60;
|
||||
@ -43,48 +47,55 @@ void guiutil::alert(GUI* gui, const std::wstring& text, gui::runnable on_hidden)
|
||||
}
|
||||
extra = std::min(extra, wrap_length);
|
||||
std::wstring part = text.substr(offset, extra);
|
||||
panel->add(new Label(part));
|
||||
panel->add(std::make_shared<Label>(part));
|
||||
offset += extra;
|
||||
}
|
||||
} else {
|
||||
panel->add(new Label(text));
|
||||
panel->add(std::make_shared<Label>(text));
|
||||
}
|
||||
panel->add((new Button(langs::get(L"Ok"), vec4(10.f)))->listenAction([=](GUI* gui) {
|
||||
if (on_hidden)
|
||||
on_hidden();
|
||||
menu->back();
|
||||
}));
|
||||
panel->add(std::make_shared<Button>(
|
||||
langs::get(L"Ok"), vec4(10.f),
|
||||
[=](GUI* gui) {
|
||||
if (on_hidden) {
|
||||
on_hidden();
|
||||
}
|
||||
menu->back();
|
||||
}
|
||||
));
|
||||
panel->refresh();
|
||||
menu->add("<alert>", panel);
|
||||
menu->set("<alert>");
|
||||
menu->addPage("<alert>", panel);
|
||||
menu->setPage("<alert>");
|
||||
}
|
||||
|
||||
void guiutil::confirm(
|
||||
GUI* gui,
|
||||
const std::wstring& text,
|
||||
gui::runnable on_confirm,
|
||||
runnable on_confirm,
|
||||
std::wstring yestext,
|
||||
std::wstring notext) {
|
||||
if (yestext.empty()) yestext = langs::get(L"Yes");
|
||||
if (notext.empty()) notext = langs::get(L"No");
|
||||
|
||||
PagesControl* menu = gui->getMenu();
|
||||
Panel* panel = new Panel(vec2(600, 200), vec4(8.0f), 8.0f);
|
||||
panel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
panel->add(new Label(text));
|
||||
Panel* subpanel = new Panel(vec2(600, 53));
|
||||
subpanel->color(vec4(0));
|
||||
subpanel->add((new Button(yestext, vec4(8.0f)))->listenAction([=](GUI*){
|
||||
auto menu = gui->getMenu();
|
||||
auto panel = std::make_shared<Panel>(vec2(600, 200), vec4(8.0f), 8.0f);
|
||||
panel->setColor(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
panel->add(std::make_shared<Label>(text));
|
||||
auto subpanel = std::make_shared<Panel>(vec2(600, 53));
|
||||
subpanel->setColor(vec4(0));
|
||||
|
||||
subpanel->add(std::make_shared<Button>(yestext, vec4(8.f), [=](GUI*){
|
||||
if (on_confirm)
|
||||
on_confirm();
|
||||
menu->back();
|
||||
}));
|
||||
subpanel->add((new Button(notext, vec4(8.0f)))->listenAction([=](GUI*){
|
||||
|
||||
subpanel->add(std::make_shared<Button>(notext, vec4(8.f), [=](GUI*){
|
||||
menu->back();
|
||||
}));
|
||||
|
||||
panel->add(subpanel);
|
||||
|
||||
panel->refresh();
|
||||
menu->add("<confirm>", panel);
|
||||
menu->set("<confirm>");
|
||||
menu->addPage("<confirm>", panel);
|
||||
menu->setPage("<confirm>");
|
||||
}
|
||||
|
||||
@ -1,19 +1,38 @@
|
||||
#ifndef FRONTEND_GUI_GUI_UTIL_H_
|
||||
#define FRONTEND_GUI_GUI_UTIL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "GUI.h"
|
||||
#include "../../delegates.h"
|
||||
|
||||
namespace gui {
|
||||
class Button;
|
||||
}
|
||||
|
||||
namespace guiutil {
|
||||
gui::Button* backButton(gui::PagesControl* menu);
|
||||
gui::Button* gotoButton(std::wstring text, const std::string& page, gui::PagesControl* menu);
|
||||
void alert(gui::GUI* gui, const std::wstring& text, gui::runnable on_hidden=nullptr);
|
||||
void confirm(gui::GUI* gui, const std::wstring& text, gui::runnable on_confirm=nullptr,
|
||||
std::wstring yestext=L"", std::wstring notext=L"");
|
||||
std::shared_ptr<gui::Button> backButton(
|
||||
std::shared_ptr<gui::PagesControl> menu
|
||||
);
|
||||
|
||||
std::shared_ptr<gui::Button> gotoButton(
|
||||
std::wstring text,
|
||||
const std::string& page,
|
||||
std::shared_ptr<gui::PagesControl> menu
|
||||
);
|
||||
|
||||
void alert(
|
||||
gui::GUI* gui,
|
||||
const std::wstring& text,
|
||||
runnable on_hidden=nullptr
|
||||
);
|
||||
|
||||
void confirm(
|
||||
gui::GUI* gui,
|
||||
const std::wstring& text,
|
||||
runnable on_confirm=nullptr,
|
||||
std::wstring yestext=L"",
|
||||
std::wstring notext=L"");
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_GUI_UTIL_H_
|
||||
|
||||
287
src/frontend/gui/gui_xml.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
#include "gui_xml.h"
|
||||
|
||||
#include <charconv>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "containers.h"
|
||||
#include "controls.h"
|
||||
|
||||
#include "../../assets/AssetsLoader.h"
|
||||
#include "../locale/langs.h"
|
||||
#include "../../logic/scripting/scripting.h"
|
||||
#include "../../util/stringutil.h"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static Align align_from_string(const std::string& str, Align def) {
|
||||
if (str == "left") return Align::left;
|
||||
if (str == "center") return Align::center;
|
||||
if (str == "right") return Align::right;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* Read basic UINode properties */
|
||||
static void _readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& node) {
|
||||
if (element->has("id")) {
|
||||
node.setId(element->attr("id").getText());
|
||||
}
|
||||
if (element->has("pos")) {
|
||||
node.setCoord(element->attr("pos").asVec2());
|
||||
}
|
||||
if (element->has("size")) {
|
||||
node.setSize(element->attr("size").asVec2());
|
||||
}
|
||||
if (element->has("color")) {
|
||||
node.setColor(element->attr("color").asColor());
|
||||
}
|
||||
if (element->has("margin")) {
|
||||
node.setMargin(element->attr("margin").asVec4());
|
||||
}
|
||||
if (element->has("z-index")) {
|
||||
node.setZIndex(element->attr("z-index").asInt());
|
||||
}
|
||||
if (element->has("interactive")) {
|
||||
node.setInteractive(element->attr("interactive").asBool());
|
||||
}
|
||||
if (element->has("visible")) {
|
||||
node.setVisible(element->attr("visible").asBool());
|
||||
}
|
||||
if (element->has("position-func")) {
|
||||
auto supplier = scripting::create_vec2_supplier(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("position-func").getText(),
|
||||
reader.getFilename()+".lua"
|
||||
);
|
||||
node.setPositionFunc(supplier);
|
||||
}
|
||||
std::string alignName = element->attr("align", "").getText();
|
||||
node.setAlign(align_from_string(alignName, node.getAlign()));
|
||||
}
|
||||
|
||||
|
||||
static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Container& container) {
|
||||
_readUINode(reader, element, container);
|
||||
|
||||
if (element->has("scrollable")) {
|
||||
container.setScrollable(element->attr("scrollable").asBool());
|
||||
}
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->isText())
|
||||
continue;
|
||||
auto subnode = reader.readUINode(sub);
|
||||
if (subnode) {
|
||||
container.add(subnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, Container& container) {
|
||||
_readContainer(reader, element, container);
|
||||
}
|
||||
|
||||
void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& node) {
|
||||
_readUINode(reader, element, node);
|
||||
}
|
||||
|
||||
static void _readPanel(UiXmlReader& reader, xml::xmlelement element, Panel& panel) {
|
||||
_readUINode(reader, element, panel);
|
||||
|
||||
if (element->has("padding")) {
|
||||
glm::vec4 padding = element->attr("padding").asVec4();
|
||||
panel.setPadding(padding);
|
||||
glm::vec2 size = panel.getSize();
|
||||
panel.setSize(glm::vec2(
|
||||
size.x + padding.x + padding.z,
|
||||
size.y + padding.y + padding.w
|
||||
));
|
||||
}
|
||||
if (element->has("size")) {
|
||||
panel.setResizing(false);
|
||||
}
|
||||
if (element->has("max-length")) {
|
||||
panel.setMaxLength(element->attr("max-length").asInt());
|
||||
}
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->isText())
|
||||
continue;
|
||||
auto subnode = reader.readUINode(sub);
|
||||
if (subnode) {
|
||||
panel.add(subnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::wstring readAndProcessInnerText(xml::xmlelement element) {
|
||||
std::wstring text = L"";
|
||||
if (element->size() == 1) {
|
||||
std::string source = element->sub(0)->attr("#").getText();
|
||||
util::trim(source);
|
||||
text = util::str2wstr_utf8(source);
|
||||
if (text[0] == '@') {
|
||||
text = langs::get(text.substr(1));
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, xml::xmlelement element) {
|
||||
std::wstring text = readAndProcessInnerText(element);
|
||||
auto label = std::make_shared<Label>(text);
|
||||
_readUINode(reader, element, *label);
|
||||
return label;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, xml::xmlelement element) {
|
||||
auto container = std::make_shared<Container>(glm::vec2(), glm::vec2());
|
||||
_readContainer(reader, element, *container);
|
||||
return container;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, xml::xmlelement element) {
|
||||
float interval = element->attr("interval", "2").asFloat();
|
||||
auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval);
|
||||
_readPanel(reader, element, *panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readButton(UiXmlReader& reader, xml::xmlelement element) {
|
||||
std::wstring text = readAndProcessInnerText(element);
|
||||
auto button = std::make_shared<Button>(text, glm::vec4(0.0f), nullptr);
|
||||
_readPanel(reader, element, *button);
|
||||
|
||||
if (element->has("onclick")) {
|
||||
auto callback = scripting::create_runnable(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("onclick").getText(),
|
||||
reader.getFilename()+".lua"
|
||||
);
|
||||
button->listenAction([callback](GUI*) {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
if (element->has("text-align")) {
|
||||
button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign()));
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, xml::xmlelement element) {
|
||||
auto placeholder = util::str2wstr_utf8(element->attr("placeholder", "").getText());
|
||||
auto text = readAndProcessInnerText(element);
|
||||
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
|
||||
_readPanel(reader, element, *textbox);
|
||||
textbox->setText(text);
|
||||
|
||||
if (element->has("consumer")) {
|
||||
auto consumer = scripting::create_wstring_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("consumer").getText(),
|
||||
reader.getFilename()+".lua"
|
||||
);
|
||||
textbox->setTextConsumer(consumer);
|
||||
}
|
||||
return textbox;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readImage(UiXmlReader& reader, xml::xmlelement element) {
|
||||
std::string src = element->attr("src", "").getText();
|
||||
auto image = std::make_shared<Image>(src);
|
||||
_readUINode(reader, element, *image);
|
||||
reader.getAssetsLoader().add(ASSET_TEXTURE, "textures/"+src+".png", src, nullptr);
|
||||
return image;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, xml::xmlelement element) {
|
||||
float min = element->attr("min", "0.0").asFloat();
|
||||
float max = element->attr("max", "1.0").asFloat();
|
||||
float def = element->attr("value", "0.0").asFloat();
|
||||
float step = element->attr("step", "1.0").asFloat();
|
||||
int trackWidth = element->attr("track-width", "1.0").asInt();
|
||||
auto bar = std::make_shared<TrackBar>(min, max, def, step, trackWidth);
|
||||
_readUINode(reader, element, *bar);
|
||||
if (element->has("consumer")) {
|
||||
auto consumer = scripting::create_number_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("consumer").getText(),
|
||||
reader.getFilename()+".lua"
|
||||
);
|
||||
bar->setConsumer(consumer);
|
||||
}
|
||||
if (element->has("supplier")) {
|
||||
auto supplier = scripting::create_number_supplier(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("supplier").getText(),
|
||||
reader.getFilename()+".lua"
|
||||
);
|
||||
bar->setSupplier(supplier);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
||||
UiXmlReader::UiXmlReader(const scripting::Environment& env, AssetsLoader& assetsLoader)
|
||||
: env(env), assetsLoader(assetsLoader)
|
||||
{
|
||||
add("image", readImage);
|
||||
add("label", readLabel);
|
||||
add("panel", readPanel);
|
||||
add("button", readButton);
|
||||
add("textbox", readTextBox);
|
||||
add("trackbar", readTrackBar);
|
||||
add("container", readContainer);
|
||||
}
|
||||
|
||||
void UiXmlReader::add(const std::string& tag, uinode_reader reader) {
|
||||
readers[tag] = reader;
|
||||
}
|
||||
|
||||
bool UiXmlReader::hasReader(const std::string& tag) const {
|
||||
return readers.find(tag) != readers.end();
|
||||
}
|
||||
|
||||
void UiXmlReader::addIgnore(const std::string& tag) {
|
||||
ignored.insert(tag);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UiXmlReader::readUINode(xml::xmlelement element) {
|
||||
const std::string& tag = element->getTag();
|
||||
|
||||
auto found = readers.find(tag);
|
||||
if (found == readers.end()) {
|
||||
if (ignored.find(tag) != ignored.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
throw std::runtime_error("unsupported element '"+tag+"'");
|
||||
}
|
||||
return found->second(*this, element);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UiXmlReader::readXML(
|
||||
const std::string& filename,
|
||||
const std::string& source
|
||||
) {
|
||||
this->filename = filename;
|
||||
auto document = xml::parse(filename, source);
|
||||
auto root = document->getRoot();
|
||||
return readUINode(root);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UiXmlReader::readXML(
|
||||
const std::string& filename,
|
||||
xml::xmlelement root
|
||||
) {
|
||||
this->filename = filename;
|
||||
return readUINode(root);
|
||||
}
|
||||
|
||||
const std::string& UiXmlReader::getFilename() const {
|
||||
return filename;
|
||||
}
|
||||
|
||||
const scripting::Environment& UiXmlReader::getEnvironment() const {
|
||||
return env;
|
||||
}
|
||||
|
||||
AssetsLoader& UiXmlReader::getAssetsLoader() {
|
||||
return assetsLoader;
|
||||
}
|
||||
67
src/frontend/gui/gui_xml.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef FRONTEND_GUI_GUI_XML_H_
|
||||
#define FRONTEND_GUI_GUI_XML_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "GUI.h"
|
||||
#include "../../coders/xml.h"
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
class AssetsLoader;
|
||||
|
||||
namespace gui {
|
||||
class UiXmlReader;
|
||||
|
||||
using uinode_reader = std::function<std::shared_ptr<UINode>(UiXmlReader&, xml::xmlelement)>;
|
||||
|
||||
class UiXmlReader {
|
||||
std::unordered_map<std::string, uinode_reader> readers;
|
||||
std::unordered_set<std::string> ignored;
|
||||
std::string filename;
|
||||
const scripting::Environment& env;
|
||||
AssetsLoader& assetsLoader;
|
||||
|
||||
public:
|
||||
UiXmlReader(const scripting::Environment& env, AssetsLoader& assetsLoader);
|
||||
|
||||
void add(const std::string& tag, uinode_reader reader);
|
||||
bool hasReader(const std::string& tag) const;
|
||||
void addIgnore(const std::string& tag);
|
||||
|
||||
std::shared_ptr<UINode> readUINode(xml::xmlelement element);
|
||||
|
||||
void readUINode(
|
||||
UiXmlReader& reader,
|
||||
xml::xmlelement element,
|
||||
UINode& node
|
||||
);
|
||||
|
||||
void readUINode(
|
||||
UiXmlReader& reader,
|
||||
xml::xmlelement element,
|
||||
Container& container
|
||||
);
|
||||
|
||||
std::shared_ptr<UINode> readXML(
|
||||
const std::string& filename,
|
||||
const std::string& source
|
||||
);
|
||||
|
||||
std::shared_ptr<UINode> readXML(
|
||||
const std::string& filename,
|
||||
xml::xmlelement root
|
||||
);
|
||||
|
||||
const scripting::Environment& getEnvironment() const;
|
||||
const std::string& getFilename() const;
|
||||
|
||||
AssetsLoader& getAssetsLoader();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_GUI_XML_H_
|
||||
@ -1,289 +0,0 @@
|
||||
#include "panels.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../../window/Window.h"
|
||||
#include "../../assets/Assets.h"
|
||||
#include "../../graphics/Batch2D.h"
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
using namespace gui;
|
||||
|
||||
using glm::vec2;
|
||||
using glm::vec4;
|
||||
|
||||
Container::Container(vec2 coord, vec2 size) : UINode(coord, size) {
|
||||
actualLength = size.y;
|
||||
}
|
||||
|
||||
shared_ptr<UINode> Container::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isInside(pos)) return nullptr;
|
||||
|
||||
for (int i = nodes.size()-1; i >= 0; i--) {
|
||||
auto& node = nodes[i];
|
||||
if (!node->visible())
|
||||
continue;
|
||||
auto hover = node->getAt(pos, node);
|
||||
if (hover != nullptr) {
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
|
||||
void Container::act(float delta) {
|
||||
for (IntervalEvent& event : intervalEvents) {
|
||||
event.timer += delta;
|
||||
if (event.timer > event.interval) {
|
||||
event.callback();
|
||||
event.timer = fmod(event.timer, event.interval);
|
||||
if (event.repeat > 0) {
|
||||
event.repeat--;
|
||||
}
|
||||
}
|
||||
}
|
||||
intervalEvents.erase(std::remove_if(
|
||||
intervalEvents.begin(), intervalEvents.end(),
|
||||
[](const IntervalEvent& event) {
|
||||
return event.repeat == 0;
|
||||
}
|
||||
), intervalEvents.end());
|
||||
|
||||
for (auto node : nodes) {
|
||||
if (node->visible()) {
|
||||
node->act(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Container::scrolled(int value) {
|
||||
int diff = (actualLength-size().y);
|
||||
if (diff > 0 && scrollable_) {
|
||||
scroll += value * 40;
|
||||
if (scroll > 0)
|
||||
scroll = 0;
|
||||
if (-scroll > diff) {
|
||||
scroll = -diff;
|
||||
}
|
||||
} else if (parent) {
|
||||
parent->scrolled(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::scrollable(bool flag) {
|
||||
scrollable_ = flag;
|
||||
}
|
||||
|
||||
void Container::draw(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
vec2 size = this->size();
|
||||
drawBackground(batch, assets);
|
||||
batch->texture(nullptr);
|
||||
batch->render();
|
||||
Window::pushScissor(vec4(coord.x, coord.y, size.x, size.y));
|
||||
for (auto node : nodes) {
|
||||
if (node->visible())
|
||||
node->draw(batch, assets);
|
||||
}
|
||||
batch->render();
|
||||
Window::popScissor();
|
||||
}
|
||||
|
||||
void Container::addBack(shared_ptr<UINode> node) {
|
||||
nodes.insert(nodes.begin(), node);
|
||||
node->setParent(this);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::add(shared_ptr<UINode> node) {
|
||||
nodes.push_back(node);
|
||||
node->setParent(this);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::add(UINode* node) {
|
||||
add(shared_ptr<UINode>(node));
|
||||
}
|
||||
|
||||
void Container::add(shared_ptr<UINode> node, glm::vec2 coord) {
|
||||
node->setCoord(coord);
|
||||
add(node);
|
||||
}
|
||||
|
||||
void Container::remove(shared_ptr<UINode> selected) {
|
||||
selected->setParent(nullptr);
|
||||
nodes.erase(std::remove_if(nodes.begin(), nodes.end(),
|
||||
[selected](const shared_ptr<UINode> node) {
|
||||
return node == selected;
|
||||
}
|
||||
), nodes.end());
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::listenInterval(float interval, ontimeout callback, int repeat) {
|
||||
intervalEvents.push_back({callback, interval, 0.0f, repeat});
|
||||
}
|
||||
|
||||
Panel::Panel(vec2 size, glm::vec4 padding, float interval, bool resizing)
|
||||
: Container(vec2(), size),
|
||||
padding(padding),
|
||||
interval(interval),
|
||||
resizing_(resizing) {
|
||||
color_ = vec4(0.0f, 0.0f, 0.0f, 0.75f);
|
||||
}
|
||||
|
||||
Panel::~Panel() {
|
||||
}
|
||||
|
||||
void Panel::drawBackground(Batch2D* batch, Assets* assets) {
|
||||
vec2 coord = calcCoord();
|
||||
batch->texture(nullptr);
|
||||
batch->color = color_;
|
||||
batch->rect(coord.x, coord.y, size_.x, size_.y);
|
||||
}
|
||||
|
||||
void Panel::maxLength(int value) {
|
||||
maxLength_ = value;
|
||||
}
|
||||
|
||||
int Panel::maxLength() const {
|
||||
return maxLength_;
|
||||
}
|
||||
|
||||
void Panel::refresh() {
|
||||
float x = padding.x;
|
||||
float y = padding.y;
|
||||
vec2 size = this->size();
|
||||
if (orientation_ == Orientation::vertical) {
|
||||
float maxw = size.x;
|
||||
for (auto& node : nodes) {
|
||||
vec2 nodesize = node->size();
|
||||
const vec4 margin = node->margin();
|
||||
y += margin.y;
|
||||
|
||||
float ex;
|
||||
float spacex = size.x - margin.z - padding.z;
|
||||
switch (node->align()) {
|
||||
case Align::center:
|
||||
ex = x + fmax(0.0f, spacex - node->size().x) / 2.0f;
|
||||
break;
|
||||
case Align::right:
|
||||
ex = x + spacex - node->size().x;
|
||||
break;
|
||||
default:
|
||||
ex = x + margin.x;
|
||||
}
|
||||
node->setCoord(vec2(ex, y));
|
||||
y += nodesize.y + margin.w + interval;
|
||||
|
||||
float width = size.x - padding.x - padding.z - margin.x - margin.z;
|
||||
node->size(vec2(width, nodesize.y));;
|
||||
node->refresh();
|
||||
maxw = fmax(maxw, ex+node->size().x+margin.z+padding.z);
|
||||
}
|
||||
if (resizing_) {
|
||||
if (maxLength_)
|
||||
this->size(vec2(size.x, glm::min(maxLength_, (int)(y+padding.w))));
|
||||
else
|
||||
this->size(vec2(size.x, y+padding.w));
|
||||
}
|
||||
actualLength = y + padding.w;
|
||||
} else {
|
||||
float maxh = size.y;
|
||||
for (auto& node : nodes) {
|
||||
vec2 nodesize = node->size();
|
||||
const vec4 margin = node->margin();
|
||||
x += margin.x;
|
||||
node->setCoord(vec2(x, y+margin.y));
|
||||
x += nodesize.x + margin.z + interval;
|
||||
|
||||
float height = size.y - padding.y - padding.w - margin.y - margin.w;
|
||||
node->size(vec2(nodesize.x, height));
|
||||
node->refresh();
|
||||
maxh = fmax(maxh, y+margin.y+node->size().y+margin.w+padding.w);
|
||||
}
|
||||
if (resizing_) {
|
||||
if (maxLength_)
|
||||
this->size(vec2(glm::min(maxLength_, (int)(x+padding.z)), size.y));
|
||||
else
|
||||
this->size(vec2(x+padding.z, size.y));
|
||||
}
|
||||
actualLength = size.y;
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::orientation(Orientation orientation) {
|
||||
this->orientation_ = orientation;
|
||||
}
|
||||
|
||||
Orientation Panel::orientation() const {
|
||||
return orientation_;
|
||||
}
|
||||
|
||||
void Panel::lock(){
|
||||
for (auto node : nodes) {
|
||||
node->lock();
|
||||
}
|
||||
resizing_ = false;
|
||||
}
|
||||
|
||||
PagesControl::PagesControl() : Container(vec2(), vec2(1)){
|
||||
}
|
||||
|
||||
bool PagesControl::has(std::string name) {
|
||||
return pages.find(name) != pages.end();
|
||||
}
|
||||
|
||||
void PagesControl::add(std::string name, std::shared_ptr<UINode> panel) {
|
||||
pages[name] = Page{panel};
|
||||
}
|
||||
|
||||
void PagesControl::add(std::string name, UINode* panel) {
|
||||
add(name, shared_ptr<UINode>(panel));
|
||||
}
|
||||
|
||||
void PagesControl::set(std::string name, bool history) {
|
||||
auto found = pages.find(name);
|
||||
if (found == pages.end()) {
|
||||
throw std::runtime_error("no page found");
|
||||
}
|
||||
if (current_.panel) {
|
||||
Container::remove(current_.panel);
|
||||
}
|
||||
if (history) {
|
||||
pageStack.push(curname_);
|
||||
}
|
||||
curname_ = name;
|
||||
current_ = found->second;
|
||||
Container::add(current_.panel);
|
||||
size(current_.panel->size());
|
||||
}
|
||||
|
||||
void PagesControl::back() {
|
||||
if (pageStack.empty())
|
||||
return;
|
||||
std::string name = pageStack.top();
|
||||
pageStack.pop();
|
||||
set(name, false);
|
||||
}
|
||||
|
||||
Page& PagesControl::current() {
|
||||
return current_;
|
||||
}
|
||||
|
||||
void PagesControl::clearHistory() {
|
||||
pageStack = std::stack<std::string>();
|
||||
}
|
||||
|
||||
void PagesControl::reset() {
|
||||
clearHistory();
|
||||
if (current_.panel) {
|
||||
curname_ = "";
|
||||
Container::remove(current_.panel);
|
||||
current_ = Page{nullptr};
|
||||
}
|
||||
}
|
||||
@ -20,6 +20,7 @@
|
||||
#include "../graphics/Font.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Mesh.h"
|
||||
#include "../graphics/Texture.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../window/Window.h"
|
||||
#include "../window/Events.h"
|
||||
@ -32,7 +33,7 @@
|
||||
#include "../physics/Hitbox.h"
|
||||
#include "../maths/voxmaths.h"
|
||||
#include "gui/controls.h"
|
||||
#include "gui/panels.h"
|
||||
#include "gui/containers.h"
|
||||
#include "gui/UINode.h"
|
||||
#include "gui/GUI.h"
|
||||
#include "ContentGfxCache.h"
|
||||
@ -41,35 +42,71 @@
|
||||
#include "BlocksPreview.h"
|
||||
#include "InventoryView.h"
|
||||
#include "LevelFrontend.h"
|
||||
#include "UiDocument.h"
|
||||
#include "../engine.h"
|
||||
#include "../delegates.h"
|
||||
#include "../core_defs.h"
|
||||
#include "../items/ItemDef.h"
|
||||
#include "../items/Inventory.h"
|
||||
#include "../items/Inventories.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
using glm::vec2;
|
||||
using glm::vec3;
|
||||
using glm::vec4;
|
||||
using namespace gui;
|
||||
|
||||
inline std::shared_ptr<Label> create_label(gui::wstringsupplier supplier) {
|
||||
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
||||
auto label = std::make_shared<Label>(L"-");
|
||||
label->textSupplier(supplier);
|
||||
return label;
|
||||
}
|
||||
|
||||
void HudRenderer::createDebugPanel(Engine* engine) {
|
||||
HudElement::HudElement(
|
||||
hud_element_mode mode,
|
||||
UiDocument* document,
|
||||
std::shared_ptr<gui::UINode> node,
|
||||
bool debug
|
||||
) : mode(mode), document(document), node(node), debug(debug) {
|
||||
}
|
||||
|
||||
void HudElement::update(bool pause, bool inventoryOpen, bool debugMode) {
|
||||
if (debug && !debugMode) {
|
||||
node->setVisible(false);
|
||||
}
|
||||
switch (mode) {
|
||||
case hud_element_mode::permanent:
|
||||
node->setVisible(true);
|
||||
break;
|
||||
case hud_element_mode::ingame:
|
||||
node->setVisible(!pause && !inventoryOpen);
|
||||
break;
|
||||
case hud_element_mode::inventory_any:
|
||||
node->setVisible(inventoryOpen);
|
||||
break;
|
||||
case hud_element_mode::inventory_bound:
|
||||
removed = !inventoryOpen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UiDocument* HudElement::getDocument() const {
|
||||
return document;
|
||||
}
|
||||
|
||||
std::shared_ptr<gui::UINode> HudElement::getNode() const {
|
||||
return node;
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> Hud::createDebugPanel(Engine* engine) {
|
||||
auto level = frontend->getLevel();
|
||||
|
||||
Panel* panel = new Panel(vec2(250, 200), vec4(5.0f), 1.0f);
|
||||
debugPanel = std::shared_ptr<UINode>(panel);
|
||||
panel->listenInterval(1.0f, [this]() {
|
||||
auto panel = std::make_shared<Panel>(glm::vec2(250, 200), glm::vec4(5.0f), 2.0f);
|
||||
panel->listenInterval(0.5f, [this]() {
|
||||
fpsString = std::to_wstring(fpsMax)+L" / "+std::to_wstring(fpsMin);
|
||||
fpsMin = fps;
|
||||
fpsMax = fps;
|
||||
});
|
||||
panel->setCoord(vec2(10, 10));
|
||||
panel->setCoord(glm::vec2(10, 10));
|
||||
panel->add(create_label([this](){ return L"fps: "+this->fpsString;}));
|
||||
panel->add(create_label([this](){
|
||||
panel->add(create_label([](){
|
||||
return L"meshes: " + std::to_wstring(Mesh::meshesCount);
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
@ -97,26 +134,26 @@ void HudRenderer::createDebugPanel(Engine* engine) {
|
||||
return L"seed: "+std::to_wstring(level->world->getSeed());
|
||||
}));
|
||||
|
||||
for (int ax = 0; ax < 3; ax++){
|
||||
Panel* sub = new Panel(vec2(10, 27), vec4(0.0f));
|
||||
sub->orientation(Orientation::horizontal);
|
||||
for (int ax = 0; ax < 3; ax++) {
|
||||
auto sub = std::make_shared<Container>(glm::vec2(), glm::vec2(250, 27));
|
||||
|
||||
std::wstring str = L"x: ";
|
||||
str[0] += ax;
|
||||
Label* label = new Label(str);
|
||||
label->margin(vec4(2, 3, 2, 3));
|
||||
auto label = std::make_shared<Label>(str);
|
||||
label->setMargin(glm::vec4(2, 3, 2, 3));
|
||||
label->setSize(glm::vec2(20, 27));
|
||||
sub->add(label);
|
||||
sub->color(vec4(0.0f));
|
||||
sub->setColor(glm::vec4(0.0f));
|
||||
|
||||
// Coord input
|
||||
TextBox* box = new TextBox(L"");
|
||||
box->textSupplier([=]() {
|
||||
auto box = std::make_shared<TextBox>(L"");
|
||||
box->setTextSupplier([=]() {
|
||||
Hitbox* hitbox = level->player->hitbox.get();
|
||||
return util::to_wstring(hitbox->position[ax], 2);
|
||||
});
|
||||
box->textConsumer([=](std::wstring text) {
|
||||
box->setTextConsumer([=](std::wstring text) {
|
||||
try {
|
||||
vec3 position = level->player->hitbox->position;
|
||||
glm::vec3 position = level->player->hitbox->position;
|
||||
position[ax] = std::stoi(text);
|
||||
level->player->teleport(position);
|
||||
} catch (std::invalid_argument& _){
|
||||
@ -124,10 +161,11 @@ void HudRenderer::createDebugPanel(Engine* engine) {
|
||||
});
|
||||
box->setOnEditStart([=](){
|
||||
Hitbox* hitbox = level->player->hitbox.get();
|
||||
box->text(std::to_wstring(int(hitbox->position[ax])));
|
||||
box->setText(std::to_wstring(int(hitbox->position[ax])));
|
||||
});
|
||||
box->setSize(glm::vec2(230, 27));
|
||||
|
||||
sub->add(box);
|
||||
sub->add(box, glm::vec2(20, 0));
|
||||
panel->add(sub);
|
||||
}
|
||||
panel->add(create_label([=](){
|
||||
@ -140,117 +178,80 @@ void HudRenderer::createDebugPanel(Engine* engine) {
|
||||
return L"time: "+timeString;
|
||||
}));
|
||||
{
|
||||
TrackBar* bar = new TrackBar(0.0f, 1.0f, 1.0f, 0.005f, 8);
|
||||
bar->supplier([=]() {return level->world->daytime;});
|
||||
bar->consumer([=](double val) {level->world->daytime = val;});
|
||||
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8);
|
||||
bar->setSupplier([=]() {return level->world->daytime;});
|
||||
bar->setConsumer([=](double val) {level->world->daytime = val;});
|
||||
panel->add(bar);
|
||||
}
|
||||
{
|
||||
TrackBar* bar = new TrackBar(0.0f, 1.0f, 0.0f, 0.005f, 8);
|
||||
bar->supplier([=]() {return WorldRenderer::fog;});
|
||||
bar->consumer([=](double val) {WorldRenderer::fog = val;});
|
||||
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
|
||||
bar->setSupplier([=]() {return WorldRenderer::fog;});
|
||||
bar->setConsumer([=](double val) {WorldRenderer::fog = val;});
|
||||
panel->add(bar);
|
||||
}
|
||||
{
|
||||
auto checkbox = new FullCheckBox(L"Show Chunk Borders", vec2(400, 32));
|
||||
checkbox->supplier([=]() {
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
L"Show Chunk Borders", glm::vec2(400, 24)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().debug.showChunkBorders;
|
||||
});
|
||||
checkbox->consumer([=](bool checked) {
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
engine->getSettings().debug.showChunkBorders = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
panel->refresh();
|
||||
return panel;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> HudRenderer::createContentAccess() {
|
||||
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||
auto level = frontend->getLevel();
|
||||
auto content = level->content;
|
||||
auto indices = content->getIndices();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
|
||||
|
||||
int itemsCount = indices->countItemDefs();
|
||||
auto accessInventory = std::make_shared<Inventory>(itemsCount);
|
||||
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
||||
for (int id = 1; id < itemsCount; id++) {
|
||||
accessInventory->getSlot(id-1).set(ItemStack(id, 1));
|
||||
}
|
||||
|
||||
SlotLayout slotLayout(glm::vec2(), false, true,
|
||||
[=](ItemStack& item) {
|
||||
SlotLayout slotLayout(-1, glm::vec2(), false, true,
|
||||
[=](uint, ItemStack& item) {
|
||||
auto copy = ItemStack(item);
|
||||
inventory->move(copy, indices);
|
||||
},
|
||||
[=](ItemStack& item, ItemStack& grabbed) {
|
||||
[=](uint, ItemStack& item) {
|
||||
inventory->getSlot(player->getChosenSlot()).set(item);
|
||||
});
|
||||
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout);
|
||||
auto layout = builder.build();
|
||||
|
||||
auto contentAccess = std::make_shared<InventoryView>(
|
||||
content,
|
||||
frontend,
|
||||
interaction.get(),
|
||||
accessInventory,
|
||||
std::move(layout)
|
||||
);
|
||||
contentAccess->build();
|
||||
return contentAccess;
|
||||
auto view = builder.build();
|
||||
view->bind(accessInventory, frontend, interaction.get());
|
||||
view->setMargin(glm::vec4());
|
||||
return view;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> HudRenderer::createHotbar() {
|
||||
std::shared_ptr<InventoryView> Hud::createHotbar() {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
auto content = level->content;
|
||||
|
||||
SlotLayout slotLayout(glm::vec2(), false, false, nullptr, nullptr);
|
||||
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr);
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout);
|
||||
auto layout = builder.build();
|
||||
auto view = builder.build();
|
||||
|
||||
layout->setOrigin(glm::vec2(layout->getSize().x/2, 0));
|
||||
auto view = std::make_shared<InventoryView>(
|
||||
content,
|
||||
frontend,
|
||||
interaction.get(),
|
||||
inventory,
|
||||
std::move(layout)
|
||||
);
|
||||
view->build();
|
||||
view->setOrigin(glm::vec2(view->getSize().x/2, 0));
|
||||
view->bind(inventory, frontend, interaction.get());
|
||||
view->setInteractive(false);
|
||||
return view;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> HudRenderer::createInventory() {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
auto content = level->content;
|
||||
|
||||
SlotLayout slotLayout(glm::vec2(), true, false, [=](ItemStack& stack) {
|
||||
stack.clear();
|
||||
}, nullptr);
|
||||
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(10, inventory->size(), glm::vec2(), 4, true, slotLayout);
|
||||
auto layout = builder.build();
|
||||
|
||||
auto view = std::make_shared<InventoryView>(
|
||||
content,
|
||||
frontend,
|
||||
interaction.get(),
|
||||
inventory,
|
||||
std::move(layout)
|
||||
);
|
||||
view->build();
|
||||
return view;
|
||||
}
|
||||
|
||||
HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
|
||||
Hud::Hud(Engine* engine, LevelFrontend* frontend)
|
||||
: assets(engine->getAssets()),
|
||||
gui(engine->getGUI()),
|
||||
frontend(frontend)
|
||||
@ -259,83 +260,96 @@ HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
|
||||
|
||||
interaction = std::make_unique<InventoryInteraction>();
|
||||
grabbedItemView = std::make_shared<SlotView>(
|
||||
interaction->getGrabbedItem(),
|
||||
frontend,
|
||||
interaction.get(),
|
||||
frontend->getLevel()->content,
|
||||
SlotLayout(glm::vec2(), false, false, nullptr, nullptr)
|
||||
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr)
|
||||
);
|
||||
grabbedItemView->color(glm::vec4());
|
||||
grabbedItemView->bind(
|
||||
0,
|
||||
interaction->getGrabbedItem(),
|
||||
frontend,
|
||||
interaction.get()
|
||||
);
|
||||
grabbedItemView->setColor(glm::vec4());
|
||||
grabbedItemView->setInteractive(false);
|
||||
grabbedItemView->setZIndex(1);
|
||||
|
||||
contentAccess = createContentAccess();
|
||||
contentAccessPanel = std::make_shared<Panel>(
|
||||
contentAccess->size(), vec4(0.0f), 0.0f
|
||||
contentAccess->getSize(), glm::vec4(0.0f), 0.0f
|
||||
);
|
||||
contentAccessPanel->color(glm::vec4());
|
||||
contentAccessPanel->setColor(glm::vec4());
|
||||
contentAccessPanel->add(contentAccess);
|
||||
contentAccessPanel->scrollable(true);
|
||||
contentAccessPanel->setScrollable(true);
|
||||
|
||||
hotbarView = createHotbar();
|
||||
inventoryView = createInventory();
|
||||
|
||||
darkOverlay = std::make_unique<Panel>(glm::vec2(4000.0f));
|
||||
darkOverlay->color(glm::vec4(0, 0, 0, 0.5f));
|
||||
darkOverlay->setColor(glm::vec4(0, 0, 0, 0.5f));
|
||||
darkOverlay->setZIndex(-1);
|
||||
darkOverlay->setVisible(false);
|
||||
|
||||
uicamera = new Camera(vec3(), 1);
|
||||
uicamera = std::make_unique<Camera>(glm::vec3(), 1);
|
||||
uicamera->perspective = false;
|
||||
uicamera->flipped = true;
|
||||
|
||||
createDebugPanel(engine);
|
||||
debugPanel = createDebugPanel(engine);
|
||||
menu->reset();
|
||||
|
||||
debugPanel->setZIndex(2);
|
||||
|
||||
gui->addBack(darkOverlay);
|
||||
gui->addBack(hotbarView);
|
||||
gui->add(darkOverlay);
|
||||
gui->add(hotbarView);
|
||||
gui->add(debugPanel);
|
||||
gui->add(contentAccessPanel);
|
||||
gui->add(inventoryView);
|
||||
gui->add(grabbedItemView);
|
||||
}
|
||||
|
||||
HudRenderer::~HudRenderer() {
|
||||
Hud::~Hud() {
|
||||
// removing all controlled ui
|
||||
gui->remove(grabbedItemView);
|
||||
gui->remove(inventoryView);
|
||||
for (auto& element : elements) {
|
||||
remove(element);
|
||||
}
|
||||
gui->remove(hotbarView);
|
||||
gui->remove(darkOverlay);
|
||||
gui->remove(contentAccessPanel);
|
||||
gui->remove(debugPanel);
|
||||
delete uicamera;
|
||||
}
|
||||
|
||||
void HudRenderer::drawDebug(int fps){
|
||||
void Hud::drawDebug(int fps){
|
||||
this->fps = fps;
|
||||
fpsMin = min(fps, fpsMin);
|
||||
fpsMax = max(fps, fpsMax);
|
||||
}
|
||||
|
||||
void HudRenderer::update(bool visible) {
|
||||
/**
|
||||
* Remove all elements marked as removed
|
||||
*/
|
||||
void Hud::cleanup() {
|
||||
auto it = std::remove_if(elements.begin(), elements.end(), [](const HudElement& e) {
|
||||
return e.isRemoved();
|
||||
});
|
||||
elements.erase(it, elements.end());
|
||||
}
|
||||
|
||||
void Hud::update(bool visible) {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto menu = gui->getMenu();
|
||||
|
||||
debugPanel->visible(player->debug && visible);
|
||||
menu->visible(pause);
|
||||
debugPanel->setVisible(player->debug && visible);
|
||||
|
||||
if (!visible && inventoryOpen) {
|
||||
closeInventory();
|
||||
}
|
||||
if (pause && menu->current().panel == nullptr) {
|
||||
pause = false;
|
||||
if (pause && menu->getCurrent().panel == nullptr) {
|
||||
setPause(false);
|
||||
}
|
||||
if (Events::jpressed(keycode::ESCAPE) && !gui->isFocusCaught()) {
|
||||
if (pause) {
|
||||
pause = false;
|
||||
menu->reset();
|
||||
setPause(false);
|
||||
} else if (inventoryOpen) {
|
||||
closeInventory();
|
||||
} else {
|
||||
pause = true;
|
||||
menu->set("pause");
|
||||
setPause(true);
|
||||
}
|
||||
}
|
||||
if (visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
||||
@ -343,7 +357,7 @@ void HudRenderer::update(bool visible) {
|
||||
if (inventoryOpen) {
|
||||
closeInventory();
|
||||
} else {
|
||||
inventoryOpen = true;
|
||||
openInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -351,11 +365,11 @@ void HudRenderer::update(bool visible) {
|
||||
Events::toggleCursor();
|
||||
}
|
||||
|
||||
glm::vec2 invSize = contentAccessPanel->size();
|
||||
inventoryView->visible(inventoryOpen);
|
||||
contentAccessPanel->visible(inventoryOpen);
|
||||
contentAccessPanel->size(glm::vec2(invSize.x, Window::height));
|
||||
hotbarView->visible(visible);
|
||||
glm::vec2 invSize = contentAccessPanel->getSize();
|
||||
contentAccessPanel->setVisible(inventoryOpen);
|
||||
contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height));
|
||||
contentAccess->setMinSize(glm::vec2(1, Window::height));
|
||||
// hotbarView->setVisible(visible && !inventoryOpen);
|
||||
|
||||
for (int i = keycode::NUM_1; i <= keycode::NUM_9; i++) {
|
||||
if (Events::jpressed(i)) {
|
||||
@ -374,24 +388,151 @@ void HudRenderer::update(bool visible) {
|
||||
player->setChosenSlot(slot);
|
||||
}
|
||||
|
||||
darkOverlay->visible(pause);
|
||||
for (auto& element : elements) {
|
||||
element.update(pause, inventoryOpen, player->debug);
|
||||
if (element.isRemoved()) {
|
||||
remove(element);
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void HudRenderer::closeInventory() {
|
||||
/**
|
||||
* Show inventory on the screen and turn on inventory mode blocking movement
|
||||
*/
|
||||
void Hud::openInventory() {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
|
||||
inventoryOpen = true;
|
||||
|
||||
auto inventoryDocument = assets->getLayout("core:inventory");
|
||||
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
||||
inventoryView->bind(inventory, frontend, interaction.get());
|
||||
add(HudElement(hud_element_mode::inventory_bound, inventoryDocument, inventoryView, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show player inventory + block UI
|
||||
* @param block world position of the open block
|
||||
* @param doc block UI document (root element must be an InventoryView)
|
||||
* @param blockinv block inventory.
|
||||
* In case of nullptr a new virtual inventory will be created
|
||||
*/
|
||||
void Hud::openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inventory> blockinv) {
|
||||
if (isInventoryOpen()) {
|
||||
closeInventory();
|
||||
}
|
||||
auto level = frontend->getLevel();
|
||||
blockUI = std::dynamic_pointer_cast<InventoryView>(doc->getRoot());
|
||||
if (blockUI == nullptr) {
|
||||
throw std::runtime_error("block UI root element must be 'inventory'");
|
||||
}
|
||||
openInventory();
|
||||
if (blockinv == nullptr) {
|
||||
Events::toggleCursor();
|
||||
abort();
|
||||
blockinv = level->inventories->createVirtual(blockUI->getSlotsCount());
|
||||
}
|
||||
blockUI->bind(blockinv, frontend, interaction.get());
|
||||
currentblock = block;
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add element as permanent overlay
|
||||
* @param doc element layout document
|
||||
*/
|
||||
void Hud::openPermanent(UiDocument* doc) {
|
||||
auto root = doc->getRoot();
|
||||
remove(root);
|
||||
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(root);
|
||||
if (invview) {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
invview->bind(inventory, frontend, interaction.get());
|
||||
}
|
||||
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide inventory and turn off inventory mode
|
||||
*/
|
||||
void Hud::closeInventory() {
|
||||
auto level = frontend->getLevel();
|
||||
|
||||
inventoryOpen = false;
|
||||
ItemStack& grabbed = interaction->getGrabbedItem();
|
||||
grabbed.clear();
|
||||
inventoryView = nullptr;
|
||||
if (blockUI) {
|
||||
auto blockinv = blockUI->getInventory();
|
||||
// todo: do it automatically
|
||||
if (blockinv->isVirtual()) {
|
||||
level->inventories->remove(blockinv->getId());
|
||||
}
|
||||
blockUI = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HudRenderer::draw(const GfxContext& ctx){
|
||||
void Hud::add(HudElement element) {
|
||||
gui->add(element.getNode());
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
if (invview) {
|
||||
auto inventory = invview->getInventory();
|
||||
scripting::on_ui_open(
|
||||
element.getDocument(),
|
||||
inventory.get(),
|
||||
currentblock
|
||||
);
|
||||
} else {
|
||||
scripting::on_ui_open(
|
||||
element.getDocument(),
|
||||
nullptr,
|
||||
currentblock
|
||||
);
|
||||
}
|
||||
}
|
||||
elements.push_back(element);
|
||||
}
|
||||
|
||||
void Hud::remove(HudElement& element) {
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
Inventory* inventory = nullptr;
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||
if (invview) {
|
||||
inventory = invview->getInventory().get();
|
||||
}
|
||||
scripting::on_ui_close(document, inventory);
|
||||
}
|
||||
gui->remove(element.getNode());
|
||||
}
|
||||
|
||||
// todo: refactor this garbage
|
||||
void Hud::remove(std::shared_ptr<UINode> node) {
|
||||
for (auto& element : elements) {
|
||||
if (element.getNode() == node) {
|
||||
element.setRemoved();
|
||||
remove(element);
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::draw(const GfxContext& ctx){
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
|
||||
const Viewport& viewport = ctx.getViewport();
|
||||
const uint width = viewport.getWidth();
|
||||
const uint height = viewport.getHeight();
|
||||
|
||||
Player* player = level->player;
|
||||
|
||||
uicamera->setFov(height);
|
||||
|
||||
auto batch = ctx.getBatch2D();
|
||||
@ -401,42 +542,93 @@ void HudRenderer::draw(const GfxContext& ctx){
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||
|
||||
// Draw selected item preview
|
||||
hotbarView->setCoord(glm::vec2(width/2, height-65));
|
||||
hotbarView->setSelected(player->getChosenSlot());
|
||||
|
||||
// Crosshair
|
||||
batch->begin();
|
||||
if (!pause && Events::_cursor_locked && !level->player->debug) {
|
||||
batch->lineWidth(2);
|
||||
batch->line(width/2, height/2-6, width/2, height/2+6, 0.2f, 0.2f, 0.2f, 1.0f);
|
||||
batch->line(width/2+6, height/2, width/2-6, height/2, 0.2f, 0.2f, 0.2f, 1.0f);
|
||||
batch->line(width/2-5, height/2-5, width/2+5, height/2+5, 0.9f, 0.9f, 0.9f, 1.0f);
|
||||
batch->line(width/2+5, height/2-5, width/2-5, height/2+5, 0.9f, 0.9f, 0.9f, 1.0f);
|
||||
GfxContext chctx = ctx.sub();
|
||||
chctx.blendMode(blendmode::inversion);
|
||||
auto texture = assets->getTexture("gui/crosshair");
|
||||
batch->texture(texture);
|
||||
int chsizex = texture != nullptr ? texture->width : 16;
|
||||
int chsizey = texture != nullptr ? texture->height : 16;
|
||||
batch->rect(
|
||||
(width-chsizex)/2, (height-chsizey)/2,
|
||||
chsizex, chsizey, 0,0, 1,1, 1,1,1,1
|
||||
);
|
||||
batch->render();
|
||||
}
|
||||
|
||||
// Delta-time visualizer
|
||||
if (level->player->debug) {
|
||||
batch->texture(nullptr);
|
||||
const int dmwidth = 256;
|
||||
const float dmscale = 4000.0f;
|
||||
static float deltameter[dmwidth]{};
|
||||
static int index=0;
|
||||
index = index + 1 % dmwidth;
|
||||
deltameter[index%dmwidth] = glm::min(0.2f, 1.f/fps)*dmscale;
|
||||
batch->lineWidth(1);
|
||||
for (int i = index+1; i < index+dmwidth; i++) {
|
||||
int j = i % dmwidth;
|
||||
batch->line(width-dmwidth+i-index, height-deltameter[j],
|
||||
width-dmwidth+i-index, height, 1.0f, 1.0f, 1.0f, 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
if (inventoryOpen) {
|
||||
auto caLayout = contentAccess->getLayout();
|
||||
auto invLayout = inventoryView->getLayout();
|
||||
float caWidth = caLayout->getSize().x;
|
||||
glm::vec2 invSize = invLayout->getSize();
|
||||
|
||||
float width = viewport.getWidth();
|
||||
|
||||
inventoryView->setCoord(glm::vec2(
|
||||
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
|
||||
height/2-invSize.y/2
|
||||
));
|
||||
float caWidth = contentAccess->getSize().x;
|
||||
contentAccessPanel->setCoord(glm::vec2(width-caWidth, 0));
|
||||
|
||||
glm::vec2 invSize = inventoryView->getSize();
|
||||
if (blockUI == nullptr) {
|
||||
inventoryView->setCoord(glm::vec2(
|
||||
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
|
||||
height/2-invSize.y/2
|
||||
));
|
||||
} else {
|
||||
glm::vec2 blockInvSize = blockUI->getSize();
|
||||
int interval = 5;
|
||||
float totalHeight = invSize.y + blockInvSize.y + interval;
|
||||
inventoryView->setCoord(glm::vec2(
|
||||
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
|
||||
height/2+totalHeight/2-invSize.y
|
||||
));
|
||||
blockUI->setCoord(glm::vec2(
|
||||
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
|
||||
height/2-totalHeight/2
|
||||
));
|
||||
}
|
||||
}
|
||||
grabbedItemView->setCoord(glm::vec2(Events::cursor));
|
||||
batch->render();
|
||||
}
|
||||
|
||||
bool HudRenderer::isInventoryOpen() const {
|
||||
bool Hud::isInventoryOpen() const {
|
||||
return inventoryOpen;
|
||||
}
|
||||
|
||||
bool HudRenderer::isPause() const {
|
||||
bool Hud::isPause() const {
|
||||
return pause;
|
||||
}
|
||||
|
||||
void Hud::setPause(bool pause) {
|
||||
if (this->pause == pause) {
|
||||
return;
|
||||
}
|
||||
this->pause = pause;
|
||||
|
||||
auto menu = gui->getMenu();
|
||||
if (pause) {
|
||||
menu->setPage("pause");
|
||||
} else {
|
||||
menu->reset();
|
||||
}
|
||||
darkOverlay->setVisible(pause);
|
||||
menu->setVisible(pause);
|
||||
}
|
||||
|
||||
Player* Hud::getPlayer() const {
|
||||
return frontend->getLevel()->player;
|
||||
}
|
||||
|
||||
@ -16,19 +16,57 @@ class Player;
|
||||
class Level;
|
||||
class Engine;
|
||||
class SlotView;
|
||||
class Inventory;
|
||||
class InventoryView;
|
||||
class LevelFrontend;
|
||||
class UiDocument;
|
||||
class InventoryInteraction;
|
||||
|
||||
namespace gui {
|
||||
class GUI;
|
||||
class UINode;
|
||||
class Panel;
|
||||
class Container;
|
||||
}
|
||||
|
||||
class HudRenderer {
|
||||
enum class hud_element_mode {
|
||||
// element is hidden if menu or inventory open
|
||||
ingame,
|
||||
// element is visible if hud is visible
|
||||
permanent,
|
||||
// element is visible in inventory mode
|
||||
inventory_any,
|
||||
// element will be removed on inventory close
|
||||
inventory_bound
|
||||
};
|
||||
|
||||
class HudElement {
|
||||
hud_element_mode mode;
|
||||
UiDocument* document;
|
||||
std::shared_ptr<gui::UINode> node;
|
||||
|
||||
bool debug;
|
||||
bool removed = false;
|
||||
public:
|
||||
HudElement(hud_element_mode mode, UiDocument* document, std::shared_ptr<gui::UINode> node, bool debug);
|
||||
|
||||
void update(bool pause, bool inventoryOpen, bool debug);
|
||||
|
||||
UiDocument* getDocument() const;
|
||||
std::shared_ptr<gui::UINode> getNode() const;
|
||||
|
||||
void setRemoved() {
|
||||
removed = true;
|
||||
}
|
||||
|
||||
bool isRemoved() const {
|
||||
return removed;
|
||||
}
|
||||
};
|
||||
|
||||
class Hud {
|
||||
Assets* assets;
|
||||
Camera* uicamera;
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
|
||||
int fps = 60;
|
||||
int fpsMin = 60;
|
||||
@ -37,10 +75,9 @@ class HudRenderer {
|
||||
bool inventoryOpen = false;
|
||||
bool pause = false;
|
||||
|
||||
std::shared_ptr<gui::Panel> contentAccessPanel;
|
||||
std::shared_ptr<gui::Container> contentAccessPanel;
|
||||
std::shared_ptr<InventoryView> contentAccess;
|
||||
std::shared_ptr<InventoryView> hotbarView;
|
||||
std::shared_ptr<InventoryView> inventoryView;
|
||||
std::shared_ptr<gui::UINode> debugPanel;
|
||||
std::shared_ptr<gui::Panel> darkOverlay;
|
||||
std::unique_ptr<InventoryInteraction> interaction;
|
||||
@ -48,14 +85,20 @@ class HudRenderer {
|
||||
gui::GUI* gui;
|
||||
LevelFrontend* frontend;
|
||||
|
||||
void createDebugPanel(Engine* engine);
|
||||
std::vector<HudElement> elements;
|
||||
|
||||
std::shared_ptr<InventoryView> inventoryView = nullptr;
|
||||
std::shared_ptr<InventoryView> blockUI = nullptr;
|
||||
glm::ivec3 currentblock {};
|
||||
|
||||
std::shared_ptr<gui::UINode> createDebugPanel(Engine* engine);
|
||||
std::shared_ptr<InventoryView> createContentAccess();
|
||||
std::shared_ptr<InventoryView> createHotbar();
|
||||
std::shared_ptr<InventoryView> createInventory();
|
||||
|
||||
void cleanup();
|
||||
public:
|
||||
HudRenderer(Engine* engine, LevelFrontend* frontend);
|
||||
~HudRenderer();
|
||||
Hud(Engine* engine, LevelFrontend* frontend);
|
||||
~Hud();
|
||||
|
||||
void update(bool hudVisible);
|
||||
void draw(const GfxContext& context);
|
||||
@ -63,8 +106,18 @@ public:
|
||||
|
||||
bool isInventoryOpen() const;
|
||||
bool isPause() const;
|
||||
void setPause(bool pause);
|
||||
|
||||
void openInventory();
|
||||
void openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inventory> blockInv);
|
||||
void closeInventory();
|
||||
void openPermanent(UiDocument* doc);
|
||||
|
||||
void add(HudElement element);
|
||||
void remove(HudElement& element);
|
||||
void remove(std::shared_ptr<gui::UINode> node);
|
||||
|
||||
Player* getPlayer() const;
|
||||
};
|
||||
|
||||
#endif /* SRC_HUD_H_ */
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "gui/GUI.h"
|
||||
#include "gui/panels.h"
|
||||
#include "gui/containers.h"
|
||||
#include "gui/controls.h"
|
||||
#include "screens.h"
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "../window/Window.h"
|
||||
#include "../engine.h"
|
||||
#include "../settings.h"
|
||||
#include "../delegates.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../content/ContentLUT.h"
|
||||
#include "../content/ContentPack.h"
|
||||
@ -46,74 +47,89 @@ inline uint64_t randU64() {
|
||||
((uint64_t)rand() << 56);
|
||||
}
|
||||
|
||||
std::shared_ptr<Panel> create_page(
|
||||
Engine* engine,
|
||||
std::string name,
|
||||
int width,
|
||||
float opacity,
|
||||
int interval) {
|
||||
PagesControl* menu = engine->getGUI()->getMenu();
|
||||
Panel* panel = new Panel(vec2(width, 200), vec4(8.0f), interval);
|
||||
panel->color(vec4(0.0f, 0.0f, 0.0f, opacity));
|
||||
|
||||
std::shared_ptr<Panel> ptr (panel);
|
||||
menu->add(name, ptr);
|
||||
return ptr;
|
||||
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
||||
auto label = std::make_shared<Label>(L"-");
|
||||
label->textSupplier(supplier);
|
||||
return label;
|
||||
}
|
||||
|
||||
Button* create_button(std::wstring text,
|
||||
glm::vec4 padding,
|
||||
glm::vec4 margin,
|
||||
gui::onaction action) {
|
||||
static std::shared_ptr<Panel> create_page(
|
||||
Engine* engine,
|
||||
std::string name,
|
||||
int width,
|
||||
float opacity,
|
||||
int interval
|
||||
) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = std::make_shared<Panel>(
|
||||
vec2(width, 200), vec4(8.0f), interval
|
||||
);
|
||||
panel->setColor(vec4(0.0f, 0.0f, 0.0f, opacity));
|
||||
menu->addPage(name, panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
auto btn = new Button(langs::get(text, L"menu"),
|
||||
padding, margin);
|
||||
btn->listenAction(action);
|
||||
static std::shared_ptr<Button> create_button(
|
||||
std::wstring text,
|
||||
vec4 padding,
|
||||
vec4 margin,
|
||||
gui::onaction action
|
||||
) {
|
||||
auto btn = std::make_shared<Button>(
|
||||
langs::get(text, L"menu"), padding, action
|
||||
);
|
||||
btn->setMargin(margin);
|
||||
return btn;
|
||||
}
|
||||
|
||||
void show_content_missing(Engine* engine, const Content* content,
|
||||
std::shared_ptr<ContentLUT> lut) {
|
||||
static void show_content_missing(
|
||||
Engine* engine,
|
||||
const Content* content,
|
||||
std::shared_ptr<ContentLUT> lut
|
||||
) {
|
||||
auto* gui = engine->getGUI();
|
||||
auto* menu = gui->getMenu();
|
||||
auto menu = gui->getMenu();
|
||||
auto panel = create_page(engine, "missing-content", 500, 0.5f, 8);
|
||||
|
||||
panel->add(new Label(langs::get(L"menu.missing-content")));
|
||||
panel->add(std::make_shared<Label>(langs::get(L"menu.missing-content")));
|
||||
|
||||
Panel* subpanel = new Panel(vec2(500, 100));
|
||||
subpanel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
auto subpanel = std::make_shared<Panel>(vec2(500, 100));
|
||||
subpanel->setColor(vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
subpanel->setScrollable(true);
|
||||
subpanel->setMaxLength(400);
|
||||
panel->add(subpanel);
|
||||
|
||||
for (auto& entry : lut->getMissingContent()) {
|
||||
Panel* hpanel = new Panel(vec2(500, 30));
|
||||
hpanel->color(vec4(0.0f));
|
||||
hpanel->orientation(Orientation::horizontal);
|
||||
auto hpanel = std::make_shared<Panel>(vec2(500, 30));
|
||||
hpanel->setColor(vec4(0.0f));
|
||||
hpanel->setOrientation(Orientation::horizontal);
|
||||
|
||||
Label* namelabel = new Label(util::str2wstr_utf8(entry.name));
|
||||
namelabel->color(vec4(1.0f, 0.2f, 0.2f, 0.5f));
|
||||
auto namelabel = std::make_shared<Label>(util::str2wstr_utf8(entry.name));
|
||||
namelabel->setColor(vec4(1.0f, 0.2f, 0.2f, 0.5f));
|
||||
|
||||
auto contentname = util::str2wstr_utf8(contenttype_name(entry.type));
|
||||
Label* typelabel = new Label(L"["+contentname+L"]");
|
||||
typelabel->color(vec4(0.5f));
|
||||
auto typelabel = std::make_shared<Label>(L"["+contentname+L"]");
|
||||
typelabel->setColor(vec4(0.5f));
|
||||
hpanel->add(typelabel);
|
||||
hpanel->add(namelabel);
|
||||
subpanel->add(hpanel);
|
||||
}
|
||||
subpanel->maxLength(400);
|
||||
panel->add(subpanel);
|
||||
|
||||
panel->add((new Button(langs::get(L"Back to Main Menu", L"menu"),
|
||||
vec4(8.0f)))
|
||||
->listenAction([=](GUI*){
|
||||
menu->back();
|
||||
}));
|
||||
menu->set("missing-content");
|
||||
|
||||
panel->add(std::make_shared<Button>(
|
||||
langs::get(L"Back to Main Menu", L"menu"), vec4(8.0f), [=](GUI*){
|
||||
menu->back();
|
||||
}
|
||||
));
|
||||
menu->setPage("missing-content");
|
||||
}
|
||||
|
||||
void show_convert_request(
|
||||
Engine* engine,
|
||||
const Content* content,
|
||||
std::shared_ptr<ContentLUT> lut,
|
||||
fs::path folder) {
|
||||
fs::path folder
|
||||
) {
|
||||
guiutil::confirm(engine->getGUI(), langs::get(L"world.convert-request"),
|
||||
[=]() {
|
||||
// TODO: add multithreading here
|
||||
@ -125,9 +141,10 @@ void show_convert_request(
|
||||
}, L"", langs::get(L"Cancel"));
|
||||
}
|
||||
|
||||
void create_languages_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_languages_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = create_page(engine, "languages", 400, 0.5f, 1);
|
||||
panel->scrollable(true);
|
||||
panel->setScrollable(true);
|
||||
|
||||
std::vector<std::string> locales;
|
||||
for (auto& entry : langs::locales_info) {
|
||||
@ -138,11 +155,14 @@ void create_languages_panel(Engine* engine, PagesControl* menu) {
|
||||
auto& locale = langs::locales_info.at(name);
|
||||
std::string& fullName = locale.name;
|
||||
|
||||
Button* button = new Button(util::str2wstr_utf8(fullName), vec4(10.f));
|
||||
button->listenAction([=](GUI*) {
|
||||
engine->setLanguage(name);
|
||||
menu->back();
|
||||
});
|
||||
auto button = std::make_shared<Button>(
|
||||
util::str2wstr_utf8(fullName),
|
||||
vec4(10.f),
|
||||
[=](GUI*) {
|
||||
engine->setLanguage(name);
|
||||
menu->back();
|
||||
}
|
||||
);
|
||||
panel->add(button);
|
||||
}
|
||||
panel->add(guiutil::backButton(menu));
|
||||
@ -191,10 +211,10 @@ void open_world(std::string name, Engine* engine) {
|
||||
}
|
||||
}
|
||||
|
||||
Panel* create_worlds_panel(Engine* engine) {
|
||||
auto panel = new Panel(vec2(390, 200), vec4(5.0f));
|
||||
panel->color(vec4(1.0f, 1.0f, 1.0f, 0.07f));
|
||||
panel->maxLength(400);
|
||||
std::shared_ptr<Panel> create_worlds_panel(Engine* engine) {
|
||||
auto panel = std::make_shared<Panel>(vec2(390, 0), vec4(5.0f));
|
||||
panel->setColor(vec4(1.0f, 1.0f, 1.0f, 0.07f));
|
||||
panel->setMaxLength(400);
|
||||
|
||||
auto paths = engine->getPaths();
|
||||
|
||||
@ -203,72 +223,77 @@ Panel* create_worlds_panel(Engine* engine) {
|
||||
auto namews = util::str2wstr_utf8(name);
|
||||
|
||||
auto btn = std::make_shared<RichButton>(vec2(390, 46));
|
||||
btn->color(vec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
btn->setHoverColor(vec4(1.0f, 1.0f, 1.0f, 0.17f));
|
||||
|
||||
auto label = std::make_shared<Label>(namews);
|
||||
label->setInteractive(false);
|
||||
btn->add(label, vec2(8, 8));
|
||||
btn->setColor(vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
btn->setHoverColor(vec4(0.09f, 0.17f, 0.2f, 0.6f));
|
||||
btn->listenAction([=](GUI*) {
|
||||
open_world(name, engine);
|
||||
});
|
||||
btn->add(std::make_shared<Label>(namews), vec2(8, 8));
|
||||
|
||||
auto image = std::make_shared<Image>("gui/delete_icon", vec2(32, 32));
|
||||
image->color(vec4(1, 1, 1, 0.5f));
|
||||
image->setColor(vec4(1, 1, 1, 0.5f));
|
||||
|
||||
auto delbtn = std::make_shared<Button>(image, vec4(2));
|
||||
delbtn->color(vec4(0.0f));
|
||||
delbtn->setColor(vec4(0.0f));
|
||||
delbtn->setHoverColor(vec4(1.0f, 1.0f, 1.0f, 0.17f));
|
||||
|
||||
btn->add(delbtn, vec2(330, 3));
|
||||
|
||||
delbtn->listenAction([=](GUI* gui) {
|
||||
guiutil::confirm(gui, langs::get(L"delete-confirm", L"world")+
|
||||
L" ("+util::str2wstr_utf8(folder.u8string())+L")", [=]()
|
||||
{
|
||||
std::cout << "deleting " << folder.u8string() << std::endl;
|
||||
fs::remove_all(folder);
|
||||
menus::refresh_menus(engine, gui->getMenu());
|
||||
menus::refresh_menus(engine);
|
||||
});
|
||||
});
|
||||
btn->add(delbtn, vec2(330, 3));
|
||||
|
||||
panel->add(btn);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
void create_main_menu_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_main_menu_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
|
||||
auto panel = create_page(engine, "main", 400, 0.0f, 1);
|
||||
panel->add(guiutil::gotoButton(L"New World", "new-world", menu));
|
||||
panel->add(create_worlds_panel(engine));
|
||||
panel->add(guiutil::gotoButton(L"Settings", "settings", menu));
|
||||
panel->add((new Button(langs::get(L"Quit", L"menu"), vec4(10.f)))
|
||||
->listenAction([](GUI* gui) {
|
||||
Window::setShouldClose(true);
|
||||
}));
|
||||
|
||||
panel->add(std::make_shared<Button>(
|
||||
langs::get(L"Quit", L"menu"), vec4(10.f), [](GUI*) {
|
||||
Window::setShouldClose(true);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
typedef std::function<void(const ContentPack& pack)> packconsumer;
|
||||
|
||||
const int PACKS_PANEL_WIDTH = 550;
|
||||
|
||||
std::shared_ptr<Panel> create_packs_panel(const std::vector<ContentPack>& packs, Engine* engine, bool backbutton, packconsumer callback) {
|
||||
std::shared_ptr<Panel> create_packs_panel(
|
||||
const std::vector<ContentPack>& packs,
|
||||
Engine* engine,
|
||||
bool backbutton,
|
||||
packconsumer callback
|
||||
){
|
||||
auto assets = engine->getAssets();
|
||||
auto panel = std::make_shared<Panel>(vec2(PACKS_PANEL_WIDTH, 200), vec4(5.0f));
|
||||
panel->color(vec4(1.0f, 1.0f, 1.0f, 0.07f));
|
||||
panel->maxLength(400);
|
||||
panel->scrollable(true);
|
||||
auto panel = std::make_shared<Panel>(vec2(550, 200), vec4(5.0f));
|
||||
panel->setColor(vec4(1.0f, 1.0f, 1.0f, 0.07f));
|
||||
panel->setMaxLength(400);
|
||||
panel->setScrollable(true);
|
||||
|
||||
for (auto& pack : packs) {
|
||||
auto packpanel = std::make_shared<RichButton>(vec2(390, 80));
|
||||
auto packpanel = std::make_shared<RichButton>(vec2(540, 80));
|
||||
packpanel->setColor(vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
if (callback) {
|
||||
packpanel->listenAction([=](GUI*) {
|
||||
callback(pack);
|
||||
});
|
||||
}
|
||||
auto idlabel = std::make_shared<Label>("["+pack.id+"]");
|
||||
idlabel->color(vec4(1, 1, 1, 0.5f));
|
||||
packpanel->add(idlabel, vec2(PACKS_PANEL_WIDTH-40-idlabel->size().x, 2));
|
||||
idlabel->setColor(vec4(1, 1, 1, 0.5f));
|
||||
idlabel->setSize(vec2(300, 25));
|
||||
idlabel->setAlign(Align::right);
|
||||
packpanel->add(idlabel, vec2(215, 2));
|
||||
|
||||
auto titlelabel = std::make_shared<Label>(pack.title);
|
||||
packpanel->add(titlelabel, vec2(78, 6));
|
||||
@ -285,28 +310,30 @@ std::shared_ptr<Panel> create_packs_panel(const std::vector<ContentPack>& packs,
|
||||
|
||||
if (!pack.creator.empty()) {
|
||||
auto creatorlabel = std::make_shared<Label>("@"+pack.creator);
|
||||
creatorlabel->color(vec4(0.8f, 1.0f, 0.9f, 0.7f));
|
||||
packpanel->add(creatorlabel, vec2(PACKS_PANEL_WIDTH-40-creatorlabel->size().x, 60));
|
||||
creatorlabel->setColor(vec4(0.8f, 1.0f, 0.9f, 0.7f));
|
||||
creatorlabel->setSize(vec2(300, 20));
|
||||
creatorlabel->setAlign(Align::right);
|
||||
packpanel->add(creatorlabel, vec2(215, 60));
|
||||
}
|
||||
|
||||
auto descriptionlabel = std::make_shared<Label>(pack.description);
|
||||
descriptionlabel->color(vec4(1, 1, 1, 0.7f));
|
||||
descriptionlabel->setColor(vec4(1, 1, 1, 0.7f));
|
||||
packpanel->add(descriptionlabel, vec2(80, 28));
|
||||
|
||||
packpanel->add(std::make_shared<Image>(icon, glm::vec2(64)), vec2(8));
|
||||
|
||||
packpanel->color(vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
packpanel->add(std::make_shared<Image>(icon, vec2(64)), vec2(8));
|
||||
panel->add(packpanel);
|
||||
}
|
||||
if (backbutton)
|
||||
if (backbutton) {
|
||||
panel->add(guiutil::backButton(engine->getGUI()->getMenu()));
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
// TODO: refactor
|
||||
void create_content_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_content_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto paths = engine->getPaths();
|
||||
auto mainPanel = create_page(engine, "content", PACKS_PANEL_WIDTH, 0.0f, 5);
|
||||
auto mainPanel = create_page(engine, "content", 550, 0.0f, 5);
|
||||
|
||||
std::vector<ContentPack> scanned;
|
||||
ContentPack::scan(engine->getPaths(), scanned);
|
||||
@ -338,19 +365,19 @@ void create_content_panel(Engine* engine, PagesControl* menu) {
|
||||
return;
|
||||
}
|
||||
if (!world->hasPack(dependency)) {
|
||||
world->wfile->addPack(dependency);
|
||||
world->wfile->addPack(world, dependency);
|
||||
}
|
||||
}
|
||||
|
||||
world->wfile->addPack(pack.id);
|
||||
world->wfile->addPack(world, pack.id);
|
||||
|
||||
std::string wname = world->getName();
|
||||
engine->setScreen(nullptr);
|
||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
||||
open_world(wname, engine);
|
||||
});
|
||||
menu->add("content-packs", panel);
|
||||
menu->set("content-packs");
|
||||
menu->addPage("content-packs", panel);
|
||||
menu->setPage("content-packs");
|
||||
}));
|
||||
mainPanel->add(guiutil::backButton(menu));
|
||||
}
|
||||
@ -369,12 +396,12 @@ inline uint64_t str2seed(std::wstring seedstr) {
|
||||
}
|
||||
}
|
||||
|
||||
void create_new_world_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_new_world_panel(Engine* engine) {
|
||||
auto panel = create_page(engine, "new-world", 400, 0.0f, 1);
|
||||
|
||||
panel->add(std::make_shared<Label>(langs::get(L"Name", L"world")));
|
||||
auto nameInput = std::make_shared<TextBox>(L"New World", vec4(6.0f));
|
||||
nameInput->textValidator([=](const std::wstring& text) {
|
||||
nameInput->setTextValidator([=](const std::wstring& text) {
|
||||
EnginePaths* paths = engine->getPaths();
|
||||
std::string textutf8 = util::wstr2str_utf8(text);
|
||||
return util::is_valid_filename(text) &&
|
||||
@ -392,77 +419,81 @@ void create_new_world_panel(Engine* engine, PagesControl* menu) {
|
||||
if (!nameInput->validate())
|
||||
return;
|
||||
|
||||
std::wstring name = nameInput->text();
|
||||
std::string nameutf8 = util::wstr2str_utf8(name);
|
||||
EnginePaths* paths = engine->getPaths();
|
||||
|
||||
std::wstring seedstr = seedInput->text();
|
||||
uint64_t seed = str2seed(seedstr);
|
||||
std::string name = util::wstr2str_utf8(nameInput->getText());
|
||||
uint64_t seed = str2seed(seedInput->getText());
|
||||
std::cout << "world seed: " << seed << std::endl;
|
||||
|
||||
auto folder = paths->getWorldsFolder()/fs::u8path(nameutf8);
|
||||
EnginePaths* paths = engine->getPaths();
|
||||
auto folder = paths->getWorldsFolder()/fs::u8path(name);
|
||||
try {
|
||||
engine->loadAllPacks();
|
||||
engine->loadContent();
|
||||
paths->setWorldFolder(folder);
|
||||
} catch (const contentpack_error& error) {
|
||||
guiutil::alert(engine->getGUI(),
|
||||
langs::get(L"Content Error", L"menu")+
|
||||
L":\n"+util::str2wstr_utf8(std::string(error.what())+
|
||||
"\npack '"+error.getPackId()+"' from "+
|
||||
error.getFolder().u8string()));
|
||||
guiutil::alert(
|
||||
engine->getGUI(),
|
||||
langs::get(L"Content Error", L"menu")+L":\n"+
|
||||
util::str2wstr_utf8(
|
||||
std::string(error.what())+
|
||||
"\npack '"+error.getPackId()+"' from "+
|
||||
error.getFolder().u8string()
|
||||
)
|
||||
);
|
||||
return;
|
||||
} catch (const std::runtime_error& error) {
|
||||
guiutil::alert(engine->getGUI(),
|
||||
langs::get(L"Content Error", L"menu")+
|
||||
L": "+util::str2wstr_utf8(error.what()));
|
||||
guiutil::alert(
|
||||
engine->getGUI(),
|
||||
langs::get(L"Content Error", L"menu")+
|
||||
L": "+util::str2wstr_utf8(error.what())
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Level* level = World::create(
|
||||
nameutf8, folder, seed,
|
||||
name, folder, seed,
|
||||
engine->getSettings(),
|
||||
engine->getContent(),
|
||||
engine->getContentPacks());
|
||||
engine->getContentPacks()
|
||||
);
|
||||
engine->setScreen(std::make_shared<LevelScreen>(engine, level));
|
||||
}));
|
||||
panel->add(guiutil::backButton(menu));
|
||||
panel->add(guiutil::backButton(engine->getGUI()->getMenu()));
|
||||
}
|
||||
|
||||
void create_controls_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_controls_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = create_page(engine, "controls", 400, 0.0f, 1);
|
||||
|
||||
/* Camera sensitivity setting track bar */{
|
||||
panel->add((new Label(L""))->textSupplier([=]() {
|
||||
panel->add(create_label([=]() {
|
||||
float s = engine->getSettings().camera.sensitivity;
|
||||
return langs::get(L"Mouse Sensitivity", L"settings")+L": "+
|
||||
util::to_wstring(s, 1);
|
||||
}));
|
||||
|
||||
TrackBar* trackbar = new TrackBar(0.1, 10.0, 2.0, 0.1, 4);
|
||||
trackbar->supplier([=]() {
|
||||
auto trackbar = std::make_shared<TrackBar>(0.1, 10.0, 2.0, 0.1, 4);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().camera.sensitivity;
|
||||
});
|
||||
trackbar->consumer([=](double value) {
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().camera.sensitivity = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
Panel* scrollPanel = new Panel(vec2(400, 200), vec4(2.0f), 1.0f);
|
||||
scrollPanel->color(vec4(0.0f, 0.0f, 0.0f, 0.3f));
|
||||
scrollPanel->maxLength(400);
|
||||
auto scrollPanel = std::make_shared<Panel>(vec2(400, 200), vec4(2.0f), 1.0f);
|
||||
scrollPanel->setColor(vec4(0.0f, 0.0f, 0.0f, 0.3f));
|
||||
scrollPanel->setMaxLength(400);
|
||||
for (auto& entry : Events::bindings){
|
||||
std::string bindname = entry.first;
|
||||
|
||||
Panel* subpanel = new Panel(vec2(400, 40), vec4(5.0f), 1.0f);
|
||||
subpanel->color(vec4(0.0f));
|
||||
subpanel->orientation(Orientation::horizontal);
|
||||
auto subpanel = std::make_shared<Panel>(vec2(400, 40), vec4(5.0f), 1.0f);
|
||||
subpanel->setColor(vec4(0.0f));
|
||||
subpanel->setOrientation(Orientation::horizontal);
|
||||
subpanel->add(std::make_shared<InputBindBox>(entry.second));
|
||||
|
||||
InputBindBox* bindbox = new InputBindBox(entry.second);
|
||||
subpanel->add(bindbox);
|
||||
Label* label = new Label(langs::get(util::str2wstr_utf8(bindname)));
|
||||
label->margin(vec4(6.0f));
|
||||
auto label = std::make_shared<Label>(langs::get(util::str2wstr_utf8(bindname)));
|
||||
label->setMargin(vec4(6.0f));
|
||||
subpanel->add(label);
|
||||
scrollPanel->add(subpanel);
|
||||
}
|
||||
@ -470,96 +501,114 @@ void create_controls_panel(Engine* engine, PagesControl* menu) {
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
void create_settings_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_settings_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = create_page(engine, "settings", 400, 0.0f, 1);
|
||||
|
||||
/* Load Distance setting track bar */{
|
||||
panel->add((new Label(L""))->textSupplier([=]() {
|
||||
panel->add(create_label([=]() {
|
||||
return langs::get(L"Load Distance", L"settings")+L": " +
|
||||
std::to_wstring(engine->getSettings().chunks.loadDistance);
|
||||
}));
|
||||
|
||||
TrackBar* trackbar = new TrackBar(3, 66, 10, 1, 3);
|
||||
trackbar->supplier([=]() {
|
||||
auto trackbar = std::make_shared<TrackBar>(3, 66, 10, 1, 3);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().chunks.loadDistance;
|
||||
});
|
||||
trackbar->consumer([=](double value) {
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().chunks.loadDistance = static_cast<uint>(value);
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* Load Speed setting track bar */{
|
||||
panel->add((new Label(L""))->textSupplier([=]() {
|
||||
panel->add(create_label([=]() {
|
||||
return langs::get(L"Load Speed", L"settings")+L": " +
|
||||
std::to_wstring(engine->getSettings().chunks.loadSpeed);
|
||||
}));
|
||||
|
||||
TrackBar* trackbar = new TrackBar(1, 32, 10, 1, 1);
|
||||
trackbar->supplier([=]() {
|
||||
auto trackbar = std::make_shared<TrackBar>(1, 32, 10, 1, 1);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().chunks.loadSpeed;
|
||||
});
|
||||
trackbar->consumer([=](double value) {
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().chunks.loadSpeed = static_cast<uint>(value);
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* Fog Curve setting track bar */{
|
||||
panel->add((new Label(L""))->textSupplier([=]() {
|
||||
panel->add(create_label([=]() {
|
||||
float value = engine->getSettings().graphics.fogCurve;
|
||||
return langs::get(L"Fog Curve", L"settings")+L": " +
|
||||
util::to_wstring(value, 1);
|
||||
}));
|
||||
|
||||
TrackBar* trackbar = new TrackBar(1.0, 6.0, 1.0, 0.1, 2);
|
||||
trackbar->supplier([=]() {
|
||||
auto trackbar = std::make_shared<TrackBar>(1.0, 6.0, 1.0, 0.1, 2);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().graphics.fogCurve;
|
||||
});
|
||||
trackbar->consumer([=](double value) {
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().graphics.fogCurve = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* Fov setting track bar */{
|
||||
panel->add((new Label(L""))->textSupplier([=]() {
|
||||
panel->add(create_label([=]() {
|
||||
int fov = (int)engine->getSettings().camera.fov;
|
||||
return langs::get(L"FOV", L"settings")+L": "+std::to_wstring(fov)+L"°";
|
||||
}));
|
||||
|
||||
TrackBar* trackbar = new TrackBar(30.0, 120.0, 90, 1, 4);
|
||||
trackbar->supplier([=]() {
|
||||
auto trackbar = std::make_shared<TrackBar>(30.0, 120.0, 90, 1, 4);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().camera.fov;
|
||||
});
|
||||
trackbar->consumer([=](double value) {
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().camera.fov = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* V-Sync checkbox */{
|
||||
auto checkbox = new FullCheckBox(langs::get(L"V-Sync", L"settings"), vec2(400, 32));
|
||||
checkbox->supplier([=]() {
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
langs::get(L"V-Sync", L"settings"), vec2(400, 32)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().display.swapInterval != 0;
|
||||
});
|
||||
checkbox->consumer([=](bool checked) {
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
engine->getSettings().display.swapInterval = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
|
||||
/* Backlight checkbox */{
|
||||
auto checkbox = new FullCheckBox(langs::get(L"Backlight", L"settings"), vec2(400, 32));
|
||||
checkbox->supplier([=]() {
|
||||
return engine->getSettings().graphics.backlight != 0;
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
langs::get(L"Backlight", L"settings"), vec2(400, 32)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().graphics.backlight;
|
||||
});
|
||||
checkbox->consumer([=](bool checked) {
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
engine->getSettings().graphics.backlight = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
|
||||
/* Camera shaking checkbox */ {
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
langs::get(L"Camera Shaking", L"settings"), vec2(400, 32)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().camera.shaking;
|
||||
});
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
engine->getSettings().camera.shaking = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
|
||||
std::string langName = langs::locales_info.at(langs::current->getId()).name;
|
||||
panel->add(guiutil::gotoButton(
|
||||
langs::get(L"Language", L"settings")+L": "+
|
||||
@ -570,15 +619,16 @@ void create_settings_panel(Engine* engine, PagesControl* menu) {
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
void create_pause_panel(Engine* engine, PagesControl* menu) {
|
||||
void create_pause_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = create_page(engine, "pause", 400, 0.0f, 1);
|
||||
|
||||
panel->add(create_button(L"Continue", vec4(10.0f), vec4(1), [=](GUI*){
|
||||
menu->reset();
|
||||
}));
|
||||
panel->add(create_button(L"Content", vec4(10.0f), vec4(1), [=](GUI*) {
|
||||
create_content_panel(engine, menu);
|
||||
menu->set("content");
|
||||
create_content_panel(engine);
|
||||
menu->setPage("content");
|
||||
}));
|
||||
panel->add(guiutil::gotoButton(L"Settings", "settings", menu));
|
||||
|
||||
@ -590,16 +640,16 @@ void create_pause_panel(Engine* engine, PagesControl* menu) {
|
||||
}));
|
||||
}
|
||||
|
||||
void menus::create_menus(Engine* engine, PagesControl* menu) {
|
||||
create_new_world_panel(engine, menu);
|
||||
create_settings_panel(engine, menu);
|
||||
create_controls_panel(engine, menu);
|
||||
create_pause_panel(engine, menu);
|
||||
create_languages_panel(engine, menu);
|
||||
create_main_menu_panel(engine, menu);
|
||||
void menus::create_menus(Engine* engine) {
|
||||
create_new_world_panel(engine);
|
||||
create_settings_panel(engine);
|
||||
create_controls_panel(engine);
|
||||
create_pause_panel(engine);
|
||||
create_languages_panel(engine);
|
||||
create_main_menu_panel(engine);
|
||||
}
|
||||
|
||||
void menus::refresh_menus(Engine* engine, PagesControl* menu) {
|
||||
create_main_menu_panel(engine, menu);
|
||||
create_new_world_panel(engine, menu);
|
||||
void menus::refresh_menus(Engine* engine) {
|
||||
create_main_menu_panel(engine);
|
||||
create_new_world_panel(engine);
|
||||
}
|
||||
|
||||
@ -3,13 +3,9 @@
|
||||
|
||||
class Engine;
|
||||
|
||||
namespace gui {
|
||||
class PagesControl;
|
||||
}
|
||||
|
||||
namespace menus {
|
||||
void create_menus(Engine* engine, gui::PagesControl* menu);
|
||||
void refresh_menus(Engine* engine, gui::PagesControl* menu);
|
||||
void create_menus(Engine* engine);
|
||||
void refresh_menus(Engine* engine);
|
||||
}
|
||||
|
||||
#endif // FRONTEND_MENU_H_
|
||||
@ -21,6 +21,8 @@
|
||||
#include "../objects/Player.h"
|
||||
#include "../logic/ChunksController.h"
|
||||
#include "../logic/LevelController.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
#include "../logic/scripting/scripting_frontend.h"
|
||||
#include "../voxels/Chunks.h"
|
||||
#include "../voxels/Chunk.h"
|
||||
#include "../engine.h"
|
||||
@ -31,7 +33,7 @@
|
||||
#include "ContentGfxCache.h"
|
||||
#include "LevelFrontend.h"
|
||||
#include "gui/GUI.h"
|
||||
#include "gui/panels.h"
|
||||
#include "gui/containers.h"
|
||||
#include "menu.h"
|
||||
|
||||
#include "../content/Content.h"
|
||||
@ -45,9 +47,9 @@ Screen::~Screen() {
|
||||
|
||||
MenuScreen::MenuScreen(Engine* engine_) : Screen(engine_) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
menus::refresh_menus(engine, menu);
|
||||
menus::refresh_menus(engine);
|
||||
menu->reset();
|
||||
menu->set("main");
|
||||
menu->setPage("main");
|
||||
|
||||
uicamera.reset(new Camera(glm::vec3(), Window::height));
|
||||
uicamera->perspective = false;
|
||||
@ -87,7 +89,7 @@ LevelScreen::LevelScreen(Engine* engine, Level* level)
|
||||
: Screen(engine),
|
||||
level(level),
|
||||
frontend(std::make_unique<LevelFrontend>(level, engine->getAssets())),
|
||||
hud(std::make_unique<HudRenderer>(engine, frontend.get())),
|
||||
hud(std::make_unique<Hud>(engine, frontend.get())),
|
||||
worldRenderer(std::make_unique<WorldRenderer>(engine, frontend.get())),
|
||||
controller(std::make_unique<LevelController>(engine->getSettings(), level)) {
|
||||
|
||||
@ -96,10 +98,21 @@ LevelScreen::LevelScreen(Engine* engine, Level* level)
|
||||
|
||||
animator.reset(new TextureAnimator());
|
||||
animator->addAnimations(engine->getAssets()->getAnimations());
|
||||
|
||||
auto content = level->content;
|
||||
for (auto& pack : content->getPacks()) {
|
||||
const ContentPack& info = pack->getInfo();
|
||||
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_hud_script(pack->getEnvironment()->getId(), info.id, scriptFile);
|
||||
}
|
||||
}
|
||||
scripting::on_frontend_init(hud.get());
|
||||
}
|
||||
|
||||
LevelScreen::~LevelScreen() {
|
||||
std::cout << "-- writing world" << std::endl;
|
||||
scripting::on_frontend_close();
|
||||
controller->onWorldSave();
|
||||
auto world = level->getWorld();
|
||||
world->write(level.get());
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
class Assets;
|
||||
class Level;
|
||||
class WorldRenderer;
|
||||
class HudRenderer;
|
||||
class Hud;
|
||||
class Engine;
|
||||
class Camera;
|
||||
class Batch2D;
|
||||
@ -40,7 +40,7 @@ public:
|
||||
class LevelScreen : public Screen {
|
||||
std::unique_ptr<Level> level;
|
||||
std::unique_ptr<LevelFrontend> frontend;
|
||||
std::unique_ptr<HudRenderer> hud;
|
||||
std::unique_ptr<Hud> hud;
|
||||
std::unique_ptr<WorldRenderer> worldRenderer;
|
||||
std::unique_ptr<LevelController> controller;
|
||||
std::unique_ptr<TextureAnimator> animator;
|
||||
|
||||
@ -11,17 +11,17 @@ using glm::vec2;
|
||||
using glm::vec3;
|
||||
using glm::vec4;
|
||||
|
||||
Batch2D::Batch2D(size_t capacity) : capacity(capacity), offset(0), color(1.0f, 1.0f, 1.0f, 1.0f){
|
||||
Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
||||
const vattr attrs[] = {
|
||||
{2}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
buffer = new float[capacity * B2D_VERTEX_SIZE];
|
||||
mesh = new Mesh(buffer, 0, attrs);
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
index = 0;
|
||||
|
||||
unsigned char pixels[] = {
|
||||
255, 255, 255, 255,
|
||||
ubyte pixels[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
blank = new Texture(pixels, 1, 1, GL_RGBA);
|
||||
_texture = nullptr;
|
||||
@ -30,7 +30,6 @@ Batch2D::Batch2D(size_t capacity) : capacity(capacity), offset(0), color(1.0f, 1
|
||||
Batch2D::~Batch2D(){
|
||||
delete blank;
|
||||
delete[] buffer;
|
||||
delete mesh;
|
||||
}
|
||||
|
||||
void Batch2D::begin(){
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#ifndef SRC_GRAPHICS_BATCH2D_H_
|
||||
#define SRC_GRAPHICS_BATCH2D_H_
|
||||
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
@ -13,8 +14,7 @@ class Sprite;
|
||||
class Batch2D {
|
||||
float* buffer;
|
||||
size_t capacity;
|
||||
size_t offset;
|
||||
Mesh* mesh;
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
size_t index;
|
||||
|
||||
Texture* blank;
|
||||
|
||||
@ -13,8 +13,7 @@ using glm::vec3;
|
||||
using glm::vec4;
|
||||
|
||||
Batch3D::Batch3D(size_t capacity)
|
||||
: capacity(capacity),
|
||||
offset(0) {
|
||||
: capacity(capacity) {
|
||||
const vattr attrs[] = {
|
||||
{3}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
@ -12,7 +12,6 @@ class Texture;
|
||||
class Batch3D {
|
||||
float* buffer;
|
||||
size_t capacity;
|
||||
size_t offset;
|
||||
Mesh* mesh;
|
||||
size_t index;
|
||||
|
||||
|
||||
@ -4,16 +4,19 @@
|
||||
|
||||
using glm::vec4;
|
||||
|
||||
Font::Font(std::vector<Texture*> pages, int lineHeight) : lineHeight_(lineHeight), pages(pages) {
|
||||
Font::Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset)
|
||||
: lineHeight(lineHeight), yoffset(yoffset), pages(std::move(pages)) {
|
||||
}
|
||||
|
||||
Font::~Font(){
|
||||
for (Texture* texture : pages)
|
||||
delete texture;
|
||||
}
|
||||
|
||||
int Font::lineHeight() const {
|
||||
return lineHeight_;
|
||||
int Font::getYOffset() const {
|
||||
return yoffset;
|
||||
}
|
||||
|
||||
int Font::getLineHeight() const {
|
||||
return lineHeight;
|
||||
}
|
||||
|
||||
bool Font::isPrintableChar(int c) {
|
||||
@ -48,11 +51,11 @@ void Font::draw(Batch2D* batch, std::wstring text, int x, int y, int style) {
|
||||
if (isPrintableChar(c)){
|
||||
int charpage = c >> 8;
|
||||
if (charpage == page){
|
||||
Texture* texture = pages[charpage];
|
||||
Texture* texture = pages[charpage].get();
|
||||
if (texture == nullptr){
|
||||
texture = pages[0];
|
||||
texture = pages[0].get();
|
||||
}
|
||||
batch->texture(pages[charpage]);
|
||||
batch->texture(texture);
|
||||
|
||||
switch (style){
|
||||
case STYLE_SHADOW:
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#ifndef GRAPHICS_FONT_H_
|
||||
#define GRAPHICS_FONT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../typedefs.h"
|
||||
@ -13,13 +14,15 @@ const uint STYLE_SHADOW = 1;
|
||||
const uint STYLE_OUTLINE = 2;
|
||||
|
||||
class Font {
|
||||
int lineHeight_;
|
||||
int lineHeight;
|
||||
int yoffset;
|
||||
public:
|
||||
std::vector<Texture*> pages;
|
||||
Font(std::vector<Texture*> pages, int lineHeight);
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset);
|
||||
~Font();
|
||||
|
||||
int lineHeight() const;
|
||||
int getLineHeight() const;
|
||||
int getYOffset() const;
|
||||
int calcWidth(std::wstring text);
|
||||
// int getGlyphWidth(char c);
|
||||
bool isPrintableChar(int c);
|
||||
|
||||
@ -9,8 +9,13 @@ GfxContext::GfxContext(const GfxContext* parent, Viewport& viewport, Batch2D* g2
|
||||
}
|
||||
|
||||
GfxContext::~GfxContext() {
|
||||
while (scissorsCount--) {
|
||||
Window::popScissor();
|
||||
}
|
||||
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (depthMask_ != parent->depthMask_) {
|
||||
glDepthMask(parent->depthMask_);
|
||||
}
|
||||
@ -77,3 +82,8 @@ void GfxContext::blendMode(blendmode mode) {
|
||||
blendMode_ = mode;
|
||||
Window::setBlendMode(mode);
|
||||
}
|
||||
|
||||
void GfxContext::scissors(glm::vec4 area) {
|
||||
Window::pushScissor(area);
|
||||
scissorsCount++;
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ class GfxContext {
|
||||
bool depthTest_ = false;
|
||||
bool cullFace_ = false;
|
||||
blendmode blendMode_ = blendmode::normal;
|
||||
int scissorsCount = 0;
|
||||
public:
|
||||
GfxContext(const GfxContext* parent, Viewport& viewport, Batch2D* g2d);
|
||||
~GfxContext();
|
||||
@ -27,6 +28,7 @@ public:
|
||||
void depthTest(bool flag);
|
||||
void cullFace(bool flag);
|
||||
void blendMode(blendmode mode);
|
||||
void scissors(glm::vec4 area);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_GFX_CONTEXT_H_
|
||||
@ -8,17 +8,23 @@ const uint LB_VERTEX_SIZE = (3+4);
|
||||
LineBatch::LineBatch(size_t capacity) : capacity(capacity) {
|
||||
const vattr attrs[] = { {3},{4}, {0} };
|
||||
buffer = new float[capacity * LB_VERTEX_SIZE * 2];
|
||||
mesh = new Mesh(buffer, 0, attrs);
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
LineBatch::~LineBatch(){
|
||||
delete[] buffer;
|
||||
delete mesh;
|
||||
}
|
||||
|
||||
void LineBatch::line(float x1, float y1, float z1, float x2, float y2, float z2,
|
||||
float r, float g, float b, float a) {
|
||||
void LineBatch::line(
|
||||
float x1, float y1,
|
||||
float z1, float x2,
|
||||
float y2, float z2,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
if (index + LB_VERTEX_SIZE * 2 >= capacity) {
|
||||
render();
|
||||
}
|
||||
buffer[index] = x1;
|
||||
buffer[index+1] = y1;
|
||||
buffer[index+2] = z1;
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
#ifndef GRAPHICS_LINEBATCH_H_
|
||||
#define GRAPHICS_LINEBATCH_H_
|
||||
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Mesh;
|
||||
|
||||
class LineBatch {
|
||||
Mesh* mesh;
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
float* buffer;
|
||||
size_t index;
|
||||
size_t capacity;
|
||||
|
||||
@ -19,7 +19,7 @@ Texture::Texture(ubyte* data, int width, int height, uint format)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -49,9 +49,30 @@ void TextureAnimator::update(float delta) {
|
||||
|
||||
float srcPosY = elem.srcTexture->height - frame.size.y - frame.srcPos.y; // vertical flip
|
||||
|
||||
glBlitFramebuffer(frame.srcPos.x, srcPosY, frame.srcPos.x + frame.size.x, srcPosY + frame.size.y,
|
||||
frame.dstPos.x, frame.dstPos.y, frame.dstPos.x + frame.size.x, frame.dstPos.y + frame.size.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
// Extensions
|
||||
const int ext = 2;
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
if (x == 0 && y == 0)
|
||||
continue;
|
||||
glBlitFramebuffer(
|
||||
frame.srcPos.x, srcPosY, frame.srcPos.x + frame.size.x, srcPosY + frame.size.y,
|
||||
frame.dstPos.x+x*ext, frame.dstPos.y+y*ext,
|
||||
frame.dstPos.x + frame.size.x+x*ext, frame.dstPos.y + frame.size.y+y*ext,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
glBlitFramebuffer(
|
||||
frame.srcPos.x, srcPosY,
|
||||
frame.srcPos.x + frame.size.x,
|
||||
srcPosY + frame.size.y,
|
||||
frame.dstPos.x, frame.dstPos.y,
|
||||
frame.dstPos.x + frame.size.x,
|
||||
frame.dstPos.y + frame.size.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
||||
);
|
||||
}
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
59
src/items/Inventories.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "Inventories.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../world/Level.h"
|
||||
#include "../world/World.h"
|
||||
|
||||
Inventories::Inventories(Level& level) : level(level) {
|
||||
}
|
||||
|
||||
Inventories::~Inventories() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::create(size_t size) {
|
||||
int64_t id = level.getWorld()->getNextInventoryId();
|
||||
auto inv = std::make_shared<Inventory>(id, size);
|
||||
store(inv);
|
||||
return inv;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::createVirtual(size_t size) {
|
||||
int64_t id;
|
||||
do {
|
||||
id = -std::max<int64_t>(1LL, std::llabs(random.rand64()));
|
||||
} while (map.find(id) != map.end());
|
||||
|
||||
auto inv = std::make_shared<Inventory>(id, size);
|
||||
store(inv);
|
||||
return inv;
|
||||
}
|
||||
|
||||
void Inventories::store(std::shared_ptr<Inventory> inv) {
|
||||
map[inv->getId()] = inv;
|
||||
}
|
||||
|
||||
void Inventories::remove(int64_t id) {
|
||||
map.erase(id);
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::get(int64_t id) {
|
||||
auto found = map.find(id);
|
||||
if (found == map.end())
|
||||
return nullptr;
|
||||
return found->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::clone(int64_t id) {
|
||||
auto original = get(id);
|
||||
if (original == nullptr)
|
||||
return nullptr;
|
||||
auto origptr = reinterpret_cast<const Inventory*>(original.get());
|
||||
auto clone = std::make_shared<Inventory>(*origptr);
|
||||
clone->setId(level.getWorld()->getNextInventoryId());
|
||||
return clone;
|
||||
}
|
||||
|
||||
const inventories_map& Inventories::getMap() const {
|
||||
return map;
|
||||
}
|
||||
44
src/items/Inventories.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef ITEMS_INVENTORIES_H_
|
||||
#define ITEMS_INVENTORIES_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Inventory.h"
|
||||
#include "../maths/util.h"
|
||||
|
||||
class Level;
|
||||
|
||||
using inventories_map = std::unordered_map<int64_t, std::shared_ptr<Inventory>>;
|
||||
|
||||
/* Inventories runtime storage */
|
||||
class Inventories {
|
||||
Level& level;
|
||||
inventories_map map;
|
||||
PseudoRandom random;
|
||||
public:
|
||||
Inventories(Level& level);
|
||||
~Inventories();
|
||||
|
||||
/* Create new inventory with new id */
|
||||
std::shared_ptr<Inventory> create(size_t size);
|
||||
|
||||
/* Create runtime-only inventory (has negative id) */
|
||||
std::shared_ptr<Inventory> createVirtual(size_t size);
|
||||
|
||||
/* Store inventory */
|
||||
void store(std::shared_ptr<Inventory> inv);
|
||||
|
||||
/* Remove inventory from map */
|
||||
void remove(int64_t id);
|
||||
|
||||
/* Get inventory by id (works with both real and virtual)*/
|
||||
std::shared_ptr<Inventory> get(int64_t id);
|
||||
|
||||
std::shared_ptr<Inventory> clone(int64_t id);
|
||||
|
||||
const inventories_map& getMap() const;
|
||||
};
|
||||
|
||||
#endif // ITEMS_INVENTORIES_H_
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "../content/ContentLUT.h"
|
||||
|
||||
Inventory::Inventory(size_t size) : slots(size) {
|
||||
Inventory::Inventory(int64_t id, size_t size) : id(id), slots(size) {
|
||||
}
|
||||
|
||||
ItemStack& Inventory::getSlot(size_t index) {
|
||||
@ -45,8 +45,12 @@ void Inventory::move(
|
||||
}
|
||||
|
||||
void Inventory::deserialize(dynamic::Map* src) {
|
||||
id = src->getNum("id", 1);
|
||||
auto slotsarr = src->list("slots");
|
||||
size_t slotscount = std::min(slotsarr->size(), slots.size());
|
||||
size_t slotscount = slotsarr->size();
|
||||
while (slots.size() < slotscount) {
|
||||
slots.push_back(ItemStack());
|
||||
}
|
||||
for (size_t i = 0; i < slotscount; i++) {
|
||||
auto item = slotsarr->map(i);
|
||||
itemid_t id = item->getInt("id", ITEM_EMPTY);
|
||||
@ -58,6 +62,8 @@ void Inventory::deserialize(dynamic::Map* src) {
|
||||
|
||||
std::unique_ptr<dynamic::Map> Inventory::serialize() const {
|
||||
auto map = std::make_unique<dynamic::Map>();
|
||||
map->put("id", id);
|
||||
|
||||
auto& slotsarr = map->putList("slots");
|
||||
for (size_t i = 0; i < slots.size(); i++) {
|
||||
auto& item = slots[i];
|
||||
|
||||