mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-13 06:51:51 +00:00
Merge pull request #778 from MihailRis/render-ui-to-texture
rendering UI to texture
This commit is contained in:
commit
49d2c98718
16 changed files with 344 additions and 44 deletions
|
|
@ -40,6 +40,37 @@ gui.get_locales_info() -> table of tables {
|
||||||
|
|
||||||
Returns information about all loaded locales (res/texts/\*).
|
Returns information about all loaded locales (res/texts/\*).
|
||||||
|
|
||||||
|
## Frames
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- A replacement for menu:reset() to close the pause menu, deactivating the main UI frame.
|
||||||
|
gui.close_menu()
|
||||||
|
|
||||||
|
-- Creates a frame.
|
||||||
|
gui.create_frame(
|
||||||
|
-- Global frame id (not related to the UI 'id' property).
|
||||||
|
id: str,
|
||||||
|
-- The texture to render the frame to.
|
||||||
|
-- If the string is empty, the frame is rendered to the screen.
|
||||||
|
output_texture: str,
|
||||||
|
-- Frame size. For example: {640, 480}
|
||||||
|
size: vec2
|
||||||
|
) -> Element, Document
|
||||||
|
|
||||||
|
-- Returns the id of the active frame (not the element id).
|
||||||
|
gui.get_active_frame() -> str
|
||||||
|
|
||||||
|
-- Sets the active frame, receiving user input.
|
||||||
|
-- An empty string specifies the null frame capturing the cursor.
|
||||||
|
gui.set_active_frame(
|
||||||
|
-- ID of the frame created via gui.create_frame
|
||||||
|
id: str,
|
||||||
|
-- Function providing the cursor position in the frame.
|
||||||
|
-- Used for custom projection (e.g., in 3D)
|
||||||
|
[optional] cursorLocator
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
gui.clear_markup(
|
gui.clear_markup(
|
||||||
-- markup language ("md" - Markdown)
|
-- markup language ("md" - Markdown)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,37 @@ gui.get_env(document: string) -> table
|
||||||
gui.get_locales_info() -> table
|
gui.get_locales_info() -> table
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Фреймы
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Замена для menu:reset() для закрытия меню паузы, деактивирующая основной фрейм UI.
|
||||||
|
gui.close_menu()
|
||||||
|
|
||||||
|
-- Создаёт фрейм.
|
||||||
|
gui.create_frame(
|
||||||
|
-- Глобальный id фрейма (не связан с UI свойством 'id').
|
||||||
|
id: str,
|
||||||
|
-- Текстура, в которую будет производиться рендер фрейма.
|
||||||
|
-- В случае пустой строки рендер производится на экран.
|
||||||
|
output_texture: str,
|
||||||
|
-- Размер фрейма. Например: {640, 480}
|
||||||
|
size: vec2
|
||||||
|
) -> Element, Document
|
||||||
|
|
||||||
|
-- Возвращает id активного фрейма (не id элемента).
|
||||||
|
gui.get_active_frame() -> str
|
||||||
|
|
||||||
|
-- Устанавливает активный фрейм, получающий пользовательский ввод.
|
||||||
|
-- Пустая строка указывает null-фрейм, при котором захватывается курсор.
|
||||||
|
gui.set_active_frame(
|
||||||
|
-- id фрейма, созданного через gui.create_frame
|
||||||
|
id: str,
|
||||||
|
-- Функция-поставщик позиции курсора во фрейме.
|
||||||
|
-- Используется для пользовательской проекции (например в 3D)
|
||||||
|
[опционально] cursorLocator
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Разметка
|
## Разметка
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
@ -109,4 +140,4 @@ gui.template(
|
||||||
```lua
|
```lua
|
||||||
-- Корневой UI документ.
|
-- Корневой UI документ.
|
||||||
gui.root: Document
|
gui.root: Document
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<panel size='400' color='0' interval='1' context='menu'>
|
<panel size='400' color='0' interval='1' context='menu'>
|
||||||
<button onclick='menu:reset()'>@Continue</button>
|
<button onclick='gui.close_menu()'>@Continue</button>
|
||||||
<button onclick='menu.page="content?mode=world"'>@Content</button>
|
<button onclick='menu.page="content?mode=world"'>@Content</button>
|
||||||
<button onclick='menu.page="settings"'>@Settings</button>
|
<button onclick='menu.page="settings"'>@Settings</button>
|
||||||
<button onclick='app.close_world(true)'>
|
<button onclick='app.close_world(true)'>
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,22 @@ _GUI_ROOT = Document.new("core:root")
|
||||||
_MENU = _GUI_ROOT.menu
|
_MENU = _GUI_ROOT.menu
|
||||||
menu = _MENU
|
menu = _MENU
|
||||||
gui.root = _GUI_ROOT
|
gui.root = _GUI_ROOT
|
||||||
|
gui.main_frame_id = "core:main"
|
||||||
|
|
||||||
|
function gui.close_menu()
|
||||||
|
if menu then
|
||||||
|
menu:reset()
|
||||||
|
end
|
||||||
|
gui.set_active_frame("")
|
||||||
|
end
|
||||||
|
|
||||||
|
local __gui_create_frame = gui.create_frame
|
||||||
|
function gui.create_frame(id, output, size)
|
||||||
|
__gui_create_frame(id, output, size)
|
||||||
|
|
||||||
|
local document = Document.new(id)
|
||||||
|
return document.root, document
|
||||||
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
local status, err = pcall(function()
|
local status, err = pcall(function()
|
||||||
|
|
@ -435,9 +451,12 @@ function __vc_on_hud_open()
|
||||||
if menu.page ~= "" then
|
if menu.page ~= "" then
|
||||||
if not menu:back() then
|
if not menu:back() then
|
||||||
menu:reset()
|
menu:reset()
|
||||||
|
gui.set_active_frame("")
|
||||||
end
|
end
|
||||||
elseif hud.is_inventory_open() then
|
elseif hud.is_inventory_open() then
|
||||||
hud.close_inventory()
|
hud.close_inventory()
|
||||||
|
elseif gui.get_active_frame() then
|
||||||
|
gui.set_active_frame("")
|
||||||
else
|
else
|
||||||
hud.pause()
|
hud.pause()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,9 @@ void Engine::startPauseLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::renderFrame() {
|
void Engine::renderFrame() {
|
||||||
|
if (input->isCursorLocked() != (gui->getActiveFrame() == nullptr)) {
|
||||||
|
input->toggleCursor();
|
||||||
|
}
|
||||||
screen->draw(time.getDelta());
|
screen->draw(time.getDelta());
|
||||||
|
|
||||||
DrawContext ctx(nullptr, *window, nullptr);
|
DrawContext ctx(nullptr, *window, nullptr);
|
||||||
|
|
|
||||||
|
|
@ -207,9 +207,6 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
||||||
}
|
}
|
||||||
|
|
||||||
Hud::~Hud() {
|
Hud::~Hud() {
|
||||||
if (input.isCursorLocked()) {
|
|
||||||
input.toggleCursor();
|
|
||||||
}
|
|
||||||
// removing all controlled ui
|
// removing all controlled ui
|
||||||
for (auto& element : elements) {
|
for (auto& element : elements) {
|
||||||
onRemove(element);
|
onRemove(element);
|
||||||
|
|
@ -342,9 +339,6 @@ void Hud::update(bool visible) {
|
||||||
if (!gui.isFocusCaught()) {
|
if (!gui.isFocusCaught()) {
|
||||||
processInput(visible);
|
processInput(visible);
|
||||||
}
|
}
|
||||||
if ((isMenuOpen || inventoryOpen) == input.isCursorLocked()) {
|
|
||||||
input.toggleCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blockUI) {
|
if (blockUI) {
|
||||||
voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z);
|
voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z);
|
||||||
|
|
@ -395,6 +389,7 @@ void Hud::openInventory(bool playerInventory) {
|
||||||
inventoryView->bind(inventory, &content);
|
inventoryView->bind(inventory, &content);
|
||||||
add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false));
|
add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false));
|
||||||
}
|
}
|
||||||
|
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Inventory> Hud::openInventory(
|
std::shared_ptr<Inventory> Hud::openInventory(
|
||||||
|
|
@ -548,6 +543,7 @@ void Hud::closeInventory() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanup();
|
cleanup();
|
||||||
|
gui.setActiveFrame("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
||||||
|
|
@ -710,9 +706,11 @@ void Hud::setPause(bool pause) {
|
||||||
|
|
||||||
if (!pause && menu.hasOpenPage()) {
|
if (!pause && menu.hasOpenPage()) {
|
||||||
menu.reset();
|
menu.reset();
|
||||||
|
gui.setActiveFrame("");
|
||||||
}
|
}
|
||||||
if (pause && !menu.hasOpenPage()) {
|
if (pause && !menu.hasOpenPage()) {
|
||||||
menu.setPage("pause");
|
menu.setPage("pause");
|
||||||
|
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ LevelScreen::LevelScreen(
|
||||||
auto& assets = *engine.getAssets();
|
auto& assets = *engine.getAssets();
|
||||||
auto menu = engine.getGUI().getMenu();
|
auto menu = engine.getGUI().getMenu();
|
||||||
menu->reset();
|
menu->reset();
|
||||||
|
gui.setActiveFrame("");
|
||||||
|
|
||||||
auto player = level->players->get(localPlayer);
|
auto player = level->players->get(localPlayer);
|
||||||
assert(player != nullptr);
|
assert(player != nullptr);
|
||||||
|
|
@ -239,7 +240,7 @@ void LevelScreen::update(float delta) {
|
||||||
|
|
||||||
auto menu = gui.getMenu();
|
auto menu = gui.getMenu();
|
||||||
bool inputLocked =
|
bool inputLocked =
|
||||||
menu->hasOpenPage() || hud->isInventoryOpen() || gui.isFocusCaught();
|
gui.getActiveFrame() || hud->isInventoryOpen() || gui.isFocusCaught();
|
||||||
bool paused = hud->isPause();
|
bool paused = hud->isPause();
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
world.updateTimers(delta);
|
world.updateTimers(delta);
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,10 @@ Texture* Framebuffer::getTexture() const {
|
||||||
return texture.get();
|
return texture.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> Framebuffer::getSharedTexture() const {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
uint Framebuffer::getWidth() const {
|
uint Framebuffer::getWidth() const {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class Framebuffer : public Bindable {
|
||||||
uint width;
|
uint width;
|
||||||
uint height;
|
uint height;
|
||||||
uint format;
|
uint format;
|
||||||
std::unique_ptr<Texture> texture;
|
std::shared_ptr<Texture> texture;
|
||||||
public:
|
public:
|
||||||
Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture);
|
Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture);
|
||||||
Framebuffer(uint width, uint height, bool alpha=false);
|
Framebuffer(uint width, uint height, bool alpha=false);
|
||||||
|
|
@ -33,6 +33,8 @@ public:
|
||||||
/// @brief Get framebuffer color attachment
|
/// @brief Get framebuffer color attachment
|
||||||
Texture* getTexture() const;
|
Texture* getTexture() const;
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> getSharedTexture() const;
|
||||||
|
|
||||||
/// @brief Get framebuffer width
|
/// @brief Get framebuffer width
|
||||||
uint getWidth() const;
|
uint getWidth() const;
|
||||||
/// @brief Get framebuffer height
|
/// @brief Get framebuffer height
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
#include "elements/Label.hpp"
|
#include "elements/Label.hpp"
|
||||||
#include "elements/Menu.hpp"
|
#include "elements/Menu.hpp"
|
||||||
#include "elements/Panel.hpp"
|
#include "elements/Panel.hpp"
|
||||||
|
#include "elements/Frame.hpp"
|
||||||
#include "elements/UINode.hpp"
|
#include "elements/UINode.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "frontend/UiDocument.hpp"
|
#include "frontend/UiDocument.hpp"
|
||||||
|
|
@ -26,13 +27,15 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
static debug::Logger logger("gui");
|
||||||
|
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
GUI::GUI(Engine& engine)
|
GUI::GUI(Engine& engine)
|
||||||
: engine(engine),
|
: engine(engine),
|
||||||
input(engine.getInput()),
|
input(engine.getInput()),
|
||||||
batch2D(std::make_unique<Batch2D>(1024)),
|
batch2D(std::make_unique<Batch2D>(1024)),
|
||||||
container(std::make_shared<Container>(*this, glm::vec2(1000))) {
|
container(std::make_shared<Frame>(*this, CORE_MAIN, "")) {
|
||||||
container->setId("root");
|
container->setId("root");
|
||||||
uicamera =
|
uicamera =
|
||||||
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
|
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
|
||||||
|
|
@ -63,6 +66,8 @@ GUI::GUI(Engine& engine)
|
||||||
std::dynamic_pointer_cast<gui::UINode>(container),
|
std::dynamic_pointer_cast<gui::UINode>(container),
|
||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
|
addFrame(container);
|
||||||
|
activeFrame = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI::~GUI() = default;
|
GUI::~GUI() = default;
|
||||||
|
|
@ -117,12 +122,19 @@ void GUI::updateTooltip(float delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Mouse related input and logic handling
|
/// @brief Mouse related input and logic handling
|
||||||
void GUI::actMouse(float delta, const CursorState& cursor) {
|
void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) {
|
||||||
float mouseDelta = glm::length(cursor.delta);
|
float mouseDelta = glm::length(cursor.delta);
|
||||||
doubleClicked = false;
|
doubleClicked = false;
|
||||||
doubleClickTimer += delta + mouseDelta * 0.1f;
|
doubleClickTimer += delta + mouseDelta * 0.1f;
|
||||||
|
|
||||||
auto hover = container->getAt(cursor.pos);
|
auto cursorPos = cursor.pos;
|
||||||
|
if (cursorLocator) {
|
||||||
|
cursorPos = cursorLocator();
|
||||||
|
if (activeFrame) {
|
||||||
|
cursorPos.y += activeFrame->getPos().y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto hover = frame.getAt(cursorPos);
|
||||||
if (this->hover && this->hover != hover) {
|
if (this->hover && this->hover != hover) {
|
||||||
this->hover->setMouseEnter(false);
|
this->hover->setMouseEnter(false);
|
||||||
}
|
}
|
||||||
|
|
@ -157,13 +169,13 @@ void GUI::actMouse(float delta, const CursorState& cursor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = mouseOver.begin(); it != mouseOver.end(); ) {
|
for (auto it = mouseOver.begin(); it != mouseOver.end(); ) {
|
||||||
auto node = it->lock();
|
auto mouseOverNode = it->lock();
|
||||||
if (node) {
|
if (mouseOverNode) {
|
||||||
if (node->isInside(cursor.pos)) {
|
if (mouseOverNode->isInside(cursorPos)) {
|
||||||
++it;
|
++it;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
node->setMouseOver(false);
|
mouseOverNode->setMouseOver(false);
|
||||||
}
|
}
|
||||||
it = mouseOver.erase(it);
|
it = mouseOver.erase(it);
|
||||||
}
|
}
|
||||||
|
|
@ -172,10 +184,10 @@ void GUI::actMouse(float delta, const CursorState& cursor) {
|
||||||
if (pressed == nullptr && this->hover) {
|
if (pressed == nullptr && this->hover) {
|
||||||
pressed = hover;
|
pressed = hover;
|
||||||
if (doubleClickTimer < doubleClickDelay) {
|
if (doubleClickTimer < doubleClickDelay) {
|
||||||
pressed->doubleClick(cursor.pos.x, cursor.pos.y);
|
pressed->doubleClick(cursorPos.x, cursorPos.y);
|
||||||
doubleClicked = true;
|
doubleClicked = true;
|
||||||
} else {
|
} else {
|
||||||
pressed->click(cursor.pos.x, cursor.pos.y);
|
pressed->click(cursorPos.x, cursorPos.y);
|
||||||
}
|
}
|
||||||
doubleClickTimer = 0.0f;
|
doubleClickTimer = 0.0f;
|
||||||
if (focus && focus != pressed) {
|
if (focus && focus != pressed) {
|
||||||
|
|
@ -192,7 +204,7 @@ void GUI::actMouse(float delta, const CursorState& cursor) {
|
||||||
focus = nullptr;
|
focus = nullptr;
|
||||||
}
|
}
|
||||||
} else if (!input.clicked(Mousecode::BUTTON_1) && pressed) {
|
} else if (!input.clicked(Mousecode::BUTTON_1) && pressed) {
|
||||||
pressed->mouseRelease(cursor.pos.x, cursor.pos.y);
|
pressed->mouseRelease(cursorPos.x, cursorPos.y);
|
||||||
pressed = nullptr;
|
pressed = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,14 +244,16 @@ void GUI::actFocused() {
|
||||||
|
|
||||||
void GUI::act(float delta, const glm::uvec2& vp) {
|
void GUI::act(float delta, const glm::uvec2& vp) {
|
||||||
container->setSize(vp);
|
container->setSize(vp);
|
||||||
container->act(delta);
|
for (auto& pair : frames) {
|
||||||
|
pair.second->act(delta);
|
||||||
|
}
|
||||||
auto prevfocus = focus;
|
auto prevfocus = focus;
|
||||||
|
|
||||||
updateTooltip(delta);
|
updateTooltip(delta);
|
||||||
|
|
||||||
const auto& cursor = input.getCursor();
|
const auto& cursor = input.getCursor();
|
||||||
if (!cursor.locked) {
|
if (!cursor.locked && activeFrame) {
|
||||||
actMouse(delta, cursor);
|
actMouse(*activeFrame, delta, cursor);
|
||||||
} else {
|
} else {
|
||||||
if (hover) {
|
if (hover) {
|
||||||
hover->setMouseEnter(false);
|
hover->setMouseEnter(false);
|
||||||
|
|
@ -262,7 +276,7 @@ void GUI::postAct() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
||||||
auto ctx = pctx.sub(batch2D.get());
|
auto ctx = pctx.sub(batch2D.get());
|
||||||
|
|
||||||
auto& viewport = ctx.getViewport();
|
auto& viewport = ctx.getViewport();
|
||||||
|
|
@ -280,11 +294,14 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
uicamera->setAspectRatio(viewport.x / static_cast<float>(viewport.y));
|
uicamera->setAspectRatio(viewport.x / static_cast<float>(viewport.y));
|
||||||
|
|
||||||
auto uishader = assets.get<Shader>("ui");
|
auto uishader = assets.get<Shader>("ui");
|
||||||
uishader->use();
|
|
||||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||||
|
uishader->use();
|
||||||
|
|
||||||
batch2D->begin();
|
batch2D->begin();
|
||||||
container->draw(ctx, assets);
|
for (auto& [outputTexture, frame] : frames) {
|
||||||
|
frame->updateOutput(assets);
|
||||||
|
frame->draw(ctx, assets);
|
||||||
|
}
|
||||||
|
|
||||||
if (hover) {
|
if (hover) {
|
||||||
engine.getWindow().setCursor(hover->getCursor());
|
engine.getWindow().setCursor(hover->getCursor());
|
||||||
|
|
@ -337,6 +354,30 @@ void GUI::add(std::shared_ptr<UINode> node) {
|
||||||
container->add(std::move(node));
|
container->add(std::move(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI::addFrame(std::shared_ptr<Frame> frame) {
|
||||||
|
const auto& id = frame->getFrameId();
|
||||||
|
frames[id] = std::move(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::setActiveFrame(const std::string& id, vec2supplier cursorLocator) {
|
||||||
|
this->cursorLocator = std::move(cursorLocator);
|
||||||
|
if (id.empty()) {
|
||||||
|
activeFrame = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& found = frames.find(id);
|
||||||
|
if (found == frames.end()) {
|
||||||
|
logger.error() << "attempted to make non-existing frame '" << id
|
||||||
|
<< "' as active";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activeFrame = found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<gui::Frame> GUI::getActiveFrame() const {
|
||||||
|
return activeFrame;
|
||||||
|
}
|
||||||
|
|
||||||
void GUI::remove(UINode* node) noexcept {
|
void GUI::remove(UINode* node) noexcept {
|
||||||
container->remove(node);
|
container->remove(node);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,14 @@ namespace gui {
|
||||||
class UINode;
|
class UINode;
|
||||||
class Container;
|
class Container;
|
||||||
class Menu;
|
class Menu;
|
||||||
|
class Frame;
|
||||||
|
|
||||||
/// @brief The main UI controller
|
/// @brief The main UI controller
|
||||||
class GUI {
|
class GUI {
|
||||||
Engine& engine;
|
Engine& engine;
|
||||||
Input& input;
|
Input& input;
|
||||||
std::unique_ptr<Batch2D> batch2D;
|
std::unique_ptr<Batch2D> batch2D;
|
||||||
std::shared_ptr<Container> container;
|
std::shared_ptr<Frame> container;
|
||||||
std::shared_ptr<UINode> hover;
|
std::shared_ptr<UINode> hover;
|
||||||
std::shared_ptr<UINode> pressed;
|
std::shared_ptr<UINode> pressed;
|
||||||
std::shared_ptr<UINode> focus;
|
std::shared_ptr<UINode> focus;
|
||||||
|
|
@ -75,11 +76,14 @@ namespace gui {
|
||||||
std::shared_ptr<UiDocument> rootDocument;
|
std::shared_ptr<UiDocument> rootDocument;
|
||||||
std::unique_ptr<FontStylesScheme> syntaxColorScheme;
|
std::unique_ptr<FontStylesScheme> syntaxColorScheme;
|
||||||
std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
|
std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
|
||||||
|
std::shared_ptr<Frame> activeFrame;
|
||||||
|
|
||||||
std::unique_ptr<Camera> uicamera;
|
std::unique_ptr<Camera> uicamera;
|
||||||
std::shared_ptr<Menu> menu;
|
std::shared_ptr<Menu> menu;
|
||||||
std::queue<runnable> postRunnables;
|
std::queue<runnable> postRunnables;
|
||||||
std::vector<std::weak_ptr<UINode>> mouseOver;
|
std::vector<std::weak_ptr<UINode>> mouseOver;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<Frame>> frames;
|
||||||
|
vec2supplier cursorLocator;
|
||||||
|
|
||||||
float tooltipTimer = 0.0f;
|
float tooltipTimer = 0.0f;
|
||||||
float doubleClickTimer = 0.0f;
|
float doubleClickTimer = 0.0f;
|
||||||
|
|
@ -87,11 +91,12 @@ namespace gui {
|
||||||
bool doubleClicked = false;
|
bool doubleClicked = false;
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
|
|
||||||
void actMouse(float delta, const CursorState& cursor);
|
void actMouse(Frame& frame, float delta, const CursorState& cursor);
|
||||||
void actFocused();
|
void actFocused();
|
||||||
void updateTooltip(float delta);
|
void updateTooltip(float delta);
|
||||||
void resetTooltip();
|
void resetTooltip();
|
||||||
public:
|
public:
|
||||||
|
static inline std::string CORE_MAIN = "core:main";
|
||||||
static constexpr int CONTEXT_MENU_ZINDEX = 999;
|
static constexpr int CONTEXT_MENU_ZINDEX = 999;
|
||||||
|
|
||||||
GUI(Engine& engine);
|
GUI(Engine& engine);
|
||||||
|
|
@ -115,7 +120,7 @@ namespace gui {
|
||||||
/// @brief Draw all visible elements on main container
|
/// @brief Draw all visible elements on main container
|
||||||
/// @param pctx parent graphics context
|
/// @param pctx parent graphics context
|
||||||
/// @param assets active assets storage
|
/// @param assets active assets storage
|
||||||
void draw(const DrawContext& pctx, const Assets& assets);
|
void draw(const DrawContext& pctx, Assets& assets);
|
||||||
|
|
||||||
void postAct();
|
void postAct();
|
||||||
|
|
||||||
|
|
@ -123,6 +128,14 @@ namespace gui {
|
||||||
/// @param node UI element
|
/// @param node UI element
|
||||||
void add(std::shared_ptr<UINode> node);
|
void add(std::shared_ptr<UINode> node);
|
||||||
|
|
||||||
|
void addFrame(std::shared_ptr<Frame> frame);
|
||||||
|
|
||||||
|
void setActiveFrame(
|
||||||
|
const std::string& id, vec2supplier cursorLocator = nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
std::shared_ptr<Frame> getActiveFrame() const;
|
||||||
|
|
||||||
/// @brief Remove node from the main container
|
/// @brief Remove node from the main container
|
||||||
void remove(UINode* node) noexcept;
|
void remove(UINode* node) noexcept;
|
||||||
|
|
||||||
|
|
|
||||||
64
src/graphics/ui/elements/Frame.cpp
Normal file
64
src/graphics/ui/elements/Frame.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include "Frame.hpp"
|
||||||
|
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
#include "graphics/core/Batch2D.hpp"
|
||||||
|
#include "graphics/core/Framebuffer.hpp"
|
||||||
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
#include "graphics/core/Texture.hpp"
|
||||||
|
#include "frontend/UiDocument.hpp"
|
||||||
|
#include "window/Window.hpp"
|
||||||
|
#include "../GUI.hpp"
|
||||||
|
|
||||||
|
using namespace gui;
|
||||||
|
|
||||||
|
static inline constexpr int MAX_TEXTURE_SIZE = 2048;
|
||||||
|
|
||||||
|
gui::Frame::Frame(GUI& gui, std::string id, std::string outputTexture)
|
||||||
|
: Container(gui, {}),
|
||||||
|
fbo(nullptr),
|
||||||
|
outputTexture(std::move(outputTexture)) {
|
||||||
|
frameId = std::move(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
gui::Frame::~Frame() = default;
|
||||||
|
|
||||||
|
void gui::Frame::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
|
if (outputTexture.empty()) {
|
||||||
|
Container::draw(pctx, assets);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fbo == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
glm::ivec2 size = getSize();
|
||||||
|
if (size.x <= 0 || size.y <= 0 ||
|
||||||
|
size.x > MAX_TEXTURE_SIZE || size.y > MAX_TEXTURE_SIZE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ui uses flipped camera with matrix based on main viewport
|
||||||
|
setPos({0, pctx.getViewport().y - size.y});
|
||||||
|
|
||||||
|
auto ctx = pctx.sub();
|
||||||
|
ctx.setFramebuffer(fbo.get());
|
||||||
|
display::clear();
|
||||||
|
Container::draw(ctx, assets);
|
||||||
|
ctx.getBatch2D()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::Frame::updateOutput(Assets& assets) {
|
||||||
|
if (fbo && (fbo->getWidth() != size.x || fbo->getHeight() != size.y)) {
|
||||||
|
fbo->resize(size.x, size.y);
|
||||||
|
assets.store(fbo->getSharedTexture(), outputTexture);
|
||||||
|
} else if (fbo == nullptr) {
|
||||||
|
fbo = std::make_unique<Framebuffer>(size.x, size.y, true);
|
||||||
|
assets.store(fbo->getSharedTexture(), outputTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& gui::Frame::getOutputTexture() const {
|
||||||
|
return outputTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& gui::Frame::getFrameId() const {
|
||||||
|
return frameId;
|
||||||
|
}
|
||||||
26
src/graphics/ui/elements/Frame.hpp
Normal file
26
src/graphics/ui/elements/Frame.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Container.hpp"
|
||||||
|
|
||||||
|
class Framebuffer;
|
||||||
|
class UiDocument;
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
class Frame final : public Container {
|
||||||
|
public:
|
||||||
|
Frame(GUI& gui, std::string id, std::string outputTexture);
|
||||||
|
virtual ~Frame();
|
||||||
|
|
||||||
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
|
||||||
|
void updateOutput(Assets& assets);
|
||||||
|
|
||||||
|
const std::string& getOutputTexture() const;
|
||||||
|
|
||||||
|
const std::string& getFrameId() const;
|
||||||
|
private:
|
||||||
|
std::string frameId;
|
||||||
|
std::unique_ptr<Framebuffer> fbo;
|
||||||
|
std::string outputTexture;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -78,6 +78,7 @@ void guiutil::alert(
|
||||||
}));
|
}));
|
||||||
menu.addPage("<alert>", panel, true);
|
menu.addPage("<alert>", panel, true);
|
||||||
menu.setPage("<alert>");
|
menu.setPage("<alert>");
|
||||||
|
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void guiutil::confirm(
|
void guiutil::confirm(
|
||||||
|
|
@ -155,6 +156,7 @@ void guiutil::confirm(
|
||||||
panel->refresh();
|
panel->refresh();
|
||||||
menu->addPage("<confirm>", container, true);
|
menu->addPage("<confirm>", container, true);
|
||||||
menu->setPage("<confirm>");
|
menu->setPage("<confirm>");
|
||||||
|
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void guiutil::confirm_with_memo(
|
void guiutil::confirm_with_memo(
|
||||||
|
|
@ -201,4 +203,5 @@ void guiutil::confirm_with_memo(
|
||||||
panel->refresh();
|
panel->refresh();
|
||||||
menu->addPage("<confirm>", panel, true);
|
menu->addPage("<confirm>", panel, true);
|
||||||
menu->setPage("<confirm>");
|
menu->setPage("<confirm>");
|
||||||
|
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <glm/gtc/constants.hpp>
|
||||||
|
|
||||||
#include "BlocksController.hpp"
|
#include "BlocksController.hpp"
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
|
|
@ -28,17 +29,19 @@
|
||||||
#include "window/input.hpp"
|
#include "window/input.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
const float INTERACTION_RELOAD = 0.160f;
|
namespace {
|
||||||
const float STEPS_SPEED = 2.2f;
|
const float INTERACTION_RELOAD = 0.160f;
|
||||||
const float CAM_SHAKE_OFFSET = 0.0075f;
|
const float STEPS_SPEED = 2.2f;
|
||||||
const float CAM_SHAKE_OFFSET_Y = 0.031f;
|
const float CAM_SHAKE_OFFSET = 0.0075f;
|
||||||
const float CAM_SHAKE_SPEED = STEPS_SPEED;
|
const float CAM_SHAKE_OFFSET_Y = 0.031f;
|
||||||
const float CAM_SHAKE_DELTA_K = 10.0f;
|
const float CAM_SHAKE_SPEED = STEPS_SPEED;
|
||||||
const float ZOOM_SPEED = 16.0f;
|
const float CAM_SHAKE_DELTA_K = 10.0f;
|
||||||
const float CROUCH_ZOOM = 0.9f;
|
const float ZOOM_SPEED = 16.0f;
|
||||||
const float RUN_ZOOM = 1.1f;
|
const float CROUCH_ZOOM = 0.9f;
|
||||||
const float C_ZOOM = 0.1f;
|
const float RUN_ZOOM = 1.1f;
|
||||||
const float CROUCH_SHIFT_Y = -0.2f;
|
const float C_ZOOM = 0.1f;
|
||||||
|
const float CROUCH_SHIFT_Y = -0.2f;
|
||||||
|
}
|
||||||
|
|
||||||
CameraControl::CameraControl(Player& player, const CameraSettings& settings)
|
CameraControl::CameraControl(Player& player, const CameraSettings& settings)
|
||||||
: player(player),
|
: player(player),
|
||||||
|
|
@ -249,17 +252,18 @@ void PlayerController::onFootstep(const Hitbox& hitbox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::updateFootsteps(float delta) {
|
void PlayerController::updateFootsteps(float delta) {
|
||||||
|
const float pi = glm::pi<float>();
|
||||||
auto hitbox = player.getHitbox();
|
auto hitbox = player.getHitbox();
|
||||||
if (hitbox && hitbox->grounded) {
|
if (hitbox && hitbox->grounded) {
|
||||||
const glm::vec3& vel = hitbox->velocity;
|
const glm::vec3& vel = hitbox->velocity;
|
||||||
float f = glm::length(glm::vec2(vel.x, vel.z));
|
float f = glm::length(glm::vec2(vel.x, vel.z));
|
||||||
stepsTimer += delta * f * STEPS_SPEED;
|
stepsTimer += delta * f * STEPS_SPEED;
|
||||||
if (stepsTimer >= M_PI) {
|
if (stepsTimer >= pi) {
|
||||||
stepsTimer = fmod(stepsTimer, M_PI);
|
stepsTimer = fmod(stepsTimer, pi);
|
||||||
onFootstep(*hitbox);
|
onFootstep(*hitbox);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stepsTimer = M_PI;
|
stepsTimer = pi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include "graphics/ui/elements/TrackBar.hpp"
|
#include "graphics/ui/elements/TrackBar.hpp"
|
||||||
#include "graphics/ui/elements/InlineFrame.hpp"
|
#include "graphics/ui/elements/InlineFrame.hpp"
|
||||||
#include "graphics/ui/elements/ModelViewer.hpp"
|
#include "graphics/ui/elements/ModelViewer.hpp"
|
||||||
|
#include "graphics/ui/elements/Frame.hpp"
|
||||||
#include "graphics/ui/gui_util.hpp"
|
#include "graphics/ui/gui_util.hpp"
|
||||||
#include "graphics/ui/markdown.hpp"
|
#include "graphics/ui/markdown.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
|
|
@ -1149,6 +1150,62 @@ static int l_set_syntax_styles(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_create_frame(lua::State* L) {
|
||||||
|
if (engine->isHeadless()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::string id = lua::require_string(L, 1);
|
||||||
|
std::string outputTexture = lua::require_string(L, 2);
|
||||||
|
auto size = lua::tovec2(L, 3);
|
||||||
|
|
||||||
|
auto& gui = engine->getGUI();
|
||||||
|
auto frame = std::make_shared<gui::Frame>(gui, id, outputTexture);
|
||||||
|
frame->setSize(std::move(size));
|
||||||
|
auto& assets = *engine->getAssets();
|
||||||
|
auto document = std::make_shared<UiDocument>(
|
||||||
|
id, UiDocScript {}, frame, nullptr
|
||||||
|
);
|
||||||
|
assets.store(document, id);
|
||||||
|
gui.addFrame(std::move(frame));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_active_frame(lua::State* L) {
|
||||||
|
if (engine->isHeadless()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::string id = lua::require_string(L, 1);
|
||||||
|
vec2supplier cursorLocator = nullptr;
|
||||||
|
if (lua::isfunction(L, 2)) {
|
||||||
|
auto lambda = lua::create_lambda(L);
|
||||||
|
cursorLocator = [lambda]() -> glm::vec2 {
|
||||||
|
auto table = lambda({});
|
||||||
|
if (!table.isList()) {
|
||||||
|
throw std::runtime_error("invalid value returned from locator");
|
||||||
|
}
|
||||||
|
glm::vec2 pos {};
|
||||||
|
table.at(0).get(pos.x);
|
||||||
|
table.at(1).get(pos.y);
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
auto& gui = engine->getGUI();
|
||||||
|
gui.setActiveFrame(id, std::move(cursorLocator));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_active_frame(lua::State* L) {
|
||||||
|
if (engine->isHeadless()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto& gui = engine->getGUI();
|
||||||
|
auto frame = gui.getActiveFrame();
|
||||||
|
if (frame == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return lua::pushstring(L, frame->getId());
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg guilib[] = {
|
const luaL_Reg guilib[] = {
|
||||||
{"get_viewport", lua::wrap<l_gui_getviewport>},
|
{"get_viewport", lua::wrap<l_gui_getviewport>},
|
||||||
{"getattr", lua::wrap<l_gui_getattr>},
|
{"getattr", lua::wrap<l_gui_getattr>},
|
||||||
|
|
@ -1162,6 +1219,9 @@ const luaL_Reg guilib[] = {
|
||||||
{"alert", lua::wrap<l_gui_alert>},
|
{"alert", lua::wrap<l_gui_alert>},
|
||||||
{"load_document", lua::wrap<l_gui_load_document>},
|
{"load_document", lua::wrap<l_gui_load_document>},
|
||||||
{"set_syntax_styles", lua::wrap<l_set_syntax_styles>},
|
{"set_syntax_styles", lua::wrap<l_set_syntax_styles>},
|
||||||
|
{"create_frame", lua::wrap<l_create_frame>},
|
||||||
|
{"set_active_frame", lua::wrap<l_set_active_frame>},
|
||||||
|
{"get_active_frame", lua::wrap<l_get_active_frame>},
|
||||||
{"__reindex", lua::wrap<l_gui_reindex>},
|
{"__reindex", lua::wrap<l_gui_reindex>},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue