UiDocument is an asset now

This commit is contained in:
MihailRis 2024-02-09 09:59:36 +03:00
parent 241d15e349
commit 07adbd04ea
21 changed files with 402 additions and 304 deletions

View File

@ -4,6 +4,7 @@
#include "../graphics/Shader.h" #include "../graphics/Shader.h"
#include "../graphics/Atlas.h" #include "../graphics/Atlas.h"
#include "../graphics/Font.h" #include "../graphics/Font.h"
#include "../frontend/UiDocument.h"
Assets::~Assets() { Assets::~Assets() {
} }
@ -62,6 +63,17 @@ void Assets::store(const TextureAnimation& animation) {
animations.emplace_back(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) { void Assets::extend(const Assets& assets) {
for (auto entry : assets.textures) { for (auto entry : assets.textures) {
textures[entry.first] = entry.second; textures[entry.first] = entry.second;

View File

@ -12,12 +12,14 @@ class Texture;
class Shader; class Shader;
class Font; class Font;
class Atlas; class Atlas;
class UiDocument;
class Assets { class Assets {
std::unordered_map<std::string, std::shared_ptr<Texture>> textures; 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<Shader>> shaders;
std::unordered_map<std::string, std::shared_ptr<Font>> fonts; 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<Atlas>> atlases;
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
std::vector<TextureAnimation> animations; std::vector<TextureAnimation> animations;
public: public:
~Assets(); ~Assets();
@ -36,6 +38,9 @@ public:
const std::vector<TextureAnimation>& getAnimations(); const std::vector<TextureAnimation>& getAnimations();
void store(const TextureAnimation& animation); void store(const TextureAnimation& animation);
UiDocument* getLayout(std::string name) const;
void store(UiDocument* layout, std::string name);
void extend(const Assets& assets); void extend(const Assets& assets);
}; };

View File

@ -48,6 +48,7 @@ void AssetsLoader::createDefaults(AssetsLoader& loader) {
loader.addLoader(ASSET_TEXTURE, assetload::texture); loader.addLoader(ASSET_TEXTURE, assetload::texture);
loader.addLoader(ASSET_FONT, assetload::font); loader.addLoader(ASSET_FONT, assetload::font);
loader.addLoader(ASSET_ATLAS, assetload::atlas); loader.addLoader(ASSET_ATLAS, assetload::atlas);
loader.addLoader(ASSET_LAYOUT, assetload::layout);
} }
void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) { void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) {
@ -66,6 +67,14 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) {
loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen"); loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen");
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/moon.png", "misc/moon"); 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_TEXTURE, TEXTURES_FOLDER"/misc/sun.png", "misc/sun");
for (fs::path& file : loader.getPaths()->listdir(LAYOUTS_FOLDER)) {
std::string packName = file.parent_path().parent_path().filename();
if (packName == "res") {
packName = "core";
}
loader.add(ASSET_LAYOUT, file.u8string(), packName+":"+file.stem().u8string());
}
} }
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks"); loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks");
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/items", "items"); loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/items", "items");

View File

@ -10,6 +10,7 @@ const short ASSET_TEXTURE = 1;
const short ASSET_SHADER = 2; const short ASSET_SHADER = 2;
const short ASSET_FONT = 3; const short ASSET_FONT = 3;
const short ASSET_ATLAS = 4; const short ASSET_ATLAS = 4;
const short ASSET_LAYOUT = 5;
class ResPaths; class ResPaths;
class Assets; class Assets;

View File

@ -13,6 +13,7 @@
#include "../graphics/Atlas.h" #include "../graphics/Atlas.h"
#include "../graphics/Font.h" #include "../graphics/Font.h"
#include "../graphics/TextureAnimation.h" #include "../graphics/TextureAnimation.h"
#include "../frontend/UiDocument.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -20,187 +21,205 @@ bool assetload::texture(Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name) { const std::string name) {
std::unique_ptr<Texture> texture( std::unique_ptr<Texture> texture(
png::load_texture(paths->find(filename).string()) png::load_texture(paths->find(filename).u8string())
); );
if (texture == nullptr) { if (texture == nullptr) {
std::cerr << "failed to load texture '" << name << "'" << std::endl; std::cerr << "failed to load texture '" << name << "'" << std::endl;
return false; return false;
} }
assets->store(texture.release(), name); assets->store(texture.release(), name);
return true; return true;
} }
bool assetload::shader(Assets* assets, bool assetload::shader(Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name) { const std::string name) {
fs::path vertexFile = paths->find(filename+".glslv"); fs::path vertexFile = paths->find(filename+".glslv");
fs::path fragmentFile = paths->find(filename+".glslf"); fs::path fragmentFile = paths->find(filename+".glslf");
std::string vertexSource = files::read_string(vertexFile); std::string vertexSource = files::read_string(vertexFile);
std::string fragmentSource = files::read_string(fragmentFile); std::string fragmentSource = files::read_string(fragmentFile);
Shader* shader = Shader::loadShader( Shader* shader = Shader::loadShader(
vertexFile.string(), vertexFile.string(),
fragmentFile.string(), fragmentFile.string(),
vertexSource, fragmentSource); vertexSource, fragmentSource);
if (shader == nullptr) { if (shader == nullptr) {
std::cerr << "failed to load shader '" << name << "'" << std::endl; std::cerr << "failed to load shader '" << name << "'" << std::endl;
return false; return false;
} }
assets->store(shader, name); assets->store(shader, name);
return true; return true;
} }
static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) { static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) {
// png is only supported format // png is only supported format
if (file.extension() != ".png") if (file.extension() != ".png")
return false; return false;
std::string name = file.stem().string(); std::string name = file.stem().string();
// skip duplicates // skip duplicates
if (atlas.has(name)) { if (atlas.has(name)) {
return false; return false;
} }
std::unique_ptr<ImageData> image(png::load_image(file.string())); std::unique_ptr<ImageData> image(png::load_image(file.string()));
if (image == nullptr) { if (image == nullptr) {
std::cerr << "could not to load " << file.string() << std::endl; std::cerr << "could not to load " << file.string() << std::endl;
return false; return false;
} }
image->fixAlphaColor(); image->fixAlphaColor();
atlas.add(name, image.release()); atlas.add(name, image.release());
return true; return true;
} }
bool assetload::atlas(Assets* assets, bool assetload::atlas(Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string directory, const std::string directory,
const std::string name) { const std::string name) {
AtlasBuilder builder; AtlasBuilder builder;
for (const auto& file : paths->listdir(directory)) { for (const auto& file : paths->listdir(directory)) {
if (!appendAtlas(builder, file)) continue; if (!appendAtlas(builder, file)) continue;
} }
Atlas* atlas = builder.build(2); Atlas* atlas = builder.build(2);
assets->store(atlas, name); assets->store(atlas, name);
for (const auto& file : builder.getNames()) { for (const auto& file : builder.getNames()) {
assetload::animation(assets, paths, "textures", file, atlas); assetload::animation(assets, paths, "textures", file, atlas);
} }
return true; return true;
} }
bool assetload::font(Assets* assets, bool assetload::font(Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name) { const std::string name) {
std::vector<std::unique_ptr<Texture>> pages; std::vector<std::unique_ptr<Texture>> pages;
for (size_t i = 0; i <= 4; i++) { for (size_t i = 0; i <= 4; i++) {
std::string name = filename + "_" + std::to_string(i) + ".png"; std::string name = filename + "_" + std::to_string(i) + ".png";
name = paths->find(name).string(); name = paths->find(name).string();
std::unique_ptr<Texture> texture (png::load_texture(name)); std::unique_ptr<Texture> texture (png::load_texture(name));
if (texture == nullptr) { if (texture == nullptr) {
std::cerr << "failed to load bitmap font '" << name; std::cerr << "failed to load bitmap font '" << name;
std::cerr << "' (missing page " << std::to_string(i) << ")"; std::cerr << "' (missing page " << std::to_string(i) << ")";
std::cerr << std::endl; std::cerr << std::endl;
return false; return false;
} }
pages.push_back(std::move(texture)); pages.push_back(std::move(texture));
} }
int res = pages[0]->height / 16; int res = pages[0]->height / 16;
assets->store(new Font(std::move(pages), res, 4), name); assets->store(new Font(std::move(pages), res, 4), name);
return true; return true;
} }
bool assetload::animation(Assets* assets, bool assetload::animation(Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string directory, const std::string directory,
const std::string name, const std::string name,
Atlas* dstAtlas) { Atlas* dstAtlas) {
std::string animsDir = directory + "/animations"; std::string animsDir = directory + "/animations";
std::string blocksDir = directory + "/blocks"; std::string blocksDir = directory + "/blocks";
for (const auto& folder : paths->listdir(animsDir)) { for (const auto& folder : paths->listdir(animsDir)) {
if (!fs::is_directory(folder)) continue; if (!fs::is_directory(folder)) continue;
if (folder.filename().string() != name) continue; if (folder.filename().string() != name) continue;
if (fs::is_empty(folder)) continue; if (fs::is_empty(folder)) continue;
AtlasBuilder builder; AtlasBuilder builder;
appendAtlas(builder, paths->find(blocksDir + "/" + name + ".png")); 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)) { if (fs::exists(animFile)) {
auto root = files::read_json(animFile); auto root = files::read_json(animFile);
auto frameArr = root->list("frames"); auto frameArr = root->list("frames");
float frameDuration = DEFAULT_FRAME_DURATION; float frameDuration = DEFAULT_FRAME_DURATION;
std::string frameName; std::string frameName;
if (frameArr) { if (frameArr) {
for (size_t i = 0; i < frameArr->size(); i++) { for (size_t i = 0; i < frameArr->size(); i++) {
auto currentFrame = frameArr->list(i); auto currentFrame = frameArr->list(i);
frameName = currentFrame->str(0); frameName = currentFrame->str(0);
if (currentFrame->size() > 1) frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000; if (currentFrame->size() > 1)
frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
frameList.emplace_back(frameName, frameDuration); frameList.emplace_back(frameName, frameDuration);
} }
} }
} }
for (const auto& file : paths->listdir(animsDir + "/" + name)) { for (const auto& file : paths->listdir(animsDir + "/" + name)) {
if (!frameList.empty()) { if (!frameList.empty()) {
bool contains = false; bool contains = false;
for (const auto& elem : frameList) { for (const auto& elem : frameList) {
if (file.stem() == elem.first) { if (file.stem() == elem.first) {
contains = true; contains = true;
break; break;
} }
} }
if (!contains) continue; if (!contains) continue;
} }
if (!appendAtlas(builder, file)) continue; if (!appendAtlas(builder, file)) continue;
} }
std::unique_ptr<Atlas> srcAtlas (builder.build(2)); std::unique_ptr<Atlas> srcAtlas (builder.build(2));
Texture* srcTex = srcAtlas->getTexture(); Texture* srcTex = srcAtlas->getTexture();
Texture* dstTex = dstAtlas->getTexture(); Texture* dstTex = dstAtlas->getTexture();
TextureAnimation animation(srcTex, dstTex); TextureAnimation animation(srcTex, dstTex);
Frame frame; Frame frame;
UVRegion region = dstAtlas->get(name); UVRegion region = dstAtlas->get(name);
frame.dstPos = glm::ivec2(region.u1 * dstTex->width, region.v1 * dstTex->height); 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.size = glm::ivec2(region.u2 * dstTex->width, region.v2 * dstTex->height) - frame.dstPos;
if (frameList.empty()) { if (frameList.empty()) {
for (const auto& elem : builder.getNames()) { for (const auto& elem : builder.getNames()) {
region = srcAtlas->get(elem); region = srcAtlas->get(elem);
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height); frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
animation.addFrame(frame); animation.addFrame(frame);
} }
} }
else { else {
for (const auto& elem : frameList) { for (const auto& elem : frameList) {
if (!srcAtlas->has(elem.first)) { if (!srcAtlas->has(elem.first)) {
std::cerr << "Unknown frame name: " << elem.first << std::endl; std::cerr << "Unknown frame name: " << elem.first << std::endl;
continue; continue;
} }
region = srcAtlas->get(elem.first); region = srcAtlas->get(elem.first);
frame.duration = elem.second; frame.duration = elem.second;
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height); frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
animation.addFrame(frame); animation.addFrame(frame);
} }
} }
assets->store(srcAtlas.release(), name + "_animation"); assets->store(srcAtlas.release(), name + "_animation");
assets->store(animation); assets->store(animation);
return true; return true;
} }
return true; return true;
}
bool assetload::layout(
Assets* assets,
const ResPaths* paths,
const std::string file,
const std::string name
) {
try {
auto document = UiDocument::read(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;
}
} }

View File

@ -8,27 +8,43 @@ class Assets;
class Atlas; class Atlas;
namespace assetload { namespace assetload {
bool texture(Assets* assets, bool texture(
const ResPaths* paths, Assets* assets,
const std::string filename, const ResPaths* paths,
const std::string name); const std::string filename,
bool shader(Assets* assets, const std::string name
const ResPaths* paths, );
const std::string filename, bool shader(
const std::string name); Assets* assets,
bool atlas(Assets* assets, const ResPaths* paths,
const ResPaths* paths, const std::string filename,
const std::string directory, const std::string name
const std::string name); );
bool font(Assets* assets, bool atlas(
const ResPaths* paths, Assets* assets,
const std::string filename, const ResPaths* paths,
const std::string name); const std::string directory,
bool animation(Assets* assets, const std::string name
const ResPaths* paths, );
const std::string directory, bool font(
const std::string name, Assets* assets,
Atlas* dstAtlas); 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 layout(
Assets* assets,
const ResPaths* paths,
const std::string file,
const std::string name
);
} }
#endif // ASSETS_ASSET_LOADERS_H_ #endif // ASSETS_ASSET_LOADERS_H_

View File

@ -36,5 +36,6 @@ constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=CHUNK_D)
#define SHADERS_FOLDER "shaders" #define SHADERS_FOLDER "shaders"
#define TEXTURES_FOLDER "textures" #define TEXTURES_FOLDER "textures"
#define FONTS_FOLDER "fonts" #define FONTS_FOLDER "fonts"
#define LAYOUTS_FOLDER "layouts"
#endif // SRC_CONSTANTS_H_ #endif // SRC_CONSTANTS_H_

View File

@ -67,6 +67,10 @@ public:
inline const std::string& getId() { inline const std::string& getId() {
return info.id; return info.id;
} }
inline const ContentPack& getInfo() const {
return info;
}
}; };
#endif // CONTENT_CONTENT_PACK_H_ #endif // CONTENT_CONTENT_PACK_H_

View File

@ -196,7 +196,6 @@ void Engine::loadContent() {
void Engine::loadWorldContent(const fs::path& folder) { void Engine::loadWorldContent(const fs::path& folder) {
contentPacks.clear(); contentPacks.clear();
auto packNames = ContentPack::worldPacksList(folder); auto packNames = ContentPack::worldPacksList(folder);
std::cout << folder << " " << packNames.size() << std::endl;
ContentPack::readPacks(paths, contentPacks, packNames, folder); ContentPack::readPacks(paths, contentPacks, packNames, folder);
loadContent(); loadContent();
} }

View File

@ -4,6 +4,7 @@
#include "../assets/Assets.h" #include "../assets/Assets.h"
#include "../content/Content.h" #include "../content/Content.h"
#include "../content/ContentPack.h"
#include "../graphics/Atlas.h" #include "../graphics/Atlas.h"
#include "../voxels/Block.h" #include "../voxels/Block.h"
#include "../core_defs.h" #include "../core_defs.h"
@ -37,3 +38,15 @@ ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : conte
ContentGfxCache::~ContentGfxCache() { ContentGfxCache::~ContentGfxCache() {
} }
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;
}

View File

@ -28,6 +28,8 @@ public:
} }
std::shared_ptr<UiDocument> getLayout(const std::string& id); std::shared_ptr<UiDocument> getLayout(const std::string& id);
const Content* getContent() const;
}; };
#endif // FRONTEND_BLOCKS_GFX_CACHE_H_ #endif // FRONTEND_BLOCKS_GFX_CACHE_H_

View File

@ -39,11 +39,8 @@ SlotLayout::SlotLayout(
shareFunc(shareFunc), shareFunc(shareFunc),
rightClick(rightClick) {} rightClick(rightClick) {}
InventoryBuilder::InventoryBuilder( InventoryBuilder::InventoryBuilder() {
LevelFrontend* frontend, view = std::make_shared<InventoryView>();
InventoryInteraction& interaction
) {
view = std::make_shared<InventoryView>(frontend, interaction);
} }
void InventoryBuilder::addGrid( void InventoryBuilder::addGrid(
@ -101,13 +98,8 @@ std::shared_ptr<InventoryView> InventoryBuilder::build() {
} }
SlotView::SlotView( SlotView::SlotView(
LevelFrontend* frontend,
InventoryInteraction& interaction,
SlotLayout layout SlotLayout layout
) : UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)), ) : UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
frontend(frontend),
interaction(interaction),
content(frontend->getLevel()->content),
layout(layout) layout(layout)
{ {
setColor(glm::vec4(0, 0, 0, 0.2f)); setColor(glm::vec4(0, 0, 0, 0.2f));
@ -206,7 +198,7 @@ void SlotView::clicked(gui::GUI* gui, int button) {
if (bound == nullptr) if (bound == nullptr)
throw std::runtime_error("unbound slot"); throw std::runtime_error("unbound slot");
ItemStack& grabbed = interaction.getGrabbedItem(); ItemStack& grabbed = interaction->getGrabbedItem();
ItemStack& stack = *bound; ItemStack& stack = *bound;
if (button == mousecode::BUTTON_1) { if (button == mousecode::BUTTON_1) {
@ -260,23 +252,21 @@ void SlotView::focus(gui::GUI* gui) {
clicked(gui, 0); clicked(gui, 0);
} }
void SlotView::bind(ItemStack& stack) { void SlotView::bind(
ItemStack& stack,
LevelFrontend* frontend,
InventoryInteraction* interaction
) {
bound = &stack; bound = &stack;
content = frontend->getLevel()->content;
this->frontend = frontend;
} }
const SlotLayout& SlotView::getLayout() const { const SlotLayout& SlotView::getLayout() const {
return layout; return layout;
} }
InventoryView::InventoryView( InventoryView::InventoryView() : Container(glm::vec2(), glm::vec2()) {
LevelFrontend* frontend,
InventoryInteraction& interaction
) : Container(glm::vec2(), glm::vec2()),
frontend(frontend),
interaction(interaction)
{
content = frontend->getLevel()->content;
indices = content->getIndices();
setColor(glm::vec4(0, 0, 0, 0.0f)); setColor(glm::vec4(0, 0, 0, 0.0f));
} }
@ -297,9 +287,7 @@ std::shared_ptr<SlotView> InventoryView::addSlot(SlotLayout layout) {
} }
setSize(vsize); setSize(vsize);
auto slot = std::make_shared<SlotView>( auto slot = std::make_shared<SlotView>(layout);
frontend, interaction, layout
);
if (!layout.background) { if (!layout.background) {
slot->setColor(glm::vec4()); slot->setColor(glm::vec4());
} }
@ -307,10 +295,21 @@ std::shared_ptr<SlotView> InventoryView::addSlot(SlotLayout layout) {
return slot; return slot;
} }
void InventoryView::bind(std::shared_ptr<Inventory> inventory) { void InventoryView::bind(
std::shared_ptr<Inventory> inventory,
LevelFrontend* frontend,
InventoryInteraction* interaction
) {
this->frontend = frontend;
this->interaction = interaction;
this->inventory = inventory; this->inventory = inventory;
content = frontend->getLevel()->content;
indices = content->getIndices();
for (auto slot : slots) { for (auto slot : slots) {
slot->bind(inventory->getSlot(slot->getLayout().index)); slot->bind(
inventory->getSlot(slot->getLayout().index),
frontend, interaction
);
} }
} }
@ -340,90 +339,98 @@ void InventoryView::setInventory(std::shared_ptr<Inventory> inventory) {
#include "../coders/xml.h" #include "../coders/xml.h"
#include "gui/gui_xml.h" #include "gui/gui_xml.h"
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();
}
auto slot = view->addSlot(layout);
reader.readUINode(reader, element, *slot);
view->add(slot);
}
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("coord")) {
layout.position = element->attr("coord").asVec2();
}
layout.padding = padding;
glm::vec2 size (
cols * slotSize + (cols - 1) * interval + padding * 2,
rows * slotSize + (rows - 1) * interval + padding * 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 + row * (slotSize + interval)
);
auto slot = view->addSlot(slotLayout);
view->add(slot, slotLayout.position);
}
}
}
std::shared_ptr<InventoryView> InventoryView::readXML( std::shared_ptr<InventoryView> InventoryView::readXML(
LevelFrontend* frontend,
InventoryInteraction& interaction,
const std::string& src, const std::string& src,
const std::string& file, const std::string& file,
const scripting::Environment& env const scripting::Environment& env
) { ) {
auto view = std::make_shared<InventoryView>(frontend, interaction); auto view = std::make_shared<InventoryView>();
gui::UiXmlReader reader(env); gui::UiXmlReader reader(env);
reader.add("inventory", [=](gui::UiXmlReader& reader, xml::xmlelement element) { createReaders(reader);
reader.readUINode(reader, element, *view);
return view;
});
reader.add("slot", [=](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();
}
auto slot = view->addSlot(layout);
reader.readUINode(reader, element, *slot);
return slot;
});
reader.add("slots-grid", [=](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("coord")) {
layout.position = element->attr("coord").asVec2();
}
layout.padding = padding;
glm::vec2 size (
cols * slotSize + (cols - 1) * interval + padding * 2,
rows * slotSize + (rows - 1) * interval + padding * 2
);
auto container = std::make_shared<Container>(layout.position, size);
int idx = 0;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++, idx++) {
if (idx >= count) {
return container;
}
SlotLayout slotLayout = layout;
slotLayout.index = startIndex + idx;
slotLayout.position = glm::vec2(
padding + col * (slotSize + interval),
padding + row * (slotSize + interval)
);
auto slot = view->addSlot(slotLayout);
container->add(slot, slotLayout.position);
}
}
return container;
});
auto document = xml::parse(file, src); auto document = xml::parse(file, src);
auto root = document->getRoot(); auto root = document->getRoot();
if (root->getTag() != "inventory") { if (root->getTag() != "inventory") {
throw std::runtime_error("'inventory' element expected"); throw std::runtime_error("'inventory' element expected");
} }
reader.readXML(file, root); return std::dynamic_pointer_cast<InventoryView>(reader.readXML(file, root));
return view; }
void InventoryView::createReaders(gui::UiXmlReader& reader) {
reader.add("inventory", [=](gui::UiXmlReader& reader, xml::xmlelement element) {
auto view = std::make_shared<InventoryView>();
reader.readUINode(reader, element, *view, true);
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;
});
} }

View File

@ -18,6 +18,10 @@ class ContentIndices;
class LevelFrontend; class LevelFrontend;
class Inventory; class Inventory;
namespace gui {
class UiXmlReader;
}
namespace scripting { namespace scripting {
class Environment; class Environment;
} }
@ -53,17 +57,15 @@ struct SlotLayout {
}; };
class SlotView : public gui::UINode { class SlotView : public gui::UINode {
LevelFrontend* frontend; LevelFrontend* frontend = nullptr;
InventoryInteraction& interaction; InventoryInteraction* interaction = nullptr;
const Content* const content; const Content* content;
SlotLayout layout; SlotLayout layout;
bool highlighted = false; bool highlighted = false;
ItemStack* bound = nullptr; ItemStack* bound = nullptr;
public: public:
SlotView(LevelFrontend* frontend, SlotView(SlotLayout layout);
InventoryInteraction& interaction,
SlotLayout layout);
virtual void draw(const GfxContext* pctx, Assets* assets) override; virtual void draw(const GfxContext* pctx, Assets* assets) override;
@ -73,7 +75,11 @@ public:
virtual void clicked(gui::GUI*, int) override; virtual void clicked(gui::GUI*, int) override;
virtual void focus(gui::GUI*) override; virtual void focus(gui::GUI*) override;
void bind(ItemStack& stack); void bind(
ItemStack& stack,
LevelFrontend* frontend,
InventoryInteraction* interaction
);
const SlotLayout& getLayout() const; const SlotLayout& getLayout() const;
}; };
@ -83,13 +89,13 @@ class InventoryView : public gui::Container {
const ContentIndices* indices; const ContentIndices* indices;
std::shared_ptr<Inventory> inventory; std::shared_ptr<Inventory> inventory;
LevelFrontend* frontend; LevelFrontend* frontend = nullptr;
InventoryInteraction& interaction; InventoryInteraction* interaction = nullptr;
std::vector<SlotView*> slots; std::vector<SlotView*> slots;
glm::vec2 origin {}; glm::vec2 origin {};
public: public:
InventoryView(LevelFrontend* frontend, InventoryInteraction& interaction); InventoryView();
virtual ~InventoryView(); virtual ~InventoryView();
void setInventory(std::shared_ptr<Inventory> inventory); void setInventory(std::shared_ptr<Inventory> inventory);
@ -101,18 +107,22 @@ public:
void setSelected(int index); void setSelected(int index);
void bind(std::shared_ptr<Inventory> inventory); void bind(
std::shared_ptr<Inventory> inventory,
LevelFrontend* frontend,
InventoryInteraction* interaction
);
std::shared_ptr<SlotView> addSlot(SlotLayout layout); std::shared_ptr<SlotView> addSlot(SlotLayout layout);
static std::shared_ptr<InventoryView> readXML( static std::shared_ptr<InventoryView> readXML(
LevelFrontend* frontend,
InventoryInteraction& interaction,
const std::string& src, const std::string& src,
const std::string& file, const std::string& file,
const scripting::Environment& env const scripting::Environment& env
); );
static void createReaders(gui::UiXmlReader& reader);
static const int SLOT_INTERVAL = 4; static const int SLOT_INTERVAL = 4;
static const int SLOT_SIZE = ITEM_ICON_SIZE; static const int SLOT_SIZE = ITEM_ICON_SIZE;
}; };
@ -120,7 +130,7 @@ public:
class InventoryBuilder { class InventoryBuilder {
std::shared_ptr<InventoryView> view; std::shared_ptr<InventoryView> view;
public: public:
InventoryBuilder(LevelFrontend* frontend, InventoryInteraction& interaction); InventoryBuilder();
void addGrid( void addGrid(
int cols, int count, int cols, int count,

View File

@ -24,5 +24,4 @@ public:
Atlas* getBlocksAtlas() const; Atlas* getBlocksAtlas() const;
}; };
#endif // FRONTEND_LEVEL_FRONTEND_H_ #endif // FRONTEND_LEVEL_FRONTEND_H_

View File

@ -5,6 +5,7 @@
#include "InventoryView.h" #include "InventoryView.h"
#include "../logic/scripting/scripting.h" #include "../logic/scripting/scripting.h"
#include "../files/files.h" #include "../files/files.h"
#include "../frontend/gui/gui_xml.h"
UiDocument::UiDocument( UiDocument::UiDocument(
std::string namesp, std::string namesp,
@ -27,16 +28,14 @@ void UiDocument::collect(uinodes_map& map, std::shared_ptr<gui::UINode> node) {
} }
} }
std::unique_ptr<UiDocument> UiDocument::readInventory( std::unique_ptr<UiDocument> UiDocument::read(std::string namesp, fs::path file) {
std::string namesp,
fs::path file,
LevelFrontend* frontend,
InventoryInteraction& interaction
) {
const std::string text = files::read_string(file); const std::string text = files::read_string(file);
auto xmldoc = xml::parse(file.u8string(), text);
auto env = scripting::create_environment(); auto env = scripting::create_environment();
auto view = InventoryView::readXML( gui::UiXmlReader reader(*env);
frontend, interaction, text, file.u8string(), *env InventoryView::createReaders(reader);
auto view = reader.readXML(
file.u8string(), xmldoc->getRoot()
); );
uidocscript script {}; uidocscript script {};
auto scriptFile = fs::path(file.u8string()+".lua"); auto scriptFile = fs::path(file.u8string()+".lua");

View File

@ -12,9 +12,6 @@ namespace gui {
class UINode; class UINode;
} }
class InventoryInteraction;
class LevelFrontend;
struct uidocscript { struct uidocscript {
int environment; int environment;
bool onopen : 1; bool onopen : 1;
@ -37,13 +34,7 @@ public:
/* Collect map of all uinodes having identifiers */ /* Collect map of all uinodes having identifiers */
static void collect(uinodes_map& map, std::shared_ptr<gui::UINode> node); static void collect(uinodes_map& map, std::shared_ptr<gui::UINode> node);
/* @return root node is always an InventoryView */ static std::unique_ptr<UiDocument> read (std::string namesp, fs::path file);
static std::unique_ptr<UiDocument> readInventory (
std::string namesp,
fs::path file,
LevelFrontend* frontend,
InventoryInteraction& interaction
);
}; };
#endif // FRONTEND_UI_DOCUMENT_H_ #endif // FRONTEND_UI_DOCUMENT_H_

View File

@ -41,7 +41,7 @@ static void _readUINode(xml::xmlelement element, UINode& node) {
} }
static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Container& container) { static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Container& container, bool ignoreUnknown) {
_readUINode(element, container); _readUINode(element, container);
if (element->has("scrollable")) { if (element->has("scrollable")) {
@ -50,12 +50,15 @@ static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Contain
for (auto& sub : element->getElements()) { for (auto& sub : element->getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
if (ignoreUnknown && !reader.hasReader(sub->getTag())) {
continue;
}
container.add(reader.readUINode(sub)); container.add(reader.readUINode(sub));
} }
} }
void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, Container& container) { void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, Container& container, bool ignoreUnknown) {
_readContainer(reader, element, container); _readContainer(reader, element, container, ignoreUnknown);
} }
void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& node) { void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& node) {
@ -104,7 +107,7 @@ static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, xml::xmlelement el
static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, xml::xmlelement element) { static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, xml::xmlelement element) {
auto container = std::make_shared<Container>(glm::vec2(), glm::vec2()); auto container = std::make_shared<Container>(glm::vec2(), glm::vec2());
_readContainer(reader, element, *container); _readContainer(reader, element, *container, false);
return container; return container;
} }
@ -153,6 +156,10 @@ void UiXmlReader::add(const std::string& tag, uinode_reader reader) {
readers[tag] = reader; readers[tag] = reader;
} }
bool UiXmlReader::hasReader(const std::string& tag) const {
return readers.find(tag) != readers.end();
}
std::shared_ptr<UINode> UiXmlReader::readUINode(xml::xmlelement element) { std::shared_ptr<UINode> UiXmlReader::readUINode(xml::xmlelement element) {
const std::string& tag = element->getTag(); const std::string& tag = element->getTag();

View File

@ -24,6 +24,7 @@ namespace gui {
UiXmlReader(const scripting::Environment& env); UiXmlReader(const scripting::Environment& env);
void add(const std::string& tag, uinode_reader reader); void add(const std::string& tag, uinode_reader reader);
bool hasReader(const std::string& tag) const;
std::shared_ptr<UINode> readUINode(xml::xmlelement element); std::shared_ptr<UINode> readUINode(xml::xmlelement element);
@ -36,7 +37,8 @@ namespace gui {
void readUINode( void readUINode(
UiXmlReader& reader, UiXmlReader& reader,
xml::xmlelement element, xml::xmlelement element,
Container& container Container& container,
bool ignoreUnknown=false
); );
std::shared_ptr<UINode> readXML( std::shared_ptr<UINode> readXML(

View File

@ -189,10 +189,10 @@ std::shared_ptr<InventoryView> HudRenderer::createContentAccess() {
inventory->getSlot(player->getChosenSlot()).set(item); inventory->getSlot(player->getChosenSlot()).set(item);
}); });
InventoryBuilder builder(frontend, *interaction); InventoryBuilder builder;
builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout); builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout);
auto view = builder.build(); auto view = builder.build();
view->bind(accessInventory); view->bind(accessInventory, frontend, interaction.get());
return view; return view;
} }
@ -202,12 +202,12 @@ std::shared_ptr<InventoryView> HudRenderer::createHotbar() {
auto inventory = player->getInventory(); auto inventory = player->getInventory();
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr); SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr);
InventoryBuilder builder(frontend, *interaction); InventoryBuilder builder;
builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout); builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout);
auto view = builder.build(); auto view = builder.build();
view->setOrigin(glm::vec2(view->getSize().x/2, 0)); view->setOrigin(glm::vec2(view->getSize().x/2, 0));
view->bind(inventory); view->bind(inventory, frontend, interaction.get());
view->setInteractive(false); view->setInteractive(false);
return view; return view;
} }
@ -221,10 +221,10 @@ std::shared_ptr<InventoryView> HudRenderer::createInventory() {
stack.clear(); stack.clear();
}, nullptr); }, nullptr);
InventoryBuilder builder(frontend, *interaction); InventoryBuilder builder;
builder.addGrid(10, inventory->size(), glm::vec2(), 4, true, slotLayout); builder.addGrid(10, inventory->size(), glm::vec2(), 4, true, slotLayout);
auto view = builder.build(); auto view = builder.build();
view->bind(inventory); view->bind(inventory, frontend, interaction.get());
return view; return view;
} }
@ -237,11 +237,13 @@ HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
interaction = std::make_unique<InventoryInteraction>(); interaction = std::make_unique<InventoryInteraction>();
grabbedItemView = std::make_shared<SlotView>( grabbedItemView = std::make_shared<SlotView>(
frontend,
*interaction,
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr) SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr)
); );
grabbedItemView->bind(interaction->getGrabbedItem()); grabbedItemView->bind(
interaction->getGrabbedItem(),
frontend,
interaction.get()
);
grabbedItemView->setColor(glm::vec4()); grabbedItemView->setColor(glm::vec4());
grabbedItemView->setInteractive(false); grabbedItemView->setInteractive(false);

View File

@ -82,7 +82,7 @@ public:
std::vector<glm::vec3> modelExtraPoints = {}; //initially made for tetragons std::vector<glm::vec3> modelExtraPoints = {}; //initially made for tetragons
std::vector<UVRegion> modelUVs = {}; // boxes' tex-UVs also there std::vector<UVRegion> modelUVs = {}; // boxes' tex-UVs also there
uint8_t emission[4] {0, 0, 0, 0}; uint8_t emission[4] {0, 0, 0, 0};
ubyte drawGroup = 0; uint8_t drawGroup = 0;
BlockModel model = BlockModel::block; BlockModel model = BlockModel::block;
bool lightPassing = false; bool lightPassing = false;
bool skyLightPassing = false; bool skyLightPassing = false;