merge main

This commit is contained in:
@clasher113
2024-05-18 16:39:02 +03:00
413 changed files with 17869 additions and 13272 deletions
-145
View File
@@ -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));
}
-35
View File
@@ -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_
+11 -17
View File
@@ -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;
}
-35
View File
@@ -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_
+27
View File
@@ -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_
-478
View File
@@ -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;
});
}
-160
View File
@@ -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_
+16 -18
View File
@@ -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
View File
@@ -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_
-346
View File
@@ -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;
-80
View File
@@ -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
+27 -23
View File
@@ -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);
}
-472
View File
@@ -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;
}
-102
View File
@@ -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
-150
View File
@@ -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();
}
-67
View File
@@ -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_
-206
View File
@@ -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);
}
-58
View File
@@ -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_
-192
View File
@@ -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);
}
-128
View File
@@ -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_
-239
View File
@@ -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();
}
}
-185
View File
@@ -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_
-300
View File
@@ -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};
}
}
-112
View File
@@ -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
-394
View File
@@ -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_
-101
View File
@@ -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>");
}
-38
View File
@@ -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_
-400
View File
@@ -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;
}
-67
View File
@@ -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
View File
@@ -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();
}
+40 -25
View File
@@ -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_
+102
View File
@@ -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);
});
}
+31
View File
@@ -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_
-297
View File
@@ -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);
}
-52
View File
@@ -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_
-42
View File
@@ -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;
}
-29
View File
@@ -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_
-162
View File
@@ -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_
-190
View File
@@ -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));
}));
}
-209
View File
@@ -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));
}
-202
View File
@@ -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();
}
-62
View File
@@ -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_
+165
View File
@@ -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();
}
+42
View File
@@ -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_
+54
View File
@@ -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();
}
+22
View File
@@ -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_
+10
View File
@@ -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() {
}
+22
View File
@@ -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_