merge main
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
#include "BlocksPreview.h"
|
||||
|
||||
#include <glm/ext.hpp>
|
||||
|
||||
#include "../assets/Assets.h"
|
||||
#include "../graphics/Viewport.h"
|
||||
#include "../graphics/Texture.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Batch3D.h"
|
||||
#include "../graphics/Framebuffer.h"
|
||||
#include "../graphics/GfxContext.h"
|
||||
#include "../window/Window.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../constants.h"
|
||||
#include "ContentGfxCache.h"
|
||||
|
||||
ImageData* BlocksPreview::draw(
|
||||
const ContentGfxCache* cache,
|
||||
Shader* shader,
|
||||
Framebuffer* fbo,
|
||||
Batch3D* batch,
|
||||
const Block* def,
|
||||
int size
|
||||
){
|
||||
Window::clear();
|
||||
blockid_t id = def->rt.id;
|
||||
const UVRegion texfaces[6]{cache->getRegion(id, 0), cache->getRegion(id, 1),
|
||||
cache->getRegion(id, 2), cache->getRegion(id, 3),
|
||||
cache->getRegion(id, 4), cache->getRegion(id, 5)};
|
||||
|
||||
glm::vec3 offset(0.1f, 0.5f, 0.1f);
|
||||
switch (def->model) {
|
||||
case BlockModel::none:
|
||||
// something went wrong...
|
||||
break;
|
||||
case BlockModel::block:
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
batch->blockCube(glm::vec3(size * 0.63f), texfaces,
|
||||
glm::vec4(1.0f), !def->rt.emissive);
|
||||
batch->flush();
|
||||
break;
|
||||
case BlockModel::aabb:
|
||||
{
|
||||
glm::vec3 hitbox = glm::vec3();
|
||||
for (const auto& box : def->hitboxes)
|
||||
hitbox = glm::max(hitbox, box.size());
|
||||
offset.y += (1.0f - hitbox).y * 0.5f;
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
batch->blockCube(hitbox * glm::vec3(size * 0.63f),
|
||||
texfaces, glm::vec4(1.0f), !def->rt.emissive);
|
||||
}
|
||||
batch->flush();
|
||||
break;
|
||||
case BlockModel::custom:
|
||||
{
|
||||
glm::vec3 hitbox = glm::vec3();
|
||||
for (const auto& box : def->modelBoxes)
|
||||
hitbox = glm::max(hitbox, box.size());
|
||||
offset.y += (1.0f - hitbox).y * 0.5f;
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
for (size_t i = 0; i < def->modelBoxes.size(); i++) {
|
||||
const UVRegion (&texfaces)[6] = {
|
||||
def->modelUVs[i * 6],
|
||||
def->modelUVs[i * 6 + 1],
|
||||
def->modelUVs[i * 6 + 2],
|
||||
def->modelUVs[i * 6 + 3],
|
||||
def->modelUVs[i * 6 + 4],
|
||||
def->modelUVs[i * 6 + 5]
|
||||
};
|
||||
batch->cube(def->modelBoxes[i].a * glm::vec3(1.0f, 1.0f, -1.0f) * glm::vec3(size * 0.63f), def->modelBoxes[i].size() * glm::vec3(size * 0.63f), texfaces, glm::vec4(1.0f), !def->rt.emissive);
|
||||
}
|
||||
for (size_t i = 0; i < def->modelExtraPoints.size() / 4; i++) {
|
||||
const UVRegion& reg = def->modelUVs[def->modelBoxes.size() * 6 + i];
|
||||
batch->point((def->modelExtraPoints[i * 4 + 0] - glm::vec3(0.0f, 0.0f, 1.0f)) * glm::vec3(size * 0.63f), glm::vec2(reg.u1, reg.v1), glm::vec4(1.0));
|
||||
batch->point((def->modelExtraPoints[i * 4 + 1] - glm::vec3(0.0f, 0.0f, 1.0f)) * glm::vec3(size * 0.63f), glm::vec2(reg.u2, reg.v1), glm::vec4(1.0));
|
||||
batch->point((def->modelExtraPoints[i * 4 + 2] - glm::vec3(0.0f, 0.0f, 1.0f)) * glm::vec3(size * 0.63f), glm::vec2(reg.u2, reg.v2), glm::vec4(1.0));
|
||||
batch->point((def->modelExtraPoints[i * 4 + 0] - glm::vec3(0.0f, 0.0f, 1.0f)) * glm::vec3(size * 0.63f), glm::vec2(reg.u1, reg.v1), glm::vec4(1.0));
|
||||
batch->point((def->modelExtraPoints[i * 4 + 2] - glm::vec3(0.0f, 0.0f, 1.0f)) * glm::vec3(size * 0.63f), glm::vec2(reg.u2, reg.v2), glm::vec4(1.0));
|
||||
batch->point((def->modelExtraPoints[i * 4 + 3] - glm::vec3(0.0f, 0.0f, 1.0f)) * glm::vec3(size * 0.63f), glm::vec2(reg.u1, reg.v2), glm::vec4(1.0));
|
||||
}
|
||||
batch->flush();
|
||||
}
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
glm::vec3 right = glm::normalize(glm::vec3(1.f, 0.f, -1.f));
|
||||
batch->sprite(right*float(size)*0.43f+glm::vec3(0, size*0.4f, 0),
|
||||
glm::vec3(0.f, 1.f, 0.f),
|
||||
right,
|
||||
size*0.5f, size*0.6f,
|
||||
texfaces[0],
|
||||
glm::vec4(1.0f));
|
||||
batch->flush();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fbo->getTexture()->readData();
|
||||
}
|
||||
|
||||
std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
const ContentGfxCache* cache,
|
||||
Assets* assets,
|
||||
const Content* content
|
||||
) {
|
||||
auto indices = content->getIndices();
|
||||
size_t count = indices->countBlockDefs();
|
||||
size_t iconSize = ITEM_ICON_SIZE;
|
||||
|
||||
Shader* shader = assets->getShader("ui3d");
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
Viewport viewport(iconSize, iconSize);
|
||||
GfxContext pctx(nullptr, viewport, nullptr);
|
||||
GfxContext ctx = pctx.sub();
|
||||
ctx.setCullFace(true);
|
||||
ctx.setDepthTest(true);
|
||||
|
||||
Framebuffer fbo(iconSize, iconSize, true);
|
||||
Batch3D batch(1024);
|
||||
batch.begin();
|
||||
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_projview",
|
||||
glm::ortho(0.0f, float(iconSize), 0.0f, float(iconSize),
|
||||
-100.0f, 100.0f) *
|
||||
glm::lookAt(glm::vec3(2, 2, 2),
|
||||
glm::vec3(0.0f),
|
||||
glm::vec3(0, 1, 0)));
|
||||
|
||||
AtlasBuilder builder;
|
||||
Window::viewport(0, 0, iconSize, iconSize);
|
||||
Window::setBgColor(glm::vec4(0.0f));
|
||||
|
||||
fbo.bind();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto def = indices->getBlockDef(i);
|
||||
atlas->getTexture()->bind();
|
||||
builder.add(def->name, draw(cache, shader, &fbo, &batch, def, iconSize));
|
||||
}
|
||||
fbo.unbind();
|
||||
|
||||
Window::viewport(0, 0, Window::width, Window::height);
|
||||
return std::unique_ptr<Atlas>(builder.build(2));
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef FRONTEND_BLOCKS_PREVIEW_H_
|
||||
#define FRONTEND_BLOCKS_PREVIEW_H_
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
|
||||
class Assets;
|
||||
class ImageData;
|
||||
class Atlas;
|
||||
class Framebuffer;
|
||||
class Batch3D;
|
||||
class Block;
|
||||
class Content;
|
||||
class ContentGfxCache;
|
||||
|
||||
class BlocksPreview {
|
||||
static ImageData* draw(
|
||||
const ContentGfxCache* cache,
|
||||
Shader* shader,
|
||||
Framebuffer* framebuffer,
|
||||
Batch3D* batch,
|
||||
const Block* block,
|
||||
int size
|
||||
);
|
||||
public:
|
||||
static std::unique_ptr<Atlas> build(
|
||||
const ContentGfxCache* cache,
|
||||
Assets* assets,
|
||||
const Content* content
|
||||
);
|
||||
};
|
||||
|
||||
#endif // FRONTEND_BLOCKS_PREVIEW_H_
|
||||
@@ -1,15 +1,17 @@
|
||||
#include "ContentGfxCache.h"
|
||||
#include "ContentGfxCache.hpp"
|
||||
|
||||
#include "UiDocument.hpp"
|
||||
|
||||
#include "../assets/Assets.hpp"
|
||||
#include "../content/Content.hpp"
|
||||
#include "../content/ContentPack.hpp"
|
||||
#include "../core_defs.hpp"
|
||||
#include "../graphics/core/Atlas.hpp"
|
||||
#include "../maths/UVRegion.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#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) : content(content) {
|
||||
auto indices = content->getIndices();
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->countBlockDefs() * 6);
|
||||
@@ -39,14 +41,6 @@ ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : conte
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#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)
|
||||
std::unique_ptr<UVRegion[]> sideregions;
|
||||
// all loaded layouts
|
||||
uidocuments_map layouts;
|
||||
public:
|
||||
ContentGfxCache(const Content* content, Assets* assets);
|
||||
~ContentGfxCache();
|
||||
|
||||
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_
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef FRONTEND_BLOCKS_GFX_CACHE_HPP_
|
||||
#define FRONTEND_BLOCKS_GFX_CACHE_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Content;
|
||||
class Assets;
|
||||
struct UVRegion;
|
||||
|
||||
class ContentGfxCache {
|
||||
const Content* content;
|
||||
// array of block sides uv regions (6 per block)
|
||||
std::unique_ptr<UVRegion[]> sideregions;
|
||||
public:
|
||||
ContentGfxCache(const Content* content, Assets* assets);
|
||||
~ContentGfxCache();
|
||||
|
||||
inline const UVRegion& getRegion(blockid_t id, int side) const {
|
||||
return sideregions[id * 6 + side];
|
||||
}
|
||||
|
||||
const Content* getContent() const;
|
||||
};
|
||||
|
||||
#endif // FRONTEND_BLOCKS_GFX_CACHE_HPP_
|
||||
@@ -1,478 +0,0 @@
|
||||
#include "InventoryView.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "BlocksPreview.h"
|
||||
#include "LevelFrontend.h"
|
||||
#include "../window/Events.h"
|
||||
#include "../window/input.h"
|
||||
#include "../assets/Assets.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Batch2D.h"
|
||||
#include "../graphics/GfxContext.h"
|
||||
#include "../graphics/Font.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../items/ItemDef.h"
|
||||
#include "../items/Inventory.h"
|
||||
#include "../items/Inventories.h"
|
||||
#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"
|
||||
#include "../world/Level.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
SlotLayout::SlotLayout(
|
||||
int index,
|
||||
glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
slotcallback updateFunc,
|
||||
slotcallback shareFunc,
|
||||
slotcallback rightClick
|
||||
) : index(index),
|
||||
position(position),
|
||||
background(background),
|
||||
itemSource(itemSource),
|
||||
updateFunc(updateFunc),
|
||||
shareFunc(shareFunc),
|
||||
rightClick(rightClick) {}
|
||||
|
||||
InventoryBuilder::InventoryBuilder() {
|
||||
view = std::make_shared<InventoryView>();
|
||||
}
|
||||
|
||||
void InventoryBuilder::addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 pos,
|
||||
int padding,
|
||||
bool addpanel,
|
||||
SlotLayout slotLayout
|
||||
) {
|
||||
const int slotSize = InventoryView::SLOT_SIZE;
|
||||
const int interval = InventoryView::SLOT_INTERVAL;
|
||||
|
||||
int rows = ceildiv(count, cols);
|
||||
|
||||
uint width = cols * (slotSize + interval) - interval + padding*2;
|
||||
uint height = rows * (slotSize + interval) - interval + padding*2;
|
||||
|
||||
glm::vec2 vsize = view->getSize();
|
||||
if (pos.x + width > vsize.x) {
|
||||
vsize.x = pos.x + width;
|
||||
}
|
||||
if (pos.y + height > vsize.y) {
|
||||
vsize.y = pos.y + height;
|
||||
}
|
||||
view->setSize(vsize);
|
||||
|
||||
if (addpanel) {
|
||||
auto panel = std::make_shared<gui::Container>(glm::vec2(width, height));
|
||||
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f));
|
||||
view->add(panel, pos);
|
||||
}
|
||||
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
if (row * cols + col >= count) {
|
||||
break;
|
||||
}
|
||||
glm::vec2 position (
|
||||
col * (slotSize + interval) + padding,
|
||||
row * (slotSize + interval) + padding
|
||||
);
|
||||
auto builtSlot = slotLayout;
|
||||
builtSlot.index = row * cols + col;
|
||||
builtSlot.position = position;
|
||||
add(builtSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryBuilder::add(SlotLayout layout) {
|
||||
view->add(view->addSlot(layout), layout.position);
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> InventoryBuilder::build() {
|
||||
return view;
|
||||
}
|
||||
|
||||
SlotView::SlotView(
|
||||
SlotLayout layout
|
||||
) : UINode(glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
layout(layout)
|
||||
{
|
||||
setColor(glm::vec4(0, 0, 0, 0.2f));
|
||||
}
|
||||
|
||||
void SlotView::draw(const GfxContext* pctx, Assets* assets) {
|
||||
if (bound == nullptr)
|
||||
return;
|
||||
|
||||
const int slotSize = InventoryView::SLOT_SIZE;
|
||||
|
||||
ItemStack& stack = *bound;
|
||||
glm::vec4 tint(1.0f);
|
||||
glm::vec2 pos = calcPos();
|
||||
glm::vec4 color = getColor();
|
||||
|
||||
if (hover || highlighted) {
|
||||
tint *= 1.333f;
|
||||
color = glm::vec4(1, 1, 1, 0.2f);
|
||||
}
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->setColor(color);
|
||||
if (color.a > 0.0) {
|
||||
batch->texture(nullptr);
|
||||
if (highlighted) {
|
||||
batch->rect(pos.x-4, pos.y-4, slotSize+8, slotSize+8);
|
||||
} else {
|
||||
batch->rect(pos.x, pos.y, slotSize, slotSize);
|
||||
}
|
||||
}
|
||||
|
||||
batch->setColor(glm::vec4(1.0f));
|
||||
|
||||
auto previews = frontend->getBlocksAtlas();
|
||||
auto indices = content->getIndices();
|
||||
|
||||
ItemDef* item = indices->getItemDef(stack.getItemId());
|
||||
switch (item->iconType) {
|
||||
case item_icon_type::none:
|
||||
break;
|
||||
case item_icon_type::block: {
|
||||
const Block& cblock = content->requireBlock(item->icon);
|
||||
batch->texture(previews->getTexture());
|
||||
|
||||
UVRegion region = previews->get(cblock.name);
|
||||
batch->rect(
|
||||
pos.x, pos.y, slotSize, slotSize,
|
||||
0, 0, 0, region, false, true, tint);
|
||||
break;
|
||||
}
|
||||
case item_icon_type::sprite: {
|
||||
size_t index = item->icon.find(':');
|
||||
std::string name = item->icon.substr(index+1);
|
||||
UVRegion region(0.0f, 0.0, 1.0f, 1.0f);
|
||||
if (index == std::string::npos) {
|
||||
batch->texture(assets->getTexture(name));
|
||||
} else {
|
||||
std::string atlasname = item->icon.substr(0, index);
|
||||
Atlas* atlas = assets->getAtlas(atlasname);
|
||||
if (atlas && atlas->has(name)) {
|
||||
region = atlas->get(name);
|
||||
batch->texture(atlas->getTexture());
|
||||
}
|
||||
}
|
||||
batch->rect(
|
||||
pos.x, pos.y, slotSize, slotSize,
|
||||
0, 0, 0, region, false, true, tint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.getCount() > 1) {
|
||||
auto font = assets->getFont("normal");
|
||||
std::wstring text = std::to_wstring(stack.getCount());
|
||||
|
||||
int x = pos.x+slotSize-text.length()*8;
|
||||
int y = pos.y+slotSize-16;
|
||||
|
||||
batch->setColor(glm::vec4(0, 0, 0, 1.0f));
|
||||
font->draw(batch, text, x+1, y+1);
|
||||
batch->setColor(glm::vec4(1.0f));
|
||||
font->draw(batch, text, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SlotView::setHighlighted(bool flag) {
|
||||
highlighted = flag;
|
||||
}
|
||||
|
||||
bool SlotView::isHighlighted() const {
|
||||
return highlighted;
|
||||
}
|
||||
|
||||
void SlotView::clicked(gui::GUI* gui, mousecode 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(layout.index, stack);
|
||||
}
|
||||
if (layout.updateFunc) {
|
||||
layout.updateFunc(layout.index, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!layout.itemSource && stack.accepts(grabbed)) {
|
||||
stack.move(grabbed, content->getIndices());
|
||||
} else {
|
||||
if (layout.itemSource) {
|
||||
if (grabbed.isEmpty()) {
|
||||
grabbed.set(stack);
|
||||
} else {
|
||||
grabbed.clear();
|
||||
}
|
||||
} else {
|
||||
std::swap(grabbed, stack);
|
||||
}
|
||||
}
|
||||
} else if (button == mousecode::BUTTON_2) {
|
||||
if (layout.rightClick) {
|
||||
layout.rightClick(inventoryid, stack);
|
||||
if (layout.updateFunc) {
|
||||
layout.updateFunc(layout.index, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (layout.itemSource)
|
||||
return;
|
||||
if (grabbed.isEmpty()) {
|
||||
if (!stack.isEmpty()) {
|
||||
grabbed.set(stack);
|
||||
int halfremain = stack.getCount() / 2;
|
||||
grabbed.setCount(stack.getCount() - halfremain);
|
||||
stack.setCount(halfremain);
|
||||
}
|
||||
} else {
|
||||
if (stack.isEmpty()) {
|
||||
stack.set(grabbed);
|
||||
stack.setCount(1);
|
||||
grabbed.setCount(grabbed.getCount()-1);
|
||||
} else if (stack.accepts(grabbed)){
|
||||
stack.setCount(stack.getCount()+1);
|
||||
grabbed.setCount(grabbed.getCount()-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout.updateFunc) {
|
||||
layout.updateFunc(layout.index, stack);
|
||||
}
|
||||
}
|
||||
|
||||
void SlotView::onFocus(gui::GUI* gui) {
|
||||
clicked(gui, mousecode::BUTTON_1);
|
||||
}
|
||||
|
||||
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()) {
|
||||
setColor(glm::vec4(0, 0, 0, 0.0f));
|
||||
}
|
||||
|
||||
InventoryView::~InventoryView() {}
|
||||
|
||||
|
||||
std::shared_ptr<SlotView> InventoryView::addSlot(SlotLayout layout) {
|
||||
uint width = InventoryView::SLOT_SIZE + layout.padding;
|
||||
uint height = InventoryView::SLOT_SIZE + layout.padding;
|
||||
|
||||
auto pos = layout.position;
|
||||
auto vsize = getSize();
|
||||
if (pos.x + width > vsize.x) {
|
||||
vsize.x = pos.x + width;
|
||||
}
|
||||
if (pos.y + height > vsize.y) {
|
||||
vsize.y = pos.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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryView::unbind() {
|
||||
if (inventory && inventory->isVirtual()) {
|
||||
frontend->getLevel()->inventories->remove(inventory->getId());
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryView::setSelected(int index) {
|
||||
for (int i = 0; i < int(slots.size()); i++) {
|
||||
auto slot = slots[i];
|
||||
slot->setHighlighted(i == index);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryView::setPos(glm::vec2 pos) {
|
||||
Container::setPos(pos - 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;
|
||||
}
|
||||
|
||||
#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);
|
||||
};
|
||||
}
|
||||
|
||||
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, nullptr);
|
||||
if (element->has("pos")) {
|
||||
layout.position = element->attr("pos").asVec2();
|
||||
}
|
||||
if (element->has("updatefunc")) {
|
||||
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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, nullptr);
|
||||
if (element->has("pos")) {
|
||||
layout.position = element->attr("pos").asVec2();
|
||||
}
|
||||
if (element->has("updatefunc")) {
|
||||
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
|
||||
}
|
||||
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;
|
||||
|
||||
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("slot");
|
||||
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;
|
||||
});
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
#ifndef FRONTEND_INVENTORY_VIEW_H_
|
||||
#define FRONTEND_INVENTORY_VIEW_H_
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../frontend/gui/UINode.h"
|
||||
#include "../frontend/gui/containers.h"
|
||||
#include "../frontend/gui/controls.h"
|
||||
#include "../items/ItemStack.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
class Assets;
|
||||
class GfxContext;
|
||||
class Content;
|
||||
class ContentIndices;
|
||||
class LevelFrontend;
|
||||
class Inventory;
|
||||
|
||||
namespace gui {
|
||||
class UiXmlReader;
|
||||
}
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
using slotcallback = std::function<void(uint, ItemStack&)>;
|
||||
|
||||
class InventoryInteraction {
|
||||
ItemStack grabbedItem;
|
||||
public:
|
||||
InventoryInteraction() = default;
|
||||
|
||||
ItemStack& getGrabbedItem() {
|
||||
return grabbedItem;
|
||||
}
|
||||
};
|
||||
|
||||
struct SlotLayout {
|
||||
int index;
|
||||
glm::vec2 position;
|
||||
bool background;
|
||||
bool itemSource;
|
||||
slotcallback updateFunc;
|
||||
slotcallback shareFunc;
|
||||
slotcallback rightClick;
|
||||
int padding = 0;
|
||||
|
||||
SlotLayout(
|
||||
int index,
|
||||
glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
slotcallback updateFunc,
|
||||
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*, mousecode) override;
|
||||
virtual void onFocus(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 setPos(glm::vec2 pos) 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
|
||||
);
|
||||
|
||||
void unbind();
|
||||
|
||||
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();
|
||||
|
||||
/// @brief Add slots grid to inventory view
|
||||
/// @param cols grid columns
|
||||
/// @param count total number of grid slots
|
||||
/// @param pos 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 addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 pos,
|
||||
int padding,
|
||||
bool addpanel,
|
||||
SlotLayout slotLayout
|
||||
);
|
||||
|
||||
void add(SlotLayout slotLayout);
|
||||
std::shared_ptr<InventoryView> build();
|
||||
};
|
||||
|
||||
#endif // FRONTEND_INVENTORY_VIEW_H_
|
||||
@@ -1,25 +1,27 @@
|
||||
#include "LevelFrontend.h"
|
||||
#include "LevelFrontend.hpp"
|
||||
|
||||
#include "BlocksPreview.h"
|
||||
#include "ContentGfxCache.h"
|
||||
#include "ContentGfxCache.hpp"
|
||||
|
||||
#include "../audio/audio.h"
|
||||
#include "../world/Level.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../assets/Assets.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../content/Content.h"
|
||||
|
||||
#include "../logic/LevelController.h"
|
||||
#include "../logic/PlayerController.h"
|
||||
#include "../assets/Assets.hpp"
|
||||
#include "../audio/audio.hpp"
|
||||
#include "../content/Content.hpp"
|
||||
#include "../graphics/core/Atlas.hpp"
|
||||
#include "../graphics/render/BlocksPreview.hpp"
|
||||
#include "../logic/LevelController.hpp"
|
||||
#include "../logic/PlayerController.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../world/Level.hpp"
|
||||
|
||||
LevelFrontend::LevelFrontend(LevelController* controller, Assets* assets)
|
||||
: level(controller->getLevel()),
|
||||
controller(controller),
|
||||
assets(assets),
|
||||
contentCache(std::make_unique<ContentGfxCache>(level->content, assets)),
|
||||
blocksAtlas(BlocksPreview::build(contentCache.get(), assets, level->content))
|
||||
contentCache(std::make_unique<ContentGfxCache>(level->content, assets))
|
||||
{
|
||||
assets->store(
|
||||
BlocksPreview::build(contentCache.get(), assets, level->content).release(),
|
||||
"block-previews"
|
||||
);
|
||||
controller->getPlayerController()->listenBlockInteraction(
|
||||
[=](Player*, glm::ivec3 pos, const Block* def, BlockInteraction type) {
|
||||
auto material = level->content->findBlockMaterial(def->material);
|
||||
@@ -81,10 +83,6 @@ ContentGfxCache* LevelFrontend::getContentGfxCache() const {
|
||||
return contentCache.get();
|
||||
}
|
||||
|
||||
Atlas* LevelFrontend::getBlocksAtlas() const {
|
||||
return blocksAtlas.get();
|
||||
}
|
||||
|
||||
LevelController* LevelFrontend::getController() const {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#ifndef FRONTEND_LEVEL_FRONTEND_H_
|
||||
#define FRONTEND_LEVEL_FRONTEND_H_
|
||||
#ifndef FRONTEND_LEVEL_FRONTEND_HPP_
|
||||
#define FRONTEND_LEVEL_FRONTEND_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Atlas;
|
||||
class Level;
|
||||
class Assets;
|
||||
class BlocksPreview;
|
||||
class ContentGfxCache;
|
||||
class LevelController;
|
||||
|
||||
@@ -15,7 +13,6 @@ class LevelFrontend {
|
||||
LevelController* controller;
|
||||
Assets* assets;
|
||||
std::unique_ptr<ContentGfxCache> contentCache;
|
||||
std::unique_ptr<Atlas> blocksAtlas;
|
||||
public:
|
||||
LevelFrontend(LevelController* controller, Assets* assets);
|
||||
~LevelFrontend();
|
||||
@@ -23,9 +20,7 @@ public:
|
||||
Level* getLevel() const;
|
||||
Assets* getAssets() const;
|
||||
ContentGfxCache* getContentGfxCache() const;
|
||||
Atlas* getBlocksAtlas() const;
|
||||
|
||||
LevelController* getController() const;
|
||||
};
|
||||
|
||||
#endif // FRONTEND_LEVEL_FRONTEND_H_
|
||||
#endif // FRONTEND_LEVEL_FRONTEND_HPP_
|
||||
+31
-32
@@ -1,27 +1,32 @@
|
||||
#include "UiDocument.h"
|
||||
#include "UiDocument.hpp"
|
||||
|
||||
#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"
|
||||
#include "../files/files.hpp"
|
||||
#include "../graphics/ui/elements/UINode.hpp"
|
||||
#include "../graphics/ui/elements/InventoryView.hpp"
|
||||
#include "../graphics/ui/gui_xml.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
|
||||
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);
|
||||
scriptenv env
|
||||
) : id(id), script(script), root(root), env(env) {
|
||||
gui::UINode::getIndices(root, map);
|
||||
}
|
||||
|
||||
void UiDocument::rebuildIndices() {
|
||||
gui::UINode::getIndices(root, map);
|
||||
}
|
||||
|
||||
const uinodes_map& UiDocument::getMap() const {
|
||||
return map;
|
||||
}
|
||||
|
||||
uinodes_map& UiDocument::getMapWriteable() {
|
||||
return map;
|
||||
}
|
||||
|
||||
const std::string& UiDocument::getId() const {
|
||||
return id;
|
||||
}
|
||||
@@ -42,30 +47,19 @@ const uidocscript& UiDocument::getScript() const {
|
||||
return script;
|
||||
}
|
||||
|
||||
int UiDocument::getEnvironment() const {
|
||||
return env->getId();
|
||||
scriptenv UiDocument::getEnvironment() const {
|
||||
return env;
|
||||
}
|
||||
|
||||
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) {
|
||||
std::unique_ptr<UiDocument> UiDocument::read(scriptenv penv, std::string name, 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 env = penv == nullptr
|
||||
? scripting::create_doc_environment(scripting::get_root_environment(), name)
|
||||
: scripting::create_doc_environment(penv, name);
|
||||
|
||||
gui::UiXmlReader reader(env);
|
||||
auto view = reader.readXML(
|
||||
file.u8string(), xmldoc->getRoot()
|
||||
);
|
||||
@@ -73,7 +67,12 @@ std::unique_ptr<UiDocument> UiDocument::read(AssetsLoader& loader, int penv, std
|
||||
uidocscript script {};
|
||||
auto scriptFile = fs::path(file.u8string()+".lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_layout_script(env->getId(), namesp, scriptFile, script);
|
||||
scripting::load_layout_script(env, name, scriptFile, script);
|
||||
}
|
||||
return std::make_unique<UiDocument>(namesp, script, view, std::move(env));
|
||||
return std::make_unique<UiDocument>(name, script, view, env);
|
||||
}
|
||||
|
||||
std::shared_ptr<gui::UINode> UiDocument::readElement(fs::path file) {
|
||||
auto document = read(nullptr, file.filename().u8string(), file);
|
||||
return document->getRoot();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#ifndef FRONTEND_UI_DOCUMENT_H_
|
||||
#define FRONTEND_UI_DOCUMENT_H_
|
||||
#ifndef FRONTEND_UI_DOCUMENT_HPP_
|
||||
#define FRONTEND_UI_DOCUMENT_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -12,44 +14,40 @@ namespace gui {
|
||||
class UINode;
|
||||
}
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
struct uidocscript {
|
||||
int environment;
|
||||
bool onopen : 1;
|
||||
bool onprogress : 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;
|
||||
scriptenv env;
|
||||
public:
|
||||
UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
std::shared_ptr<gui::UINode> root,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
scriptenv env
|
||||
);
|
||||
|
||||
void rebuildIndices();
|
||||
|
||||
const std::string& getId() const;
|
||||
const uinodes_map& getMap() const;
|
||||
uinodes_map& getMapWriteable();
|
||||
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);
|
||||
scriptenv getEnvironment() const;
|
||||
|
||||
static std::unique_ptr<UiDocument> read(AssetsLoader& loader, int env, std::string namesp, fs::path file);
|
||||
static std::unique_ptr<UiDocument> read(scriptenv parent_env, std::string name, fs::path file);
|
||||
static std::shared_ptr<gui::UINode> readElement(fs::path file);
|
||||
};
|
||||
|
||||
#endif // FRONTEND_UI_DOCUMENT_H_
|
||||
#endif // FRONTEND_UI_DOCUMENT_HPP_
|
||||
@@ -1,346 +0,0 @@
|
||||
#include "WorldRenderer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <GL/glew.h>
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../window/Window.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../graphics/Mesh.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Batch3D.h"
|
||||
#include "../graphics/Texture.h"
|
||||
#include "../graphics/LineBatch.h"
|
||||
#include "../graphics/PostProcessing.h"
|
||||
#include "../voxels/Chunks.h"
|
||||
#include "../voxels/Chunk.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../world/World.h"
|
||||
#include "../world/Level.h"
|
||||
#include "../world/LevelEvents.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../assets/Assets.h"
|
||||
#include "../logic/PlayerController.h"
|
||||
#include "../maths/FrustumCulling.h"
|
||||
#include "../maths/voxmaths.h"
|
||||
#include "../settings.h"
|
||||
#include "../engine.h"
|
||||
#include "../items/ItemDef.h"
|
||||
#include "../items/ItemStack.h"
|
||||
#include "../items/Inventory.h"
|
||||
#include "LevelFrontend.h"
|
||||
#include "graphics/Skybox.h"
|
||||
#include "graphics/ChunksRenderer.h"
|
||||
|
||||
WorldRenderer::WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player)
|
||||
: engine(engine),
|
||||
level(frontend->getLevel()),
|
||||
player(player)
|
||||
{
|
||||
postProcessing = std::make_unique<PostProcessing>();
|
||||
frustumCulling = std::make_unique<Frustum>();
|
||||
lineBatch = std::make_unique<LineBatch>();
|
||||
renderer = std::make_unique<ChunksRenderer>(
|
||||
level,
|
||||
frontend->getContentGfxCache(),
|
||||
engine->getSettings()
|
||||
);
|
||||
batch3d = std::make_unique<Batch3D>(4096);
|
||||
|
||||
auto& settings = engine->getSettings();
|
||||
level->events->listen(EVT_CHUNK_HIDDEN,
|
||||
[this](lvl_event_type type, Chunk* chunk) {
|
||||
renderer->unload(chunk);
|
||||
}
|
||||
);
|
||||
auto assets = engine->getAssets();
|
||||
skybox = std::make_unique<Skybox>(
|
||||
settings.graphics.skyboxResolution,
|
||||
assets->getShader("skybox_gen")
|
||||
);
|
||||
}
|
||||
|
||||
WorldRenderer::~WorldRenderer() {
|
||||
}
|
||||
|
||||
bool WorldRenderer::drawChunk(
|
||||
size_t index,
|
||||
Camera* camera,
|
||||
Shader* shader,
|
||||
bool culling
|
||||
){
|
||||
auto chunk = level->chunks->chunks[index];
|
||||
if (!chunk->isLighted()) {
|
||||
return false;
|
||||
}
|
||||
float distance = glm::distance(
|
||||
camera->position,
|
||||
glm::vec3((chunk->x + 0.5f) * CHUNK_W,
|
||||
camera->position.y,
|
||||
(chunk->z + 0.5f) * CHUNK_D)
|
||||
);
|
||||
auto mesh = renderer->getOrRender(chunk, distance < CHUNK_W*1.5f);
|
||||
if (mesh == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (culling){
|
||||
glm::vec3 min(
|
||||
chunk->x * CHUNK_W,
|
||||
chunk->bottom,
|
||||
chunk->z * CHUNK_D
|
||||
);
|
||||
glm::vec3 max(
|
||||
chunk->x * CHUNK_W + CHUNK_W,
|
||||
chunk->top,
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustumCulling->IsBoxVisible(min, max))
|
||||
return false;
|
||||
}
|
||||
glm::vec3 coord(chunk->x*CHUNK_W+0.5f, 0.5f, chunk->z*CHUNK_D+0.5f);
|
||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||
shader->uniformMatrix("u_model", model);
|
||||
mesh->draw();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) {
|
||||
renderer->update();
|
||||
std::vector<size_t> indices;
|
||||
for (size_t i = 0; i < chunks->volume; i++){
|
||||
if (chunks->chunks[i] == nullptr)
|
||||
continue;
|
||||
indices.push_back(i);
|
||||
}
|
||||
float px = camera->position.x / (float)CHUNK_W;
|
||||
float pz = camera->position.z / (float)CHUNK_D;
|
||||
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) +
|
||||
(a->z + 0.5f - pz)*(a->z + 0.5f - pz)
|
||||
>
|
||||
(b->x + 0.5f - px)*(b->x + 0.5f - px) +
|
||||
(b->z + 0.5f - pz)*(b->z + 0.5f - pz));
|
||||
});
|
||||
|
||||
bool culling = engine->getSettings().graphics.frustumCulling;
|
||||
if (culling) {
|
||||
frustumCulling->update(camera->getProjView());
|
||||
}
|
||||
chunks->visible = 0;
|
||||
for (size_t i = 0; i < indices.size(); i++){
|
||||
chunks->visible += drawChunk(indices[i], camera, shader, culling);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::renderLevel(
|
||||
const GfxContext& ctx,
|
||||
Camera* camera,
|
||||
const EngineSettings& settings
|
||||
) {
|
||||
Assets* assets = engine->getAssets();
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
Shader* shader = assets->getShader("main");
|
||||
auto indices = level->content->getIndices();
|
||||
|
||||
float fogFactor = 15.0f / ((float)settings.chunks.loadDistance-2);
|
||||
|
||||
// Setting up main shader
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_proj", camera->getProjection());
|
||||
shader->uniformMatrix("u_view", camera->getView());
|
||||
shader->uniform1f("u_gamma", settings.graphics.gamma);
|
||||
shader->uniform1f("u_fogFactor", fogFactor);
|
||||
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve);
|
||||
shader->uniform1f("u_dayTime", level->world->daytime);
|
||||
shader->uniform3f("u_cameraPos", camera->position);
|
||||
shader->uniform1i("u_cubemap", 1);
|
||||
|
||||
// Light emission when an emissive item is chosen
|
||||
{
|
||||
auto inventory = player->getInventory();
|
||||
ItemStack& stack = inventory->getSlot(player->getChosenSlot());
|
||||
auto item = indices->getItemDef(stack.getItemId());
|
||||
float multiplier = 0.5f;
|
||||
shader->uniform3f("u_torchlightColor",
|
||||
item->emission[0] / 15.0f * multiplier,
|
||||
item->emission[1] / 15.0f * multiplier,
|
||||
item->emission[2] / 15.0f * multiplier
|
||||
);
|
||||
shader->uniform1f("u_torchlightDistance", 6.0f);
|
||||
}
|
||||
|
||||
// Binding main shader textures
|
||||
skybox->bind();
|
||||
atlas->getTexture()->bind();
|
||||
|
||||
drawChunks(level->chunks.get(), camera, shader);
|
||||
|
||||
skybox->unbind();
|
||||
}
|
||||
|
||||
void WorldRenderer::renderBlockSelection(Camera* camera, Shader* linesShader) {
|
||||
auto indices = level->content->getIndices();
|
||||
blockid_t id = PlayerController::selectedBlockId;
|
||||
auto block = indices->getBlockDef(id);
|
||||
const glm::vec3 pos = PlayerController::selectedBlockPosition;
|
||||
const glm::vec3 point = PlayerController::selectedPointPosition;
|
||||
const glm::vec3 norm = PlayerController::selectedBlockNormal;
|
||||
|
||||
std::vector<AABB>& hitboxes = block->rotatable
|
||||
? block->rt.hitboxes[PlayerController::selectedBlockStates]
|
||||
: block->hitboxes;
|
||||
|
||||
linesShader->use();
|
||||
linesShader->uniformMatrix("u_projview", camera->getProjView());
|
||||
lineBatch->lineWidth(2.0f);
|
||||
for (auto& hitbox: hitboxes) {
|
||||
const glm::vec3 center = pos + hitbox.center();
|
||||
const glm::vec3 size = hitbox.size();
|
||||
lineBatch->box(center, size + glm::vec3(0.02), glm::vec4(0.f, 0.f, 0.f, 0.5f));
|
||||
if (player->debug) {
|
||||
lineBatch->line(point, point+norm*0.5f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
lineBatch->render();
|
||||
}
|
||||
|
||||
void WorldRenderer::renderDebugLines(
|
||||
const GfxContext& pctx,
|
||||
Camera* camera,
|
||||
Shader* linesShader,
|
||||
const EngineSettings& settings
|
||||
) {
|
||||
GfxContext ctx = pctx.sub();
|
||||
const auto& viewport = ctx.getViewport();
|
||||
uint displayWidth = viewport.getWidth();
|
||||
uint displayHeight = viewport.getHeight();
|
||||
|
||||
ctx.setDepthTest(true);
|
||||
|
||||
linesShader->use();
|
||||
|
||||
if (settings.debug.showChunkBorders){
|
||||
linesShader->uniformMatrix("u_projview", camera->getProjView());
|
||||
glm::vec3 coord = player->camera->position;
|
||||
if (coord.x < 0) coord.x--;
|
||||
if (coord.z < 0) coord.z--;
|
||||
int cx = floordiv((int)coord.x, CHUNK_W);
|
||||
int cz = floordiv((int)coord.z, CHUNK_D);
|
||||
|
||||
drawBorders(
|
||||
cx * CHUNK_W, 0, cz * CHUNK_D,
|
||||
(cx + 1) * CHUNK_W, CHUNK_H, (cz + 1) * CHUNK_D
|
||||
);
|
||||
}
|
||||
|
||||
float length = 40.f;
|
||||
glm::vec3 tsl(displayWidth/2, displayHeight/2, 0.f);
|
||||
glm::mat4 model(glm::translate(glm::mat4(1.f), tsl));
|
||||
linesShader->uniformMatrix("u_projview", glm::ortho(
|
||||
0.f, (float)displayWidth,
|
||||
0.f, (float)displayHeight,
|
||||
-length, length) * model * glm::inverse(camera->rotation)
|
||||
);
|
||||
|
||||
ctx.setDepthTest(false);
|
||||
lineBatch->lineWidth(4.0f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->render();
|
||||
|
||||
ctx.setDepthTest(true);
|
||||
lineBatch->lineWidth(2.0f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
|
||||
lineBatch->render();
|
||||
}
|
||||
|
||||
void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool hudVisible){
|
||||
EngineSettings& settings = engine->getSettings();
|
||||
skybox->refresh(pctx, level->world->daytime, 1.0f+fog*2.0f, 4);
|
||||
|
||||
Assets* assets = engine->getAssets();
|
||||
Shader* linesShader = assets->getShader("lines");
|
||||
|
||||
// World render scope with diegetic HUD included
|
||||
{
|
||||
GfxContext wctx = pctx.sub();
|
||||
postProcessing->use(wctx);
|
||||
|
||||
Window::clearDepth();
|
||||
|
||||
// Drawing background sky plane
|
||||
skybox->draw(pctx, camera, assets, level->getWorld()->daytime, fog);
|
||||
|
||||
// Actually world render with depth buffer on
|
||||
{
|
||||
GfxContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.setCullFace(true);
|
||||
renderLevel(ctx, camera, settings);
|
||||
// Selected block
|
||||
if (PlayerController::selectedBlockId != -1 && hudVisible){
|
||||
renderBlockSelection(camera, linesShader);
|
||||
}
|
||||
}
|
||||
|
||||
if (hudVisible && player->debug) {
|
||||
renderDebugLines(wctx, camera, linesShader, settings);
|
||||
}
|
||||
}
|
||||
|
||||
// Rendering fullscreen quad with
|
||||
auto screenShader = assets->getShader("screen");
|
||||
screenShader->use();
|
||||
screenShader->uniform1f("u_timer", Window::time());
|
||||
screenShader->uniform1f("u_dayTime", level->world->daytime);
|
||||
postProcessing->render(pctx, screenShader);
|
||||
}
|
||||
|
||||
void WorldRenderer::drawBorders(int sx, int sy, int sz, int ex, int ey, int ez) {
|
||||
int ww = ex-sx;
|
||||
int dd = ez-sz;
|
||||
/*corner*/ {
|
||||
lineBatch->line(sx, sy, sz,
|
||||
sx, ey, sz, 0.8f, 0, 0.8f, 1);
|
||||
lineBatch->line(sx, sy, ez,
|
||||
sx, ey, ez, 0.8f, 0, 0.8f, 1);
|
||||
lineBatch->line(ex, sy, sz,
|
||||
ex, ey, sz, 0.8f, 0, 0.8f, 1);
|
||||
lineBatch->line(ex, sy, ez,
|
||||
ex, ey, ez, 0.8f, 0, 0.8f, 1);
|
||||
}
|
||||
for (int i = 2; i < ww; i+=2) {
|
||||
lineBatch->line(sx + i, sy, sz,
|
||||
sx + i, ey, sz, 0, 0, 0.8f, 1);
|
||||
lineBatch->line(sx + i, sy, ez,
|
||||
sx + i, ey, ez, 0, 0, 0.8f, 1);
|
||||
}
|
||||
for (int i = 2; i < dd; i+=2) {
|
||||
lineBatch->line(sx, sy, sz + i,
|
||||
sx, ey, sz + i, 0.8f, 0, 0, 1);
|
||||
lineBatch->line(ex, sy, sz + i,
|
||||
ex, ey, sz + i, 0.8f, 0, 0, 1);
|
||||
}
|
||||
for (int i = sy; i < ey; i+=2){
|
||||
lineBatch->line(sx, i, sz,
|
||||
sx, i, ez, 0, 0.8f, 0, 1);
|
||||
lineBatch->line(sx, i, ez,
|
||||
ex, i, ez, 0, 0.8f, 0, 1);
|
||||
lineBatch->line(ex, i, ez,
|
||||
ex, i, sz, 0, 0.8f, 0, 1);
|
||||
lineBatch->line(ex, i, sz,
|
||||
sx, i, sz, 0, 0.8f, 0, 1);
|
||||
}
|
||||
lineBatch->render();
|
||||
}
|
||||
|
||||
float WorldRenderer::fog = 0.0f;
|
||||
@@ -1,80 +0,0 @@
|
||||
#ifndef WORLD_RENDERER_CPP
|
||||
#define WORLD_RENDERER_CPP
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <GL/glew.h>
|
||||
#include <string>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "../graphics/GfxContext.h"
|
||||
|
||||
class Level;
|
||||
class Player;
|
||||
class Camera;
|
||||
class Batch3D;
|
||||
class LineBatch;
|
||||
class ChunksRenderer;
|
||||
class Shader;
|
||||
class Frustum;
|
||||
class Engine;
|
||||
class Chunks;
|
||||
class LevelFrontend;
|
||||
class Skybox;
|
||||
class PostProcessing;
|
||||
|
||||
class WorldRenderer {
|
||||
Engine* engine;
|
||||
Level* level;
|
||||
Player* player;
|
||||
std::unique_ptr<PostProcessing> postProcessing;
|
||||
std::unique_ptr<Frustum> frustumCulling;
|
||||
std::unique_ptr<LineBatch> lineBatch;
|
||||
std::unique_ptr<ChunksRenderer> renderer;
|
||||
std::unique_ptr<Skybox> skybox;
|
||||
std::unique_ptr<Batch3D> batch3d;
|
||||
bool drawChunk(size_t index, Camera* camera, Shader* shader, bool culling);
|
||||
void drawChunks(Chunks* chunks, Camera* camera, Shader* shader);
|
||||
|
||||
/// @brief Render level without diegetic interface
|
||||
/// @param context graphics context
|
||||
/// @param camera active camera
|
||||
/// @param settings engine settings
|
||||
void renderLevel(
|
||||
const GfxContext& context,
|
||||
Camera* camera,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
|
||||
/// @brief Render block selection lines
|
||||
/// @param camera active camera
|
||||
/// @param linesShader shader used
|
||||
void renderBlockSelection(Camera* camera, Shader* linesShader);
|
||||
|
||||
/// @brief Render all debug lines (chunks borders, coord system guides)
|
||||
/// @param context graphics context
|
||||
/// @param camera active camera
|
||||
/// @param linesShader shader used
|
||||
/// @param settings engine settings
|
||||
void renderDebugLines(
|
||||
const GfxContext& context,
|
||||
Camera* camera,
|
||||
Shader* linesShader,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
public:
|
||||
WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player);
|
||||
~WorldRenderer();
|
||||
|
||||
void draw(const GfxContext& context, Camera* camera, bool hudVisible);
|
||||
void drawBorders(int sx, int sy, int sz, int ex, int ey, int ez);
|
||||
|
||||
static float fog;
|
||||
};
|
||||
|
||||
|
||||
#endif // WORLD_RENDERER_CPP
|
||||
@@ -1,22 +1,25 @@
|
||||
#include "../audio/audio.hpp"
|
||||
#include "../delegates.hpp"
|
||||
#include "../engine.hpp"
|
||||
#include "../graphics/core/Mesh.hpp"
|
||||
#include "../graphics/ui/elements/CheckBox.hpp"
|
||||
#include "../graphics/ui/elements/TextBox.hpp"
|
||||
#include "../graphics/ui/elements/TrackBar.hpp"
|
||||
#include "../graphics/ui/elements/InputBindBox.hpp"
|
||||
#include "../graphics/render/WorldRenderer.hpp"
|
||||
#include "../objects/Player.hpp"
|
||||
#include "../physics/Hitbox.hpp"
|
||||
#include "../util/stringutil.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../voxels/Chunk.hpp"
|
||||
#include "../voxels/Chunks.hpp"
|
||||
#include "../world/Level.hpp"
|
||||
#include "../world/World.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "gui/controls.h"
|
||||
#include "../audio/audio.h"
|
||||
#include "../graphics/Mesh.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../physics/Hitbox.h"
|
||||
#include "../world/Level.h"
|
||||
#include "../world/World.h"
|
||||
#include "../voxels/Chunks.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../util/stringutil.h"
|
||||
#include "../delegates.h"
|
||||
#include "../engine.h"
|
||||
|
||||
#include "WorldRenderer.h"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
||||
@@ -60,7 +63,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
auto& settings = engine->getSettings();
|
||||
bool culling = settings.graphics.frustumCulling;
|
||||
bool culling = settings.graphics.frustumCulling.get();
|
||||
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
|
||||
}));
|
||||
panel->add(create_label([=]() {
|
||||
@@ -79,7 +82,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
L" "+stream.str();
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
return L"seed: "+std::to_wstring(level->world->getSeed());
|
||||
return L"seed: "+std::to_wstring(level->getWorld()->getSeed());
|
||||
}));
|
||||
|
||||
for (int ax = 0; ax < 3; ax++) {
|
||||
@@ -95,6 +98,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
|
||||
// Coord input
|
||||
auto box = std::make_shared<TextBox>(L"");
|
||||
auto boxRef = box.get();
|
||||
box->setTextSupplier([=]() {
|
||||
Hitbox* hitbox = player->hitbox.get();
|
||||
return util::to_wstring(hitbox->position[ax], 2);
|
||||
@@ -109,7 +113,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
});
|
||||
box->setOnEditStart([=](){
|
||||
Hitbox* hitbox = player->hitbox.get();
|
||||
box->setText(std::to_wstring(int(hitbox->position[ax])));
|
||||
boxRef->setText(std::to_wstring(int(hitbox->position[ax])));
|
||||
});
|
||||
box->setSize(glm::vec2(230, 27));
|
||||
|
||||
@@ -118,7 +122,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
}
|
||||
panel->add(create_label([=](){
|
||||
int hour, minute, second;
|
||||
timeutil::from_value(level->world->daytime, hour, minute, second);
|
||||
timeutil::from_value(level->getWorld()->daytime, hour, minute, second);
|
||||
|
||||
std::wstring timeString =
|
||||
util::lfill(std::to_wstring(hour), 2, L'0') + L":" +
|
||||
@@ -127,8 +131,8 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
}));
|
||||
{
|
||||
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;});
|
||||
bar->setSupplier([=]() {return level->getWorld()->daytime;});
|
||||
bar->setConsumer([=](double val) {level->getWorld()->daytime = val;});
|
||||
panel->add(bar);
|
||||
}
|
||||
{
|
||||
@@ -142,10 +146,10 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
L"Show Chunk Borders", glm::vec2(400, 24)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().debug.showChunkBorders;
|
||||
return WorldRenderer::showChunkBorders;
|
||||
});
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
engine->getSettings().debug.showChunkBorders = checked;
|
||||
WorldRenderer::showChunkBorders = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
|
||||
@@ -1,472 +0,0 @@
|
||||
#include "BlocksRenderer.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../../graphics/Mesh.h"
|
||||
#include "../../graphics/UVRegion.h"
|
||||
#include "../../constants.h"
|
||||
#include "../../content/Content.h"
|
||||
#include "../../voxels/Block.h"
|
||||
#include "../../voxels/Chunk.h"
|
||||
#include "../../voxels/VoxelsVolume.h"
|
||||
#include "../../voxels/ChunksStorage.h"
|
||||
#include "../../lighting/Lightmap.h"
|
||||
#include "../../frontend/ContentGfxCache.h"
|
||||
|
||||
using glm::ivec3;
|
||||
using glm::vec3;
|
||||
using glm::vec4;
|
||||
|
||||
const uint BlocksRenderer::VERTEX_SIZE = 6;
|
||||
const vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
||||
|
||||
BlocksRenderer::BlocksRenderer(size_t capacity,
|
||||
const Content* content,
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings& settings)
|
||||
: content(content),
|
||||
vertexOffset(0),
|
||||
indexOffset(0),
|
||||
indexSize(0),
|
||||
capacity(capacity),
|
||||
cache(cache),
|
||||
settings(settings) {
|
||||
vertexBuffer = new float[capacity];
|
||||
indexBuffer = new int[capacity];
|
||||
voxelsBuffer = new VoxelsVolume(CHUNK_W + 2, CHUNK_H, CHUNK_D + 2);
|
||||
blockDefsCache = content->getIndices()->getBlockDefs();
|
||||
}
|
||||
|
||||
BlocksRenderer::~BlocksRenderer() {
|
||||
delete voxelsBuffer;
|
||||
delete[] vertexBuffer;
|
||||
delete[] indexBuffer;
|
||||
}
|
||||
|
||||
/* Basic vertex add method */
|
||||
void BlocksRenderer::vertex(const vec3& coord, float u, float v, const vec4& light) {
|
||||
vertexBuffer[vertexOffset++] = coord.x;
|
||||
vertexBuffer[vertexOffset++] = coord.y;
|
||||
vertexBuffer[vertexOffset++] = coord.z;
|
||||
|
||||
vertexBuffer[vertexOffset++] = u;
|
||||
vertexBuffer[vertexOffset++] = v;
|
||||
|
||||
union {
|
||||
float floating;
|
||||
uint32_t integer;
|
||||
} compressed;
|
||||
|
||||
compressed.integer = (uint32_t(light.r * 255) & 0xff) << 24;
|
||||
compressed.integer |= (uint32_t(light.g * 255) & 0xff) << 16;
|
||||
compressed.integer |= (uint32_t(light.b * 255) & 0xff) << 8;
|
||||
compressed.integer |= (uint32_t(light.a * 255) & 0xff);
|
||||
|
||||
vertexBuffer[vertexOffset++] = compressed.floating;
|
||||
}
|
||||
|
||||
void BlocksRenderer::index(int a, int b, int c, int d, int e, int f) {
|
||||
indexBuffer[indexSize++] = indexOffset + a;
|
||||
indexBuffer[indexSize++] = indexOffset + b;
|
||||
indexBuffer[indexSize++] = indexOffset + c;
|
||||
indexBuffer[indexSize++] = indexOffset + d;
|
||||
indexBuffer[indexSize++] = indexOffset + e;
|
||||
indexBuffer[indexSize++] = indexOffset + f;
|
||||
indexOffset += 4;
|
||||
}
|
||||
|
||||
/* Add face with precalculated lights */
|
||||
void BlocksRenderer::face(const vec3& coord,
|
||||
float w, float h, float d,
|
||||
const vec3& axisX,
|
||||
const vec3& axisY,
|
||||
const vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
const vec4(&lights)[4],
|
||||
const vec4& tint) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
vec3 X = axisX * w;
|
||||
vec3 Y = axisY * h;
|
||||
vec3 Z = axisZ * d;
|
||||
float s = 0.5f;
|
||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint);
|
||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint);
|
||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint);
|
||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint);
|
||||
index(0, 1, 3, 1, 2, 3);
|
||||
}
|
||||
|
||||
void BlocksRenderer::vertex(const vec3& coord,
|
||||
float u, float v,
|
||||
const vec4& tint,
|
||||
const vec3& axisX,
|
||||
const vec3& axisY,
|
||||
const vec3& axisZ) {
|
||||
vec3 pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f;
|
||||
vec4 light = pickSoftLight(ivec3(round(pos.x), round(pos.y), round(pos.z)), axisX, axisY);
|
||||
vertex(coord, u, v, light * tint);
|
||||
}
|
||||
|
||||
void BlocksRenderer::face(const vec3& coord,
|
||||
const vec3& X,
|
||||
const vec3& Y,
|
||||
const vec3& Z,
|
||||
const UVRegion& region,
|
||||
bool lights) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
|
||||
float s = 0.5f;
|
||||
if (lights) {
|
||||
float d = glm::dot(Z, SUN_VECTOR);
|
||||
d = 0.8f + d * 0.2f;
|
||||
|
||||
vec3 axisX = glm::normalize(X);
|
||||
vec3 axisY = glm::normalize(Y);
|
||||
vec3 axisZ = glm::normalize(Z);
|
||||
|
||||
vec4 tint(d);
|
||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisX, axisY, axisZ);
|
||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisX, axisY, axisZ);
|
||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ);
|
||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ);
|
||||
} else {
|
||||
vec4 tint(1.0f);
|
||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint);
|
||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint);
|
||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint);
|
||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint);
|
||||
}
|
||||
index(0, 1, 2, 0, 2, 3);
|
||||
}
|
||||
|
||||
void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1,
|
||||
const vec3& p2, const vec3& p3, const vec3& p4,
|
||||
const vec3& X,
|
||||
const vec3& Y,
|
||||
const vec3& Z,
|
||||
const UVRegion& texreg,
|
||||
bool lights) {
|
||||
|
||||
const vec3 fp1 = (p1.x - 0.5f) * X + (p1.y - 0.5f) * Y + (p1.z - 0.5f) * Z;
|
||||
const vec3 fp2 = (p2.x - 0.5f) * X + (p2.y - 0.5f) * Y + (p2.z - 0.5f) * Z;
|
||||
const vec3 fp3 = (p3.x - 0.5f) * X + (p3.y - 0.5f) * Y + (p3.z - 0.5f) * Z;
|
||||
const vec3 fp4 = (p4.x - 0.5f) * X + (p4.y - 0.5f) * Y + (p4.z - 0.5f) * Z;
|
||||
|
||||
vec4 tint(1.0f);
|
||||
if (lights) {
|
||||
vec3 dir = glm::cross(fp2 - fp1, fp3 - fp1);
|
||||
vec3 normal = glm::normalize(dir);
|
||||
|
||||
float d = glm::dot(normal, SUN_VECTOR);
|
||||
d = 0.8f + d * 0.2f;
|
||||
tint *= d;
|
||||
tint *= pickLight(coord);
|
||||
// debug normal
|
||||
// tint.x = normal.x * 0.5f + 0.5f;
|
||||
// tint.y = normal.y * 0.5f + 0.5f;
|
||||
// tint.z = normal.z * 0.5f + 0.5f;
|
||||
}
|
||||
vertex(coord + fp1, texreg.u1, texreg.v1, tint);
|
||||
vertex(coord + fp2, texreg.u2, texreg.v1, tint);
|
||||
vertex(coord + fp3, texreg.u2, texreg.v2, tint);
|
||||
vertex(coord + fp4, texreg.u1, texreg.v2, tint);
|
||||
index(0, 1, 3, 1, 2, 3);
|
||||
}
|
||||
|
||||
void BlocksRenderer::blockXSprite(int x, int y, int z,
|
||||
const vec3& size,
|
||||
const UVRegion& texface1,
|
||||
const UVRegion& texface2,
|
||||
float spread) {
|
||||
vec4 lights[]{
|
||||
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) };
|
||||
|
||||
int rand = ((x * z + y) ^ (z * y - x)) * (z + y);
|
||||
|
||||
float xs = ((float)(char)rand / 512) * spread;
|
||||
float zs = ((float)(char)(rand >> 8) / 512) * spread;
|
||||
|
||||
const float w = size.x / 1.41f;
|
||||
const float tint = 0.8f;
|
||||
|
||||
face(vec3(x + xs, y, z + zs),
|
||||
w, size.y, 0, vec3(1, 0, 1), vec3(0, 1, 0), vec3(),
|
||||
texface1, lights, vec4(tint));
|
||||
face(vec3(x + xs, y, z + zs),
|
||||
w, size.y, 0, vec3(-1, 0, -1), vec3(0, 1, 0), vec3(),
|
||||
texface1, lights, vec4(tint));
|
||||
|
||||
face(vec3(x + xs, y, z + zs),
|
||||
w, size.y, 0, vec3(1, 0, -1), vec3(0, 1, 0), vec3(),
|
||||
texface1, lights, vec4(tint));
|
||||
face(vec3(x + xs, y, z + zs),
|
||||
w, size.y, 0, vec3(-1, 0, 1), vec3(0, 1, 0), vec3(),
|
||||
texface1, lights, vec4(tint));
|
||||
}
|
||||
|
||||
// HINT: texture faces order: {east, west, bottom, top, south, north}
|
||||
|
||||
/* AABB blocks render method */
|
||||
void BlocksRenderer::blockAABB(const ivec3& icoord,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const Block* block, ubyte rotation,
|
||||
bool lights) {
|
||||
if (block->hitboxes.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AABB hitbox = block->hitboxes[0];
|
||||
for (const auto& box : block->hitboxes) {
|
||||
hitbox.a = glm::min(hitbox.a, box.a);
|
||||
hitbox.b = glm::max(hitbox.b, box.b);
|
||||
}
|
||||
|
||||
vec3 size = hitbox.size();
|
||||
vec3 X(1, 0, 0);
|
||||
vec3 Y(0, 1, 0);
|
||||
vec3 Z(0, 0, 1);
|
||||
vec3 coord(icoord);
|
||||
if (block->rotatable) {
|
||||
auto& rotations = block->rotations;
|
||||
auto& orient = rotations.variants[rotation];
|
||||
X = orient.axisX;
|
||||
Y = orient.axisY;
|
||||
Z = orient.axisZ;
|
||||
orient.transform(hitbox);
|
||||
}
|
||||
|
||||
coord = vec3(icoord) - vec3(0.5f) + hitbox.center();
|
||||
|
||||
face(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], lights); // north
|
||||
face(coord, -X*size.x, Y*size.y, -Z*size.z, texfaces[4], lights); // south
|
||||
|
||||
face(coord, X*size.x, -Z*size.z, Y*size.y, texfaces[3], lights); // top
|
||||
face(coord, -X*size.x, -Z*size.z, -Y*size.y, texfaces[2], lights); // bottom
|
||||
|
||||
face(coord, -Z*size.z, Y*size.y, X*size.x, texfaces[1], lights); // west
|
||||
face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east
|
||||
}
|
||||
|
||||
void BlocksRenderer::blockCustomModel(const ivec3& icoord,
|
||||
const Block* block, ubyte rotation, bool lights) {
|
||||
vec3 X(1, 0, 0);
|
||||
vec3 Y(0, 1, 0);
|
||||
vec3 Z(0, 0, 1);
|
||||
CoordSystem orient(X,Y,Z);
|
||||
vec3 coord(icoord);
|
||||
if (block->rotatable) {
|
||||
auto& rotations = block->rotations;
|
||||
orient = rotations.variants[rotation];
|
||||
X = orient.axisX;
|
||||
Y = orient.axisY;
|
||||
Z = orient.axisZ;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < block->modelBoxes.size(); i++) {
|
||||
AABB box = block->modelBoxes[i];
|
||||
vec3 size = box.size();
|
||||
if (block->rotatable) {
|
||||
orient.transform(box);
|
||||
}
|
||||
vec3 center_coord = coord - vec3(0.5f) + box.center();
|
||||
face(center_coord, X * size.x, Y * size.y, Z * size.z, block->modelUVs[i * 6 + 5], lights); // north
|
||||
face(center_coord, -X * size.x, Y * size.y, -Z * size.z, block->modelUVs[i * 6 + 4], lights); // south
|
||||
face(center_coord, X * size.x, -Z * size.z, Y * size.y, block->modelUVs[i * 6 + 3], lights); // top
|
||||
face(center_coord, -X * size.x, -Z * size.z, -Y * size.y, block->modelUVs[i * 6 + 2], lights); // bottom
|
||||
face(center_coord, -Z * size.z, Y * size.y, X * size.x, block->modelUVs[i * 6 + 1], lights); // west
|
||||
face(center_coord, Z * size.z, Y * size.y, -X * size.x, block->modelUVs[i * 6 + 0], lights); // east
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < block->modelExtraPoints.size()/4; i++) {
|
||||
tetragonicFace(coord,
|
||||
block->modelExtraPoints[i * 4 + 0],
|
||||
block->modelExtraPoints[i * 4 + 1],
|
||||
block->modelExtraPoints[i * 4 + 2],
|
||||
block->modelExtraPoints[i * 4 + 3],
|
||||
X, Y, Z,
|
||||
block->modelUVs[block->modelBoxes.size()*6 + i], lights);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fastest solid shaded blocks render method */
|
||||
void BlocksRenderer::blockCube(int x, int y, int z,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const Block* block,
|
||||
ubyte states,
|
||||
bool lights) {
|
||||
ubyte group = block->drawGroup;
|
||||
|
||||
vec3 X(1, 0, 0);
|
||||
vec3 Y(0, 1, 0);
|
||||
vec3 Z(0, 0, 1);
|
||||
vec3 coord(x, y, z);
|
||||
if (block->rotatable) {
|
||||
auto& rotations = block->rotations;
|
||||
auto& orient = rotations.variants[states & BLOCK_ROT_MASK];
|
||||
X = orient.axisX;
|
||||
Y = orient.axisY;
|
||||
Z = orient.axisZ;
|
||||
}
|
||||
|
||||
if (isOpen(x+Z.x, y+Z.y, z+Z.z, group)) {
|
||||
face(coord, X, Y, Z, texfaces[5], lights);
|
||||
}
|
||||
if (isOpen(x-Z.x, y-Z.y, z-Z.z, group)) {
|
||||
face(coord, -X, Y, -Z, texfaces[4], lights);
|
||||
}
|
||||
if (isOpen(x+Y.x, y+Y.y, z+Y.z, group)) {
|
||||
face(coord, X, -Z, Y, texfaces[3], lights);
|
||||
}
|
||||
if (isOpen(x-Y.x, y-Y.y, z-Y.z, group)) {
|
||||
face(coord, X, Z, -Y, texfaces[2], lights);
|
||||
}
|
||||
if (isOpen(x+X.x, y+X.y, z+X.z, group)) {
|
||||
face(coord, -Z, Y, X, texfaces[1], lights);
|
||||
}
|
||||
if (isOpen(x-X.x, y-X.y, z-X.z, group)) {
|
||||
face(coord, Z, Y, -X, texfaces[0], lights);
|
||||
}
|
||||
}
|
||||
|
||||
// Does block allow to see other blocks sides (is it transparent)
|
||||
bool BlocksRenderer::isOpen(int x, int y, int z, ubyte group) const {
|
||||
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
if (id == BLOCK_VOID)
|
||||
return false;
|
||||
const Block& block = *blockDefsCache[id];
|
||||
if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) {
|
||||
return true;
|
||||
}
|
||||
return !id;
|
||||
}
|
||||
|
||||
bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
|
||||
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
if (id == BLOCK_VOID)
|
||||
return false;
|
||||
const Block& block = *blockDefsCache[id];
|
||||
if (block.lightPassing) {
|
||||
return true;
|
||||
}
|
||||
return !id;
|
||||
}
|
||||
|
||||
vec4 BlocksRenderer::pickLight(int x, int y, int z) const {
|
||||
if (isOpenForLight(x, y, z)) {
|
||||
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
return vec4(Lightmap::extract(light, 0) / 15.0f,
|
||||
Lightmap::extract(light, 1) / 15.0f,
|
||||
Lightmap::extract(light, 2) / 15.0f,
|
||||
Lightmap::extract(light, 3) / 15.0f);
|
||||
}
|
||||
else {
|
||||
return vec4(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec4 BlocksRenderer::pickLight(const ivec3& coord) const {
|
||||
return pickLight(coord.x, coord.y, coord.z);
|
||||
}
|
||||
|
||||
vec4 BlocksRenderer::pickSoftLight(const ivec3& coord,
|
||||
const ivec3& right,
|
||||
const ivec3& up) const {
|
||||
return (
|
||||
pickLight(coord) +
|
||||
pickLight(coord - right) +
|
||||
pickLight(coord - right - up) +
|
||||
pickLight(coord - up)) * 0.25f;
|
||||
}
|
||||
|
||||
vec4 BlocksRenderer::pickSoftLight(float x, float y, float z,
|
||||
const ivec3& right,
|
||||
const ivec3& up) const {
|
||||
return pickSoftLight({int(round(x)), int(round(y)), int(round(z))}, right, up);
|
||||
}
|
||||
|
||||
void BlocksRenderer::render(const voxel* voxels) {
|
||||
int begin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||
int end = chunk->top * (CHUNK_W * CHUNK_D);
|
||||
for (const auto drawGroup : *content->drawGroups) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
const Block& def = *blockDefsCache[id];
|
||||
if (id == 0 || def.drawGroup != drawGroup)
|
||||
continue;
|
||||
const UVRegion texfaces[6]{ cache->getRegion(id, 0),
|
||||
cache->getRegion(id, 1),
|
||||
cache->getRegion(id, 2),
|
||||
cache->getRegion(id, 3),
|
||||
cache->getRegion(id, 4),
|
||||
cache->getRegion(id, 5)};
|
||||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
int z = (i / CHUNK_D) % CHUNK_W;
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
blockCube(x, y, z, texfaces, &def, vox.states, !def.rt.emissive);
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
blockXSprite(x, y, z, vec3(1.0f),
|
||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||
break;
|
||||
}
|
||||
case BlockModel::aabb: {
|
||||
blockAABB(ivec3(x,y,z), texfaces, &def, vox.rotation(), !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
case BlockModel::custom: {
|
||||
blockCustomModel(ivec3(x, y, z), &def, vox.rotation(), !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (overflow)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
this->chunk = chunk;
|
||||
voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1);
|
||||
chunks->getVoxels(voxelsBuffer, settings.graphics.backlight);
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
const voxel* voxels = chunk->voxels;
|
||||
render(voxels);
|
||||
}
|
||||
|
||||
Mesh* BlocksRenderer::createMesh() {
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
||||
Mesh* mesh = new Mesh(vertexBuffer, vcount, indexBuffer, indexSize, attrs);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
build(chunk, chunks);
|
||||
return createMesh();
|
||||
}
|
||||
|
||||
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
||||
return voxelsBuffer;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
#ifndef GRAPHICS_BLOCKS_RENDERER_H
|
||||
#define GRAPHICS_BLOCKS_RENDERER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include "../../graphics/UVRegion.h"
|
||||
#include "../../voxels/voxel.h"
|
||||
#include "../../typedefs.h"
|
||||
#include "../../settings.h"
|
||||
|
||||
class Content;
|
||||
class Mesh;
|
||||
class Block;
|
||||
class Chunk;
|
||||
class Chunks;
|
||||
class VoxelsVolume;
|
||||
class ChunksStorage;
|
||||
class ContentGfxCache;
|
||||
|
||||
class BlocksRenderer {
|
||||
static const glm::vec3 SUN_VECTOR;
|
||||
static const uint VERTEX_SIZE;
|
||||
const Content* const content;
|
||||
float* vertexBuffer;
|
||||
int* indexBuffer;
|
||||
size_t vertexOffset;
|
||||
size_t indexOffset, indexSize;
|
||||
size_t capacity;
|
||||
|
||||
bool overflow = false;
|
||||
|
||||
const Chunk* chunk = nullptr;
|
||||
VoxelsVolume* voxelsBuffer;
|
||||
|
||||
const Block* const* blockDefsCache;
|
||||
const ContentGfxCache* const cache;
|
||||
const EngineSettings& settings;
|
||||
|
||||
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
|
||||
void index(int a, int b, int c, int d, int e, int f);
|
||||
|
||||
void vertex(const glm::vec3& coord, float u, float v,
|
||||
const glm::vec4& brightness,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ);
|
||||
|
||||
void face(const glm::vec3& coord, float w, float h, float d,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
const glm::vec4(&lights)[4],
|
||||
const glm::vec4& tint);
|
||||
|
||||
void face(const glm::vec3& coord,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
bool lights);
|
||||
|
||||
void tetragonicFace(const glm::vec3& coord,
|
||||
const glm::vec3& p1, const glm::vec3& p2,
|
||||
const glm::vec3& p3, const glm::vec3& p4,
|
||||
const glm::vec3& X,
|
||||
const glm::vec3& Y,
|
||||
const glm::vec3& Z,
|
||||
const UVRegion& texreg,
|
||||
bool lights);
|
||||
|
||||
void blockCube(int x, int y, int z, const UVRegion(&faces)[6], const Block* block, ubyte states, bool lights);
|
||||
void blockAABB(const glm::ivec3& coord,
|
||||
const UVRegion(&faces)[6],
|
||||
const Block* block,
|
||||
ubyte rotation,
|
||||
bool lights);
|
||||
void blockXSprite(int x, int y, int z, const glm::vec3& size, const UVRegion& face1, const UVRegion& face2, float spread);
|
||||
void blockCustomModel(const glm::ivec3& icoord,
|
||||
const Block* block, ubyte rotation,
|
||||
bool lights);
|
||||
|
||||
bool isOpenForLight(int x, int y, int z) const;
|
||||
bool isOpen(int x, int y, int z, ubyte group) const;
|
||||
|
||||
glm::vec4 pickLight(int x, int y, int z) const;
|
||||
glm::vec4 pickLight(const glm::ivec3& coord) const;
|
||||
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
void render(const voxel* voxels);
|
||||
public:
|
||||
BlocksRenderer(size_t capacity, const Content* content, const ContentGfxCache* cache, const EngineSettings& settings);
|
||||
virtual ~BlocksRenderer();
|
||||
|
||||
void build(const Chunk* chunk, const ChunksStorage* chunks);
|
||||
Mesh* render(const Chunk* chunk, const ChunksStorage* chunks);
|
||||
Mesh* createMesh();
|
||||
VoxelsVolume* getVoxelsBuffer() const;
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_BLOCKS_RENDERER_H
|
||||
@@ -1,150 +0,0 @@
|
||||
#include "ChunksRenderer.h"
|
||||
|
||||
#include "../../graphics/Mesh.h"
|
||||
#include "BlocksRenderer.h"
|
||||
#include "../../voxels/Chunk.h"
|
||||
#include "../../world/Level.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
|
||||
ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings)
|
||||
: level(level), cache(cache), settings(settings) {
|
||||
const int MAX_FULL_CUBES = 3000;
|
||||
renderer = std::make_unique<BlocksRenderer>(
|
||||
9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings
|
||||
);
|
||||
|
||||
const uint num_threads = std::thread::hardware_concurrency();
|
||||
for (uint i = 0; i < num_threads; i++) {
|
||||
threads.emplace_back(&ChunksRenderer::threadLoop, this, i);
|
||||
workersBlocked.emplace_back();
|
||||
}
|
||||
std::cout << "created " << num_threads << " chunks rendering threads" << std::endl;
|
||||
}
|
||||
|
||||
ChunksRenderer::~ChunksRenderer() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(jobsMutex);
|
||||
working = false;
|
||||
}
|
||||
|
||||
resultsMutex.lock();
|
||||
while (!results.empty()) {
|
||||
mesh_entry entry = results.front();
|
||||
results.pop();
|
||||
entry.locked = false;
|
||||
entry.variable.notify_all();
|
||||
}
|
||||
resultsMutex.unlock();
|
||||
|
||||
jobsMutexCondition.notify_all();
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ChunksRenderer::threadLoop(int index) {
|
||||
const int MAX_FULL_CUBES = 3000;
|
||||
BlocksRenderer renderer(
|
||||
9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings
|
||||
);
|
||||
|
||||
std::condition_variable variable;
|
||||
std::mutex mutex;
|
||||
bool locked = false;
|
||||
while (working) {
|
||||
std::shared_ptr<Chunk> chunk;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(jobsMutex);
|
||||
jobsMutexCondition.wait(lock, [this] {
|
||||
return !jobs.empty() || !working;
|
||||
});
|
||||
if (!working) {
|
||||
break;
|
||||
}
|
||||
chunk = jobs.front();
|
||||
jobs.pop();
|
||||
}
|
||||
process(chunk, renderer);
|
||||
{
|
||||
resultsMutex.lock();
|
||||
results.push(mesh_entry {renderer, variable, index, locked, glm::ivec2(chunk->x, chunk->z)});
|
||||
locked = true;
|
||||
resultsMutex.unlock();
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
variable.wait(lock, [&] {
|
||||
return !working || !locked;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChunksRenderer::process(std::shared_ptr<Chunk> chunk, BlocksRenderer& renderer) {
|
||||
renderer.build(chunk.get(), level->chunksStorage.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::render(std::shared_ptr<Chunk> chunk, bool important) {
|
||||
chunk->setModified(false);
|
||||
|
||||
if (important) {
|
||||
Mesh* mesh = renderer->render(chunk.get(), level->chunksStorage.get());
|
||||
auto sptr = std::shared_ptr<Mesh>(mesh);
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = sptr;
|
||||
return sptr;
|
||||
}
|
||||
|
||||
glm::ivec2 key(chunk->x, chunk->z);
|
||||
if (inwork.find(key) != inwork.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inwork[key] = true;
|
||||
jobsMutex.lock();
|
||||
jobs.push(chunk);
|
||||
jobsMutex.unlock();
|
||||
jobsMutexCondition.notify_one();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ChunksRenderer::unload(Chunk* chunk) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found != meshes.end()) {
|
||||
meshes.erase(found);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(std::shared_ptr<Chunk> chunk, bool important) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found != meshes.end()){
|
||||
if (chunk->isModified()) {
|
||||
render(chunk, important);
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
return render(chunk, important);
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::get(Chunk* chunk) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found != meshes.end()) {
|
||||
return found->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ChunksRenderer::update() {
|
||||
resultsMutex.lock();
|
||||
while (!results.empty()) {
|
||||
mesh_entry entry = results.front();
|
||||
results.pop();
|
||||
meshes[entry.key] = std::shared_ptr<Mesh>(entry.renderer.createMesh());
|
||||
inwork.erase(entry.key);
|
||||
entry.locked = false;
|
||||
entry.variable.notify_all();
|
||||
}
|
||||
resultsMutex.unlock();
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#ifndef SRC_GRAPHICS_CHUNKSRENDERER_H_
|
||||
#define SRC_GRAPHICS_CHUNKSRENDERER_H_
|
||||
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <glm/glm.hpp>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "../../voxels/Block.h"
|
||||
#include "../../voxels/ChunksStorage.h"
|
||||
#include "../../settings.h"
|
||||
|
||||
class Mesh;
|
||||
class Chunk;
|
||||
class Level;
|
||||
class BlocksRenderer;
|
||||
class ContentGfxCache;
|
||||
|
||||
struct mesh_entry {
|
||||
BlocksRenderer& renderer;
|
||||
std::condition_variable& variable;
|
||||
int workerIndex;
|
||||
bool& locked;
|
||||
glm::ivec2 key;
|
||||
};
|
||||
|
||||
class ChunksRenderer {
|
||||
std::unique_ptr<BlocksRenderer> renderer;
|
||||
Level* level;
|
||||
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
||||
std::unordered_map<glm::ivec2, bool> inwork;
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
std::queue<mesh_entry> results;
|
||||
std::mutex resultsMutex;
|
||||
|
||||
std::queue<std::shared_ptr<Chunk>> jobs;
|
||||
std::condition_variable jobsMutexCondition;
|
||||
std::mutex jobsMutex;
|
||||
|
||||
bool working = true;
|
||||
const ContentGfxCache* cache;
|
||||
const EngineSettings& settings;
|
||||
std::vector<std::unique_lock<std::mutex>> workersBlocked;
|
||||
|
||||
void threadLoop(int index);
|
||||
void process(std::shared_ptr<Chunk> chunk, BlocksRenderer& renderer);
|
||||
public:
|
||||
ChunksRenderer(Level* level,
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings& settings);
|
||||
virtual ~ChunksRenderer();
|
||||
|
||||
std::shared_ptr<Mesh> render(std::shared_ptr<Chunk> chunk, bool important);
|
||||
void unload(Chunk* chunk);
|
||||
|
||||
std::shared_ptr<Mesh> getOrRender(std::shared_ptr<Chunk> chunk, bool important);
|
||||
std::shared_ptr<Mesh> get(Chunk* chunk);
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
#endif // SRC_GRAPHICS_CHUNKSRENDERER_H_
|
||||
@@ -1,206 +0,0 @@
|
||||
#include "Skybox.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../../assets/Assets.h"
|
||||
#include "../../graphics/Shader.h"
|
||||
#include "../../graphics/Mesh.h"
|
||||
#include "../../graphics/Batch3D.h"
|
||||
#include "../../graphics/Texture.h"
|
||||
#include "../../graphics/Cubemap.h"
|
||||
#include "../../graphics/Framebuffer.h"
|
||||
#include "../../window/Window.h"
|
||||
#include "../../window/Camera.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592
|
||||
#endif // M_PI
|
||||
|
||||
const int STARS_COUNT = 3000;
|
||||
const int STARS_SEED = 632;
|
||||
|
||||
Skybox::Skybox(uint size, Shader* shader)
|
||||
: size(size),
|
||||
shader(shader),
|
||||
batch3d(std::make_unique<Batch3D>(4096))
|
||||
{
|
||||
auto cubemap = std::make_unique<Cubemap>(size, size, ImageFormat::rgb888);
|
||||
|
||||
uint fboid;
|
||||
glGenFramebuffers(1, &fboid);
|
||||
fbo = std::make_unique<Framebuffer>(fboid, 0, std::move(cubemap));
|
||||
|
||||
float vertices[] {
|
||||
-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}};
|
||||
mesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||
|
||||
sprites.push_back(skysprite {
|
||||
"misc/moon",
|
||||
M_PI*0.5f,
|
||||
4.0f,
|
||||
false
|
||||
});
|
||||
|
||||
sprites.push_back(skysprite {
|
||||
"misc/sun",
|
||||
M_PI*1.5f,
|
||||
4.0f,
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
Skybox::~Skybox() {
|
||||
}
|
||||
|
||||
void Skybox::drawBackground(Camera* camera, Assets* assets, int width, int height) {
|
||||
Shader* backShader = assets->getShader("background");
|
||||
backShader->use();
|
||||
backShader->uniformMatrix("u_view", camera->getView(false));
|
||||
backShader->uniform1f("u_zoom", camera->zoom*camera->getFov()/(M_PI*0.5f));
|
||||
backShader->uniform1f("u_ar", float(width)/float(height));
|
||||
backShader->uniform1i("u_cubemap", 1);
|
||||
bind();
|
||||
mesh->draw();
|
||||
unbind();
|
||||
}
|
||||
|
||||
void Skybox::drawStars(float angle, float opacity) {
|
||||
batch3d->texture(nullptr);
|
||||
random.setSeed(STARS_SEED);
|
||||
for (int i = 0; i < STARS_COUNT; i++) {
|
||||
float rx = (random.randFloat()) - 0.5f;
|
||||
float ry = (random.randFloat()) - 0.5f;
|
||||
float z = (random.randFloat()) - 0.5f;
|
||||
float x = rx * sin(angle) + ry * -cos(angle);
|
||||
float y = rx * cos(angle) + ry * sin(angle);
|
||||
|
||||
float sopacity = random.randFloat();
|
||||
if (y < 0.0f)
|
||||
continue;
|
||||
|
||||
sopacity *= (0.2f+sqrt(cos(angle))*0.5) - 0.05;
|
||||
glm::vec4 tint (1,1,1, sopacity * opacity);
|
||||
batch3d->point(glm::vec3(x, y, z), tint);
|
||||
}
|
||||
batch3d->flushPoints();
|
||||
}
|
||||
|
||||
void Skybox::draw(
|
||||
const GfxContext& pctx,
|
||||
Camera* camera,
|
||||
Assets* assets,
|
||||
float daytime,
|
||||
float fog)
|
||||
{
|
||||
const Viewport& viewport = pctx.getViewport();
|
||||
int width = viewport.getWidth();
|
||||
int height = viewport.getHeight();
|
||||
|
||||
drawBackground(camera, assets, width, height);
|
||||
|
||||
GfxContext ctx = pctx.sub();
|
||||
ctx.setBlendMode(blendmode::addition);
|
||||
|
||||
Shader* shader = assets->getShader("ui3d");
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_projview", camera->getProjView(false));
|
||||
shader->uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
batch3d->begin();
|
||||
|
||||
float angle = daytime * M_PI * 2;
|
||||
float opacity = glm::pow(1.0f-fog, 7.0f);
|
||||
|
||||
for (auto& sprite : sprites) {
|
||||
batch3d->texture(assets->getTexture(sprite.texture));
|
||||
|
||||
float sangle = daytime * M_PI*2 + sprite.phase;
|
||||
float distance = sprite.distance;
|
||||
|
||||
glm::vec3 pos(-cos(sangle)*distance, sin(sangle)*distance, 0);
|
||||
glm::vec3 up(-sin(-sangle), cos(-sangle), 0.0f);
|
||||
glm::vec4 tint (1,1,1, opacity);
|
||||
if (!sprite.emissive) {
|
||||
tint *= 0.6f+cos(angle)*0.4;
|
||||
}
|
||||
batch3d->sprite(pos, glm::vec3(0, 0, 1),
|
||||
up, 1, 1, UVRegion(), tint);
|
||||
}
|
||||
|
||||
drawStars(angle, opacity);
|
||||
}
|
||||
|
||||
void Skybox::refresh(const GfxContext& pctx, float t, float mie, uint quality) {
|
||||
GfxContext ctx = pctx.sub();
|
||||
ctx.setDepthMask(false);
|
||||
ctx.setDepthTest(false);
|
||||
ctx.setFramebuffer(fbo.get());
|
||||
ctx.setViewport(Viewport(size, size));
|
||||
|
||||
auto cubemap = dynamic_cast<Cubemap*>(fbo->getTexture());
|
||||
|
||||
ready = true;
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
cubemap->bind();
|
||||
shader->use();
|
||||
|
||||
const glm::vec3 xaxs[] = {
|
||||
{0.0f, 0.0f, -1.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
{-1.0f, 0.0f, 0.0f},
|
||||
|
||||
{-1.0f, 0.0f, 0.0f},
|
||||
{-1.0f, 0.0f, 0.0f},
|
||||
{1.0f, 0.0f, 0.0f},
|
||||
};
|
||||
const glm::vec3 yaxs[] = {
|
||||
{0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f},
|
||||
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
{0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.0f},
|
||||
};
|
||||
|
||||
const glm::vec3 zaxs[] = {
|
||||
{1.0f, 0.0f, 0.0f},
|
||||
{-1.0f, 0.0f, 0.0f},
|
||||
{0.0f, -1.0f, 0.0f},
|
||||
|
||||
{0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
};
|
||||
t *= M_PI*2.0f;
|
||||
|
||||
shader->uniform1i("u_quality", quality);
|
||||
shader->uniform1f("u_mie", mie);
|
||||
shader->uniform1f("u_fog", mie - 1.0f);
|
||||
shader->uniform3f("u_lightDir", glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f)));
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0);
|
||||
shader->uniform3f("u_xaxis", xaxs[face]);
|
||||
shader->uniform3f("u_yaxis", yaxs[face]);
|
||||
shader->uniform3f("u_zaxis", zaxs[face]);
|
||||
mesh->draw();
|
||||
}
|
||||
cubemap->unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void Skybox::bind() const {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
fbo->getTexture()->bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void Skybox::unbind() const {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
fbo->getTexture()->unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#ifndef FRONTEND_GRAPHICS_SKYBOX_H_
|
||||
#define FRONTEND_GRAPHICS_SKYBOX_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../../typedefs.h"
|
||||
#include "../../maths/fastmaths.h"
|
||||
#include "../../graphics/GfxContext.h"
|
||||
|
||||
class Mesh;
|
||||
class Shader;
|
||||
class Assets;
|
||||
class Camera;
|
||||
class Batch3D;
|
||||
class Framebuffer;
|
||||
|
||||
struct skysprite {
|
||||
std::string texture;
|
||||
float phase;
|
||||
float distance;
|
||||
bool emissive;
|
||||
};
|
||||
|
||||
class Skybox {
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
uint size;
|
||||
Shader* shader;
|
||||
bool ready = false;
|
||||
FastRandom random;
|
||||
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
std::unique_ptr<Batch3D> batch3d;
|
||||
std::vector<skysprite> sprites;
|
||||
|
||||
void drawStars(float angle, float opacity);
|
||||
void drawBackground(Camera* camera, Assets* assets, int width, int height);
|
||||
public:
|
||||
Skybox(uint size, Shader* shader);
|
||||
~Skybox();
|
||||
|
||||
void draw(
|
||||
const GfxContext& pctx,
|
||||
Camera* camera,
|
||||
Assets* assets,
|
||||
float daytime,
|
||||
float fog
|
||||
);
|
||||
|
||||
void refresh(const GfxContext& pctx, float t, float mie, uint quality);
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
bool isReady() const {
|
||||
return ready;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // FRONTEND_GRAPHICS_SKYBOX_H_
|
||||
@@ -1,192 +0,0 @@
|
||||
#include "GUI.h"
|
||||
#include "UINode.h"
|
||||
#include "containers.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#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 namespace gui;
|
||||
|
||||
GUI::GUI() {
|
||||
container = std::make_shared<Container>(glm::vec2(1000));
|
||||
uicamera = std::make_unique<Camera>(glm::vec3(), Window::height);
|
||||
uicamera->perspective = false;
|
||||
uicamera->flipped = true;
|
||||
|
||||
menu = std::make_shared<PagesControl>();
|
||||
container->add(menu);
|
||||
container->setScrollable(false);
|
||||
}
|
||||
|
||||
GUI::~GUI() {
|
||||
}
|
||||
|
||||
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->setHover(false);
|
||||
}
|
||||
if (hover) {
|
||||
hover->setHover(true);
|
||||
if (Events::scroll) {
|
||||
hover->scrolled(Events::scroll);
|
||||
}
|
||||
}
|
||||
this->hover = hover;
|
||||
|
||||
if (Events::jclicked(mousecode::BUTTON_1)) {
|
||||
if (pressed == nullptr && this->hover) {
|
||||
pressed = hover;
|
||||
pressed->click(this, Events::cursor.x, Events::cursor.y);
|
||||
if (focus && focus != pressed) {
|
||||
focus->defocus();
|
||||
}
|
||||
if (focus != pressed) {
|
||||
focus = pressed;
|
||||
focus->onFocus(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this->hover == nullptr && focus) {
|
||||
focus->defocus();
|
||||
focus = nullptr;
|
||||
}
|
||||
} else if (pressed) {
|
||||
pressed->mouseRelease(this, Events::cursor.x, Events::cursor.y);
|
||||
pressed = nullptr;
|
||||
}
|
||||
|
||||
if (hover) {//WTF?! FIXME
|
||||
for (int i = static_cast<int>(mousecode::BUTTON_1); i < static_cast<int>(mousecode::BUTTON_1)+12; i++) {
|
||||
if (Events::jclicked(i)) {
|
||||
hover->clicked(this, static_cast<mousecode>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Processing user input and UI logic
|
||||
* @param delta delta time
|
||||
*/
|
||||
void GUI::act(float delta) {
|
||||
while (!postRunnables.empty()) {
|
||||
runnable callback = postRunnables.back();
|
||||
postRunnables.pop();
|
||||
callback();
|
||||
}
|
||||
|
||||
container->setSize(glm::vec2(Window::width, Window::height));
|
||||
container->act(delta);
|
||||
auto prevfocus = focus;
|
||||
|
||||
if (!Events::_cursor_locked) {
|
||||
actMouse(delta);
|
||||
}
|
||||
|
||||
if (focus) {
|
||||
if (Events::jpressed(keycode::ESCAPE)) {
|
||||
focus->defocus();
|
||||
focus = nullptr;
|
||||
} else {
|
||||
for (auto codepoint : Events::codepoints) {
|
||||
focus->typed(codepoint);
|
||||
}
|
||||
for (auto key : Events::pressedKeys) {
|
||||
focus->keyPressed(key);
|
||||
}
|
||||
|
||||
if (!Events::_cursor_locked) {
|
||||
if (Events::clicked(mousecode::BUTTON_1)) {
|
||||
if (Events::jclicked(mousecode::BUTTON_1) ||
|
||||
Events::delta.x || Events::delta.y)
|
||||
{
|
||||
focus->mouseMove(this, Events::cursor.x, Events::cursor.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (focus && !focus->isFocused()) {
|
||||
focus = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::draw(const GfxContext* pctx, Assets* assets) {
|
||||
auto& viewport = pctx->getViewport();
|
||||
glm::vec2 wsize = viewport.size();
|
||||
|
||||
menu->setPos((wsize - menu->getSize()) / 2.0f);
|
||||
uicamera->setFov(wsize.y);
|
||||
|
||||
Shader* uishader = assets->getShader("ui");
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjection()*uicamera->getView());
|
||||
|
||||
pctx->getBatch2D()->begin();
|
||||
container->draw(pctx, assets);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> GUI::getFocused() const {
|
||||
return focus;
|
||||
}
|
||||
|
||||
bool GUI::isFocusCaught() const {
|
||||
return focus && focus->isFocuskeeper();
|
||||
}
|
||||
|
||||
void GUI::add(std::shared_ptr<UINode> node) {
|
||||
container->add(node);
|
||||
}
|
||||
|
||||
void GUI::remove(std::shared_ptr<UINode> node) noexcept {
|
||||
container->remove(node);
|
||||
}
|
||||
|
||||
void GUI::store(std::string name, std::shared_ptr<UINode> node) {
|
||||
storage[name] = node;
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> GUI::get(std::string name) noexcept {
|
||||
auto found = storage.find(name);
|
||||
if (found == storage.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
void GUI::remove(std::string name) noexcept {
|
||||
storage.erase(name);
|
||||
}
|
||||
|
||||
void GUI::setFocus(std::shared_ptr<UINode> node) {
|
||||
if (focus) {
|
||||
focus->defocus();
|
||||
}
|
||||
focus = node;
|
||||
if (focus) {
|
||||
focus->onFocus(this);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Container> GUI::getContainer() const {
|
||||
return container;
|
||||
}
|
||||
|
||||
void GUI::postRunnable(runnable callback) {
|
||||
postRunnables.push(callback);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
#ifndef FRONTEND_GUI_GUI_H_
|
||||
#define FRONTEND_GUI_GUI_H_
|
||||
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../../delegates.h"
|
||||
|
||||
class GfxContext;
|
||||
class Assets;
|
||||
class Camera;
|
||||
|
||||
/*
|
||||
Some info about padding and margin.
|
||||
Padding is element inner space, margin is outer
|
||||
glm::vec4 usage:
|
||||
x - left
|
||||
y - top
|
||||
z - right
|
||||
w - bottom
|
||||
|
||||
Outer element
|
||||
+======================================================================+
|
||||
| . . . . |
|
||||
| .padding.y . . . |
|
||||
| padding.x . . . . padding.z |
|
||||
|- - - - - - + - - - - - + - - - - - - - - - -+- - - - - + - - - - - - |
|
||||
| . . . . |
|
||||
| . .margin.y . . |
|
||||
| .margin.x . . margin.z. |
|
||||
|- - - - - - + - - - - - +====================+- - - - - + - - - - - - |
|
||||
| . | Inner element | . |
|
||||
|- - - - - - + - - - - - +====================+- - - - - + - - - - - - |
|
||||
| . . . . |
|
||||
| . .margin.w . . |
|
||||
| . . . . |
|
||||
|- - - - - - + - - - - - + - - - - - - - - - -+- - - - - + - - - - - - |
|
||||
| . . . . |
|
||||
| .padding.w . . . |
|
||||
| . . . . |
|
||||
+======================================================================+
|
||||
*/
|
||||
|
||||
namespace gui {
|
||||
class UINode;
|
||||
class Container;
|
||||
class PagesControl;
|
||||
|
||||
/// @brief The main UI controller
|
||||
class GUI {
|
||||
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;
|
||||
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
std::shared_ptr<PagesControl> menu;
|
||||
std::queue<runnable> postRunnables;
|
||||
void actMouse(float delta);
|
||||
public:
|
||||
GUI();
|
||||
~GUI();
|
||||
|
||||
/// @brief Get the main menu (PagesControl) node
|
||||
std::shared_ptr<PagesControl> getMenu();
|
||||
|
||||
/// @brief Get current focused node
|
||||
/// @return focused node or nullptr
|
||||
std::shared_ptr<UINode> getFocused() const;
|
||||
|
||||
/// @brief Check if all user input is caught by some element like TextBox
|
||||
bool isFocusCaught() const;
|
||||
|
||||
/// @brief Main input handling and logic update method
|
||||
/// @param delta delta time
|
||||
void act(float delta);
|
||||
|
||||
/// @brief Draw all visible elements on main container
|
||||
/// @param pctx parent graphics context
|
||||
/// @param assets active assets storage
|
||||
void draw(const GfxContext* pctx, Assets* assets);
|
||||
|
||||
/// @brief Add element to the main container
|
||||
/// @param node UI element
|
||||
void add(std::shared_ptr<UINode> node);
|
||||
|
||||
/// @brief Add element to the main container
|
||||
/// @param node UI element
|
||||
/// @param coord element position within the main container
|
||||
void add(std::shared_ptr<UINode> node, glm::vec2 coord);
|
||||
|
||||
/// @brief Remove node from the main container
|
||||
void remove(std::shared_ptr<UINode> node) noexcept;
|
||||
|
||||
/// @brief 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);
|
||||
|
||||
/// @brief 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;
|
||||
|
||||
/// @brief Remove node from the GUI nodes dictionary
|
||||
/// @param name node key
|
||||
void remove(std::string name) noexcept;
|
||||
|
||||
/// @brief Set node as focused
|
||||
/// @param node new focused node or nullptr to remove focus
|
||||
void setFocus(std::shared_ptr<UINode> node);
|
||||
|
||||
/// @brief Get the main container
|
||||
/// @deprecated
|
||||
std::shared_ptr<Container> getContainer() const;
|
||||
|
||||
void postRunnable(runnable callback);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_GUI_H_
|
||||
@@ -1,239 +0,0 @@
|
||||
#include "UINode.h"
|
||||
|
||||
#include "../../graphics/Batch2D.h"
|
||||
|
||||
using gui::UINode;
|
||||
using gui::Align;
|
||||
|
||||
UINode::UINode(glm::vec2 size) : size(size) {
|
||||
}
|
||||
|
||||
UINode::~UINode() {
|
||||
}
|
||||
|
||||
bool UINode::isVisible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
void UINode::setVisible(bool flag) {
|
||||
visible = flag;
|
||||
}
|
||||
|
||||
Align UINode::getAlign() const {
|
||||
return align;
|
||||
}
|
||||
|
||||
void UINode::setAlign(Align align) {
|
||||
this->align = align;
|
||||
}
|
||||
|
||||
void UINode::setHover(bool flag) {
|
||||
hover = flag;
|
||||
}
|
||||
|
||||
bool UINode::isHover() const {
|
||||
return hover;
|
||||
}
|
||||
|
||||
void UINode::setParent(UINode* node) {
|
||||
parent = node;
|
||||
}
|
||||
|
||||
UINode* UINode::getParent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
void UINode::click(GUI*, int x, int y) {
|
||||
pressed = true;
|
||||
}
|
||||
|
||||
void UINode::mouseRelease(GUI*, int x, int y) {
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
bool UINode::isPressed() const {
|
||||
return pressed;
|
||||
}
|
||||
|
||||
void UINode::defocus() {
|
||||
focused = false;
|
||||
}
|
||||
|
||||
bool UINode::isFocused() const {
|
||||
return focused;
|
||||
}
|
||||
|
||||
bool UINode::isInside(glm::vec2 point) {
|
||||
glm::vec2 pos = calcPos();
|
||||
glm::vec2 size = getSize();
|
||||
return (point.x >= pos.x && point.y >= pos.y &&
|
||||
point.x < pos.x + size.x && point.y < pos.y + size.y);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
return isInside(point) ? self : nullptr;
|
||||
}
|
||||
|
||||
bool UINode::isInteractive() const {
|
||||
return interactive && isVisible();
|
||||
}
|
||||
|
||||
void UINode::setInteractive(bool flag) {
|
||||
interactive = flag;
|
||||
}
|
||||
|
||||
void UINode::setResizing(bool flag) {
|
||||
resizing = flag;
|
||||
}
|
||||
|
||||
bool UINode::isResizing() const {
|
||||
return resizing;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::calcPos() const {
|
||||
if (parent) {
|
||||
return pos + parent->calcPos() + parent->contentOffset();
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
void UINode::scrolled(int value) {
|
||||
if (parent) {
|
||||
parent->scrolled(value);
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setPos(glm::vec2 pos) {
|
||||
this->pos = pos;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::getPos() const {
|
||||
return pos;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void UINode::setSize(glm::vec2 size) {
|
||||
this->size = glm::vec2(
|
||||
glm::max(minSize.x, size.x), glm::max(minSize.y, size.y)
|
||||
);
|
||||
}
|
||||
|
||||
glm::vec2 UINode::getMinSize() const {
|
||||
return minSize;
|
||||
}
|
||||
|
||||
void UINode::setMinSize(glm::vec2 minSize) {
|
||||
this->minSize = minSize;
|
||||
setSize(getSize());
|
||||
}
|
||||
|
||||
void UINode::setColor(glm::vec4 color) {
|
||||
this->color = color;
|
||||
this->hoverColor = color;
|
||||
}
|
||||
|
||||
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) {
|
||||
setPos(positionfunc());
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setGravity(Gravity gravity) {
|
||||
if (gravity == Gravity::none) {
|
||||
setPositionFunc(nullptr);
|
||||
return;
|
||||
}
|
||||
setPositionFunc([this, gravity](){
|
||||
auto parent = getParent();
|
||||
if (parent == nullptr) {
|
||||
return getPos();
|
||||
}
|
||||
glm::vec4 margin = getMargin();
|
||||
glm::vec2 size = getSize();
|
||||
glm::vec2 parentSize = parent->getSize();
|
||||
|
||||
float x = 0.0f, y = 0.0f;
|
||||
switch (gravity) {
|
||||
case Gravity::top_left:
|
||||
case Gravity::center_left:
|
||||
case Gravity::bottom_left: x = parentSize.x+margin.x; break;
|
||||
case Gravity::top_center:
|
||||
case Gravity::center_center:
|
||||
case Gravity::bottom_center: x = (parentSize.x-size.x)/2.0f; break;
|
||||
case Gravity::top_right:
|
||||
case Gravity::center_right:
|
||||
case Gravity::bottom_right: x = parentSize.x-size.x-margin.z; break;
|
||||
default: break;
|
||||
}
|
||||
switch (gravity) {
|
||||
case Gravity::top_left:
|
||||
case Gravity::top_center:
|
||||
case Gravity::top_right: y = parentSize.y+margin.y; break;
|
||||
case Gravity::center_left:
|
||||
case Gravity::center_center:
|
||||
case Gravity::center_right: y = (parentSize.y-size.y)/2.0f; break;
|
||||
case Gravity::bottom_left:
|
||||
case Gravity::bottom_center:
|
||||
case Gravity::bottom_right: y = parentSize.y-size.y-margin.w; break;
|
||||
default: break;
|
||||
}
|
||||
return glm::vec2(x, y);
|
||||
});
|
||||
|
||||
if (parent) {
|
||||
reposition();
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
#ifndef FRONTEND_GUI_UINODE_H_
|
||||
#define FRONTEND_GUI_UINODE_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "../../delegates.h"
|
||||
#include "../../window/input.h"
|
||||
|
||||
class GfxContext;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
class UINode;
|
||||
class GUI;
|
||||
|
||||
using onaction = std::function<void(GUI*)>;
|
||||
using onnumberchange = std::function<void(GUI*, double)>;
|
||||
|
||||
enum class Align {
|
||||
left, center, right,
|
||||
top=left, bottom=right,
|
||||
};
|
||||
|
||||
enum class Gravity {
|
||||
none,
|
||||
|
||||
top_left,
|
||||
top_center,
|
||||
top_right,
|
||||
|
||||
center_left,
|
||||
center_center,
|
||||
center_right,
|
||||
|
||||
bottom_left,
|
||||
bottom_center,
|
||||
bottom_right
|
||||
};
|
||||
|
||||
/// @brief Base abstract class for all UI elements
|
||||
class UINode {
|
||||
/// @brief element identifier used for direct access in UiDocument
|
||||
std::string id = "";
|
||||
protected:
|
||||
/// @brief element position within the parent element
|
||||
glm::vec2 pos {0.0f};
|
||||
/// @brief element size (width, height)
|
||||
glm::vec2 size;
|
||||
/// @brief minimal element size
|
||||
glm::vec2 minSize {1.0f};
|
||||
/// @brief element primary color (background-color or text-color if label)
|
||||
glm::vec4 color {1.0f};
|
||||
/// @brief element color when mouse is over it
|
||||
glm::vec4 hoverColor {1.0f};
|
||||
/// @brief element margin (only supported for Panel sub-nodes)
|
||||
glm::vec4 margin {1.0f};
|
||||
/// @brief is element visible
|
||||
bool visible = true;
|
||||
/// @brief is mouse over the element
|
||||
bool hover = false;
|
||||
/// @brief is mouse has been pressed over the element and not released yet
|
||||
bool pressed = false;
|
||||
/// @brief is element focused
|
||||
bool focused = false;
|
||||
/// @brief is element opaque for cursor interaction
|
||||
bool interactive = true;
|
||||
/// @brief does the element support resizing by parent elements
|
||||
bool resizing = true;
|
||||
/// @brief z-index property specifies the stack order of an element
|
||||
int zindex = 0;
|
||||
/// @brief element content alignment (supported by Label only)
|
||||
Align align = Align::left;
|
||||
/// @brief parent element
|
||||
UINode* parent = nullptr;
|
||||
/// @brief position supplier for the element (called on parent element size update)
|
||||
vec2supplier positionfunc = nullptr;
|
||||
|
||||
UINode(glm::vec2 size);
|
||||
public:
|
||||
virtual ~UINode();
|
||||
|
||||
/// @brief Called every frame for all visible elements
|
||||
/// @param delta delta timУ
|
||||
virtual void act(float delta) {};
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) = 0;
|
||||
|
||||
virtual void setVisible(bool flag);
|
||||
bool isVisible() const;
|
||||
|
||||
virtual void setAlign(Align align);
|
||||
Align getAlign() const;
|
||||
|
||||
virtual void setHover(bool flag);
|
||||
bool isHover() const;
|
||||
|
||||
virtual void setParent(UINode* node);
|
||||
UINode* getParent() const;
|
||||
|
||||
/// @brief Set element color (doesn't affect inner elements).
|
||||
/// Also replaces hover color to avoid adding extra properties
|
||||
virtual void setColor(glm::vec4 newColor);
|
||||
|
||||
/// @brief Get element color
|
||||
/// @return (float R,G,B,A in range [0.0, 1.0])
|
||||
glm::vec4 getColor() const;
|
||||
|
||||
virtual void setHoverColor(glm::vec4 newColor);
|
||||
glm::vec4 getHoverColor() const;
|
||||
|
||||
virtual void setMargin(glm::vec4 margin);
|
||||
glm::vec4 getMargin() const;
|
||||
|
||||
/// @brief Specifies the stack order of an element
|
||||
/// @attention Is not supported by Panel
|
||||
virtual void setZIndex(int idx);
|
||||
|
||||
/// @brief Get element z-index
|
||||
int getZIndex() const;
|
||||
|
||||
virtual void onFocus(GUI*) {focused = true;}
|
||||
virtual void click(GUI*, int x, int y);
|
||||
virtual void clicked(GUI*, mousecode 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;
|
||||
void defocus();
|
||||
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(keycode 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);};
|
||||
/* Calculate screen position of the element */
|
||||
virtual glm::vec2 calcPos() const;
|
||||
virtual void setPos(glm::vec2 pos);
|
||||
virtual glm::vec2 getPos() 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 pos from positionfunc if assigned */
|
||||
void reposition();
|
||||
|
||||
virtual void setGravity(Gravity gravity);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_UINODE_H_
|
||||
@@ -1,300 +0,0 @@
|
||||
#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 size) : UINode(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 (auto node : nodes) {
|
||||
if (node->isVisible()) {
|
||||
node->act(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());
|
||||
}
|
||||
|
||||
void Container::scrolled(int value) {
|
||||
int diff = (actualLength-getSize().y);
|
||||
if (scroll < 0 && diff <= 0) {
|
||||
scroll = 0;
|
||||
}
|
||||
if (diff > 0 && scrollable) {
|
||||
scroll += value * scrollStep;
|
||||
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 pos = calcPos();
|
||||
glm::vec2 size = getSize();
|
||||
drawBackground(pctx, assets);
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->flush();
|
||||
{
|
||||
GfxContext ctx = pctx->sub();
|
||||
ctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
|
||||
for (auto node : nodes) {
|
||||
glm::vec2 nodePos = node->calcPos();
|
||||
glm::vec2 nodeSize = node->getSize();
|
||||
if (node->isVisible() && (nodePos.y + nodeSize.y > pos.y && nodePos.y < pos.y + size.y &&
|
||||
nodePos.x + nodeSize.x > pos.x && nodePos.x < pos.x + size.x))
|
||||
node->draw(pctx, assets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Container::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
if (color.a <= 0.0f)
|
||||
return;
|
||||
glm::vec2 pos = calcPos();
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->setColor(color);
|
||||
batch->rect(pos.x, pos.y, size.x, size.y);
|
||||
}
|
||||
|
||||
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 pos) {
|
||||
node->setPos(pos);
|
||||
add(node);
|
||||
}
|
||||
|
||||
void Container::remove(std::shared_ptr<UINode> selected) {
|
||||
selected->setParent(nullptr);
|
||||
nodes.erase(std::remove(nodes.begin(), nodes.end(), 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();
|
||||
}
|
||||
scrolled(0);
|
||||
}
|
||||
|
||||
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(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->setPos(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->setPos(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(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,112 +0,0 @@
|
||||
#ifndef FRONTEND_GUI_CONTAINERS_H_
|
||||
#define FRONTEND_GUI_CONTAINERS_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "UINode.h"
|
||||
|
||||
class Batch2D;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
using ontimeout = std::function<void()>;
|
||||
|
||||
struct IntervalEvent {
|
||||
ontimeout callback;
|
||||
float interval;
|
||||
float timer;
|
||||
// -1 - infinity, 1 - one time event
|
||||
int repeat;
|
||||
};
|
||||
|
||||
enum class Orientation { vertical, horizontal };
|
||||
|
||||
class Container : public UINode {
|
||||
protected:
|
||||
std::vector<std::shared_ptr<UINode>> nodes;
|
||||
std::vector<IntervalEvent> intervalEvents;
|
||||
int scroll = 0;
|
||||
int scrollStep = 40;
|
||||
int actualLength = 0;
|
||||
bool scrollable = true;
|
||||
public:
|
||||
Container(glm::vec2 size);
|
||||
|
||||
virtual void act(float delta) 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 add(std::shared_ptr<UINode> node);
|
||||
virtual void add(std::shared_ptr<UINode> node, glm::vec2 pos);
|
||||
virtual void remove(std::shared_ptr<UINode> node);
|
||||
virtual void scrolled(int value) override;
|
||||
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;
|
||||
glm::vec4 padding {2.0f};
|
||||
float interval = 2.0f;
|
||||
int maxLength = 0;
|
||||
public:
|
||||
Panel(
|
||||
glm::vec2 size,
|
||||
glm::vec4 padding=glm::vec4(2.0f),
|
||||
float interval=2.0f
|
||||
);
|
||||
virtual ~Panel();
|
||||
|
||||
virtual void cropToContent();
|
||||
|
||||
virtual void setOrientation(Orientation orientation);
|
||||
Orientation getOrientation() const;
|
||||
|
||||
virtual void add(std::shared_ptr<UINode> node) override;
|
||||
|
||||
virtual void refresh() override;
|
||||
|
||||
virtual void setMaxLength(int value);
|
||||
int getMaxLength() const;
|
||||
|
||||
virtual void setPadding(glm::vec4 padding);
|
||||
glm::vec4 getPadding() const;
|
||||
};
|
||||
|
||||
struct Page {
|
||||
std::shared_ptr<UINode> panel = nullptr;
|
||||
|
||||
~Page() {
|
||||
panel = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class PagesControl : public Container {
|
||||
protected:
|
||||
std::unordered_map<std::string, Page> pages;
|
||||
std::stack<std::string> pageStack;
|
||||
Page current;
|
||||
std::string curname = "";
|
||||
public:
|
||||
PagesControl();
|
||||
|
||||
bool has(std::string name);
|
||||
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& getCurrent();
|
||||
};
|
||||
}
|
||||
#endif // FRONTEND_GUI_CONTAINERS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,394 +0,0 @@
|
||||
#ifndef FRONTEND_GUI_CONTROLS_H_
|
||||
#define FRONTEND_GUI_CONTROLS_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "GUI.h"
|
||||
#include "UINode.h"
|
||||
#include "containers.h"
|
||||
#include "../../window/input.h"
|
||||
#include "../../delegates.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
class Batch2D;
|
||||
class Assets;
|
||||
class Font;
|
||||
|
||||
namespace gui {
|
||||
class Label : public UINode {
|
||||
protected:
|
||||
std::wstring text;
|
||||
std::string fontName;
|
||||
wstringsupplier supplier = nullptr;
|
||||
uint lines = 1;
|
||||
float lineInterval = 1.5f;
|
||||
Align valign = Align::center;
|
||||
|
||||
bool multiline = false;
|
||||
|
||||
// runtime values
|
||||
|
||||
/// @brief Text offset relative to label position
|
||||
/// (last calculated alignment)
|
||||
glm::vec2 textOffset{ 0.f, 0.f };
|
||||
|
||||
/// @brief Text line height multiplied by line interval
|
||||
int totalLineHeight = 1;
|
||||
public:
|
||||
Label(std::string text, std::string fontName="normal");
|
||||
Label(std::wstring text, std::string fontName="normal");
|
||||
|
||||
virtual void setText(std::wstring text);
|
||||
const std::wstring& getText() const;
|
||||
|
||||
virtual void setFontName(std::string name);
|
||||
virtual const std::string& getFontName() const;
|
||||
|
||||
/// @brief Set text vertical alignment (default value: center)
|
||||
/// @param align Align::top / Align::center / Align::bottom
|
||||
virtual void setVerticalAlign(Align align);
|
||||
virtual Align getVerticalAlign() const;
|
||||
|
||||
/// @brief Get line height multiplier used for multiline labels
|
||||
/// (default value: 1.5)
|
||||
virtual float getLineInterval() const;
|
||||
|
||||
/// @brief Set line height multiplier used for multiline labels
|
||||
virtual void setLineInterval(float interval);
|
||||
|
||||
virtual void setTextOffset(glm::vec2 offset);
|
||||
|
||||
/// @return the difference between the height of the text
|
||||
// and the height of the label depending on the alignment
|
||||
virtual float getVerticalAlignmentOffset() const;
|
||||
|
||||
/// @brief Get position of the text relative to label position
|
||||
/// @return offset
|
||||
virtual glm::vec2 getTextOffset() const;
|
||||
|
||||
/// @brief Get Y position of the line relative to label position
|
||||
/// @param line target line index
|
||||
/// @return Y offset
|
||||
virtual int getLineYOffset(uint line) const;
|
||||
|
||||
/// @brief Get position of line start in the text
|
||||
/// @param line target line index
|
||||
/// @return position in the text [0..length]
|
||||
virtual size_t getTextLineOffset(uint line) const;
|
||||
|
||||
/// @brief Get line index by its Y offset relative to label position
|
||||
/// @param offset target Y offset
|
||||
/// @return line index [0..+]
|
||||
virtual uint getLineByYOffset(int offset) const;
|
||||
virtual uint getLineByTextIndex(size_t index) const;
|
||||
virtual uint getLinesNumber() const;
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void textSupplier(wstringsupplier supplier);
|
||||
|
||||
virtual void setMultiline(bool multiline);
|
||||
virtual bool isMultiline() const;
|
||||
};
|
||||
|
||||
class Image : public UINode {
|
||||
protected:
|
||||
std::string texture;
|
||||
bool autoresize = false;
|
||||
public:
|
||||
Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
|
||||
|
||||
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 pressedColor {0.0f, 0.0f, 0.0f, 0.95f};
|
||||
std::vector<onaction> actions;
|
||||
std::shared_ptr<Label> label = nullptr;
|
||||
public:
|
||||
Button(std::shared_ptr<UINode> content,
|
||||
glm::vec4 padding=glm::vec4(2.0f));
|
||||
|
||||
Button(std::wstring text,
|
||||
glm::vec4 padding,
|
||||
onaction action,
|
||||
glm::vec2 size=glm::vec2(-1));
|
||||
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual Button* listenAction(onaction action);
|
||||
|
||||
virtual Align getTextAlign() const;
|
||||
virtual void setTextAlign(Align align);
|
||||
|
||||
virtual void setText(std::wstring text);
|
||||
virtual std::wstring getText() const;
|
||||
|
||||
virtual glm::vec4 getPressedColor() const;
|
||||
virtual void setPressedColor(glm::vec4 color);
|
||||
|
||||
virtual Button* textSupplier(wstringsupplier supplier);
|
||||
|
||||
virtual void refresh() override;
|
||||
};
|
||||
|
||||
class RichButton : public Container {
|
||||
protected:
|
||||
glm::vec4 pressedColor {0.0f, 0.0f, 0.0f, 0.95f};
|
||||
std::vector<onaction> actions;
|
||||
public:
|
||||
RichButton(glm::vec2 size);
|
||||
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual RichButton* listenAction(onaction action);
|
||||
};
|
||||
|
||||
class TextBox : public Panel {
|
||||
protected:
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||
std::shared_ptr<Label> label;
|
||||
std::wstring input;
|
||||
std::wstring placeholder;
|
||||
wstringsupplier supplier = nullptr;
|
||||
wstringconsumer consumer = nullptr;
|
||||
wstringchecker validator = nullptr;
|
||||
runnable onEditStart = nullptr;
|
||||
bool valid = true;
|
||||
/// @brief text input pointer, value may be greather than text length
|
||||
uint caret = 0;
|
||||
/// @brief actual local (line) position of the caret on vertical move
|
||||
uint maxLocalCaret = 0;
|
||||
int textInitX;
|
||||
/// @brief last time of the caret was moved (used for blink animation)
|
||||
double caretLastMove = 0.0;
|
||||
Font* font = nullptr;
|
||||
|
||||
size_t selectionStart = 0;
|
||||
size_t selectionEnd = 0;
|
||||
size_t selectionOrigin = 0;
|
||||
|
||||
bool multiline = false;
|
||||
bool editable = true;
|
||||
|
||||
size_t normalizeIndex(int index);
|
||||
|
||||
int calcIndexAt(int x, int y) const;
|
||||
void paste(const std::wstring& text);
|
||||
void erase(size_t start, size_t length);
|
||||
bool eraseSelected();
|
||||
void resetSelection();
|
||||
void extendSelection(int index);
|
||||
size_t getLineLength(uint line) const;
|
||||
|
||||
/// @brief Get total length of the selection
|
||||
size_t getSelectionLength() const;
|
||||
|
||||
/// @brief Set maxLocalCaret to local (line) caret position
|
||||
void resetMaxLocalCaret();
|
||||
|
||||
void performEditingKeyboardEvents(keycode key);
|
||||
public:
|
||||
TextBox(
|
||||
std::wstring placeholder,
|
||||
glm::vec4 padding=glm::vec4(4.0f)
|
||||
);
|
||||
|
||||
virtual void setTextSupplier(wstringsupplier supplier);
|
||||
|
||||
/// @brief Consumer called on stop editing text (textbox defocus)
|
||||
/// @param consumer std::wstring consumer function
|
||||
virtual void setTextConsumer(wstringconsumer consumer);
|
||||
|
||||
/// @brief Text validator called while text editing and returns true if
|
||||
/// text is valid
|
||||
/// @param validator std::wstring consumer returning boolean
|
||||
virtual void setTextValidator(wstringchecker validator);
|
||||
|
||||
virtual void setFocusedColor(glm::vec4 color);
|
||||
virtual glm::vec4 getFocusedColor() const;
|
||||
|
||||
/// @brief Set color of textbox marked by validator as invalid
|
||||
virtual void setErrorColor(glm::vec4 color);
|
||||
|
||||
/// @brief Get color of textbox marked by validator as invalid
|
||||
virtual glm::vec4 getErrorColor() const;
|
||||
|
||||
/// @brief Get TextBox content text or placeholder if empty
|
||||
virtual std::wstring getText() const;
|
||||
|
||||
/// @brief Set TextBox content text
|
||||
virtual void setText(std::wstring value);
|
||||
|
||||
/// @brief Get text placeholder
|
||||
virtual std::wstring getPlaceholder() const;
|
||||
|
||||
/// @brief Set text placeholder
|
||||
/// @param text will be used instead of empty
|
||||
virtual void setPlaceholder(const std::wstring& text);
|
||||
|
||||
/// @brief Get selected text
|
||||
virtual std::wstring getSelection() const;
|
||||
|
||||
/// @brief Get current caret position in text
|
||||
/// @return integer in range [0, text.length()]
|
||||
virtual uint getCaret() const;
|
||||
|
||||
/// @brief Set caret position in the text
|
||||
/// @param position integer in range [0, text.length()]
|
||||
virtual void setCaret(uint position);
|
||||
|
||||
/// @brief Select part of the text
|
||||
/// @param start index of the first selected character
|
||||
/// @param end index of the last selected character + 1
|
||||
virtual void select(int start, int end);
|
||||
|
||||
/// @brief Check text with validator set with setTextValidator
|
||||
/// @return true if text is valid
|
||||
virtual bool validate();
|
||||
|
||||
virtual void setValid(bool valid);
|
||||
virtual bool isValid() const;
|
||||
|
||||
/// @brief Enable/disable multiline mode
|
||||
virtual void setMultiline(bool multiline);
|
||||
|
||||
/// @brief Check if multiline mode is enabled
|
||||
virtual bool isMultiline() const;
|
||||
|
||||
/// @brief Enable/disable text editing feature
|
||||
virtual void setEditable(bool editable);
|
||||
|
||||
/// @brief Check if text editing feature is enabled
|
||||
virtual bool isEditable() const;
|
||||
|
||||
/// @brief Set runnable called on textbox focus
|
||||
virtual void setOnEditStart(runnable oneditstart);
|
||||
|
||||
virtual void onFocus(GUI*) override;
|
||||
virtual void click(GUI*, int, int) override;
|
||||
virtual void mouseMove(GUI*, int x, int y) override;
|
||||
virtual void scrolled(int value) override;
|
||||
virtual bool isFocuskeeper() const override {return true;}
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual void typed(unsigned int codepoint) override;
|
||||
virtual void keyPressed(keycode key) override;
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) 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};
|
||||
std::shared_ptr<Label> label;
|
||||
Binding& binding;
|
||||
public:
|
||||
InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f));
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void clicked(GUI*, mousecode button) override;
|
||||
virtual void keyPressed(keycode key) override;
|
||||
virtual bool isFocuskeeper() const override {return true;}
|
||||
};
|
||||
|
||||
class TrackBar : public UINode {
|
||||
protected:
|
||||
glm::vec4 trackColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
doublesupplier supplier = nullptr;
|
||||
doubleconsumer consumer = nullptr;
|
||||
double min;
|
||||
double max;
|
||||
double value;
|
||||
double step;
|
||||
int trackWidth;
|
||||
public:
|
||||
TrackBar(double min,
|
||||
double max,
|
||||
double value,
|
||||
double step=1.0,
|
||||
int trackWidth=1);
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void setSupplier(doublesupplier supplier);
|
||||
virtual void setConsumer(doubleconsumer consumer);
|
||||
|
||||
virtual void mouseMove(GUI*, int x, int y) override;
|
||||
|
||||
virtual double getValue() const;
|
||||
virtual double getMin() const;
|
||||
virtual double getMax() const;
|
||||
virtual double getStep() const;
|
||||
virtual int getTrackWidth() const;
|
||||
virtual glm::vec4 getTrackColor() const;
|
||||
|
||||
virtual void setValue(double);
|
||||
virtual void setMin(double);
|
||||
virtual void setMax(double);
|
||||
virtual void setStep(double);
|
||||
virtual void setTrackWidth(int);
|
||||
virtual void setTrackColor(glm::vec4);
|
||||
};
|
||||
|
||||
class CheckBox : public UINode {
|
||||
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;
|
||||
public:
|
||||
CheckBox(bool checked=false);
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
|
||||
virtual void setSupplier(boolsupplier supplier);
|
||||
virtual void setConsumer(boolconsumer consumer);
|
||||
|
||||
virtual CheckBox* setChecked(bool flag);
|
||||
|
||||
virtual bool isChecked() const {
|
||||
if (supplier)
|
||||
return supplier();
|
||||
return checked;
|
||||
}
|
||||
};
|
||||
|
||||
class FullCheckBox : public Panel {
|
||||
protected:
|
||||
std::shared_ptr<CheckBox> checkbox;
|
||||
public:
|
||||
FullCheckBox(std::wstring text, glm::vec2 size, bool checked=false);
|
||||
|
||||
virtual void setSupplier(boolsupplier supplier) {
|
||||
checkbox->setSupplier(supplier);
|
||||
}
|
||||
|
||||
virtual void setConsumer(boolconsumer consumer) {
|
||||
checkbox->setConsumer(consumer);
|
||||
}
|
||||
|
||||
virtual void setChecked(bool flag) {
|
||||
checkbox->setChecked(flag);
|
||||
}
|
||||
|
||||
virtual bool isChecked() const {
|
||||
return checkbox->isChecked();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FRONTEND_GUI_CONTROLS_H_
|
||||
@@ -1,101 +0,0 @@
|
||||
#include "gui_util.h"
|
||||
#include "controls.h"
|
||||
#include "containers.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../locale/langs.h"
|
||||
#include "../../delegates.h"
|
||||
|
||||
using namespace gui;
|
||||
using glm::vec2;
|
||||
using glm::vec4;
|
||||
|
||||
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();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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 std::make_shared<Button>(text, vec4(10.f), [=](GUI* gui) {
|
||||
menu->setPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
if (text.length() > wrap_length) {
|
||||
size_t offset = 0;
|
||||
int extra;
|
||||
while ((extra = text.length() - offset) > 0) {
|
||||
size_t endline = text.find(L'\n', offset);
|
||||
if (endline != std::string::npos) {
|
||||
extra = std::min(extra, int(endline-offset)+1);
|
||||
}
|
||||
extra = std::min(extra, wrap_length);
|
||||
std::wstring part = text.substr(offset, extra);
|
||||
panel->add(std::make_shared<Label>(part));
|
||||
offset += extra;
|
||||
}
|
||||
} else {
|
||||
panel->add(std::make_shared<Label>(text));
|
||||
}
|
||||
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->addPage("<alert>", panel);
|
||||
menu->setPage("<alert>");
|
||||
}
|
||||
|
||||
void guiutil::confirm(
|
||||
GUI* gui,
|
||||
const std::wstring& text,
|
||||
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");
|
||||
|
||||
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(std::make_shared<Button>(notext, vec4(8.f), [=](GUI*){
|
||||
menu->back();
|
||||
}));
|
||||
|
||||
panel->add(subpanel);
|
||||
|
||||
panel->refresh();
|
||||
menu->addPage("<confirm>", panel);
|
||||
menu->setPage("<confirm>");
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#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 {
|
||||
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_
|
||||
@@ -1,400 +0,0 @@
|
||||
#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;
|
||||
if (str == "top") return Align::top;
|
||||
if (str == "bottom") return Align::bottom;
|
||||
return def;
|
||||
}
|
||||
|
||||
static Gravity gravity_from_string(const std::string& str) {
|
||||
static const std::unordered_map<std::string, Gravity> gravity_names {
|
||||
{"top-left", Gravity::top_left},
|
||||
{"top-center", Gravity::top_center},
|
||||
{"top-right", Gravity::top_right},
|
||||
{"center-left", Gravity::center_left},
|
||||
{"center-center", Gravity::center_center},
|
||||
{"center-right", Gravity::center_right},
|
||||
{"bottom-left", Gravity::bottom_left},
|
||||
{"bottom-center", Gravity::bottom_center},
|
||||
{"bottom-right", Gravity::bottom_right},
|
||||
};
|
||||
auto found = gravity_names.find(str);
|
||||
if (found == gravity_names.end()) {
|
||||
return found->second;
|
||||
}
|
||||
return Gravity::none;
|
||||
}
|
||||
|
||||
/* 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.setPos(element->attr("pos").asVec2());
|
||||
}
|
||||
if (element->has("size")) {
|
||||
node.setSize(element->attr("size").asVec2());
|
||||
}
|
||||
if (element->has("color")) {
|
||||
glm::vec4 color = element->attr("color").asColor();
|
||||
glm::vec4 hoverColor = color;
|
||||
if (element->has("hover-color")) {
|
||||
hoverColor = node.getHoverColor();
|
||||
}
|
||||
node.setColor(color);
|
||||
node.setHoverColor(hoverColor);
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (element->has("hover-color")) {
|
||||
node.setHoverColor(element->attr("hover-color").asColor());
|
||||
}
|
||||
std::string alignName = element->attr("align", "").getText();
|
||||
node.setAlign(align_from_string(alignName, node.getAlign()));
|
||||
|
||||
if (element->has("gravity")) {
|
||||
node.setGravity(gravity_from_string(
|
||||
element->attr("gravity").getText()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if (element->has("valign")) {
|
||||
label->setVerticalAlign(
|
||||
align_from_string(element->attr("valign").getText(), label->getVerticalAlign())
|
||||
);
|
||||
}
|
||||
if (element->has("supplier")) {
|
||||
auto supplier = scripting::create_wstring_supplier(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("supplier").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
label->textSupplier(supplier);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, xml::xmlelement element) {
|
||||
auto container = std::make_shared<Container>(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()
|
||||
);
|
||||
button->listenAction([callback](GUI*) {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
if (element->has("text-align")) {
|
||||
button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign()));
|
||||
}
|
||||
if (element->has("pressed-color")) {
|
||||
button->setPressedColor(element->attr("pressed-color").asColor());
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, xml::xmlelement element) {
|
||||
auto text = readAndProcessInnerText(element);
|
||||
bool checked = element->attr("checked", "false").asBool();
|
||||
auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(), checked);
|
||||
_readPanel(reader, element, *checkbox);
|
||||
|
||||
if (element->has("consumer")) {
|
||||
auto consumer = scripting::create_bool_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("consumer").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
checkbox->setConsumer(consumer);
|
||||
}
|
||||
|
||||
if (element->has("supplier")) {
|
||||
auto supplier = scripting::create_bool_supplier(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("supplier").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
checkbox->setSupplier(supplier);
|
||||
}
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
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("multiline")) {
|
||||
textbox->setMultiline(element->attr("multiline").asBool());
|
||||
}
|
||||
|
||||
if (element->has("editable")) {
|
||||
textbox->setEditable(element->attr("editable").asBool());
|
||||
}
|
||||
|
||||
if (element->has("consumer")) {
|
||||
auto consumer = scripting::create_wstring_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("consumer").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
textbox->setTextConsumer(consumer);
|
||||
}
|
||||
|
||||
if (element->has("supplier")) {
|
||||
auto supplier = scripting::create_wstring_supplier(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("consumer").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
textbox->setTextSupplier(supplier);
|
||||
}
|
||||
if (element->has("focused-color")) {
|
||||
textbox->setFocusedColor(element->attr("focused-color").asColor());
|
||||
}
|
||||
if (element->has("error-color")) {
|
||||
textbox->setErrorColor(element->attr("error-color").asColor());
|
||||
}
|
||||
if (element->has("validator")) {
|
||||
auto validator = scripting::create_wstring_validator(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("validator").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
textbox->setTextValidator(validator);
|
||||
}
|
||||
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(AssetType::texture, "textures/"+src, 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()
|
||||
);
|
||||
bar->setConsumer(consumer);
|
||||
}
|
||||
if (element->has("supplier")) {
|
||||
auto supplier = scripting::create_number_supplier(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("supplier").getText(),
|
||||
reader.getFilename()
|
||||
);
|
||||
bar->setSupplier(supplier);
|
||||
}
|
||||
if (element->has("track-color")) {
|
||||
bar->setTrackColor(element->attr("track-color").asColor());
|
||||
}
|
||||
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("chackbox", readCheckBox);
|
||||
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;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#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_
|
||||
+157
-162
@@ -1,105 +1,67 @@
|
||||
#include "hud.h"
|
||||
#include "hud.hpp"
|
||||
|
||||
#include "ContentGfxCache.hpp"
|
||||
#include "LevelFrontend.hpp"
|
||||
#include "UiDocument.hpp"
|
||||
|
||||
#include "../assets/Assets.hpp"
|
||||
#include "../content/Content.hpp"
|
||||
#include "../core_defs.hpp"
|
||||
#include "../delegates.hpp"
|
||||
#include "../engine.hpp"
|
||||
#include "../graphics/core/Atlas.hpp"
|
||||
#include "../graphics/core/Batch2D.hpp"
|
||||
#include "../graphics/core/Batch3D.hpp"
|
||||
#include "../graphics/core/DrawContext.hpp"
|
||||
#include "../graphics/core/Font.hpp"
|
||||
#include "../graphics/core/Mesh.hpp"
|
||||
#include "../graphics/core/Shader.hpp"
|
||||
#include "../graphics/core/Texture.hpp"
|
||||
#include "../graphics/render/WorldRenderer.hpp"
|
||||
#include "../graphics/ui/elements/InventoryView.hpp"
|
||||
#include "../graphics/ui/elements/Menu.hpp"
|
||||
#include "../graphics/ui/elements/Panel.hpp"
|
||||
#include "../graphics/ui/elements/Plotter.hpp"
|
||||
#include "../graphics/ui/elements/UINode.hpp"
|
||||
#include "../graphics/ui/gui_util.hpp"
|
||||
#include "../graphics/ui/GUI.hpp"
|
||||
#include "../items/Inventories.hpp"
|
||||
#include "../items/Inventory.hpp"
|
||||
#include "../items/ItemDef.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
#include "../maths/voxmaths.hpp"
|
||||
#include "../objects/Player.hpp"
|
||||
#include "../physics/Hitbox.hpp"
|
||||
#include "../typedefs.hpp"
|
||||
#include "../util/stringutil.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../voxels/Chunk.hpp"
|
||||
#include "../voxels/Chunks.hpp"
|
||||
#include "../window/Camera.hpp"
|
||||
#include "../window/Events.hpp"
|
||||
#include "../window/input.hpp"
|
||||
#include "../window/Window.hpp"
|
||||
#include "../world/Level.hpp"
|
||||
#include "../world/World.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../util/stringutil.h"
|
||||
#include "../util/timeutil.h"
|
||||
#include "../assets/Assets.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Batch2D.h"
|
||||
#include "../graphics/Batch3D.h"
|
||||
#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"
|
||||
#include "../window/input.h"
|
||||
#include "../voxels/Chunks.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../voxels/Chunk.h"
|
||||
#include "../world/World.h"
|
||||
#include "../world/Level.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../physics/Hitbox.h"
|
||||
#include "../maths/voxmaths.h"
|
||||
#include "gui/controls.h"
|
||||
#include "gui/containers.h"
|
||||
#include "gui/UINode.h"
|
||||
#include "gui/GUI.h"
|
||||
#include "ContentGfxCache.h"
|
||||
#include "menu/menu.h"
|
||||
#include "screens.h"
|
||||
#include "WorldRenderer.h"
|
||||
#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 namespace gui;
|
||||
|
||||
// implemented in debug_panel.cpp
|
||||
extern std::shared_ptr<gui::UINode> create_debug_panel(
|
||||
extern std::shared_ptr<UINode> create_debug_panel(
|
||||
Engine* engine,
|
||||
Level* level,
|
||||
Player* player
|
||||
);
|
||||
|
||||
class DeltaGrapher : public gui::UINode {
|
||||
std::unique_ptr<int[]> points;
|
||||
float multiplier;
|
||||
int index = 0;
|
||||
int dmwidth;
|
||||
int dmheight;
|
||||
public:
|
||||
DeltaGrapher(uint width, uint height, float multiplier)
|
||||
: gui::UINode(glm::vec2(width, height)),
|
||||
multiplier(multiplier),
|
||||
dmwidth(width),
|
||||
dmheight(height)
|
||||
{
|
||||
points = std::make_unique<int[]>(width);
|
||||
}
|
||||
|
||||
void act(float delta) override {
|
||||
index = index + 1 % dmwidth;
|
||||
int value = static_cast<int>(delta * multiplier);
|
||||
points[index % dmwidth] = std::min(value, dmheight);
|
||||
}
|
||||
|
||||
void draw(const GfxContext* pctx, Assets* assets) override {
|
||||
glm::vec2 pos = calcPos();
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->lineWidth(1);
|
||||
for (int i = index+1; i < index+dmwidth; i++) {
|
||||
int j = i % dmwidth;
|
||||
batch->line(
|
||||
pos.x + i - index, pos.y + size.y - points[j],
|
||||
pos.x + i - index, pos.y + size.y, 1.0f, 1.0f, 1.0f, 0.2f
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HudElement::HudElement(
|
||||
hud_element_mode mode,
|
||||
UiDocument* document,
|
||||
std::shared_ptr<gui::UINode> node,
|
||||
std::shared_ptr<UINode> node,
|
||||
bool debug
|
||||
) : mode(mode), document(document), node(node), debug(debug) {
|
||||
}
|
||||
@@ -129,7 +91,7 @@ UiDocument* HudElement::getDocument() const {
|
||||
return document;
|
||||
}
|
||||
|
||||
std::shared_ptr<gui::UINode> HudElement::getNode() const {
|
||||
std::shared_ptr<UINode> HudElement::getNode() const {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -157,13 +119,14 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout);
|
||||
auto view = builder.build();
|
||||
view->bind(accessInventory, frontend, interaction.get());
|
||||
view->bind(accessInventory, content);
|
||||
view->setMargin(glm::vec4());
|
||||
return view;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> Hud::createHotbar() {
|
||||
auto inventory = player->getInventory();
|
||||
auto content = frontend->getLevel()->content;
|
||||
|
||||
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
|
||||
InventoryBuilder builder;
|
||||
@@ -171,34 +134,19 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
|
||||
auto view = builder.build();
|
||||
|
||||
view->setOrigin(glm::vec2(view->getSize().x/2, 0));
|
||||
view->bind(inventory, frontend, interaction.get());
|
||||
view->bind(inventory, content);
|
||||
view->setInteractive(false);
|
||||
return view;
|
||||
}
|
||||
|
||||
Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
|
||||
: engine(engine),
|
||||
assets(engine->getAssets()),
|
||||
: assets(engine->getAssets()),
|
||||
gui(engine->getGUI()),
|
||||
frontend(frontend),
|
||||
player(player)
|
||||
{
|
||||
interaction = std::make_unique<InventoryInteraction>();
|
||||
grabbedItemView = std::make_shared<SlotView>(
|
||||
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr)
|
||||
);
|
||||
grabbedItemView->bind(
|
||||
0,
|
||||
interaction->getGrabbedItem(),
|
||||
frontend,
|
||||
interaction.get()
|
||||
);
|
||||
grabbedItemView->setColor(glm::vec4());
|
||||
grabbedItemView->setInteractive(false);
|
||||
grabbedItemView->setZIndex(1);
|
||||
|
||||
contentAccess = createContentAccess();
|
||||
contentAccessPanel = std::make_shared<gui::Panel>(
|
||||
contentAccessPanel = std::make_shared<Panel>(
|
||||
contentAccess->getSize(), glm::vec4(0.0f), 0.0f
|
||||
);
|
||||
contentAccessPanel->setColor(glm::vec4());
|
||||
@@ -206,10 +154,9 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
|
||||
contentAccessPanel->setScrollable(true);
|
||||
|
||||
hotbarView = createHotbar();
|
||||
darkOverlay = std::make_unique<gui::Panel>(glm::vec2(4000.0f));
|
||||
darkOverlay->setColor(glm::vec4(0, 0, 0, 0.5f));
|
||||
darkOverlay->setZIndex(-1);
|
||||
darkOverlay->setVisible(false);
|
||||
darkOverlay = guiutil::create(
|
||||
"<container size='4000' color='#00000080' z-index='-1' visible='false'/>"
|
||||
);
|
||||
|
||||
uicamera = std::make_unique<Camera>(glm::vec3(), 1);
|
||||
uicamera->perspective = false;
|
||||
@@ -222,16 +169,14 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
|
||||
gui->add(hotbarView);
|
||||
gui->add(debugPanel);
|
||||
gui->add(contentAccessPanel);
|
||||
gui->add(grabbedItemView);
|
||||
|
||||
auto dgrapher = std::make_shared<DeltaGrapher>(350, 250, 2000);
|
||||
dgrapher->setGravity(gui::Gravity::bottom_right);
|
||||
add(HudElement(hud_element_mode::permanent, nullptr, dgrapher, true));
|
||||
auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16);
|
||||
dplotter->setGravity(Gravity::bottom_right);
|
||||
add(HudElement(hud_element_mode::permanent, nullptr, dplotter, true));
|
||||
}
|
||||
|
||||
Hud::~Hud() {
|
||||
// removing all controlled ui
|
||||
gui->remove(grabbedItemView);
|
||||
for (auto& element : elements) {
|
||||
onRemove(element);
|
||||
}
|
||||
@@ -259,6 +204,12 @@ void Hud::processInput(bool visible) {
|
||||
setPause(true);
|
||||
}
|
||||
}
|
||||
if (!pause && Events::active(BIND_DEVTOOLS_CONSOLE)) {
|
||||
showOverlay(assets->getLayout("core:console"), false);
|
||||
}
|
||||
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
||||
setPause(true);
|
||||
}
|
||||
|
||||
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
||||
if (inventoryOpen) {
|
||||
@@ -268,27 +219,31 @@ void Hud::processInput(bool visible) {
|
||||
}
|
||||
}
|
||||
if (!pause) {
|
||||
if (!inventoryOpen && Events::scroll) {
|
||||
int slot = player->getChosenSlot();
|
||||
slot = (slot - Events::scroll) % 10;
|
||||
if (slot < 0) {
|
||||
slot += 10;
|
||||
}
|
||||
player->setChosenSlot(slot);
|
||||
updateHotbarControl();
|
||||
}
|
||||
}
|
||||
|
||||
void Hud::updateHotbarControl() {
|
||||
if (!inventoryOpen && Events::scroll) {
|
||||
int slot = player->getChosenSlot();
|
||||
slot = (slot - Events::scroll) % 10;
|
||||
if (slot < 0) {
|
||||
slot += 10;
|
||||
}
|
||||
for (
|
||||
int i = static_cast<int>(keycode::NUM_1);
|
||||
i <= static_cast<int>(keycode::NUM_9);
|
||||
i++
|
||||
) {
|
||||
if (Events::jpressed(i)) {
|
||||
player->setChosenSlot(i - static_cast<int>(keycode::NUM_1));
|
||||
}
|
||||
}
|
||||
if (Events::jpressed(keycode::NUM_0)) {
|
||||
player->setChosenSlot(9);
|
||||
player->setChosenSlot(slot);
|
||||
}
|
||||
for (
|
||||
int i = static_cast<int>(keycode::NUM_1);
|
||||
i <= static_cast<int>(keycode::NUM_9);
|
||||
i++
|
||||
) {
|
||||
if (Events::jpressed(i)) {
|
||||
player->setChosenSlot(i - static_cast<int>(keycode::NUM_1));
|
||||
}
|
||||
}
|
||||
if (Events::jpressed(keycode::NUM_0)) {
|
||||
player->setChosenSlot(9);
|
||||
}
|
||||
}
|
||||
|
||||
void Hud::update(bool visible) {
|
||||
@@ -341,12 +296,17 @@ void Hud::update(bool visible) {
|
||||
|
||||
/// @brief Show inventory on the screen and turn on inventory mode blocking movement
|
||||
void Hud::openInventory() {
|
||||
auto level = frontend->getLevel();
|
||||
auto content = level->content;
|
||||
showExchangeSlot();
|
||||
|
||||
inventoryOpen = true;
|
||||
auto inventory = player->getInventory();
|
||||
auto inventoryDocument = assets->getLayout("core:inventory");
|
||||
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
||||
inventoryView->bind(inventory, frontend, interaction.get());
|
||||
inventoryView->bind(inventory, content);
|
||||
add(HudElement(hud_element_mode::inventory_bound, inventoryDocument, inventoryView, false));
|
||||
add(HudElement(hud_element_mode::inventory_bound, nullptr, exchangeSlot, false));
|
||||
}
|
||||
|
||||
void Hud::openInventory(
|
||||
@@ -359,6 +319,7 @@ void Hud::openInventory(
|
||||
closeInventory();
|
||||
}
|
||||
auto level = frontend->getLevel();
|
||||
auto content = level->content;
|
||||
blockUI = std::dynamic_pointer_cast<InventoryView>(doc->getRoot());
|
||||
if (blockUI == nullptr) {
|
||||
throw std::runtime_error("block UI root element must be 'inventory'");
|
||||
@@ -373,12 +334,27 @@ void Hud::openInventory(
|
||||
blockinv = level->inventories->createVirtual(blockUI->getSlotsCount());
|
||||
}
|
||||
level->chunks->getChunkByVoxel(block.x, block.y, block.z)->setUnsaved(true);
|
||||
blockUI->bind(blockinv, frontend, interaction.get());
|
||||
blockUI->bind(blockinv, content);
|
||||
blockPos = block;
|
||||
currentblockid = level->chunks->get(block.x, block.y, block.z)->id;
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
|
||||
}
|
||||
|
||||
void Hud::showExchangeSlot() {
|
||||
auto level = frontend->getLevel();
|
||||
auto content = level->content;
|
||||
exchangeSlotInv = level->inventories->createVirtual(1);
|
||||
exchangeSlot = std::make_shared<SlotView>(
|
||||
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr)
|
||||
);
|
||||
exchangeSlot->bind(exchangeSlotInv->getId(), exchangeSlotInv->getSlot(0), content);
|
||||
exchangeSlot->setColor(glm::vec4());
|
||||
exchangeSlot->setInteractive(false);
|
||||
exchangeSlot->setZIndex(1);
|
||||
gui->store(SlotView::EXCHANGE_SLOT_NAME, exchangeSlot);
|
||||
|
||||
}
|
||||
|
||||
void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
||||
if (isInventoryOpen()) {
|
||||
closeInventory();
|
||||
@@ -387,6 +363,7 @@ void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
||||
if (playerInventory) {
|
||||
openInventory();
|
||||
} else {
|
||||
showExchangeSlot();
|
||||
inventoryOpen = true;
|
||||
}
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
|
||||
@@ -398,45 +375,51 @@ void Hud::openPermanent(UiDocument* doc) {
|
||||
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(root);
|
||||
if (invview) {
|
||||
auto inventory = player->getInventory();
|
||||
invview->bind(inventory, frontend, interaction.get());
|
||||
invview->bind(player->getInventory(), frontend->getLevel()->content);
|
||||
}
|
||||
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
||||
}
|
||||
|
||||
void Hud::closeInventory() {
|
||||
gui->remove(SlotView::EXCHANGE_SLOT_NAME);
|
||||
exchangeSlot = nullptr;
|
||||
exchangeSlotInv = nullptr;
|
||||
inventoryOpen = false;
|
||||
ItemStack& grabbed = interaction->getGrabbedItem();
|
||||
grabbed.clear();
|
||||
inventoryView = nullptr;
|
||||
blockUI = nullptr;
|
||||
secondUI = nullptr;
|
||||
|
||||
for (auto& element : elements) {
|
||||
if (element.isInventoryBound()) {
|
||||
element.setRemoved();
|
||||
onRemove(element);
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::add(HudElement element) {
|
||||
using namespace dynamic;
|
||||
|
||||
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(),
|
||||
blockPos
|
||||
);
|
||||
} else {
|
||||
scripting::on_ui_open(
|
||||
element.getDocument(),
|
||||
nullptr,
|
||||
blockPos
|
||||
);
|
||||
auto inventory = invview ? invview->getInventory() : nullptr;
|
||||
std::vector<Value> args;
|
||||
args.push_back(inventory ? inventory.get()->getId() : 0);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
args.push_back(static_cast<integer_t>(blockPos[i]));
|
||||
}
|
||||
scripting::on_ui_open(
|
||||
element.getDocument(),
|
||||
std::move(args)
|
||||
);
|
||||
}
|
||||
elements.push_back(element);
|
||||
}
|
||||
|
||||
void Hud::onRemove(HudElement& element) {
|
||||
void Hud::onRemove(const HudElement& element) {
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
Inventory* inventory = nullptr;
|
||||
@@ -452,7 +435,7 @@ void Hud::onRemove(HudElement& element) {
|
||||
gui->remove(element.getNode());
|
||||
}
|
||||
|
||||
void Hud::remove(std::shared_ptr<gui::UINode> node) {
|
||||
void Hud::remove(std::shared_ptr<UINode> node) {
|
||||
for (auto& element : elements) {
|
||||
if (element.getNode() == node) {
|
||||
element.setRemoved();
|
||||
@@ -462,7 +445,7 @@ void Hud::remove(std::shared_ptr<gui::UINode> node) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::draw(const GfxContext& ctx){
|
||||
void Hud::draw(const DrawContext& ctx){
|
||||
const Viewport& viewport = ctx.getViewport();
|
||||
const uint width = viewport.getWidth();
|
||||
const uint height = viewport.getHeight();
|
||||
@@ -480,8 +463,8 @@ void Hud::draw(const GfxContext& ctx){
|
||||
|
||||
// Crosshair
|
||||
if (!pause && !inventoryOpen && !player->debug) {
|
||||
GfxContext chctx = ctx.sub();
|
||||
chctx.setBlendMode(blendmode::inversion);
|
||||
DrawContext chctx = ctx.sub();
|
||||
chctx.setBlendMode(BlendMode::inversion);
|
||||
auto texture = assets->getTexture("gui/crosshair");
|
||||
batch->texture(texture);
|
||||
int chsizex = texture != nullptr ? texture->getWidth() : 16;
|
||||
@@ -521,12 +504,14 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
|
||||
));
|
||||
}
|
||||
secondUI->setPos(glm::vec2(
|
||||
glm::min(width/2-invwidth/2, width-caWidth-10-invwidth),
|
||||
glm::min(width/2-invwidth/2, width-caWidth-(inventoryView ? 10 : 0)-invwidth),
|
||||
height/2-totalHeight/2
|
||||
));
|
||||
}
|
||||
}
|
||||
grabbedItemView->setPos(glm::vec2(Events::cursor));
|
||||
if (exchangeSlot != nullptr) {
|
||||
exchangeSlot->setPos(glm::vec2(Events::cursor));
|
||||
}
|
||||
hotbarView->setPos(glm::vec2(width/2, height-65));
|
||||
hotbarView->setSelected(player->getChosenSlot());
|
||||
}
|
||||
@@ -544,10 +529,13 @@ void Hud::setPause(bool pause) {
|
||||
return;
|
||||
}
|
||||
this->pause = pause;
|
||||
|
||||
if (inventoryOpen) {
|
||||
closeInventory();
|
||||
}
|
||||
|
||||
auto menu = gui->getMenu();
|
||||
if (pause) {
|
||||
menus::create_pause_panel(engine, frontend->getController());
|
||||
menu->setPage("pause");
|
||||
} else {
|
||||
menu->reset();
|
||||
@@ -559,3 +547,10 @@ void Hud::setPause(bool pause) {
|
||||
Player* Hud::getPlayer() const {
|
||||
return player;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Hud::getBlockInventory() {
|
||||
if (blockUI == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return blockUI->getInventory();
|
||||
}
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
#ifndef SRC_HUD_H_
|
||||
#define SRC_HUD_H_
|
||||
#ifndef FRONTEND_HUD_HPP_
|
||||
#define FRONTEND_HUD_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "../util/ObjectsKeeper.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../graphics/GfxContext.h"
|
||||
|
||||
class Camera;
|
||||
class Block;
|
||||
class Assets;
|
||||
class Player;
|
||||
class Engine;
|
||||
class SlotView;
|
||||
class Inventory;
|
||||
class InventoryView;
|
||||
class LevelFrontend;
|
||||
class UiDocument;
|
||||
class InventoryInteraction;
|
||||
class DrawContext;
|
||||
class Viewport;
|
||||
|
||||
namespace gui {
|
||||
class GUI;
|
||||
class UINode;
|
||||
class Panel;
|
||||
class Container;
|
||||
class InventoryView;
|
||||
class SlotView;
|
||||
}
|
||||
|
||||
enum class hud_element_mode {
|
||||
@@ -53,6 +55,10 @@ public:
|
||||
UiDocument* getDocument() const;
|
||||
std::shared_ptr<gui::UINode> getNode() const;
|
||||
|
||||
bool isInventoryBound() const {
|
||||
return mode == hud_element_mode::inventory_bound;
|
||||
}
|
||||
|
||||
void setRemoved() {
|
||||
removed = true;
|
||||
}
|
||||
@@ -62,8 +68,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class Hud {
|
||||
Engine* engine;
|
||||
class Hud : public util::ObjectsKeeper {
|
||||
Assets* assets;
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
gui::GUI* gui;
|
||||
@@ -78,24 +83,24 @@ class Hud {
|
||||
/// @brief Content access panel scroll container
|
||||
std::shared_ptr<gui::Container> contentAccessPanel;
|
||||
/// @brief Content access panel itself
|
||||
std::shared_ptr<InventoryView> contentAccess;
|
||||
std::shared_ptr<gui::InventoryView> contentAccess;
|
||||
/// @brief Player inventory hotbar
|
||||
std::shared_ptr<InventoryView> hotbarView;
|
||||
std::shared_ptr<gui::InventoryView> hotbarView;
|
||||
/// @brief Debug info and control panel (F3 key)
|
||||
std::shared_ptr<gui::UINode> debugPanel;
|
||||
/// @brief Overlay used in pause mode
|
||||
std::shared_ptr<gui::Panel> darkOverlay;
|
||||
/// @brief Inventories interaction agent (grabbed item and other info)
|
||||
std::unique_ptr<InventoryInteraction> interaction;
|
||||
/// @brief Grabbed item visual element
|
||||
std::shared_ptr<SlotView> grabbedItemView;
|
||||
std::shared_ptr<gui::UINode> darkOverlay;
|
||||
/// @brief Inventories interaction agent (grabbed item)
|
||||
std::shared_ptr<gui::SlotView> exchangeSlot;
|
||||
/// @brief Exchange slot inventory (1 slot only)
|
||||
std::shared_ptr<Inventory> exchangeSlotInv = nullptr;
|
||||
/// @brief List of all controlled hud elements
|
||||
std::vector<HudElement> elements;
|
||||
|
||||
/// @brief Player inventory view
|
||||
std::shared_ptr<InventoryView> inventoryView = nullptr;
|
||||
std::shared_ptr<gui::InventoryView> inventoryView = nullptr;
|
||||
/// @brief Block inventory view
|
||||
std::shared_ptr<InventoryView> blockUI = nullptr;
|
||||
std::shared_ptr<gui::InventoryView> blockUI = nullptr;
|
||||
/// @brief Position of the block open
|
||||
glm::ivec3 blockPos {};
|
||||
/// @brief Id of the block open (used to detect block destruction or replacement)
|
||||
@@ -104,18 +109,21 @@ class Hud {
|
||||
/// @brief UI element will be dynamicly positioned near to inventory or in screen center
|
||||
std::shared_ptr<gui::UINode> secondUI = nullptr;
|
||||
|
||||
std::shared_ptr<InventoryView> createContentAccess();
|
||||
std::shared_ptr<InventoryView> createHotbar();
|
||||
std::shared_ptr<gui::InventoryView> createContentAccess();
|
||||
std::shared_ptr<gui::InventoryView> createHotbar();
|
||||
|
||||
void processInput(bool visible);
|
||||
void updateElementsPosition(const Viewport& viewport);
|
||||
void updateHotbarControl();
|
||||
void cleanup();
|
||||
|
||||
void showExchangeSlot();
|
||||
public:
|
||||
Hud(Engine* engine, LevelFrontend* frontend, Player* player);
|
||||
~Hud();
|
||||
|
||||
void update(bool hudVisible);
|
||||
void draw(const GfxContext& context);
|
||||
void draw(const DrawContext& context);
|
||||
|
||||
/// @brief Check if inventory mode on
|
||||
bool isInventoryOpen() const;
|
||||
@@ -134,7 +142,12 @@ public:
|
||||
/// @param doc block ui layout
|
||||
/// @param blockInv block inventory
|
||||
/// @param playerInventory show player inventory too
|
||||
void openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inventory> blockInv, bool playerInventory);
|
||||
void openInventory(
|
||||
glm::ivec3 block,
|
||||
UiDocument* doc,
|
||||
std::shared_ptr<Inventory> blockInv,
|
||||
bool playerInventory
|
||||
);
|
||||
|
||||
/// @brief Show element in inventory-mode
|
||||
/// @param doc element layout
|
||||
@@ -149,10 +162,12 @@ public:
|
||||
void openPermanent(UiDocument* doc);
|
||||
|
||||
void add(HudElement element);
|
||||
void onRemove(HudElement& element);
|
||||
void onRemove(const HudElement& element);
|
||||
void remove(std::shared_ptr<gui::UINode> node);
|
||||
|
||||
Player* getPlayer() const;
|
||||
|
||||
std::shared_ptr<Inventory> getBlockInventory();
|
||||
};
|
||||
|
||||
#endif // SRC_HUD_H_
|
||||
#endif // FRONTEND_HUD_HPP_
|
||||
@@ -1,15 +1,17 @@
|
||||
#include "langs.h"
|
||||
#include "locale.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include "../coders/json.hpp"
|
||||
#include "../coders/commons.hpp"
|
||||
#include "../content/ContentPack.hpp"
|
||||
#include "../files/files.hpp"
|
||||
#include "../util/stringutil.hpp"
|
||||
#include "../data/dynamic.hpp"
|
||||
#include "../debug/Logger.hpp"
|
||||
|
||||
#include "../../coders/json.h"
|
||||
#include "../../coders/commons.h"
|
||||
#include "../../content/ContentPack.h"
|
||||
#include "../../files/files.h"
|
||||
#include "../../util/stringutil.h"
|
||||
#include "../../data/dynamic.h"
|
||||
static debug::Logger logger("locale");
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
std::unique_ptr<langs::Lang> langs::current;
|
||||
std::unordered_map<std::string, langs::LocaleInfo> langs::locales_info;
|
||||
@@ -33,8 +35,8 @@ const std::string& langs::Lang::getId() const {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/* Language key-value txt files parser */
|
||||
class Reader : public BasicParser {
|
||||
// @brief Language key-value txt files parser
|
||||
class Reader : BasicParser {
|
||||
void skipWhitespace() override {
|
||||
BasicParser::skipWhitespace();
|
||||
if (hasNext() && source[pos] == '#') {
|
||||
@@ -45,7 +47,7 @@ class Reader : public BasicParser {
|
||||
}
|
||||
}
|
||||
public:
|
||||
Reader(std::string file, std::string source) : BasicParser(file, source) {
|
||||
Reader(std::string_view file, std::string_view source) : BasicParser(file, source) {
|
||||
}
|
||||
|
||||
void read(langs::Lang& lang, std::string prefix) {
|
||||
@@ -72,21 +74,21 @@ void langs::loadLocalesInfo(const fs::path& resdir, std::string& fallback) {
|
||||
|
||||
auto langs = root->map("langs");
|
||||
if (langs) {
|
||||
std::cout << "locales ";
|
||||
auto logline = logger.info();
|
||||
logline << "locales ";
|
||||
for (auto& entry : langs->values) {
|
||||
auto langInfo = entry.second.get();
|
||||
auto langInfo = entry.second;
|
||||
|
||||
std::string name;
|
||||
if (langInfo->type == dynamic::valtype::map) {
|
||||
name = langInfo->value.map->getStr("name", "none");
|
||||
if (auto mapptr = std::get_if<dynamic::Map_sptr>(&langInfo)) {
|
||||
name = (*mapptr)->get("name", "none"s);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "[" << entry.first << " (" << name << ")] ";
|
||||
logline << "[" << entry.first << " (" << name << ")] ";
|
||||
langs::locales_info[entry.first] = LocaleInfo {entry.first, name};
|
||||
}
|
||||
std::cout << "added" << std::endl;
|
||||
logline << "added";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,17 +98,17 @@ std::string langs::locale_by_envlocale(const std::string& envlocale, const fs::p
|
||||
loadLocalesInfo(resdir, fallback);
|
||||
}
|
||||
if (locales_info.find(envlocale) != locales_info.end()) {
|
||||
std::cout << "locale " << envlocale << " is automatically selected" << std::endl;
|
||||
logger.info() << "locale " << envlocale << " is automatically selected";
|
||||
return envlocale;
|
||||
}
|
||||
else {
|
||||
for (const auto& loc : locales_info) {
|
||||
if (loc.first.find(envlocale.substr(0, 2)) != std::string::npos) {
|
||||
std::cout << "locale " << loc.first << " is automatically selected" << std::endl;
|
||||
logger.info() << "locale " << loc.first << " is automatically selected";
|
||||
return loc.first;
|
||||
}
|
||||
}
|
||||
std::cout << "locale " << fallback << " is automatically selected" << std::endl;
|
||||
logger.info() << "locale " << fallback << " is automatically selected";
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +130,7 @@ void langs::load(const fs::path& resdir,
|
||||
if (fs::is_regular_file(file)) {
|
||||
std::string text = files::read_string(file);
|
||||
Reader reader(file.string(), text);
|
||||
reader.read(lang, pack.id+":");
|
||||
reader.read(lang, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef FRONTEND_LOCALE_LANGS_H
|
||||
#define FRONTEND_LOCALE_LANGS_H
|
||||
#ifndef FRONTEND_LOCALES_HPP_
|
||||
#define FRONTEND_LOCALES_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../../content/ContentPack.h"
|
||||
struct ContentPack;
|
||||
|
||||
namespace langs {
|
||||
const char LANG_FILE_EXT[] = ".txt";
|
||||
@@ -69,4 +69,4 @@ namespace langs {
|
||||
const std::vector<ContentPack>& packs);
|
||||
}
|
||||
|
||||
#endif // FRONTEND_LOCALE_LANGS_H
|
||||
#endif // FRONTEND_LOCALES_HPP_
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "menu.hpp"
|
||||
|
||||
#include "locale.hpp"
|
||||
#include "UiDocument.hpp"
|
||||
|
||||
#include "../delegates.hpp"
|
||||
#include "../engine.hpp"
|
||||
#include "../data/dynamic.hpp"
|
||||
#include "../interfaces/Task.hpp"
|
||||
#include "../files/engine_paths.hpp"
|
||||
#include "../graphics/ui/elements/Menu.hpp"
|
||||
#include "../graphics/ui/gui_util.hpp"
|
||||
#include "../graphics/ui/GUI.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
#include "../settings.hpp"
|
||||
#include "../coders/commons.hpp"
|
||||
#include "../util/stringutil.hpp"
|
||||
#include "../window/Window.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace gui;
|
||||
|
||||
void menus::create_version_label(Engine* engine) {
|
||||
auto gui = engine->getGUI();
|
||||
auto text = ENGINE_VERSION_STRING+" debug build";
|
||||
gui->add(guiutil::create(
|
||||
"<label z-index='1000' color='#FFFFFF80' gravity='top-right' margin='4'>"
|
||||
+text+
|
||||
"</label>"
|
||||
));
|
||||
}
|
||||
|
||||
gui::page_loader_func menus::create_page_loader(Engine* engine) {
|
||||
return [=](const std::string& query) {
|
||||
using namespace dynamic;
|
||||
|
||||
std::vector<Value> args;
|
||||
|
||||
std::string name;
|
||||
size_t index = query.find('?');
|
||||
if (index != std::string::npos) {
|
||||
auto argstr = query.substr(index+1);
|
||||
name = query.substr(0, index);
|
||||
|
||||
auto map = create_map();
|
||||
auto filename = "query for "+name;
|
||||
BasicParser parser(filename, argstr);
|
||||
while (parser.hasNext()) {
|
||||
auto key = std::string(parser.readUntil('='));
|
||||
parser.nextChar();
|
||||
auto value = std::string(parser.readUntil('&'));
|
||||
map->put(key, value);
|
||||
}
|
||||
args.push_back(map);
|
||||
} else {
|
||||
name = query;
|
||||
}
|
||||
|
||||
auto file = engine->getResPaths()->find("layouts/pages/"+name+".xml");
|
||||
auto fullname = "core:pages/"+name;
|
||||
|
||||
auto document = UiDocument::read(scripting::get_root_environment(), fullname, file).release();
|
||||
engine->getAssets()->store(document, fullname);
|
||||
|
||||
scripting::on_ui_open(document, std::move(args));
|
||||
return document->getRoot();
|
||||
};
|
||||
}
|
||||
|
||||
UiDocument* menus::show(Engine* engine, const std::string& name, std::vector<dynamic::Value> args) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto file = engine->getResPaths()->find("layouts/"+name+".xml");
|
||||
auto fullname = "core:layouts/"+name;
|
||||
|
||||
auto document = UiDocument::read(scripting::get_root_environment(), fullname, file).release();
|
||||
engine->getAssets()->store(document, fullname);
|
||||
scripting::on_ui_open(document, std::move(args));
|
||||
menu->addPage(name, document->getRoot());
|
||||
menu->setPage(name);
|
||||
return document;
|
||||
}
|
||||
|
||||
void menus::show_process_panel(Engine* engine, std::shared_ptr<Task> task, std::wstring text) {
|
||||
using namespace dynamic;
|
||||
|
||||
uint initialWork = task->getWorkTotal();
|
||||
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
menu->reset();
|
||||
auto doc = menus::show(engine, "process", {
|
||||
util::wstr2str_utf8(langs::get(text))
|
||||
});
|
||||
std::dynamic_pointer_cast<Container>(doc->getRoot())->listenInterval(0.01f, [=]() {
|
||||
task->update();
|
||||
|
||||
uint tasksDone = task->getWorkDone();
|
||||
scripting::on_ui_progress(doc, tasksDone, initialWork);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef FRONTEND_MENU_MENU_HPP_
|
||||
#define FRONTEND_MENU_MENU_HPP_
|
||||
|
||||
#include "../data/dynamic.hpp"
|
||||
#include "../graphics/ui/elements/Menu.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class Task;
|
||||
class Engine;
|
||||
|
||||
class UiDocument;
|
||||
|
||||
namespace menus {
|
||||
/// @brief Create development version label at the top-right screen corner
|
||||
void create_version_label(Engine* engine);
|
||||
|
||||
gui::page_loader_func create_page_loader(Engine* engine);
|
||||
|
||||
UiDocument* show(
|
||||
Engine* engine,
|
||||
const std::string& name,
|
||||
std::vector<dynamic::Value> args
|
||||
);
|
||||
|
||||
void show_process_panel(Engine* engine, std::shared_ptr<Task> task, std::wstring text=L"");
|
||||
}
|
||||
|
||||
#endif // FRONTEND_MENU_MENU_HPP_
|
||||
@@ -1,297 +0,0 @@
|
||||
#include "menu.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../gui/GUI.h"
|
||||
#include "../gui/containers.h"
|
||||
#include "../gui/controls.h"
|
||||
#include "../screens.h"
|
||||
|
||||
#include "../../coders/png.h"
|
||||
#include "../../util/stringutil.h"
|
||||
#include "../../files/engine_paths.h"
|
||||
#include "../../files/WorldConverter.h"
|
||||
#include "../../files/WorldFiles.h"
|
||||
#include "../../world/World.h"
|
||||
#include "../../world/WorldGenerators.h"
|
||||
#include "../../world/Level.h"
|
||||
#include "../../window/Events.h"
|
||||
#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"
|
||||
|
||||
#include "../gui/gui_util.h"
|
||||
#include "../locale/langs.h"
|
||||
|
||||
#include "menu_commons.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace gui;
|
||||
|
||||
namespace menus {
|
||||
extern std::string generatorID;
|
||||
}
|
||||
|
||||
void menus::create_version_label(Engine* engine) {
|
||||
auto gui = engine->getGUI();
|
||||
auto vlabel = std::make_shared<gui::Label>(
|
||||
util::str2wstr_utf8(ENGINE_VERSION_STRING+" development build ")
|
||||
);
|
||||
vlabel->setZIndex(1000);
|
||||
vlabel->setColor(glm::vec4(1, 1, 1, 0.5f));
|
||||
vlabel->setPositionFunc([=]() {
|
||||
return glm::vec2(Window::width-vlabel->getSize().x, 2);
|
||||
});
|
||||
gui->add(vlabel);
|
||||
}
|
||||
|
||||
static void show_content_missing(
|
||||
Engine* engine,
|
||||
const Content* content,
|
||||
std::shared_ptr<ContentLUT> lut
|
||||
) {
|
||||
auto* gui = engine->getGUI();
|
||||
auto menu = gui->getMenu();
|
||||
auto panel = menus::create_page(engine, "missing-content", 500, 0.5f, 8);
|
||||
|
||||
panel->add(std::make_shared<Label>(langs::get(L"menu.missing-content")));
|
||||
|
||||
auto subpanel = std::make_shared<Panel>(glm::vec2(500, 100));
|
||||
subpanel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
subpanel->setScrollable(true);
|
||||
subpanel->setMaxLength(400);
|
||||
panel->add(subpanel);
|
||||
|
||||
for (auto& entry : lut->getMissingContent()) {
|
||||
auto hpanel = std::make_shared<Panel>(glm::vec2(500, 30));
|
||||
hpanel->setColor(glm::vec4(0.0f));
|
||||
hpanel->setOrientation(Orientation::horizontal);
|
||||
|
||||
auto namelabel = std::make_shared<Label>(util::str2wstr_utf8(entry.name));
|
||||
namelabel->setColor(glm::vec4(1.0f, 0.2f, 0.2f, 0.5f));
|
||||
|
||||
auto contentname = util::str2wstr_utf8(contenttype_name(entry.type));
|
||||
auto typelabel = std::make_shared<Label>(L"["+contentname+L"]");
|
||||
typelabel->setColor(glm::vec4(0.5f));
|
||||
hpanel->add(typelabel);
|
||||
hpanel->add(namelabel);
|
||||
subpanel->add(hpanel);
|
||||
}
|
||||
|
||||
panel->add(std::make_shared<Button>(
|
||||
langs::get(L"Back to Main Menu", L"menu"), glm::vec4(8.0f), [=](GUI*){
|
||||
menu->back();
|
||||
}
|
||||
));
|
||||
menu->setPage("missing-content");
|
||||
}
|
||||
|
||||
void show_process_panel(Engine* engine, std::shared_ptr<WorldConverter> converter, runnable postRunnable) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = menus::create_page(engine, "process", 400, 0.5f, 1);
|
||||
|
||||
panel->add(std::make_shared<Label>(langs::get(L"Converting world...")));
|
||||
|
||||
auto label = std::make_shared<Label>(L"0%");
|
||||
panel->add(label);
|
||||
|
||||
uint initialTasks = converter->getTotalTasks();
|
||||
|
||||
panel->listenInterval(0.01f, [=]() {
|
||||
if (!converter->hasNext()) {
|
||||
converter->write();
|
||||
|
||||
menu->reset();
|
||||
menu->setPage("main", false);
|
||||
engine->getGUI()->postRunnable([=]() {
|
||||
postRunnable();
|
||||
});
|
||||
return;
|
||||
}
|
||||
converter->convertNext();
|
||||
|
||||
uint tasksDone = initialTasks-converter->getTotalTasks();
|
||||
float progress = tasksDone/static_cast<float>(initialTasks);
|
||||
label->setText(
|
||||
std::to_wstring(tasksDone)+
|
||||
L"/"+std::to_wstring(initialTasks)+L" ("+
|
||||
std::to_wstring(int(progress*100))+L"%)"
|
||||
);
|
||||
});
|
||||
|
||||
menu->reset();
|
||||
menu->setPage("process", false);
|
||||
}
|
||||
|
||||
void show_convert_request(
|
||||
Engine* engine,
|
||||
const Content* content,
|
||||
std::shared_ptr<ContentLUT> lut,
|
||||
fs::path folder,
|
||||
runnable postRunnable
|
||||
) {
|
||||
guiutil::confirm(engine->getGUI(), langs::get(L"world.convert-request"), [=]() {
|
||||
show_process_panel(engine, std::make_shared<WorldConverter>(folder, content, lut), postRunnable);
|
||||
}, L"", langs::get(L"Cancel"));
|
||||
}
|
||||
|
||||
void create_languages_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = menus::create_page(engine, "languages", 400, 0.5f, 1);
|
||||
panel->setScrollable(true);
|
||||
|
||||
std::vector<std::string> locales;
|
||||
for (auto& entry : langs::locales_info) {
|
||||
locales.push_back(entry.first);
|
||||
}
|
||||
std::sort(locales.begin(), locales.end());
|
||||
for (std::string& name : locales) {
|
||||
auto& locale = langs::locales_info.at(name);
|
||||
std::string& fullName = locale.name;
|
||||
|
||||
auto button = std::make_shared<Button>(
|
||||
util::str2wstr_utf8(fullName),
|
||||
glm::vec4(10.f),
|
||||
[=](GUI*) {
|
||||
engine->setLanguage(name);
|
||||
menu->back();
|
||||
}
|
||||
);
|
||||
panel->add(button);
|
||||
}
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
void menus::open_world(std::string name, Engine* engine, bool confirmConvert) {
|
||||
auto paths = engine->getPaths();
|
||||
auto folder = paths->getWorldsFolder()/fs::u8path(name);
|
||||
try {
|
||||
engine->loadWorldContent(folder);
|
||||
} catch (const contentpack_error& error) {
|
||||
// could not to find or read pack
|
||||
guiutil::alert(
|
||||
engine->getGUI(), langs::get(L"error.pack-not-found")+L": "+
|
||||
util::str2wstr_utf8(error.getPackId())
|
||||
);
|
||||
return;
|
||||
} catch (const std::runtime_error& error) {
|
||||
guiutil::alert(
|
||||
engine->getGUI(), langs::get(L"Content Error", L"menu")+L": "+
|
||||
util::str2wstr_utf8(error.what())
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& packs = engine->getContentPacks();
|
||||
auto* content = engine->getContent();
|
||||
auto& settings = engine->getSettings();
|
||||
|
||||
std::shared_ptr<ContentLUT> lut (World::checkIndices(folder, content));
|
||||
if (lut) {
|
||||
if (lut->hasMissingContent()) {
|
||||
show_content_missing(engine, content, lut);
|
||||
} else {
|
||||
if (confirmConvert) {
|
||||
show_process_panel(engine, std::make_shared<WorldConverter>(folder, content, lut), [=](){
|
||||
open_world(name, engine, false);
|
||||
});
|
||||
} else {
|
||||
show_convert_request(engine, content, lut, folder, [=](){
|
||||
open_world(name, engine, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Level* level = World::load(folder, settings, content, packs);
|
||||
level->world->wfile->createDirectories();
|
||||
engine->setScreen(std::make_shared<LevelScreen>(engine, level));
|
||||
} catch (const world_load_error& error) {
|
||||
guiutil::alert(
|
||||
engine->getGUI(), langs::get(L"Error")+L": "+
|
||||
util::str2wstr_utf8(error.what())
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Panel> create_worlds_panel(Engine* engine) {
|
||||
auto panel = std::make_shared<Panel>(glm::vec2(390, 0), glm::vec4(5.0f));
|
||||
panel->setColor(glm::vec4(1.0f, 1.0f, 1.0f, 0.07f));
|
||||
panel->setMaxLength(400);
|
||||
|
||||
auto paths = engine->getPaths();
|
||||
|
||||
for (auto folder : paths->scanForWorlds()) {
|
||||
auto name = folder.filename().u8string();
|
||||
auto namews = util::str2wstr_utf8(name);
|
||||
|
||||
auto btn = std::make_shared<RichButton>(glm::vec2(390, 46));
|
||||
btn->setColor(glm::vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
btn->setHoverColor(glm::vec4(0.09f, 0.17f, 0.2f, 0.6f));
|
||||
btn->listenAction([=](GUI*) {
|
||||
menus::open_world(name, engine, false);
|
||||
});
|
||||
btn->add(std::make_shared<Label>(namews), glm::vec2(8, 8));
|
||||
|
||||
auto image = std::make_shared<Image>("gui/delete_icon", glm::vec2(32, 32));
|
||||
image->setColor(glm::vec4(1, 1, 1, 0.5f));
|
||||
|
||||
auto delbtn = std::make_shared<Button>(image, glm::vec4(2));
|
||||
delbtn->setColor(glm::vec4(0.0f));
|
||||
delbtn->setHoverColor(glm::vec4(1.0f, 1.0f, 1.0f, 0.17f));
|
||||
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);
|
||||
});
|
||||
});
|
||||
btn->add(delbtn, glm::vec2(330, 3));
|
||||
|
||||
panel->add(btn);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
void create_main_menu_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
|
||||
auto panel = menus::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(std::make_shared<Button>(
|
||||
langs::get(L"Quit", L"menu"), glm::vec4(10.f), [](GUI*) {
|
||||
Window::setShouldClose(true);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
void menus::create_menus(Engine* engine) {
|
||||
menus::generatorID = WorldGenerators::getDefaultGeneratorID();
|
||||
create_new_world_panel(engine);
|
||||
create_settings_panel(engine);
|
||||
create_languages_panel(engine);
|
||||
create_main_menu_panel(engine);
|
||||
create_world_generators_panel(engine);
|
||||
}
|
||||
|
||||
void menus::refresh_menus(Engine* engine) {
|
||||
create_main_menu_panel(engine);
|
||||
create_new_world_panel(engine);
|
||||
create_world_generators_panel(engine);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef FRONTEND_MENU_MENU_H_
|
||||
#define FRONTEND_MENU_MENU_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include "../../content/ContentPack.h"
|
||||
|
||||
|
||||
namespace gui {
|
||||
class Panel;
|
||||
}
|
||||
|
||||
class Engine;
|
||||
class LevelController;
|
||||
|
||||
using packconsumer = std::function<void(const ContentPack& pack)>;
|
||||
|
||||
namespace menus {
|
||||
// implemented in menu_settings.cpp
|
||||
extern void create_settings_panel(Engine* engine);
|
||||
|
||||
// implemented in menu_create_world.cpp
|
||||
extern void create_new_world_panel(Engine* engine);
|
||||
|
||||
// implemented in menu_create_world.cpp
|
||||
extern void create_world_generators_panel(Engine* engine);
|
||||
|
||||
// implemented in menu_pause.cpp
|
||||
extern void create_pause_panel(Engine* engine, LevelController* controller);
|
||||
extern std::shared_ptr<gui::Panel> create_packs_panel(
|
||||
const std::vector<ContentPack>& packs,
|
||||
Engine* engine,
|
||||
bool backbutton,
|
||||
packconsumer callback,
|
||||
packconsumer remover
|
||||
);
|
||||
|
||||
/// @brief Load world, convert if required and set to LevelScreen.
|
||||
/// @param name world name
|
||||
/// @param engine engine instance
|
||||
/// @param confirmConvert automatically confirm convert if requested
|
||||
void open_world(std::string name, Engine* engine, bool confirmConvert);
|
||||
|
||||
/// @brief Create development version label at the top-right screen corner
|
||||
void create_version_label(Engine* engine);
|
||||
void create_menus(Engine* engine);
|
||||
void refresh_menus(Engine* engine);
|
||||
}
|
||||
|
||||
#endif // FRONTEND_MENU_MENU_H_
|
||||
@@ -1,42 +0,0 @@
|
||||
#include "menu_commons.h"
|
||||
|
||||
#include "../../engine.h"
|
||||
#include "../gui/containers.h"
|
||||
#include "../locale/langs.h"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
std::shared_ptr<Label> menus::create_label(wstringsupplier supplier) {
|
||||
auto label = std::make_shared<Label>(L"-");
|
||||
label->textSupplier(supplier);
|
||||
return label;
|
||||
}
|
||||
|
||||
std::shared_ptr<Panel> menus::create_page(
|
||||
Engine* engine,
|
||||
std::string name,
|
||||
int width,
|
||||
float opacity,
|
||||
int interval
|
||||
) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = std::make_shared<Panel>(
|
||||
glm::vec2(width, 200), glm::vec4(8.0f), interval
|
||||
);
|
||||
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, opacity));
|
||||
menu->addPage(name, panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
std::shared_ptr<Button> menus::create_button(
|
||||
std::wstring text,
|
||||
glm::vec4 padding,
|
||||
glm::vec4 margin,
|
||||
gui::onaction action
|
||||
) {
|
||||
auto btn = std::make_shared<Button>(
|
||||
langs::get(text, L"menu"), padding, action
|
||||
);
|
||||
btn->setMargin(margin);
|
||||
return btn;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef FRONTEND_MENU_MENU_COMMONS_H_
|
||||
#define FRONTEND_MENU_MENU_COMMONS_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../gui/controls.h"
|
||||
|
||||
class Engine;
|
||||
|
||||
namespace menus {
|
||||
extern std::shared_ptr<gui::Label> create_label(wstringsupplier supplier);
|
||||
extern std::shared_ptr<gui::Panel> create_page(
|
||||
Engine* engine,
|
||||
std::string name,
|
||||
int width,
|
||||
float opacity,
|
||||
int interval
|
||||
);
|
||||
extern std::shared_ptr<gui::Button> create_button(
|
||||
std::wstring text,
|
||||
glm::vec4 padding,
|
||||
glm::vec4 margin,
|
||||
gui::onaction action
|
||||
);
|
||||
}
|
||||
|
||||
#endif // FRONTEND_MENU_MENU_COMMONS_H_
|
||||
@@ -1,162 +0,0 @@
|
||||
#ifndef FRONTEND_MENU_MENU_CREATE_WORLD_H_
|
||||
#define FRONTEND_MENU_MENU_CREATE_WORLD_H_
|
||||
|
||||
#include "menu.h"
|
||||
#include "menu_commons.h"
|
||||
#include "../gui/controls.h"
|
||||
#include "../gui/containers.h"
|
||||
#include "../gui/gui_util.h"
|
||||
#include "../locale/langs.h"
|
||||
#include "../screens.h"
|
||||
#include "../../world/WorldGenerators.h"
|
||||
#include "../../files/WorldFiles.h"
|
||||
#include "../../world/World.h"
|
||||
#include "../../world/Level.h"
|
||||
#include "../../util/stringutil.h"
|
||||
#include "../../engine.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace gui;
|
||||
|
||||
std::shared_ptr<gui::Button> generatorTypeButton;
|
||||
|
||||
namespace menus {
|
||||
std::string generatorID;
|
||||
}
|
||||
|
||||
inline uint64_t randU64() {
|
||||
srand(time(NULL));
|
||||
return rand() ^ (rand() << 8) ^
|
||||
(rand() << 16) ^ (rand() << 24) ^
|
||||
((uint64_t)rand() << 32) ^
|
||||
((uint64_t)rand() << 40) ^
|
||||
((uint64_t)rand() << 56);
|
||||
}
|
||||
|
||||
inline uint64_t str2seed(std::wstring seedstr) {
|
||||
if (util::is_integer(seedstr)) {
|
||||
try {
|
||||
return std::stoull(seedstr);
|
||||
} catch (const std::out_of_range& err) {
|
||||
std::hash<std::wstring> hash;
|
||||
return hash(seedstr);
|
||||
}
|
||||
} else {
|
||||
std::hash<std::wstring> hash;
|
||||
return hash(seedstr);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string translate_generator_id(std::string& id) {
|
||||
int delimiterPosition = id.find(":");
|
||||
std::string pack = id.substr(0, delimiterPosition);
|
||||
std::string generator = id.substr(delimiterPosition + 1);
|
||||
|
||||
if(pack == "core") {
|
||||
return util::wstr2str_utf8(langs::get(util::str2wstr_utf8(generator), L"world.generators"));
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
void menus::create_world_generators_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = menus::create_page(engine, "world_generators", 400, 0.5f, 1);
|
||||
panel->setScrollable(true);
|
||||
|
||||
std::vector<std::string> generatorsIDs = WorldGenerators::getGeneratorsIDs();
|
||||
std::sort(generatorsIDs.begin(), generatorsIDs.end());
|
||||
for (std::string& id : generatorsIDs) {
|
||||
const std::string& fullName = translate_generator_id(id);
|
||||
auto button = std::make_shared<RichButton>(glm::vec2(80, 30));
|
||||
|
||||
auto idlabel = std::make_shared<Label>("["+id+"]");
|
||||
idlabel->setColor(glm::vec4(1, 1, 1, 0.5f));
|
||||
idlabel->setSize(glm::vec2(300, 25));
|
||||
idlabel->setAlign(Align::right);
|
||||
|
||||
button->add(idlabel, glm::vec2(80, 4));
|
||||
button->add(std::make_shared<Label>(fullName), glm::vec2(0, 8));
|
||||
button->listenAction(
|
||||
[=](GUI*) {
|
||||
menus::generatorID = id;
|
||||
generatorTypeButton->setText(langs::get(L"World generator", L"world") + (L": ") + util::str2wstr_utf8(translate_generator_id(menus::generatorID)));
|
||||
menu->back();
|
||||
}
|
||||
);
|
||||
panel->add(button);
|
||||
}
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
void menus::create_new_world_panel(Engine* engine) {
|
||||
auto panel = menus::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", glm::vec4(6.0f));
|
||||
nameInput->setTextValidator([=](const std::wstring& text) {
|
||||
EnginePaths* paths = engine->getPaths();
|
||||
std::string textutf8 = util::wstr2str_utf8(text);
|
||||
return util::is_valid_filename(text) &&
|
||||
!paths->isWorldNameUsed(textutf8);
|
||||
});
|
||||
panel->add(nameInput);
|
||||
|
||||
panel->add(std::make_shared<Label>(langs::get(L"Seed", L"world")));
|
||||
auto seedstr = std::to_wstring(randU64());
|
||||
auto seedInput = std::make_shared<TextBox>(seedstr, glm::vec4(6.0f));
|
||||
panel->add(seedInput);
|
||||
|
||||
generatorTypeButton = guiutil::gotoButton(langs::get(L"World generator", L"world") + (L": ") + util::str2wstr_utf8(translate_generator_id(menus::generatorID)), "world_generators", engine->getGUI()->getMenu());
|
||||
panel->add(generatorTypeButton);
|
||||
|
||||
panel->add(menus::create_button(L"Create World", glm::vec4(10), glm::vec4(1, 20, 1, 1),
|
||||
[=](GUI*) {
|
||||
if (!nameInput->validate())
|
||||
return;
|
||||
|
||||
std::string name = util::wstr2str_utf8(nameInput->getText());
|
||||
uint64_t seed = str2seed(seedInput->getText());
|
||||
std::cout << "world seed: " << seed << std::endl;
|
||||
|
||||
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()
|
||||
)
|
||||
);
|
||||
return;
|
||||
} catch (const std::runtime_error& error) {
|
||||
guiutil::alert(
|
||||
engine->getGUI(),
|
||||
langs::get(L"Content Error", L"menu")+
|
||||
L": "+util::str2wstr_utf8(error.what())
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Level* level = World::create(
|
||||
name, menus::generatorID, folder, seed,
|
||||
engine->getSettings(),
|
||||
engine->getContent(),
|
||||
engine->getContentPacks()
|
||||
);
|
||||
level->world->wfile->createDirectories();
|
||||
menus::generatorID = WorldGenerators::getDefaultGeneratorID();
|
||||
engine->setScreen(std::make_shared<LevelScreen>(engine, level));
|
||||
}));
|
||||
panel->add(guiutil::backButton(engine->getGUI()->getMenu()));
|
||||
}
|
||||
|
||||
#endif // FRONTEND_MENU_MENU_CREATE_WORLD_H_
|
||||
@@ -1,190 +0,0 @@
|
||||
#include "menu.h"
|
||||
#include "menu_commons.h"
|
||||
#include "../locale/langs.h"
|
||||
#include "../gui/gui_util.h"
|
||||
#include "../screens.h"
|
||||
#include "../../engine.h"
|
||||
#include "../../world/Level.h"
|
||||
#include "../../world/World.h"
|
||||
#include "../../coders/png.h"
|
||||
#include "../../util/stringutil.h"
|
||||
#include "../../files/WorldFiles.h"
|
||||
#include "../../content/ContentLUT.h"
|
||||
#include "../../logic/LevelController.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
using namespace gui;
|
||||
|
||||
std::shared_ptr<Panel> menus::create_packs_panel(
|
||||
const std::vector<ContentPack>& packs,
|
||||
Engine* engine,
|
||||
bool backbutton,
|
||||
packconsumer callback,
|
||||
packconsumer remover
|
||||
){
|
||||
auto assets = engine->getAssets();
|
||||
auto panel = std::make_shared<Panel>(glm::vec2(550, 200), glm::vec4(5.0f));
|
||||
panel->setColor(glm::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>(glm::vec2(540, 80));
|
||||
packpanel->setColor(glm::vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
if (callback) {
|
||||
packpanel->listenAction([=](GUI*) {
|
||||
callback(pack);
|
||||
});
|
||||
}
|
||||
auto runtime = engine->getContent() ? engine->getContent()->getPackRuntime(pack.id) : nullptr;
|
||||
auto idlabel = std::make_shared<Label>(
|
||||
(runtime && runtime->getStats().hasSavingContent())
|
||||
? "*["+pack.id+"]"
|
||||
: "["+pack.id+"]"
|
||||
);
|
||||
idlabel->setColor(glm::vec4(1, 1, 1, 0.5f));
|
||||
idlabel->setSize(glm::vec2(300, 25));
|
||||
idlabel->setAlign(Align::right);
|
||||
packpanel->add(idlabel, glm::vec2(215, 2));
|
||||
|
||||
auto titlelabel = std::make_shared<Label>(pack.title);
|
||||
packpanel->add(titlelabel, glm::vec2(78, 6));
|
||||
|
||||
std::string icon = pack.id+".icon";
|
||||
if (assets->getTexture(icon) == nullptr) {
|
||||
auto iconfile = pack.folder/fs::path("icon.png");
|
||||
if (fs::is_regular_file(iconfile)) {
|
||||
assets->store(png::load_texture(iconfile.string()), icon);
|
||||
} else {
|
||||
icon = "gui/no_icon";
|
||||
}
|
||||
}
|
||||
|
||||
if (!pack.creator.empty()) {
|
||||
auto creatorlabel = std::make_shared<Label>("@"+pack.creator);
|
||||
creatorlabel->setColor(glm::vec4(0.8f, 1.0f, 0.9f, 0.7f));
|
||||
creatorlabel->setSize(glm::vec2(300, 20));
|
||||
creatorlabel->setAlign(Align::right);
|
||||
packpanel->add(creatorlabel, glm::vec2(215, 60));
|
||||
}
|
||||
|
||||
auto descriptionlabel = std::make_shared<Label>(pack.description);
|
||||
descriptionlabel->setColor(glm::vec4(1, 1, 1, 0.7f));
|
||||
packpanel->add(descriptionlabel, glm::vec2(80, 28));
|
||||
|
||||
packpanel->add(std::make_shared<Image>(icon, glm::vec2(64)), glm::vec2(8));
|
||||
|
||||
if (remover && pack.id != "base") {
|
||||
auto remimg = std::make_shared<Image>("gui/cross", glm::vec2(32));
|
||||
remimg->setColor(glm::vec4(1.f, 1.f, 1.f, 0.5f));
|
||||
auto rembtn = std::make_shared<Button>(remimg, glm::vec4(2));
|
||||
rembtn->setColor(glm::vec4(0.0f));
|
||||
rembtn->setHoverColor(glm::vec4(1.0f, 1.0f, 1.0f, 0.17f));
|
||||
rembtn->listenAction([=](GUI* gui) {
|
||||
remover(pack);
|
||||
});
|
||||
packpanel->add(rembtn, glm::vec2(470, 22));
|
||||
}
|
||||
|
||||
panel->add(packpanel);
|
||||
}
|
||||
if (backbutton) {
|
||||
panel->add(guiutil::backButton(engine->getGUI()->getMenu()));
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
static void reopen_world(Engine* engine, World* world) {
|
||||
std::string wname = world->wfile->directory.stem().u8string();
|
||||
engine->setScreen(nullptr);
|
||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
||||
menus::open_world(wname, engine, true);
|
||||
}
|
||||
|
||||
void create_content_panel(Engine* engine, LevelController* controller) {
|
||||
auto level = controller->getLevel();
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto paths = engine->getPaths();
|
||||
auto mainPanel = menus::create_page(engine, "content", 550, 0.0f, 5);
|
||||
|
||||
std::vector<ContentPack> scanned;
|
||||
ContentPack::scan(engine->getPaths(), scanned);
|
||||
for (const auto& pack : engine->getContentPacks()) {
|
||||
for (size_t i = 0; i < scanned.size(); i++) {
|
||||
if (scanned[i].id == pack.id) {
|
||||
scanned.erase(scanned.begin()+i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto panel = menus::create_packs_panel(
|
||||
engine->getContentPacks(), engine, false, nullptr,
|
||||
[=](const ContentPack& pack) {
|
||||
auto world = level->getWorld();
|
||||
auto runtime = engine->getContent()->getPackRuntime(pack.id);
|
||||
if (runtime->getStats().hasSavingContent()) {
|
||||
guiutil::confirm(engine->getGUI(), langs::get(L"remove-confirm", L"pack")+
|
||||
L" ("+util::str2wstr_utf8(pack.id)+L")", [=]() {
|
||||
controller->saveWorld();
|
||||
world->wfile->removePack(world, pack.id);
|
||||
reopen_world(engine, world);
|
||||
});
|
||||
} else {
|
||||
controller->saveWorld();
|
||||
world->wfile->removePack(world, pack.id);
|
||||
reopen_world(engine, world);
|
||||
}
|
||||
}
|
||||
);
|
||||
mainPanel->add(panel);
|
||||
mainPanel->add(menus::create_button(
|
||||
langs::get(L"Add", L"content"), glm::vec4(10.0f), glm::vec4(1), [=](GUI* gui) {
|
||||
auto panel = menus::create_packs_panel(scanned, engine, true,
|
||||
[=](const ContentPack& pack) {
|
||||
auto world = level->getWorld();
|
||||
auto worldFolder = paths->getWorldFolder();
|
||||
for (const auto& dependency : pack.dependencies) {
|
||||
fs::path folder = ContentPack::findPack(paths, worldFolder, dependency);
|
||||
if (!fs::is_directory(folder)) {
|
||||
guiutil::alert(gui, langs::get(L"error.dependency-not-found")+
|
||||
L": "+util::str2wstr_utf8(dependency));
|
||||
return;
|
||||
}
|
||||
if (!world->hasPack(dependency)) {
|
||||
world->wfile->addPack(world, dependency);
|
||||
}
|
||||
}
|
||||
world->wfile->addPack(world, pack.id);
|
||||
controller->saveWorld();
|
||||
reopen_world(engine, world);
|
||||
}, nullptr);
|
||||
menu->addPage("content-packs", panel);
|
||||
menu->setPage("content-packs");
|
||||
}));
|
||||
mainPanel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
void menus::create_pause_panel(Engine* engine, LevelController* controller) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = create_page(engine, "pause", 400, 0.0f, 1);
|
||||
|
||||
panel->add(create_button(L"Continue", glm::vec4(10.0f), glm::vec4(1), [=](GUI*){
|
||||
menu->reset();
|
||||
}));
|
||||
panel->add(create_button(L"Content", glm::vec4(10.0f), glm::vec4(1), [=](GUI*) {
|
||||
create_content_panel(engine, controller);
|
||||
menu->setPage("content");
|
||||
}));
|
||||
panel->add(guiutil::gotoButton(L"Settings", "settings", menu));
|
||||
|
||||
panel->add(create_button(L"Save and Quit to Menu", glm::vec4(10.f), glm::vec4(1), [=](GUI*){
|
||||
// save world
|
||||
controller->saveWorld();
|
||||
// destroy LevelScreen and run quit callbacks
|
||||
engine->setScreen(nullptr);
|
||||
// create and go to menu screen
|
||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
||||
}));
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
#include "menu.h"
|
||||
#include "menu_commons.h"
|
||||
|
||||
#include "../locale/langs.h"
|
||||
#include "../gui/GUI.h"
|
||||
#include "../gui/gui_util.h"
|
||||
#include "../../engine.h"
|
||||
#include "../../util/stringutil.h"
|
||||
#include "../../window/Events.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static void create_volume_trackbar(
|
||||
std::shared_ptr<Panel> panel,
|
||||
const std::wstring& name,
|
||||
float* field
|
||||
) {
|
||||
panel->add(menus::create_label([=]() {
|
||||
return langs::get(name, L"settings")+L": " +
|
||||
std::to_wstring(int(*field*100))+L"%";
|
||||
}));
|
||||
|
||||
auto trackbar = std::make_shared<TrackBar>(0.0, 1.0, 1.0, 0.01, 5);
|
||||
trackbar->setSupplier([=]() {
|
||||
return *field;
|
||||
});
|
||||
trackbar->setConsumer([=](double value) {
|
||||
*field = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
void create_audio_settings_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = menus::create_page(engine, "settings-audio", 400, 0.0f, 1);
|
||||
|
||||
auto& settings = engine->getSettings().audio;
|
||||
create_volume_trackbar(panel, L"Master Volume", &settings.volumeMaster);
|
||||
create_volume_trackbar(panel, L"Regular Sounds", &settings.volumeRegular);
|
||||
create_volume_trackbar(panel, L"UI Sounds", &settings.volumeUI);
|
||||
create_volume_trackbar(panel, L"Ambient", &settings.volumeAmbient);
|
||||
create_volume_trackbar(panel, L"Music", &settings.volumeMusic);
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
static void create_controls_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = menus::create_page(engine, "controls", 400, 0.0f, 1);
|
||||
|
||||
/* Camera sensitivity setting track bar */{
|
||||
panel->add(menus::create_label([=]() {
|
||||
float s = engine->getSettings().camera.sensitivity;
|
||||
return langs::get(L"Mouse Sensitivity", L"settings")+L": "+
|
||||
util::to_wstring(s, 1);
|
||||
}));
|
||||
|
||||
auto trackbar = std::make_shared<TrackBar>(0.1, 10.0, 2.0, 0.1, 4);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().camera.sensitivity;
|
||||
});
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().camera.sensitivity = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
auto scrollPanel = std::make_shared<Panel>(glm::vec2(400, 200), glm::vec4(2.0f), 1.0f);
|
||||
scrollPanel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.3f));
|
||||
scrollPanel->setMaxLength(400);
|
||||
for (auto& entry : Events::bindings){
|
||||
std::string bindname = entry.first;
|
||||
|
||||
auto subpanel = std::make_shared<Panel>(glm::vec2(400, 40), glm::vec4(5.0f), 1.0f);
|
||||
subpanel->setColor(glm::vec4(0.0f));
|
||||
subpanel->setOrientation(Orientation::horizontal);
|
||||
subpanel->add(std::make_shared<InputBindBox>(entry.second));
|
||||
|
||||
auto label = std::make_shared<Label>(langs::get(util::str2wstr_utf8(bindname)));
|
||||
label->setMargin(glm::vec4(6.0f));
|
||||
subpanel->add(label);
|
||||
scrollPanel->add(subpanel);
|
||||
}
|
||||
panel->add(scrollPanel);
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
|
||||
void menus::create_settings_panel(Engine* engine) {
|
||||
create_audio_settings_panel(engine);
|
||||
create_controls_panel(engine);
|
||||
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto panel = menus::create_page(engine, "settings", 400, 0.0f, 1);
|
||||
|
||||
/* Load Distance setting track bar */{
|
||||
panel->add(menus::create_label([=]() {
|
||||
return langs::get(L"Load Distance", L"settings")+L": " +
|
||||
std::to_wstring(engine->getSettings().chunks.loadDistance);
|
||||
}));
|
||||
|
||||
auto trackbar = std::make_shared<TrackBar>(3, 66, 10, 1, 3);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().chunks.loadDistance;
|
||||
});
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().chunks.loadDistance = static_cast<uint>(value);
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* Load Speed setting track bar */{
|
||||
panel->add(menus::create_label([=]() {
|
||||
return langs::get(L"Load Speed", L"settings")+L": " +
|
||||
std::to_wstring(engine->getSettings().chunks.loadSpeed);
|
||||
}));
|
||||
|
||||
auto trackbar = std::make_shared<TrackBar>(1, 32, 10, 1, 1);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().chunks.loadSpeed;
|
||||
});
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().chunks.loadSpeed = static_cast<uint>(value);
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* Fog Curve setting track bar */{
|
||||
panel->add(menus::create_label([=]() {
|
||||
float value = engine->getSettings().graphics.fogCurve;
|
||||
return langs::get(L"Fog Curve", L"settings")+L": " +
|
||||
util::to_wstring(value, 1);
|
||||
}));
|
||||
|
||||
auto trackbar = std::make_shared<TrackBar>(1.0, 6.0, 1.0, 0.1, 2);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().graphics.fogCurve;
|
||||
});
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().graphics.fogCurve = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* Fov setting track bar */{
|
||||
panel->add(menus::create_label([=]() {
|
||||
int fov = (int)engine->getSettings().camera.fov;
|
||||
return langs::get(L"FOV", L"settings")+L": "+std::to_wstring(fov)+L"°";
|
||||
}));
|
||||
|
||||
auto trackbar = std::make_shared<TrackBar>(30.0, 120.0, 90, 1, 4);
|
||||
trackbar->setSupplier([=]() {
|
||||
return engine->getSettings().camera.fov;
|
||||
});
|
||||
trackbar->setConsumer([=](double value) {
|
||||
engine->getSettings().camera.fov = value;
|
||||
});
|
||||
panel->add(trackbar);
|
||||
}
|
||||
|
||||
/* V-Sync checkbox */{
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
langs::get(L"V-Sync", L"settings"), glm::vec2(400, 32)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().display.swapInterval != 0;
|
||||
});
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
engine->getSettings().display.swapInterval = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
|
||||
/* Backlight checkbox */{
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
langs::get(L"Backlight", L"settings"), glm::vec2(400, 32)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return engine->getSettings().graphics.backlight;
|
||||
});
|
||||
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"), glm::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": "+
|
||||
util::str2wstr_utf8(langName),
|
||||
"languages", menu));
|
||||
|
||||
panel->add(guiutil::gotoButton(L"Audio", "settings-audio", menu));
|
||||
panel->add(guiutil::gotoButton(L"Controls", "controls", menu));
|
||||
panel->add(guiutil::backButton(menu));
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
#include "screens.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../audio/audio.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../window/Events.h"
|
||||
#include "../window/input.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Batch2D.h"
|
||||
#include "../graphics/GfxContext.h"
|
||||
#include "../graphics/TextureAnimation.h"
|
||||
#include "../assets/Assets.h"
|
||||
#include "../world/Level.h"
|
||||
#include "../world/World.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../physics/Hitbox.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"
|
||||
#include "../util/stringutil.h"
|
||||
#include "../core_defs.h"
|
||||
#include "WorldRenderer.h"
|
||||
#include "hud.h"
|
||||
#include "ContentGfxCache.h"
|
||||
#include "LevelFrontend.h"
|
||||
#include "gui/GUI.h"
|
||||
#include "gui/containers.h"
|
||||
#include "menu/menu.h"
|
||||
|
||||
#include "../content/Content.h"
|
||||
#include "../voxels/Block.h"
|
||||
|
||||
Screen::Screen(Engine* engine) : engine(engine), batch(new Batch2D(1024)) {
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
}
|
||||
|
||||
MenuScreen::MenuScreen(Engine* engine_) : Screen(engine_) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
menus::refresh_menus(engine);
|
||||
menu->reset();
|
||||
menu->setPage("main");
|
||||
|
||||
uicamera.reset(new Camera(glm::vec3(), Window::height));
|
||||
uicamera->perspective = false;
|
||||
uicamera->flipped = true;
|
||||
}
|
||||
|
||||
MenuScreen::~MenuScreen() {
|
||||
}
|
||||
|
||||
void MenuScreen::update(float delta) {
|
||||
}
|
||||
|
||||
void MenuScreen::draw(float delta) {
|
||||
Window::clear();
|
||||
Window::setBgColor(glm::vec3(0.2f));
|
||||
|
||||
uicamera->setFov(Window::height);
|
||||
Shader* uishader = engine->getAssets()->getShader("ui");
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||
|
||||
uint width = Window::width;
|
||||
uint height = Window::height;
|
||||
|
||||
batch->begin();
|
||||
batch->texture(engine->getAssets()->getTexture("gui/menubg"));
|
||||
batch->rect(
|
||||
0, 0,
|
||||
width, height, 0, 0, 0,
|
||||
UVRegion(0, 0, width/64, height/64),
|
||||
false, false, glm::vec4(1.0f)
|
||||
);
|
||||
batch->flush();
|
||||
}
|
||||
|
||||
static bool backlight;
|
||||
|
||||
LevelScreen::LevelScreen(Engine* engine, Level* level) : Screen(engine) {
|
||||
auto& settings = engine->getSettings();
|
||||
auto assets = engine->getAssets();
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
menu->reset();
|
||||
|
||||
controller = std::make_unique<LevelController>(settings, level);
|
||||
frontend = std::make_unique<LevelFrontend>(controller.get(), assets);
|
||||
|
||||
worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get(), controller->getPlayer());
|
||||
hud = std::make_unique<Hud>(engine, frontend.get(), controller->getPlayer());
|
||||
|
||||
|
||||
backlight = settings.graphics.backlight;
|
||||
|
||||
animator = std::make_unique<TextureAnimator>();
|
||||
animator->addAnimations(assets->getAnimations());
|
||||
|
||||
auto content = level->content;
|
||||
for (auto& entry : content->getPacks()) {
|
||||
auto pack = entry.second.get();
|
||||
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() {
|
||||
scripting::on_frontend_close();
|
||||
controller->onWorldQuit();
|
||||
engine->getPaths()->setWorldFolder(fs::path());
|
||||
}
|
||||
|
||||
void LevelScreen::updateHotkeys() {
|
||||
auto& settings = engine->getSettings();
|
||||
if (Events::jpressed(keycode::O)) {
|
||||
settings.graphics.frustumCulling = !settings.graphics.frustumCulling;
|
||||
}
|
||||
if (Events::jpressed(keycode::F1)) {
|
||||
hudVisible = !hudVisible;
|
||||
}
|
||||
if (Events::jpressed(keycode::F3)) {
|
||||
controller->getPlayer()->debug = !controller->getPlayer()->debug;
|
||||
}
|
||||
if (Events::jpressed(keycode::F5)) {
|
||||
controller->getLevel()->chunks->saveAndClear();
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::update(float delta) {
|
||||
gui::GUI* gui = engine->getGUI();
|
||||
|
||||
bool inputLocked = hud->isPause() ||
|
||||
hud->isInventoryOpen() ||
|
||||
gui->isFocusCaught();
|
||||
if (!gui->isFocusCaught()) {
|
||||
updateHotkeys();
|
||||
}
|
||||
|
||||
auto player = controller->getPlayer();
|
||||
auto camera = player->camera;
|
||||
|
||||
bool paused = hud->isPause();
|
||||
audio::get_channel("regular")->setPaused(paused);
|
||||
audio::get_channel("ambient")->setPaused(paused);
|
||||
audio::set_listener(
|
||||
camera->position-camera->dir,
|
||||
player->hitbox->velocity,
|
||||
camera->dir,
|
||||
camera->up
|
||||
);
|
||||
|
||||
// TODO: subscribe for setting change
|
||||
EngineSettings& settings = engine->getSettings();
|
||||
controller->getPlayer()->camera->setFov(glm::radians(settings.camera.fov));
|
||||
if (settings.graphics.backlight != backlight) {
|
||||
controller->getLevel()->chunks->saveAndClear();
|
||||
backlight = settings.graphics.backlight;
|
||||
}
|
||||
|
||||
if (!hud->isPause()) {
|
||||
controller->getLevel()->world->updateTimers(delta);
|
||||
animator->update(delta);
|
||||
}
|
||||
controller->update(delta, !inputLocked, hud->isPause());
|
||||
hud->update(hudVisible);
|
||||
}
|
||||
|
||||
void LevelScreen::draw(float delta) {
|
||||
auto camera = controller->getPlayer()->currentCamera;
|
||||
|
||||
Viewport viewport(Window::width, Window::height);
|
||||
GfxContext ctx(nullptr, viewport, batch.get());
|
||||
|
||||
worldRenderer->draw(ctx, camera.get(), hudVisible);
|
||||
|
||||
if (hudVisible) {
|
||||
hud->draw(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::onEngineShutdown() {
|
||||
controller->saveWorld();
|
||||
}
|
||||
|
||||
LevelController* LevelScreen::getLevelController() const {
|
||||
return controller.get();
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef FRONTEND_SCREENS_H_
|
||||
#define FRONTEND_SCREENS_H_
|
||||
|
||||
#include <memory>
|
||||
#include "../settings.h"
|
||||
|
||||
class Assets;
|
||||
class Level;
|
||||
class WorldRenderer;
|
||||
class Hud;
|
||||
class Engine;
|
||||
class Camera;
|
||||
class Batch2D;
|
||||
class LevelFrontend;
|
||||
class LevelController;
|
||||
class TextureAnimator;
|
||||
|
||||
/// @brief Screen is a mainloop state
|
||||
class Screen {
|
||||
protected:
|
||||
Engine* engine;
|
||||
std::unique_ptr<Batch2D> batch;
|
||||
public:
|
||||
Screen(Engine* engine);
|
||||
virtual ~Screen();
|
||||
virtual void update(float delta) = 0;
|
||||
virtual void draw(float delta) = 0;
|
||||
virtual void onEngineShutdown() {};
|
||||
};
|
||||
|
||||
class MenuScreen : public Screen {
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
public:
|
||||
MenuScreen(Engine* engine);
|
||||
~MenuScreen();
|
||||
|
||||
void update(float delta) override;
|
||||
void draw(float delta) override;
|
||||
};
|
||||
|
||||
class LevelScreen : public Screen {
|
||||
std::unique_ptr<LevelFrontend> frontend;
|
||||
std::unique_ptr<Hud> hud;
|
||||
std::unique_ptr<WorldRenderer> worldRenderer;
|
||||
std::unique_ptr<LevelController> controller;
|
||||
std::unique_ptr<TextureAnimator> animator;
|
||||
|
||||
bool hudVisible = true;
|
||||
void updateHotkeys();
|
||||
public:
|
||||
LevelScreen(Engine* engine, Level* level);
|
||||
~LevelScreen();
|
||||
|
||||
void update(float delta) override;
|
||||
void draw(float delta) override;
|
||||
|
||||
void onEngineShutdown() override;
|
||||
|
||||
LevelController* getLevelController() const;
|
||||
};
|
||||
|
||||
#endif // FRONTEND_SCREENS_H_
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "LevelScreen.hpp"
|
||||
|
||||
#include "../hud.hpp"
|
||||
#include "../LevelFrontend.hpp"
|
||||
#include "../../debug/Logger.hpp"
|
||||
#include "../../audio/audio.hpp"
|
||||
#include "../../coders/imageio.hpp"
|
||||
#include "../../graphics/core/PostProcessing.hpp"
|
||||
#include "../../graphics/core/DrawContext.hpp"
|
||||
#include "../../graphics/core/Viewport.hpp"
|
||||
#include "../../graphics/core/ImageData.hpp"
|
||||
#include "../../graphics/ui/GUI.hpp"
|
||||
#include "../../graphics/ui/elements/Menu.hpp"
|
||||
#include "../../graphics/render/WorldRenderer.hpp"
|
||||
#include "../../logic/LevelController.hpp"
|
||||
#include "../../logic/scripting/scripting_hud.hpp"
|
||||
#include "../../physics/Hitbox.hpp"
|
||||
#include "../../voxels/Chunks.hpp"
|
||||
#include "../../world/Level.hpp"
|
||||
#include "../../world/World.hpp"
|
||||
#include "../../window/Camera.hpp"
|
||||
#include "../../window/Events.hpp"
|
||||
#include "../../window/Window.hpp"
|
||||
#include "../../engine.hpp"
|
||||
|
||||
static debug::Logger logger("level-screen");
|
||||
|
||||
LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> level)
|
||||
: Screen(engine), postProcessing(std::make_unique<PostProcessing>())
|
||||
{
|
||||
auto& settings = engine->getSettings();
|
||||
auto assets = engine->getAssets();
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
menu->reset();
|
||||
|
||||
controller = std::make_unique<LevelController>(settings, std::move(level));
|
||||
frontend = std::make_unique<LevelFrontend>(controller.get(), assets);
|
||||
|
||||
worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get(), controller->getPlayer());
|
||||
hud = std::make_unique<Hud>(engine, frontend.get(), controller->getPlayer());
|
||||
|
||||
keepAlive(settings.graphics.backlight.observe([=](bool) {
|
||||
controller->getLevel()->chunks->saveAndClear();
|
||||
}));
|
||||
keepAlive(settings.camera.fov.observe([=](double value) {
|
||||
controller->getPlayer()->camera->setFov(glm::radians(value));
|
||||
}));
|
||||
|
||||
animator = std::make_unique<TextureAnimator>();
|
||||
animator->addAnimations(assets->getAnimations());
|
||||
|
||||
initializeContent();
|
||||
}
|
||||
|
||||
void LevelScreen::initializeContent() {
|
||||
auto content = controller->getLevel()->content;
|
||||
for (auto& entry : content->getPacks()) {
|
||||
auto pack = entry.second.get();
|
||||
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(), info.id, scriptFile);
|
||||
}
|
||||
}
|
||||
scripting::on_frontend_init(hud.get());
|
||||
}
|
||||
|
||||
LevelScreen::~LevelScreen() {
|
||||
saveWorldPreview();
|
||||
scripting::on_frontend_close();
|
||||
controller->onWorldQuit();
|
||||
engine->getPaths()->setWorldFolder(fs::path());
|
||||
}
|
||||
|
||||
void LevelScreen::saveWorldPreview() {
|
||||
try {
|
||||
logger.info() << "saving world preview";
|
||||
auto paths = engine->getPaths();
|
||||
auto player = controller->getPlayer();
|
||||
auto& settings = engine->getSettings();
|
||||
int previewSize = settings.ui.worldPreviewSize.get();
|
||||
|
||||
// camera special copy for world preview
|
||||
Camera camera = *player->camera;
|
||||
camera.setFov(glm::radians(70.0f));
|
||||
Viewport viewport(previewSize * 1.5, previewSize);
|
||||
DrawContext ctx(nullptr, viewport, batch.get());
|
||||
|
||||
worldRenderer->draw(ctx, &camera, false, postProcessing.get());
|
||||
auto image = postProcessing->toImage();
|
||||
image->flipY();
|
||||
imageio::write(paths->resolve("world:preview.png").u8string(), image.get());
|
||||
} catch (const std::exception& err) {
|
||||
logger.error() << err.what();
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::updateHotkeys() {
|
||||
auto& settings = engine->getSettings();
|
||||
if (Events::jpressed(keycode::O)) {
|
||||
settings.graphics.frustumCulling.toggle();
|
||||
}
|
||||
if (Events::jpressed(keycode::F1)) {
|
||||
hudVisible = !hudVisible;
|
||||
}
|
||||
if (Events::jpressed(keycode::F3)) {
|
||||
controller->getPlayer()->debug = !controller->getPlayer()->debug;
|
||||
}
|
||||
if (Events::jpressed(keycode::F5)) {
|
||||
controller->getLevel()->chunks->saveAndClear();
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::update(float delta) {
|
||||
gui::GUI* gui = engine->getGUI();
|
||||
|
||||
bool inputLocked = hud->isPause() ||
|
||||
hud->isInventoryOpen() ||
|
||||
gui->isFocusCaught();
|
||||
if (!gui->isFocusCaught()) {
|
||||
updateHotkeys();
|
||||
}
|
||||
|
||||
auto player = controller->getPlayer();
|
||||
auto camera = player->camera;
|
||||
|
||||
bool paused = hud->isPause();
|
||||
audio::get_channel("regular")->setPaused(paused);
|
||||
audio::get_channel("ambient")->setPaused(paused);
|
||||
audio::set_listener(
|
||||
camera->position-camera->dir,
|
||||
player->hitbox->velocity,
|
||||
camera->dir,
|
||||
glm::vec3(0, 1, 0)
|
||||
);
|
||||
|
||||
if (!hud->isPause()) {
|
||||
controller->getLevel()->getWorld()->updateTimers(delta);
|
||||
animator->update(delta);
|
||||
}
|
||||
controller->update(delta, !inputLocked, hud->isPause());
|
||||
hud->update(hudVisible);
|
||||
}
|
||||
|
||||
void LevelScreen::draw(float) {
|
||||
auto camera = controller->getPlayer()->currentCamera;
|
||||
|
||||
Viewport viewport(Window::width, Window::height);
|
||||
DrawContext ctx(nullptr, viewport, batch.get());
|
||||
|
||||
worldRenderer->draw(ctx, camera.get(), hudVisible, postProcessing.get());
|
||||
|
||||
if (hudVisible) {
|
||||
hud->draw(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::onEngineShutdown() {
|
||||
controller->saveWorld();
|
||||
}
|
||||
|
||||
LevelController* LevelScreen::getLevelController() const {
|
||||
return controller.get();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef FRONTEND_SCREENS_LEVEL_SCREEN_HPP_
|
||||
#define FRONTEND_SCREENS_LEVEL_SCREEN_HPP_
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Engine;
|
||||
class LevelFrontend;
|
||||
class Hud;
|
||||
class LevelController;
|
||||
class WorldRenderer;
|
||||
class TextureAnimator;
|
||||
class PostProcessing;
|
||||
class Level;
|
||||
|
||||
class LevelScreen : public Screen {
|
||||
std::unique_ptr<LevelFrontend> frontend;
|
||||
std::unique_ptr<Hud> hud;
|
||||
std::unique_ptr<LevelController> controller;
|
||||
std::unique_ptr<WorldRenderer> worldRenderer;
|
||||
std::unique_ptr<TextureAnimator> animator;
|
||||
std::unique_ptr<PostProcessing> postProcessing;
|
||||
|
||||
void saveWorldPreview();
|
||||
|
||||
bool hudVisible = true;
|
||||
void updateHotkeys();
|
||||
void initializeContent();
|
||||
public:
|
||||
LevelScreen(Engine* engine, std::unique_ptr<Level> level);
|
||||
~LevelScreen();
|
||||
|
||||
void update(float delta) override;
|
||||
void draw(float delta) override;
|
||||
|
||||
void onEngineShutdown() override;
|
||||
|
||||
LevelController* getLevelController() const;
|
||||
};
|
||||
|
||||
#endif // FRONTEND_SCREENS_LEVEL_SCREEN_HPP_
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "MenuScreen.hpp"
|
||||
|
||||
#include "../../graphics/ui/GUI.hpp"
|
||||
#include "../../graphics/ui/elements/Menu.hpp"
|
||||
#include "../../graphics/core/Batch2D.hpp"
|
||||
#include "../../graphics/core/Shader.hpp"
|
||||
#include "../../graphics/core/Texture.hpp"
|
||||
#include "../../window/Window.hpp"
|
||||
#include "../../window/Camera.hpp"
|
||||
#include "../../engine.hpp"
|
||||
|
||||
MenuScreen::MenuScreen(Engine* engine) : Screen(engine) {
|
||||
engine->resetContent();
|
||||
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
menu->reset();
|
||||
menu->setPage("main");
|
||||
|
||||
uicamera = std::make_unique<Camera>(glm::vec3(), Window::height);
|
||||
uicamera->perspective = false;
|
||||
uicamera->flipped = true;
|
||||
}
|
||||
|
||||
MenuScreen::~MenuScreen() {
|
||||
}
|
||||
|
||||
void MenuScreen::update(float delta) {
|
||||
}
|
||||
|
||||
void MenuScreen::draw(float delta) {
|
||||
auto assets = engine->getAssets();
|
||||
|
||||
Window::clear();
|
||||
Window::setBgColor(glm::vec3(0.2f));
|
||||
|
||||
uicamera->setFov(Window::height);
|
||||
Shader* uishader = assets->getShader("ui");
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||
|
||||
uint width = Window::width;
|
||||
uint height = Window::height;
|
||||
|
||||
auto bg = assets->getTexture("gui/menubg");
|
||||
batch->begin();
|
||||
batch->texture(bg);
|
||||
batch->rect(
|
||||
0, 0,
|
||||
width, height, 0, 0, 0,
|
||||
UVRegion(0, 0, width/bg->getWidth(), height/bg->getHeight()),
|
||||
false, false, glm::vec4(1.0f)
|
||||
);
|
||||
batch->flush();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef FRONTEND_SCREENS_MENU_SCREEN_HPP_
|
||||
#define FRONTEND_SCREENS_MENU_SCREEN_HPP_
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Camera;
|
||||
class Engine;
|
||||
|
||||
class MenuScreen : public Screen {
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
public:
|
||||
MenuScreen(Engine* engine);
|
||||
~MenuScreen();
|
||||
|
||||
void update(float delta) override;
|
||||
void draw(float delta) override;
|
||||
};
|
||||
|
||||
|
||||
#endif // FRONTEND_SCREENS_MENU_SCREEN_HPP_
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "Screen.hpp"
|
||||
|
||||
#include "../../graphics/core/Batch2D.hpp"
|
||||
#include "../../engine.hpp"
|
||||
|
||||
Screen::Screen(Engine* engine) : engine(engine), batch(new Batch2D(1024)) {
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef FRONTEND_SCREENS_SCREEN_HPP_
|
||||
#define FRONTEND_SCREENS_SCREEN_HPP_
|
||||
|
||||
#include "../../util/ObjectsKeeper.hpp"
|
||||
|
||||
class Engine;
|
||||
class Batch2D;
|
||||
|
||||
/// @brief Screen is a mainloop state
|
||||
class Screen : public util::ObjectsKeeper {
|
||||
protected:
|
||||
Engine* engine;
|
||||
std::unique_ptr<Batch2D> batch;
|
||||
public:
|
||||
Screen(Engine* engine);
|
||||
virtual ~Screen();
|
||||
virtual void update(float delta) = 0;
|
||||
virtual void draw(float delta) = 0;
|
||||
virtual void onEngineShutdown() {};
|
||||
};
|
||||
|
||||
#endif // FRONTEND_SCREENS_SCREEN_HPP_
|
||||
Reference in New Issue
Block a user