Merge branch 'main' into lua_audio

This commit is contained in:
MihailRis 2024-03-06 13:56:40 +03:00
commit dc13c109e6
19 changed files with 709 additions and 483 deletions

View File

@ -16,6 +16,7 @@
#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"
@ -47,7 +48,7 @@ InventoryBuilder::InventoryBuilder() {
/** Add slots grid to inventory view
* @param cols grid columns
* @param count total number of grid slots
* @param coord position of the first slot of the grid
* @param 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
@ -55,7 +56,7 @@ InventoryBuilder::InventoryBuilder() {
*/
void InventoryBuilder::addGrid(
int cols, int count,
glm::vec2 coord,
glm::vec2 pos,
int padding,
bool addpanel,
SlotLayout slotLayout)
@ -69,18 +70,18 @@ void InventoryBuilder::addGrid(
uint height = rows * (slotSize + interval) - interval + padding*2;
glm::vec2 vsize = view->getSize();
if (coord.x + width > vsize.x) {
vsize.x = coord.x + width;
if (pos.x + width > vsize.x) {
vsize.x = pos.x + width;
}
if (coord.y + height > vsize.y) {
vsize.y = coord.y + height;
if (pos.y + height > vsize.y) {
vsize.y = pos.y + height;
}
view->setSize(vsize);
if (addpanel) {
auto panel = std::make_shared<gui::Container>(coord, glm::vec2(width, height));
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);
view->add(panel, pos);
}
for (int row = 0; row < rows; row++) {
@ -109,7 +110,7 @@ std::shared_ptr<InventoryView> InventoryBuilder::build() {
SlotView::SlotView(
SlotLayout layout
) : UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
) : UINode(glm::vec2(InventoryView::SLOT_SIZE)),
layout(layout)
{
setColor(glm::vec4(0, 0, 0, 0.2f));
@ -121,7 +122,7 @@ void SlotView::draw(const GfxContext* pctx, Assets* assets) {
ItemStack& stack = *bound;
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
int slotSize = InventoryView::SLOT_SIZE;
@ -137,9 +138,9 @@ void SlotView::draw(const GfxContext* pctx, Assets* assets) {
if (color.a > 0.0) {
batch->texture(nullptr);
if (highlighted) {
batch->rect(coord.x-4, coord.y-4, slotSize+8, slotSize+8);
batch->rect(pos.x-4, pos.y-4, slotSize+8, slotSize+8);
} else {
batch->rect(coord.x, coord.y, slotSize, slotSize);
batch->rect(pos.x, pos.y, slotSize, slotSize);
}
}
@ -158,7 +159,7 @@ void SlotView::draw(const GfxContext* pctx, Assets* assets) {
UVRegion region = previews->get(cblock.name);
batch->rect(
coord.x, coord.y, slotSize, slotSize,
pos.x, pos.y, slotSize, slotSize,
0, 0, 0, region, false, true, tint);
break;
}
@ -177,7 +178,7 @@ void SlotView::draw(const GfxContext* pctx, Assets* assets) {
}
}
batch->rect(
coord.x, coord.y, slotSize, slotSize,
pos.x, pos.y, slotSize, slotSize,
0, 0, 0, region, false, true, tint);
break;
}
@ -187,8 +188,8 @@ void SlotView::draw(const GfxContext* pctx, Assets* assets) {
auto font = assets->getFont("normal");
std::wstring text = std::to_wstring(stack.getCount());
int x = coord.x+slotSize-text.length()*8;
int y = coord.y+slotSize-16;
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);
@ -259,7 +260,7 @@ void SlotView::clicked(gui::GUI* gui, mousecode button) {
}
}
void SlotView::focus(gui::GUI* gui) {
void SlotView::onFocus(gui::GUI* gui) {
clicked(gui, mousecode::BUTTON_1);
}
@ -280,7 +281,7 @@ const SlotLayout& SlotView::getLayout() const {
return layout;
}
InventoryView::InventoryView() : Container(glm::vec2(), glm::vec2()) {
InventoryView::InventoryView() : Container(glm::vec2()) {
setColor(glm::vec4(0, 0, 0, 0.0f));
}
@ -291,13 +292,13 @@ std::shared_ptr<SlotView> InventoryView::addSlot(SlotLayout layout) {
uint width = InventoryView::SLOT_SIZE + layout.padding;
uint height = InventoryView::SLOT_SIZE + layout.padding;
auto coord = layout.position;
auto pos = layout.position;
auto vsize = getSize();
if (coord.x + width > vsize.x) {
vsize.x = coord.x + width;
if (pos.x + width > vsize.x) {
vsize.x = pos.x + width;
}
if (coord.y + height > vsize.y) {
vsize.y = coord.y + height;
if (pos.y + height > vsize.y) {
vsize.y = pos.y + height;
}
setSize(vsize);
@ -337,6 +338,12 @@ void InventoryView::bind(
}
}
void InventoryView::unbind() {
if (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];
@ -344,8 +351,8 @@ void InventoryView::setSelected(int index) {
}
}
void InventoryView::setCoord(glm::vec2 coord) {
Container::setCoord(coord - origin);
void InventoryView::setPos(glm::vec2 pos) {
Container::setPos(pos - origin);
}
void InventoryView::setOrigin(glm::vec2 origin) {
@ -378,8 +385,8 @@ static void readSlot(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelem
int index = element->attr("index", "0").asInt();
bool itemSource = element->attr("item-source", "false").asBool();
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr);
if (element->has("coord")) {
layout.position = element->attr("coord").asVec2();
if (element->has("pos")) {
layout.position = element->attr("pos").asVec2();
}
if (element->has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");

View File

@ -73,7 +73,7 @@ public:
bool isHighlighted() const;
virtual void clicked(gui::GUI*, mousecode) override;
virtual void focus(gui::GUI*) override;
virtual void onFocus(gui::GUI*) override;
void bind(
int64_t inventoryid,
@ -101,7 +101,7 @@ public:
void setInventory(std::shared_ptr<Inventory> inventory);
virtual void setCoord(glm::vec2 coord) override;
virtual void setPos(glm::vec2 pos) override;
void setOrigin(glm::vec2 origin);
glm::vec2 getOrigin() const;
@ -113,6 +113,8 @@ public:
LevelFrontend* frontend,
InventoryInteraction* interaction
);
void unbind();
std::shared_ptr<SlotView> addSlot(SlotLayout layout);
@ -133,7 +135,7 @@ public:
void addGrid(
int cols, int count,
glm::vec2 coord,
glm::vec2 pos,
int padding,
bool addpanel,
SlotLayout slotLayout

View File

@ -0,0 +1,148 @@
#include <string>
#include <memory>
#include "gui/controls.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) {
auto label = std::make_shared<Label>(L"-");
label->textSupplier(supplier);
return label;
}
std::shared_ptr<UINode> create_debug_panel(
Engine* engine,
Level* level,
Player* player
) {
auto panel = std::make_shared<Panel>(glm::vec2(250, 200), glm::vec4(5.0f), 2.0f);
panel->setPos(glm::vec2(10, 10));
static int fps = 0;
static int fpsMin = fps;
static int fpsMax = fps;
static std::wstring fpsString = L"";
panel->listenInterval(0.016f, [engine]() {
fps = 1.0f / engine->getDelta();
fpsMin = std::min(fps, fpsMin);
fpsMax = std::max(fps, fpsMax);
});
panel->listenInterval(0.5f, []() {
fpsString = std::to_wstring(fpsMax)+L" / "+std::to_wstring(fpsMin);
fpsMin = fps;
fpsMax = fps;
});
panel->add(create_label([](){ return L"fps: "+fpsString;}));
panel->add(create_label([](){
return L"meshes: " + std::to_wstring(Mesh::meshesCount);
}));
panel->add(create_label([=](){
auto& settings = engine->getSettings();
bool culling = settings.graphics.frustumCulling;
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
}));
panel->add(create_label([=]() {
return L"chunks: "+std::to_wstring(level->chunks->chunksCount)+
L" visible: "+std::to_wstring(level->chunks->visible);
}));
panel->add(create_label([=](){
auto* indices = level->content->getIndices();
auto def = indices->getBlockDef(player->selectedVoxel.id);
std::wstringstream stream;
stream << std::hex << player->selectedVoxel.states;
if (def) {
stream << L" (" << util::str2wstr_utf8(def->name) << L")";
}
return L"block: "+std::to_wstring(player->selectedVoxel.id)+
L" "+stream.str();
}));
panel->add(create_label([=](){
return L"seed: "+std::to_wstring(level->world->getSeed());
}));
for (int ax = 0; ax < 3; ax++) {
auto sub = std::make_shared<Container>(glm::vec2(250, 27));
std::wstring str = L"x: ";
str[0] += ax;
auto label = std::make_shared<Label>(str);
label->setMargin(glm::vec4(2, 3, 2, 3));
label->setSize(glm::vec2(20, 27));
sub->add(label);
sub->setColor(glm::vec4(0.0f));
// Coord input
auto box = std::make_shared<TextBox>(L"");
box->setTextSupplier([=]() {
Hitbox* hitbox = player->hitbox.get();
return util::to_wstring(hitbox->position[ax], 2);
});
box->setTextConsumer([=](std::wstring text) {
try {
glm::vec3 position = player->hitbox->position;
position[ax] = std::stoi(text);
player->teleport(position);
} catch (std::invalid_argument& _){
}
});
box->setOnEditStart([=](){
Hitbox* hitbox = player->hitbox.get();
box->setText(std::to_wstring(int(hitbox->position[ax])));
});
box->setSize(glm::vec2(230, 27));
sub->add(box, glm::vec2(20, 0));
panel->add(sub);
}
panel->add(create_label([=](){
int hour, minute, second;
timeutil::from_value(level->world->daytime, hour, minute, second);
std::wstring timeString =
util::lfill(std::to_wstring(hour), 2, L'0') + L":" +
util::lfill(std::to_wstring(minute), 2, L'0');
return L"time: "+timeString;
}));
{
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8);
bar->setSupplier([=]() {return level->world->daytime;});
bar->setConsumer([=](double val) {level->world->daytime = val;});
panel->add(bar);
}
{
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
bar->setSupplier([=]() {return WorldRenderer::fog;});
bar->setConsumer([=](double val) {WorldRenderer::fog = val;});
panel->add(bar);
}
{
auto checkbox = std::make_shared<FullCheckBox>(
L"Show Chunk Borders", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return engine->getSettings().debug.showChunkBorders;
});
checkbox->setConsumer([=](bool checked) {
engine->getSettings().debug.showChunkBorders = checked;
});
panel->add(checkbox);
}
panel->refresh();
return panel;
}

View File

@ -16,7 +16,7 @@
using namespace gui;
GUI::GUI() {
container = std::make_shared<Container>(glm::vec2(0, 0), glm::vec2(1000));
container = std::make_shared<Container>(glm::vec2(1000));
uicamera = std::make_unique<Camera>(glm::vec3(), Window::height);
uicamera->perspective = false;
uicamera->flipped = true;
@ -58,7 +58,7 @@ void GUI::actMouse(float delta) {
}
if (focus != pressed) {
focus = pressed;
focus->focus(this);
focus->onFocus(this);
return;
}
}
@ -126,7 +126,7 @@ void GUI::draw(const GfxContext* pctx, Assets* assets) {
auto& viewport = pctx->getViewport();
glm::vec2 wsize = viewport.size();
menu->setCoord((wsize - menu->getSize()) / 2.0f);
menu->setPos((wsize - menu->getSize()) / 2.0f);
uicamera->setFov(wsize.y);
Shader* uishader = assets->getShader("ui");
@ -175,7 +175,7 @@ void GUI::setFocus(std::shared_ptr<UINode> node) {
}
focus = node;
if (focus) {
focus->focus(this);
focus->onFocus(this);
}
}

View File

@ -51,7 +51,7 @@ namespace gui {
class Container;
class PagesControl;
/** The main UI controller */
/// @brief The main UI controller
class GUI {
std::shared_ptr<Container> container;
std::shared_ptr<UINode> hover = nullptr;
@ -67,55 +67,58 @@ namespace gui {
GUI();
~GUI();
/** Get the main menu (PagesControl) node */
/// @brief Get the main menu (PagesControl) node
std::shared_ptr<PagesControl> getMenu();
/** Get current focused node
* @return focused node or nullptr */
/// @brief Get current focused node
/// @return focused node or nullptr
std::shared_ptr<UINode> getFocused() const;
/** Check if all user input is caught by some element like TextBox */
/// @brief Check if all user input is caught by some element like TextBox
bool isFocusCaught() const;
/** Main input handling and logic update method
* @param delta delta time */
/// @brief Main input handling and logic update method
/// @param delta delta time
void act(float delta);
/** Draw all visible elements on main container
* @param pctx parent graphics context
* @param assets active assets storage */
/// @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);
/** Add node to the main container */
/// @brief Add element to the main container
/// @param node UI element
void add(std::shared_ptr<UINode> node);
/** Remove node from the main container */
/// @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;
/** Store node in the GUI nodes dictionary
* (does not add node to the main container)
* @param name node key
* @param node target node
*/
/// @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);
/** Get node from the GUI nodes dictionary
* @param name node key
* @return stored node or nullptr
*/
/// @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;
/** Remove node from the GUI nodes dictionary
* @param name node key
*/
/// @brief Remove node from the GUI nodes dictionary
/// @param name node key
void remove(std::string name) noexcept;
/** Set node as focused
* @param node new focused node or nullptr to remove focus
*/
/// @brief Set node as focused
/// @param node new focused node or nullptr to remove focus
void setFocus(std::shared_ptr<UINode> node);
/** Get the main container */
/// @brief Get the main container
/// @deprecated
std::shared_ptr<Container> getContainer() const;
void postRunnable(runnable callback);

View File

@ -5,7 +5,7 @@
using gui::UINode;
using gui::Align;
UINode::UINode(glm::vec2 coord, glm::vec2 size) : coord(coord), size(size) {
UINode::UINode(glm::vec2 size) : size(size) {
}
UINode::~UINode() {
@ -63,18 +63,18 @@ bool UINode::isFocused() const {
return focused;
}
bool UINode::isInside(glm::vec2 pos) {
glm::vec2 coord = calcCoord();
bool UINode::isInside(glm::vec2 point) {
glm::vec2 pos = calcPos();
glm::vec2 size = getSize();
return (pos.x >= coord.x && pos.y >= coord.y &&
pos.x < coord.x + size.x && pos.y < coord.y + size.y);
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 pos, std::shared_ptr<UINode> self) {
std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) {
if (!interactive) {
return nullptr;
}
return isInside(pos) ? self : nullptr;
return isInside(point) ? self : nullptr;
}
bool UINode::isInteractive() const {
@ -93,11 +93,11 @@ bool UINode::isResizing() const {
return resizing;
}
glm::vec2 UINode::calcCoord() const {
glm::vec2 UINode::calcPos() const {
if (parent) {
return coord + parent->calcCoord() + parent->contentOffset();
return pos + parent->calcPos() + parent->contentOffset();
}
return coord;
return pos;
}
void UINode::scrolled(int value) {
@ -106,12 +106,12 @@ void UINode::scrolled(int value) {
}
}
void UINode::setCoord(glm::vec2 coord) {
this->coord = coord;
void UINode::setPos(glm::vec2 pos) {
this->pos = pos;
}
glm::vec2 UINode::getCoord() const {
return coord;
glm::vec2 UINode::getPos() const {
return pos;
}
glm::vec2 UINode::getSize() const {
@ -187,6 +187,53 @@ const std::string& UINode::getId() const {
void UINode::reposition() {
if (positionfunc) {
setCoord(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();
}
}

View File

@ -24,34 +24,66 @@ namespace gui {
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 {
/**
* element identifier used for direct access in UiDocument
*/
/// @brief element identifier used for direct access in UiDocument
std::string id = "";
protected:
glm::vec2 coord;
/// @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 coord, glm::vec2 size);
UINode(glm::vec2 size);
public:
virtual ~UINode();
/** Called every frame for all visible elements
* @param delta delta time
*/
/// @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;
@ -67,11 +99,12 @@ namespace gui {
virtual void setParent(UINode* node);
UINode* getParent() const;
/** Set element color (doesn't affect inner elements).
Also replaces hover color to avoid adding extra properties. */
/// @brief Set element color (doesn't affect inner elements).
/// Also replaces hover color to avoid adding extra properties
virtual void setColor(glm::vec4 newColor);
/** Get element color (float R,G,B,A in range [0.0, 1.0])*/
/// @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);
@ -80,12 +113,14 @@ namespace gui {
virtual void setMargin(glm::vec4 margin);
glm::vec4 getMargin() const;
/** Influences container elements sort order
Doesn't work in Panel */
/// @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 focus(GUI*) {focused = true;}
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) {};
@ -123,9 +158,9 @@ namespace gui {
/* 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 calcCoord() const;
virtual void setCoord(glm::vec2 coord);
virtual glm::vec2 getCoord() const;
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;
@ -140,8 +175,10 @@ namespace gui {
void setId(const std::string& id);
const std::string& getId() const;
/* Fetch coord from positionfunc if assigned */
/* Fetch pos from positionfunc if assigned */
void reposition();
virtual void setGravity(Gravity gravity);
};
}

View File

@ -10,7 +10,7 @@
using namespace gui;
Container::Container(glm::vec2 coord, glm::vec2 size) : UINode(coord, size) {
Container::Container(glm::vec2 size) : UINode(size) {
actualLength = size.y;
setColor(glm::vec4());
}
@ -79,7 +79,7 @@ void Container::setScrollable(bool flag) {
}
void Container::draw(const GfxContext* pctx, Assets* assets) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
glm::vec2 size = getSize();
drawBackground(pctx, assets);
@ -88,7 +88,7 @@ void Container::draw(const GfxContext* pctx, Assets* assets) {
batch->flush();
{
GfxContext ctx = pctx->sub();
ctx.scissors(glm::vec4(coord.x, coord.y, size.x, size.y));
ctx.scissors(glm::vec4(pos.x, pos.y, size.x, size.y));
for (auto node : nodes) {
if (node->isVisible())
node->draw(pctx, assets);
@ -100,12 +100,12 @@ void Container::draw(const GfxContext* pctx, Assets* assets) {
void Container::drawBackground(const GfxContext* pctx, Assets* assets) {
if (color.a <= 0.0f)
return;
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
batch->setColor(color);
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
}
void Container::add(std::shared_ptr<UINode> node) {
@ -115,8 +115,8 @@ void Container::add(std::shared_ptr<UINode> node) {
refresh();
}
void Container::add(std::shared_ptr<UINode> node, glm::vec2 coord) {
node->setCoord(coord);
void Container::add(std::shared_ptr<UINode> node, glm::vec2 pos) {
node->setPos(pos);
add(node);
}
@ -157,9 +157,10 @@ const std::vector<std::shared_ptr<UINode>>& Container::getNodes() const {
}
Panel::Panel(glm::vec2 size, glm::vec4 padding, float interval)
: Container(glm::vec2(), size),
padding(padding),
interval(interval) {
: Container(size),
padding(padding),
interval(interval)
{
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
}
@ -210,7 +211,7 @@ void Panel::refresh() {
y += margin.y;
float ex = x + margin.x;
node->setCoord(glm::vec2(ex, y));
node->setPos(glm::vec2(ex, y));
y += nodesize.y + margin.w + interval;
float width = size.x - padding.x - padding.z - margin.x - margin.z;
@ -227,7 +228,7 @@ void Panel::refresh() {
glm::vec2 nodesize = node->getSize();
const glm::vec4 margin = node->getMargin();
x += margin.x;
node->setCoord(glm::vec2(x, y+margin.y));
node->setPos(glm::vec2(x, y+margin.y));
x += nodesize.x + margin.z + interval;
node->refresh();
@ -245,7 +246,7 @@ Orientation Panel::getOrientation() const {
return orientation;
}
PagesControl::PagesControl() : Container(glm::vec2(), glm::vec2(1)){
PagesControl::PagesControl() : Container(glm::vec2(1)){
}
bool PagesControl::has(std::string name) {

View File

@ -33,14 +33,14 @@ namespace gui {
int actualLength = 0;
bool scrollable = true;
public:
Container(glm::vec2 coord, glm::vec2 size);
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 coord);
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);

View File

@ -17,17 +17,19 @@
using namespace gui;
Label::Label(std::string text, std::string fontName)
: UINode(glm::vec2(), glm::vec2(text.length() * 8, 15)),
text(util::str2wstr_utf8(text)),
fontName(fontName) {
: UINode(glm::vec2(text.length() * 8, 15)),
text(util::str2wstr_utf8(text)),
fontName(fontName)
{
setInteractive(false);
}
Label::Label(std::wstring text, std::string fontName)
: UINode(glm::vec2(), glm::vec2(text.length() * 8, 15)),
text(text),
fontName(fontName) {
: UINode(glm::vec2(text.length() * 8, 15)),
text(text),
fontName(fontName)
{
setInteractive(false);
}
@ -137,28 +139,28 @@ void Label::draw(const GfxContext* pctx, Assets* assets) {
(lines == 1 ? lineHeight : lineHeight*lineInterval)*lines + font->getYOffset()
);
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
switch (align) {
case Align::left:
break;
case Align::center:
coord.x += (size.x-newsize.x)*0.5f;
pos.x += (size.x-newsize.x)*0.5f;
break;
case Align::right:
coord.x += size.x-newsize.x;
pos.x += size.x-newsize.x;
break;
}
switch (valign) {
case Align::top:
break;
case Align::center:
coord.y += (size.y-newsize.y)*0.5f;
pos.y += (size.y-newsize.y)*0.5f;
break;
case Align::bottom:
coord.y += size.y-newsize.y;
pos.y += size.y-newsize.y;
break;
}
textYOffset = coord.y-calcCoord().y;
textYOffset = pos.y-calcPos().y;
totalLineHeight = lineHeight * lineInterval;
if (multiline) {
@ -170,10 +172,10 @@ void Label::draw(const GfxContext* pctx, Assets* assets) {
view = std::wstring_view(text.c_str()+offset, end);
offset += end + 1;
}
font->draw(batch, view, coord.x, coord.y + i * totalLineHeight, FontStyle::none);
font->draw(batch, view, pos.x, pos.y + i * totalLineHeight, FontStyle::none);
}
} else {
font->draw(batch, text, coord.x, coord.y, FontStyle::none);
font->draw(batch, text, pos.x, pos.y, FontStyle::none);
}
}
@ -191,12 +193,12 @@ bool Label::isMultiline() const {
}
// ================================= Image ====================================
Image::Image(std::string texture, glm::vec2 size) : UINode(glm::vec2(), size), texture(texture) {
Image::Image(std::string texture, glm::vec2 size) : UINode(size), texture(texture) {
setInteractive(false);
}
void Image::draw(const GfxContext* pctx, Assets* assets) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
glm::vec4 color = getColor();
auto batch = pctx->getBatch2D();
@ -206,7 +208,7 @@ void Image::draw(const GfxContext* pctx, Assets* assets) {
}
batch->texture(texture);
batch->setColor(color);
batch->rect(coord.x, coord.y, size.x, size.y,
batch->rect(pos.x, pos.y, size.x, size.y,
0, 0, 0, UVRegion(), false, true, color);
}
@ -294,11 +296,11 @@ void Button::refresh() {
}
void Button::drawBackground(const GfxContext* pctx, Assets* assets) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
batch->setColor(isPressed() ? pressedColor : (hover ? hoverColor : color));
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
}
void Button::mouseRelease(GUI* gui, int x, int y) {
@ -330,7 +332,7 @@ Align Button::getTextAlign() const {
}
// ============================== RichButton ==================================
RichButton::RichButton(glm::vec2 size) : Container(glm::vec2(), size) {
RichButton::RichButton(glm::vec2 size) : Container(size) {
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
}
@ -349,11 +351,11 @@ RichButton* RichButton::listenAction(onaction action) {
}
void RichButton::drawBackground(const GfxContext* pctx, Assets* assets) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
batch->setColor(isPressed() ? pressedColor : (hover ? hoverColor : color));
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
}
// ================================ TextBox ===================================
@ -366,7 +368,7 @@ TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
add(label);
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
textInitX = label->getCoord().x;
textInitX = label->getPos().x;
}
void TextBox::draw(const GfxContext* pctx, Assets* assets) {
@ -377,14 +379,14 @@ void TextBox::draw(const GfxContext* pctx, Assets* assets) {
if (!isFocused())
return;
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
glm::vec2 size = getSize();
auto subctx = pctx->sub();
subctx.scissors(glm::vec4(coord.x, coord.y, size.x, size.y));
subctx.scissors(glm::vec4(pos.x, pos.y, size.x, size.y));
const int lineHeight = font->getLineHeight() * label->getLineInterval();
glm::vec2 lcoord = label->calcCoord();
glm::vec2 lcoord = label->calcPos();
lcoord.y -= 2;
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
@ -420,7 +422,7 @@ void TextBox::draw(const GfxContext* pctx, Assets* assets) {
}
void TextBox::drawBackground(const GfxContext* pctx, Assets* assets) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
@ -437,14 +439,14 @@ void TextBox::drawBackground(const GfxContext* pctx, Assets* assets) {
batch->setColor(invalidColor);
}
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
if (!isFocused() && supplier) {
input = supplier();
}
if (isFocused() && multiline) {
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
glm::vec2 lcoord = label->calcCoord();
glm::vec2 lcoord = label->calcPos();
lcoord.y -= 2;
uint line = label->getLineByTextIndex(caret);
int lineY = label->getLineYOffset(line);
@ -536,7 +538,7 @@ size_t TextBox::getSelectionLength() const {
/// @brief Set scroll offset
/// @param x scroll offset
void TextBox::setTextOffset(uint x) {
label->setCoord(glm::vec2(textInitX - int(x), label->getCoord().y));
label->setPos(glm::vec2(textInitX - int(x), label->getPos().y));
textOffset = x;
}
@ -583,8 +585,8 @@ void TextBox::setOnEditStart(runnable oneditstart) {
onEditStart = oneditstart;
}
void TextBox::focus(GUI* gui) {
Panel::focus(gui);
void TextBox::onFocus(GUI* gui) {
Panel::onFocus(gui);
if (onEditStart){
setCaret(input.size());
onEditStart();
@ -611,7 +613,7 @@ size_t TextBox::normalizeIndex(int index) {
int TextBox::calcIndexAt(int x, int y) const {
if (font == nullptr)
return 0;
glm::vec2 lcoord = label->calcCoord();
glm::vec2 lcoord = label->calcPos();
uint line = label->getLineByYOffset(y-lcoord.y);
line = std::min(line, label->getLinesNumber()-1);
size_t lineLength = getLineLength(line);
@ -883,11 +885,11 @@ InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding)
}
void InputBindBox::drawBackground(const GfxContext* pctx, Assets* assets) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
batch->setColor(isFocused() ? focusedColor : (hover ? hoverColor : color));
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
label->setText(util::str2wstr_utf8(binding.text()));
}
@ -904,17 +906,19 @@ void InputBindBox::keyPressed(keycode key) {
}
// ================================ TrackBar ==================================
TrackBar::TrackBar(double min,
double max,
double value,
double step,
int trackWidth)
: UINode(glm::vec2(), glm::vec2(26)),
min(min),
max(max),
value(value),
step(step),
trackWidth(trackWidth) {
TrackBar::TrackBar(
double min,
double max,
double value,
double step,
int trackWidth
) : UINode(glm::vec2(26)),
min(min),
max(max),
value(value),
step(step),
trackWidth(trackWidth)
{
setColor(glm::vec4(0.f, 0.f, 0.f, 0.4f));
setHoverColor(glm::vec4(0.01f, 0.02f, 0.03f, 0.5f));
}
@ -923,18 +927,18 @@ void TrackBar::draw(const GfxContext* pctx, Assets* assets) {
if (supplier) {
value = supplier();
}
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
batch->setColor(hover ? hoverColor : color);
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
float width = size.x;
float t = (value - min) / (max-min+trackWidth*step);
batch->setColor(trackColor);
int actualWidth = size.x * (trackWidth / (max-min+trackWidth*step) * step);
batch->rect(coord.x + width * t, coord.y, actualWidth, size.y);
batch->rect(pos.x + width * t, pos.y, actualWidth, size.y);
}
void TrackBar::setSupplier(doublesupplier supplier) {
@ -946,9 +950,9 @@ void TrackBar::setConsumer(doubleconsumer consumer) {
}
void TrackBar::mouseMove(GUI*, int x, int y) {
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
value = x;
value -= coord.x;
value -= pos.x;
value = (value)/size.x * (max-min+trackWidth*step);
value += min;
value = (value > max) ? max : value;
@ -1009,7 +1013,7 @@ void TrackBar::setTrackColor(glm::vec4 color) {
}
// ================================ CheckBox ==================================
CheckBox::CheckBox(bool checked) : UINode(glm::vec2(), glm::vec2(32.0f)), checked(checked) {
CheckBox::CheckBox(bool checked) : UINode(glm::vec2(32.0f)), checked(checked) {
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
}
@ -1017,11 +1021,11 @@ void CheckBox::draw(const GfxContext* pctx, Assets* assets) {
if (supplier) {
checked = supplier();
}
glm::vec2 coord = calcCoord();
glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D();
batch->texture(nullptr);
batch->setColor(checked ? checkColor : (hover ? hoverColor : color));
batch->rect(coord.x, coord.y, size.x, size.y);
batch->rect(pos.x, pos.y, size.x, size.y);
}
void CheckBox::mouseRelease(GUI*, int x, int y) {

View File

@ -272,7 +272,7 @@ namespace gui {
/// @brief Set runnable called on textbox focus
virtual void setOnEditStart(runnable oneditstart);
virtual void focus(GUI*) override;
virtual void onFocus(GUI*) override;
virtual void refresh() override;
virtual void click(GUI*, int, int) override;
virtual void mouseMove(GUI*, int x, int y) override;

View File

@ -22,13 +22,32 @@ static Align align_from_string(const std::string& str, Align def) {
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.setCoord(element->attr("pos").asVec2());
node.setPos(element->attr("pos").asVec2());
}
if (element->has("size")) {
node.setSize(element->attr("size").asVec2());
@ -67,6 +86,12 @@ static void _readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& no
}
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()
));
}
}
@ -149,7 +174,7 @@ static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, xml::xmlelement el
}
static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, xml::xmlelement element) {
auto container = std::make_shared<Container>(glm::vec2(), glm::vec2());
auto container = std::make_shared<Container>(glm::vec2());
_readContainer(reader, element, *container);
return container;
}

View File

@ -1,15 +1,11 @@
#include "hud.h"
// TODO: refactor this garbage
#include <iostream>
#include <sstream>
#include <memory>
#include <string>
#include <assert.h>
#include <stdexcept>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "../typedefs.h"
#include "../content/Content.h"
@ -55,13 +51,50 @@
#include "../items/Inventories.h"
#include "../logic/scripting/scripting.h"
using namespace gui;
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
auto label = std::make_shared<Label>(L"-");
label->textSupplier(supplier);
return label;
}
// implemented in debug_panel.cpp
extern std::shared_ptr<gui::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,
@ -74,6 +107,7 @@ HudElement::HudElement(
void HudElement::update(bool pause, bool inventoryOpen, bool debugMode) {
if (debug && !debugMode) {
node->setVisible(false);
return;
}
switch (mode) {
case hud_element_mode::permanent:
@ -99,115 +133,6 @@ std::shared_ptr<gui::UINode> HudElement::getNode() const {
return node;
}
std::shared_ptr<UINode> Hud::createDebugPanel(Engine* engine) {
auto level = frontend->getLevel();
auto panel = std::make_shared<Panel>(glm::vec2(250, 200), glm::vec4(5.0f), 2.0f);
panel->listenInterval(0.5f, [this]() {
fpsString = std::to_wstring(fpsMax)+L" / "+std::to_wstring(fpsMin);
fpsMin = fps;
fpsMax = fps;
});
panel->setCoord(glm::vec2(10, 10));
panel->add(create_label([this](){ return L"fps: "+this->fpsString;}));
panel->add(create_label([](){
return L"meshes: " + std::to_wstring(Mesh::meshesCount);
}));
panel->add(create_label([=](){
auto& settings = engine->getSettings();
bool culling = settings.graphics.frustumCulling;
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
}));
panel->add(create_label([=]() {
return L"chunks: "+std::to_wstring(level->chunks->chunksCount)+
L" visible: "+std::to_wstring(level->chunks->visible);
}));
panel->add(create_label([=](){
auto* indices = level->content->getIndices();
auto def = indices->getBlockDef(player->selectedVoxel.id);
std::wstringstream stream;
stream << std::hex << player->selectedVoxel.states;
if (def) {
stream << L" (" << util::str2wstr_utf8(def->name) << L")";
}
return L"block: "+std::to_wstring(player->selectedVoxel.id)+
L" "+stream.str();
}));
panel->add(create_label([=](){
return L"seed: "+std::to_wstring(level->world->getSeed());
}));
for (int ax = 0; ax < 3; ax++) {
auto sub = std::make_shared<Container>(glm::vec2(), glm::vec2(250, 27));
std::wstring str = L"x: ";
str[0] += ax;
auto label = std::make_shared<Label>(str);
label->setMargin(glm::vec4(2, 3, 2, 3));
label->setSize(glm::vec2(20, 27));
sub->add(label);
sub->setColor(glm::vec4(0.0f));
// Coord input
auto box = std::make_shared<TextBox>(L"");
box->setTextSupplier([=]() {
Hitbox* hitbox = player->hitbox.get();
return util::to_wstring(hitbox->position[ax], 2);
});
box->setTextConsumer([=](std::wstring text) {
try {
glm::vec3 position = player->hitbox->position;
position[ax] = std::stoi(text);
player->teleport(position);
} catch (std::invalid_argument& _){
}
});
box->setOnEditStart([=](){
Hitbox* hitbox = player->hitbox.get();
box->setText(std::to_wstring(int(hitbox->position[ax])));
});
box->setSize(glm::vec2(230, 27));
sub->add(box, glm::vec2(20, 0));
panel->add(sub);
}
panel->add(create_label([=](){
int hour, minute, second;
timeutil::from_value(level->world->daytime, hour, minute, second);
std::wstring timeString =
util::lfill(std::to_wstring(hour), 2, L'0') + L":" +
util::lfill(std::to_wstring(minute), 2, L'0');
return L"time: "+timeString;
}));
{
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8);
bar->setSupplier([=]() {return level->world->daytime;});
bar->setConsumer([=](double val) {level->world->daytime = val;});
panel->add(bar);
}
{
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
bar->setSupplier([=]() {return WorldRenderer::fog;});
bar->setConsumer([=](double val) {WorldRenderer::fog = val;});
panel->add(bar);
}
{
auto checkbox = std::make_shared<FullCheckBox>(
L"Show Chunk Borders", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return engine->getSettings().debug.showChunkBorders;
});
checkbox->setConsumer([=](bool checked) {
engine->getSettings().debug.showChunkBorders = checked;
});
panel->add(checkbox);
}
panel->refresh();
return panel;
}
std::shared_ptr<InventoryView> Hud::createContentAccess() {
auto level = frontend->getLevel();
auto content = level->content;
@ -252,14 +177,12 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
}
Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
: engine(engine),
assets(engine->getAssets()),
gui(engine->getGUI()),
frontend(frontend),
player(player)
: engine(engine),
assets(engine->getAssets()),
gui(engine->getGUI()),
frontend(frontend),
player(player)
{
auto menu = gui->getMenu();
interaction = std::make_unique<InventoryInteraction>();
grabbedItemView = std::make_shared<SlotView>(
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr)
@ -275,7 +198,7 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
grabbedItemView->setZIndex(1);
contentAccess = createContentAccess();
contentAccessPanel = std::make_shared<Panel>(
contentAccessPanel = std::make_shared<gui::Panel>(
contentAccess->getSize(), glm::vec4(0.0f), 0.0f
);
contentAccessPanel->setColor(glm::vec4());
@ -283,7 +206,7 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
contentAccessPanel->setScrollable(true);
hotbarView = createHotbar();
darkOverlay = std::make_unique<Panel>(glm::vec2(4000.0f));
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);
@ -292,9 +215,7 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
uicamera->perspective = false;
uicamera->flipped = true;
debugPanel = createDebugPanel(engine);
menu->reset();
debugPanel = create_debug_panel(engine, frontend->getLevel(), player);
debugPanel->setZIndex(2);
gui->add(darkOverlay);
@ -302,13 +223,17 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
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));
}
Hud::~Hud() {
// removing all controlled ui
gui->remove(grabbedItemView);
for (auto& element : elements) {
remove(element);
onRemove(element);
}
gui->remove(hotbarView);
gui->remove(darkOverlay);
@ -316,21 +241,55 @@ Hud::~Hud() {
gui->remove(debugPanel);
}
void Hud::drawDebug(int fps){
this->fps = fps;
fpsMin = min(fps, fpsMin);
fpsMax = max(fps, fpsMax);
}
/**
* Remove all elements marked as removed
*/
/// @brief Remove all elements marked as removed
void Hud::cleanup() {
auto it = std::remove_if(elements.begin(), elements.end(), [](const HudElement& e) {
return e.isRemoved();
});
elements.erase(it, elements.end());
}
}
void Hud::processInput(bool visible) {
if (Events::jpressed(keycode::ESCAPE)) {
if (pause) {
setPause(false);
} else if (inventoryOpen) {
closeInventory();
} else {
setPause(true);
}
}
if (visible && Events::jactive(BIND_HUD_INVENTORY)) {
if (inventoryOpen) {
closeInventory();
} else {
openInventory();
}
}
if (!pause) {
if (!inventoryOpen && Events::scroll) {
int slot = player->getChosenSlot();
slot = (slot - Events::scroll) % 10;
if (slot < 0) {
slot += 10;
}
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) {
auto level = frontend->getLevel();
@ -344,31 +303,16 @@ void Hud::update(bool visible) {
if (pause && menu->getCurrent().panel == nullptr) {
setPause(false);
}
if (Events::jpressed(keycode::ESCAPE) && !gui->isFocusCaught()) {
if (pause) {
setPause(false);
} else if (inventoryOpen) {
closeInventory();
} else {
setPause(true);
}
}
if (visible && !gui->isFocusCaught() && !pause) {
if (Events::jactive(BIND_HUD_INVENTORY)) {
if (inventoryOpen) {
closeInventory();
} else {
openInventory();
}
}
if (!gui->isFocusCaught()) {
processInput(visible);
}
if ((pause || inventoryOpen) == Events::_cursor_locked) {
Events::toggleCursor();
}
if (blockUI) {
voxel* vox = level->chunks->get(currentblock.x, currentblock.y, currentblock.z);
voxel* vox = level->chunks->get(blockPos.x, blockPos.y, blockPos.z);
if (vox == nullptr || vox->id != currentblockid) {
closeInventory();
}
@ -384,58 +328,33 @@ void Hud::update(bool visible) {
contentAccess->setMinSize(glm::vec2(1, Window::height));
hotbarView->setVisible(visible);
if (!gui->isFocusCaught() && !pause) {
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);
}
}
if (!pause && !inventoryOpen && Events::scroll) {
int slot = player->getChosenSlot();
slot = (slot - Events::scroll) % 10;
if (slot < 0) {
slot += 10;
}
player->setChosenSlot(slot);
}
if (visible) {
for (auto& element : elements) {
element.update(pause, inventoryOpen, player->debug);
if (element.isRemoved()) {
remove(element);
onRemove(element);
}
}
}
cleanup();
}
/**
* Show inventory on the screen and turn on inventory mode blocking movement
*/
/// @brief Show inventory on the screen and turn on inventory mode blocking movement
void Hud::openInventory() {
auto inventory = player->getInventory();
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());
add(HudElement(hud_element_mode::inventory_bound, inventoryDocument, inventoryView, false));
}
/**
* Show player inventory + block UI
* @param block world position of the open block
* @param doc block UI document (root element must be an InventoryView)
* @param blockinv block inventory.
* In case of nullptr a new virtual inventory will be created
*/
void Hud::openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inventory> blockinv, bool playerInventory) {
void Hud::openInventory(
glm::ivec3 block,
UiDocument* doc,
std::shared_ptr<Inventory> blockinv,
bool playerInventory
) {
if (isInventoryOpen()) {
closeInventory();
}
@ -444,6 +363,7 @@ void Hud::openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inven
if (blockUI == nullptr) {
throw std::runtime_error("block UI root element must be 'inventory'");
}
secondUI = blockUI;
if (playerInventory) {
openInventory();
} else {
@ -454,15 +374,24 @@ void Hud::openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inven
}
level->chunks->getChunkByVoxel(block.x, block.y, block.z)->setUnsaved(true);
blockUI->bind(blockinv, frontend, interaction.get());
currentblock = block;
blockPos = block;
currentblockid = level->chunks->get(block.x, block.y, block.z)->id;
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
}
/**
* Add element as permanent overlay
* @param doc element layout document
*/
void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
if (isInventoryOpen()) {
closeInventory();
}
secondUI = doc->getRoot();
if (playerInventory) {
openInventory();
} else {
inventoryOpen = true;
}
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
}
void Hud::openPermanent(UiDocument* doc) {
auto root = doc->getRoot();
remove(root);
@ -475,24 +404,13 @@ void Hud::openPermanent(UiDocument* doc) {
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
}
/**
* Hide inventory and turn off inventory mode
*/
void Hud::closeInventory() {
auto level = frontend->getLevel();
inventoryOpen = false;
ItemStack& grabbed = interaction->getGrabbedItem();
grabbed.clear();
inventoryView = nullptr;
if (blockUI) {
auto blockinv = blockUI->getInventory();
// todo: do it automatically
if (blockinv->isVirtual()) {
level->inventories->remove(blockinv->getId());
}
blockUI = nullptr;
}
blockUI = nullptr;
secondUI = nullptr;
}
void Hud::add(HudElement element) {
@ -505,20 +423,20 @@ void Hud::add(HudElement element) {
scripting::on_ui_open(
element.getDocument(),
inventory.get(),
currentblock
blockPos
);
} else {
scripting::on_ui_open(
element.getDocument(),
nullptr,
currentblock
blockPos
);
}
}
elements.push_back(element);
}
void Hud::remove(HudElement& element) {
void Hud::onRemove(HudElement& element) {
auto document = element.getDocument();
if (document) {
Inventory* inventory = nullptr;
@ -527,16 +445,18 @@ void Hud::remove(HudElement& element) {
inventory = invview->getInventory().get();
}
scripting::on_ui_close(document, inventory);
if (invview) {
invview->unbind();
}
}
gui->remove(element.getNode());
}
// todo: refactor this garbage
void Hud::remove(std::shared_ptr<UINode> node) {
void Hud::remove(std::shared_ptr<gui::UINode> node) {
for (auto& element : elements) {
if (element.getNode() == node) {
element.setRemoved();
remove(element);
onRemove(element);
}
}
cleanup();
@ -547,6 +467,8 @@ void Hud::draw(const GfxContext& ctx){
const uint width = viewport.getWidth();
const uint height = viewport.getHeight();
updateElementsPosition(viewport);
uicamera->setFov(height);
auto batch = ctx.getBatch2D();
@ -555,12 +477,9 @@ void Hud::draw(const GfxContext& ctx){
Shader* uishader = assets->getShader("ui");
uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView());
hotbarView->setCoord(glm::vec2(width/2, height-65));
hotbarView->setSelected(player->getChosenSlot());
// Crosshair
if (!pause && Events::_cursor_locked && !player->debug) {
if (!pause && !inventoryOpen && !player->debug) {
GfxContext chctx = ctx.sub();
chctx.blendMode(blendmode::inversion);
auto texture = assets->getTexture("gui/crosshair");
@ -573,55 +492,45 @@ void Hud::draw(const GfxContext& ctx){
);
batch->flush();
}
batch->flush();
}
// Delta-time visualizer
if (player->debug) {
batch->texture(nullptr);
const int dmwidth = 256;
const float dmscale = 4000.0f;
static float deltameter[dmwidth]{};
static int index=0;
index = index + 1 % dmwidth;
deltameter[index%dmwidth] = glm::min(0.2f, 1.f/fps)*dmscale;
batch->lineWidth(1);
for (int i = index+1; i < index+dmwidth; i++) {
int j = i % dmwidth;
batch->line(width-dmwidth+i-index, height-deltameter[j],
width-dmwidth+i-index, height, 1.0f, 1.0f, 1.0f, 0.2f);
}
}
void Hud::updateElementsPosition(const Viewport& viewport) {
const uint width = viewport.getWidth();
const uint height = viewport.getHeight();
if (inventoryOpen) {
float caWidth = inventoryView ? contentAccess->getSize().x : 0.0f;
contentAccessPanel->setCoord(glm::vec2(width-caWidth, 0));
contentAccessPanel->setPos(glm::vec2(width-caWidth, 0));
glm::vec2 invSize = inventoryView ? inventoryView->getSize() : glm::vec2();
if (blockUI == nullptr) {
if (secondUI == nullptr) {
if (inventoryView) {
inventoryView->setCoord(glm::vec2(
inventoryView->setPos(glm::vec2(
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
height/2-invSize.y/2
));
}
} else {
glm::vec2 blockInvSize = blockUI->getSize();
float invwidth = glm::max(invSize.x, blockInvSize.x);
glm::vec2 secondUISize = secondUI->getSize();
float invwidth = glm::max(invSize.x, secondUISize.x);
int interval = invSize.y > 0.0 ? 5 : 0;
float totalHeight = invSize.y + blockInvSize.y + interval;
float totalHeight = invSize.y + secondUISize.y + interval;
if (inventoryView) {
inventoryView->setCoord(glm::vec2(
inventoryView->setPos(glm::vec2(
glm::min(width/2-invwidth/2, width-caWidth-10-invwidth),
height/2+totalHeight/2-invSize.y
));
}
blockUI->setCoord(glm::vec2(
secondUI->setPos(glm::vec2(
glm::min(width/2-invwidth/2, width-caWidth-10-invwidth),
height/2-totalHeight/2
));
}
}
grabbedItemView->setCoord(glm::vec2(Events::cursor));
batch->flush();
grabbedItemView->setPos(glm::vec2(Events::cursor));
hotbarView->setPos(glm::vec2(width/2, height-65));
hotbarView->setSelected(player->getChosenSlot());
}
bool Hud::isInventoryOpen() const {

View File

@ -9,11 +9,9 @@
#include "../graphics/GfxContext.h"
class Camera;
class Level;
class Block;
class Assets;
class Player;
class Level;
class Engine;
class SlotView;
class Inventory;
@ -23,104 +21,138 @@ class UiDocument;
class InventoryInteraction;
namespace gui {
class GUI;
class UINode;
class GUI;
class UINode;
class Panel;
class Container;
class Container;
}
enum class hud_element_mode {
// element is hidden if menu or inventory open
ingame,
// element is visible if hud is visible
permanent,
// element is visible in inventory mode
inventory_any,
// element will be removed on inventory close
inventory_bound
// element is hidden if menu or inventory open
ingame,
// element is visible if hud is visible
permanent,
// element is visible in inventory mode
inventory_any,
// element will be removed on inventory close
inventory_bound
};
class HudElement {
hud_element_mode mode;
UiDocument* document;
std::shared_ptr<gui::UINode> node;
hud_element_mode mode;
UiDocument* document;
std::shared_ptr<gui::UINode> node;
bool debug;
bool removed = false;
bool debug;
bool removed = false;
public:
HudElement(hud_element_mode mode, UiDocument* document, std::shared_ptr<gui::UINode> node, bool debug);
HudElement(hud_element_mode mode, UiDocument* document, std::shared_ptr<gui::UINode> node, bool debug);
void update(bool pause, bool inventoryOpen, bool debug);
void update(bool pause, bool inventoryOpen, bool debug);
UiDocument* getDocument() const;
std::shared_ptr<gui::UINode> getNode() const;
UiDocument* getDocument() const;
std::shared_ptr<gui::UINode> getNode() const;
void setRemoved() {
removed = true;
}
bool isRemoved() const {
return removed;
}
bool isRemoved() const {
return removed;
}
};
class Hud {
Engine* engine;
Assets* assets;
std::unique_ptr<Camera> uicamera;
int fps = 60;
int fpsMin = 60;
int fpsMax = 60;
std::wstring fpsString;
bool inventoryOpen = false;
bool pause = false;
std::shared_ptr<gui::Container> contentAccessPanel;
std::shared_ptr<InventoryView> contentAccess;
std::shared_ptr<InventoryView> hotbarView;
std::shared_ptr<gui::UINode> debugPanel;
std::shared_ptr<gui::Panel> darkOverlay;
std::unique_ptr<InventoryInteraction> interaction;
std::shared_ptr<SlotView> grabbedItemView;
gui::GUI* gui;
LevelFrontend* frontend;
std::unique_ptr<Camera> uicamera;
gui::GUI* gui;
LevelFrontend* frontend;
Player* player;
std::vector<HudElement> elements;
/// @brief Is any overlay/inventory open
bool inventoryOpen = false;
/// @brief Is pause mode on
bool pause = false;
/// @brief Content access panel scroll container
std::shared_ptr<gui::Container> contentAccessPanel;
/// @brief Content access panel itself
std::shared_ptr<InventoryView> contentAccess;
/// @brief Player inventory hotbar
std::shared_ptr<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;
/// @brief List of all controlled hud elements
std::vector<HudElement> elements;
/// @brief Player inventory view
std::shared_ptr<InventoryView> inventoryView = nullptr;
std::shared_ptr<InventoryView> blockUI = nullptr;
glm::ivec3 currentblock {};
/// @brief Block inventory view
std::shared_ptr<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)
blockid_t currentblockid = 0;
std::shared_ptr<gui::UINode> createDebugPanel(Engine* engine);
/// @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();
void processInput(bool visible);
void updateElementsPosition(const Viewport& viewport);
void cleanup();
public:
Hud(Engine* engine, LevelFrontend* frontend, Player* player);
~Hud();
Hud(Engine* engine, LevelFrontend* frontend, Player* player);
~Hud();
void update(bool hudVisible);
void draw(const GfxContext& context);
void drawDebug(int fps);
void update(bool hudVisible);
void draw(const GfxContext& context);
bool isInventoryOpen() const;
bool isPause() const;
/// @brief Check if inventory mode on
bool isInventoryOpen() const;
/// @brief Check if pause mode on
bool isPause() const;
/// @brief Enable/disable pause mode
void setPause(bool pause);
/// @brief Show player inventory in inventory-mode
void openInventory();
void openInventory(glm::ivec3 block, UiDocument* doc, std::shared_ptr<Inventory> blockInv, bool playerInventory);
/// @brief Show block inventory in inventory-mode
/// @param block block position
/// @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);
/// @brief Show element in inventory-mode
/// @param doc element layout
/// @param playerInventory show player inventory too
void showOverlay(UiDocument* doc, bool playerInventory);
/// @brief Close all open inventories and overlay
void closeInventory();
/// @brief Add element will be visible until removed
/// @param doc element layout
void openPermanent(UiDocument* doc);
void add(HudElement element);
void remove(HudElement& element);
void add(HudElement element);
void onRemove(HudElement& element);
void remove(std::shared_ptr<gui::UINode> node);
Player* getPlayer() const;
};
#endif /* SRC_HUD_H_ */
#endif // SRC_HUD_H_

View File

@ -92,6 +92,8 @@ 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>(level, assets);
@ -186,8 +188,5 @@ void LevelScreen::draw(float delta) {
if (hudVisible) {
hud->draw(ctx);
if (controller->getPlayer()->debug) {
hud->drawDebug(1 / delta);
}
}
}

View File

@ -19,12 +19,11 @@ Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
ubyte pixels[] = {
0xFF, 0xFF, 0xFF, 0xFF
};
blank = new Texture(pixels, 1, 1, GL_RGBA);
blank = std::make_unique<Texture>(pixels, 1, 1, GL_RGBA);
_texture = nullptr;
}
Batch2D::~Batch2D(){
delete blank;
delete[] buffer;
}

View File

@ -15,10 +15,9 @@ class Batch2D {
float* buffer;
size_t capacity;
std::unique_ptr<Mesh> mesh;
std::unique_ptr<Texture> blank;
size_t index;
glm::vec4 color;
Texture* blank;
Texture* _texture;
void vertex(

View File

@ -171,7 +171,7 @@ static int l_gui_getattr(lua_State* L) {
if (attr == "color") {
return lua::pushcolor_arr(L, node->getColor());
} else if (attr == "pos") {
return lua::pushvec2_arr(L, node->getCoord());
return lua::pushvec2_arr(L, node->getPos());
} else if (attr == "size") {
return lua::pushvec2_arr(L, node->getSize());
} else if (attr == "hoverColor") {
@ -210,7 +210,7 @@ static int l_gui_setattr(lua_State* L) {
auto node = getDocumentNode(L, docname, element);
if (attr == "pos") {
node->setCoord(lua::tovec2(L, 4));
node->setPos(lua::tovec2(L, 4));
} else if (attr == "size") {
node->setSize(lua::tovec2(L, 4));
} else if (attr == "color") {

View File

@ -65,6 +65,19 @@ static int l_hud_open_block(lua_State* L) {
return 2;
}
static int l_hud_show_overlay(lua_State* L) {
const char* name = lua_tostring(L, 1);
bool playerInventory = lua_toboolean(L, 2);
auto assets = scripting::engine->getAssets();
auto layout = assets->getLayout(name);
if (layout == nullptr) {
luaL_error(L, "there is no ui layout '%s'", name);
}
scripting::hud->showOverlay(layout, playerInventory);
return 0;
}
static UiDocument* require_layout(lua_State* L, const char* name) {
auto assets = scripting::engine->getAssets();
auto layout = assets->getLayout(name);
@ -91,6 +104,7 @@ const luaL_Reg hudlib [] = {
{"close_inventory", lua_wrap_errors<l_hud_close_inventory>},
{"open_block", lua_wrap_errors<l_hud_open_block>},
{"open_permanent", lua_wrap_errors<l_hud_open_permanent>},
{"show_overlay", lua_wrap_errors<l_hud_show_overlay>},
{"close", lua_wrap_errors<l_hud_close>},
{NULL, NULL}
};