Merge branch 'main' into release-0.26
This commit is contained in:
@@ -49,10 +49,11 @@ world.is_night() -> bool
|
|||||||
world.count_chunks() -> int
|
world.count_chunks() -> int
|
||||||
|
|
||||||
-- Returns the compressed chunk data to send.
|
-- Returns the compressed chunk data to send.
|
||||||
|
-- If the chunk is not loaded, returns the saved data.
|
||||||
-- Currently includes:
|
-- Currently includes:
|
||||||
-- 1. Voxel data (id and state)
|
-- 1. Voxel data (id and state)
|
||||||
-- 2. Voxel metadata (fields)
|
-- 2. Voxel metadata (fields)
|
||||||
world.get_chunk_data(x: int, z: int) -> Bytearray
|
world.get_chunk_data(x: int, z: int) -> Bytearray or nil
|
||||||
|
|
||||||
-- Modifies the chunk based on the compressed data.
|
-- Modifies the chunk based on the compressed data.
|
||||||
-- Returns true if the chunk exists.
|
-- Returns true if the chunk exists.
|
||||||
@@ -61,4 +62,12 @@ world.set_chunk_data(
|
|||||||
-- compressed chunk data
|
-- compressed chunk data
|
||||||
data: Bytearray
|
data: Bytearray
|
||||||
) -> bool
|
) -> bool
|
||||||
|
|
||||||
|
-- Saves chunk data to region.
|
||||||
|
-- Changes will be written to file only on world save.
|
||||||
|
world.save_chunk_data(
|
||||||
|
x: int, z: int,
|
||||||
|
-- compressed chunk data
|
||||||
|
data: Bytearray
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -156,6 +156,25 @@ Properties:
|
|||||||
| ----- | ------ | ---- | ----- | ------------ |
|
| ----- | ------ | ---- | ----- | ------------ |
|
||||||
| src | string | yes | yes | texture name |
|
| src | string | yes | yes | texture name |
|
||||||
|
|
||||||
|
## Canvas
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
|
||||||
|
| Title | Type | Read | Write | Description |
|
||||||
|
|-------|--------| ---- |-------|-------------|
|
||||||
|
| data | Canvas | yes | no | canvas data |
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
|
||||||
|
| Method | Description |
|
||||||
|
|----------------------------------------------------------|---------------------------------------------------------|
|
||||||
|
| data:at(x: int, y: int) | returns an RGBA pixel at the given coordinates |
|
||||||
|
| data:set(x: int, y: int, rgba: int) | updates an RGBA pixel at the given coordinates |
|
||||||
|
| data:set(x: int, y: int, r: int, g: int, b: int) | updates an RGBA pixel at the given coordinates |
|
||||||
|
| data:set(x: int, y: int, r: int, g: int, b: int, a: int) | updates an RGBA pixel at the given coordinates |
|
||||||
|
| data:update() | applies changes to the canvas and uploads it to the GPU |
|
||||||
|
|
||||||
|
|
||||||
## Inventory
|
## Inventory
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
|
|||||||
@@ -97,7 +97,11 @@ Inner text is a button text.
|
|||||||
- `src` - name of an image stored in textures folder. Extension is not specified. Type: string.
|
- `src` - name of an image stored in textures folder. Extension is not specified. Type: string.
|
||||||
Example: *gui/error*
|
Example: *gui/error*
|
||||||
|
|
||||||
## *textbox*
|
## *canvas*
|
||||||
|
|
||||||
|
- _No additional attributes_
|
||||||
|
|
||||||
|
## *textbox*
|
||||||
|
|
||||||
Inner text - initially entered text
|
Inner text - initially entered text
|
||||||
|
|
||||||
|
|||||||
@@ -48,10 +48,11 @@ world.is_night() -> bool
|
|||||||
world.count_chunks() -> int
|
world.count_chunks() -> int
|
||||||
|
|
||||||
-- Возвращает сжатые данные чанка для отправки.
|
-- Возвращает сжатые данные чанка для отправки.
|
||||||
|
-- Если чанк не загружен, возвращает сохранённые данные.
|
||||||
-- На данный момент включает:
|
-- На данный момент включает:
|
||||||
-- 1. Данные вокселей (id и состояние)
|
-- 1. Данные вокселей (id и состояние)
|
||||||
-- 2. Метаданные (поля) вокселей
|
-- 2. Метаданные (поля) вокселей
|
||||||
world.get_chunk_data(x: int, z: int) -> Bytearray
|
world.get_chunk_data(x: int, z: int) -> Bytearray или nil
|
||||||
|
|
||||||
-- Изменяет чанк на основе сжатых данных.
|
-- Изменяет чанк на основе сжатых данных.
|
||||||
-- Возвращает true если чанк существует.
|
-- Возвращает true если чанк существует.
|
||||||
@@ -60,4 +61,12 @@ world.set_chunk_data(
|
|||||||
-- сжатые данные чанка
|
-- сжатые данные чанка
|
||||||
data: Bytearray
|
data: Bytearray
|
||||||
) -> bool
|
) -> bool
|
||||||
|
|
||||||
|
-- Сохраняет данные чанка в регион.
|
||||||
|
-- Изменения будет записаны в файл только после сохранения мира.
|
||||||
|
world.save_chunk_data(
|
||||||
|
x: int, z: int,
|
||||||
|
-- сжатые данные чанка
|
||||||
|
data: Bytearray
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -156,6 +156,25 @@ document["worlds-panel"]:clear()
|
|||||||
| -------- | ------ | ------ | ------ | --------------------- |
|
| -------- | ------ | ------ | ------ | --------------------- |
|
||||||
| src | string | да | да | отображаемая текстура |
|
| src | string | да | да | отображаемая текстура |
|
||||||
|
|
||||||
|
|
||||||
|
## Холст (canvas)
|
||||||
|
|
||||||
|
Свойства:
|
||||||
|
|
||||||
|
| Название | Тип | Чтение | Запись | Описание |
|
||||||
|
|----------|--------| ------ |--------|----------------|
|
||||||
|
| data | Canvas | да | нет | пиксели холста |
|
||||||
|
|
||||||
|
Методы:
|
||||||
|
|
||||||
|
| Метод | Описание |
|
||||||
|
|----------------------------------------------------------|-----------------------------------------------------|
|
||||||
|
| data:at(x: int, y: int) | возвращает RGBA пиксель по указанным координатам |
|
||||||
|
| data:set(x: int, y: int, rgba: int) | изменяет RGBA пиксель по указанным координатам |
|
||||||
|
| data:set(x: int, y: int, r: int, g: int, b: int) | изменяет RGBA пиксель по указанным координатам |
|
||||||
|
| data:set(x: int, y: int, r: int, g: int, b: int, a: int) | изменяет RGBA пиксель по указанным координатам |
|
||||||
|
| data:update() | применяет изменения и загружает холст в видеопамять |
|
||||||
|
|
||||||
## Inventory (inventory)
|
## Inventory (inventory)
|
||||||
|
|
||||||
Свойства:
|
Свойства:
|
||||||
|
|||||||
@@ -98,6 +98,10 @@
|
|||||||
|
|
||||||
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
|
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
|
||||||
|
|
||||||
|
## Холст - *canvas*
|
||||||
|
|
||||||
|
- _Нет дополнительных свойств_
|
||||||
|
|
||||||
## Текстовое поле - *textbox*
|
## Текстовое поле - *textbox*
|
||||||
|
|
||||||
Внутренний текст - изначально введенный текст
|
Внутренний текст - изначально введенный текст
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ local animation_fps = 30
|
|||||||
|
|
||||||
local function remove_line(line)
|
local function remove_line(line)
|
||||||
document[line[1]]:destruct()
|
document[line[1]]:destruct()
|
||||||
time.post_runnable(function() document.root:reposition() end)
|
time.post_runnable(function()
|
||||||
|
if world.is_open() then document.root:reposition() end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_line(line, uptime)
|
local function update_line(line, uptime)
|
||||||
|
|||||||
@@ -313,8 +313,8 @@ function bit_converter.bytes_to_uint16(bytes, order)
|
|||||||
|
|
||||||
return
|
return
|
||||||
bit.bor(
|
bit.bor(
|
||||||
bit.lshift(bytes[2], 8),
|
bit.lshift(bytes[1], 8),
|
||||||
bytes[1], 0)
|
bytes[2], 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function bit_converter.bytes_to_int64(bytes, order)
|
function bit_converter.bytes_to_int64(bytes, order)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function gui_util.add_page_dispatcher(dispatcher)
|
|||||||
table.insert(gui_util.local_dispatchers, dispatcher)
|
table.insert(gui_util.local_dispatchers, dispatcher)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gui_util.reset_local()
|
function gui_util.__reset_local()
|
||||||
gui_util.local_dispatchers = {}
|
gui_util.local_dispatchers = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -132,5 +132,8 @@ return {
|
|||||||
end
|
end
|
||||||
return values
|
return values
|
||||||
end
|
end
|
||||||
|
end,
|
||||||
|
__reset = function()
|
||||||
|
entities = {}
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,8 +343,8 @@ function __vc_on_hud_open()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
input.add_callback("key:escape", function()
|
input.add_callback("key:escape", function()
|
||||||
if hud.is_paused() then
|
if menu.page ~= "" then
|
||||||
hud.resume()
|
menu:reset()
|
||||||
elseif hud.is_inventory_open() then
|
elseif hud.is_inventory_open() then
|
||||||
hud.close_inventory()
|
hud.close_inventory()
|
||||||
else
|
else
|
||||||
@@ -375,7 +375,8 @@ end
|
|||||||
|
|
||||||
function __vc_on_world_quit()
|
function __vc_on_world_quit()
|
||||||
_rules.clear()
|
_rules.clear()
|
||||||
gui_util:reset_local()
|
gui_util:__reset_local()
|
||||||
|
stdcomp.__reset()
|
||||||
end
|
end
|
||||||
|
|
||||||
local __vc_coroutines = {}
|
local __vc_coroutines = {}
|
||||||
|
|||||||
@@ -687,9 +687,10 @@ void Hud::setPause(bool pause) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& menu = gui.getMenu();
|
const auto& menu = gui.getMenu();
|
||||||
if (menu->hasOpenPage()) {
|
if (!pause && menu->hasOpenPage()) {
|
||||||
menu->reset();
|
menu->reset();
|
||||||
} else {
|
}
|
||||||
|
if (pause && !menu->hasOpenPage()) {
|
||||||
menu->setPage("pause");
|
menu->setPage("pause");
|
||||||
}
|
}
|
||||||
menu->setVisible(pause);
|
menu->setVisible(pause);
|
||||||
|
|||||||
@@ -117,7 +117,9 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LevelScreen::~LevelScreen() {
|
LevelScreen::~LevelScreen() {
|
||||||
saveWorldPreview();
|
if (!controller->getLevel()->getWorld()->isNameless()) {
|
||||||
|
saveWorldPreview();
|
||||||
|
}
|
||||||
scripting::on_frontend_close();
|
scripting::on_frontend_close();
|
||||||
// unblock all bindings
|
// unblock all bindings
|
||||||
Events::enableBindings();
|
Events::enableBindings();
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "Canvas.hpp"
|
||||||
|
|
||||||
|
#include "graphics/core/Batch2D.hpp"
|
||||||
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
#include "graphics/core/Texture.hpp"
|
||||||
|
|
||||||
|
gui::Canvas::Canvas(ImageFormat inFormat, glm::uvec2 inSize) : UINode(inSize) {
|
||||||
|
ImageData data {inFormat, inSize.x, inSize.y};
|
||||||
|
mTexture = Texture::from(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::Canvas::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
|
auto pos = calcPos();
|
||||||
|
auto col = calcColor();
|
||||||
|
|
||||||
|
auto batch = pctx.getBatch2D();
|
||||||
|
batch->texture(mTexture.get());
|
||||||
|
batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, true, col);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "UINode.hpp"
|
||||||
|
#include "graphics/core/ImageData.hpp"
|
||||||
|
#include "graphics/core/Texture.hpp"
|
||||||
|
|
||||||
|
class Texture;
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
class Canvas final : public UINode {
|
||||||
|
public:
|
||||||
|
explicit Canvas(ImageFormat inFormat, glm::uvec2 inSize);
|
||||||
|
|
||||||
|
~Canvas() override = default;
|
||||||
|
|
||||||
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<::Texture> texture() const {
|
||||||
|
return mTexture;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<::Texture> mTexture;
|
||||||
|
std::unique_ptr<ImageData> mData;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "elements/Image.hpp"
|
#include "elements/Image.hpp"
|
||||||
#include "elements/Menu.hpp"
|
#include "elements/Menu.hpp"
|
||||||
#include "elements/Button.hpp"
|
#include "elements/Button.hpp"
|
||||||
|
#include "elements/Canvas.hpp"
|
||||||
#include "elements/CheckBox.hpp"
|
#include "elements/CheckBox.hpp"
|
||||||
#include "elements/TextBox.hpp"
|
#include "elements/TextBox.hpp"
|
||||||
#include "elements/TrackBar.hpp"
|
#include "elements/TrackBar.hpp"
|
||||||
@@ -455,6 +456,18 @@ static std::shared_ptr<UINode> readImage(
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<UINode> readCanvas(
|
||||||
|
const UiXmlReader& reader, const xml::xmlelement& element
|
||||||
|
) {
|
||||||
|
auto size = glm::uvec2{32, 32};
|
||||||
|
if (element.has("size")) {
|
||||||
|
size = element.attr("size").asVec2();
|
||||||
|
}
|
||||||
|
auto image = std::make_shared<Canvas>(ImageFormat::rgba8888, size);
|
||||||
|
_readUINode(reader, element, *image);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
static std::shared_ptr<UINode> readTrackBar(
|
static std::shared_ptr<UINode> readTrackBar(
|
||||||
const UiXmlReader& reader, const xml::xmlelement& element
|
const UiXmlReader& reader, const xml::xmlelement& element
|
||||||
) {
|
) {
|
||||||
@@ -634,6 +647,7 @@ static std::shared_ptr<UINode> readPageBox(UiXmlReader& reader, const xml::xmlel
|
|||||||
UiXmlReader::UiXmlReader(const scriptenv& env) : env(env) {
|
UiXmlReader::UiXmlReader(const scriptenv& env) : env(env) {
|
||||||
contextStack.emplace("");
|
contextStack.emplace("");
|
||||||
add("image", readImage);
|
add("image", readImage);
|
||||||
|
add("canvas", readCanvas);
|
||||||
add("label", readLabel);
|
add("label", readLabel);
|
||||||
add("panel", readPanel);
|
add("panel", readPanel);
|
||||||
add("button", readButton);
|
add("button", readButton);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "frontend/locale.hpp"
|
#include "frontend/locale.hpp"
|
||||||
#include "graphics/ui/elements/Button.hpp"
|
#include "graphics/ui/elements/Button.hpp"
|
||||||
|
#include "graphics/ui/elements/Canvas.hpp"
|
||||||
#include "graphics/ui/elements/CheckBox.hpp"
|
#include "graphics/ui/elements/CheckBox.hpp"
|
||||||
#include "graphics/ui/elements/Image.hpp"
|
#include "graphics/ui/elements/Image.hpp"
|
||||||
#include "graphics/ui/elements/InventoryView.hpp"
|
#include "graphics/ui/elements/InventoryView.hpp"
|
||||||
@@ -323,6 +324,13 @@ static int p_get_src(UINode* node, lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int p_get_data(UINode* node, lua::State* L) {
|
||||||
|
if (auto canvas = dynamic_cast<Canvas*>(node)) {
|
||||||
|
return lua::newuserdata<lua::LuaCanvas>(L, canvas->texture());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int p_get_add(UINode* node, lua::State* L) {
|
static int p_get_add(UINode* node, lua::State* L) {
|
||||||
if (dynamic_cast<Container*>(node)) {
|
if (dynamic_cast<Container*>(node)) {
|
||||||
return lua::pushcfunction(L, lua::wrap<l_container_add>);
|
return lua::pushcfunction(L, lua::wrap<l_container_add>);
|
||||||
@@ -464,6 +472,7 @@ static int l_gui_getattr(lua::State* L) {
|
|||||||
{"inventory", p_get_inventory},
|
{"inventory", p_get_inventory},
|
||||||
{"focused", p_get_focused},
|
{"focused", p_get_focused},
|
||||||
{"cursor", p_get_cursor},
|
{"cursor", p_get_cursor},
|
||||||
|
{"data", p_get_data},
|
||||||
};
|
};
|
||||||
auto func = getters.find(attr);
|
auto func = getters.find(attr);
|
||||||
if (func != getters.end()) {
|
if (func != getters.end()) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "assets/AssetsLoader.hpp"
|
#include "assets/AssetsLoader.hpp"
|
||||||
#include "coders/json.hpp"
|
#include "coders/json.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
|
#include "files/WorldFiles.hpp"
|
||||||
#include "files/engine_paths.hpp"
|
#include "files/engine_paths.hpp"
|
||||||
#include "files/files.hpp"
|
#include "files/files.hpp"
|
||||||
#include "lighting/Lighting.hpp"
|
#include "lighting/Lighting.hpp"
|
||||||
@@ -125,11 +126,21 @@ static int l_get_chunk_data(lua::State* L) {
|
|||||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||||
const auto& chunk = level->chunks->getChunk(x, z);
|
const auto& chunk = level->chunks->getChunk(x, z);
|
||||||
|
|
||||||
|
std::vector<ubyte> chunkData;
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
lua::pushnil(L);
|
auto& regions = level->getWorld()->wfile->getRegions();
|
||||||
return 0;
|
auto voxelData = regions.getVoxels(x, z);
|
||||||
|
if (voxelData == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static util::Buffer<ubyte> rleBuffer(CHUNK_DATA_LEN * 2);
|
||||||
|
auto metadata = regions.getBlocksData(x, z);
|
||||||
|
chunkData =
|
||||||
|
compressed_chunks::encode(voxelData.get(), metadata, rleBuffer);
|
||||||
|
} else {
|
||||||
|
chunkData = compressed_chunks::encode(*chunk);
|
||||||
}
|
}
|
||||||
auto chunkData = compressed_chunks::encode(*chunk);
|
|
||||||
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
|
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,9 +169,14 @@ static void integrate_chunk_client(Chunk& chunk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_chunk_data(lua::State* L) {
|
static int l_set_chunk_data(lua::State* L) {
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("no open world");
|
||||||
|
}
|
||||||
|
|
||||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||||
auto buffer = lua::require_bytearray(L, 3);
|
auto buffer = lua::require_bytearray(L, 3);
|
||||||
|
|
||||||
auto chunk = level->chunks->getChunk(x, z);
|
auto chunk = level->chunks->getChunk(x, z);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
return lua::pushboolean(L, false);
|
return lua::pushboolean(L, false);
|
||||||
@@ -175,6 +191,21 @@ static int l_set_chunk_data(lua::State* L) {
|
|||||||
return lua::pushboolean(L, true);
|
return lua::pushboolean(L, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_save_chunk_data(lua::State* L) {
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("no open world");
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||||
|
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||||
|
auto buffer = lua::require_bytearray(L, 3);
|
||||||
|
|
||||||
|
compressed_chunks::save(
|
||||||
|
x, z, std::move(buffer), level->getWorld()->wfile->getRegions()
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int l_count_chunks(lua::State* L) {
|
static int l_count_chunks(lua::State* L) {
|
||||||
if (level == nullptr) {
|
if (level == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -197,6 +228,7 @@ const luaL_Reg worldlib[] = {
|
|||||||
{"exists", lua::wrap<l_exists>},
|
{"exists", lua::wrap<l_exists>},
|
||||||
{"get_chunk_data", lua::wrap<l_get_chunk_data>},
|
{"get_chunk_data", lua::wrap<l_get_chunk_data>},
|
||||||
{"set_chunk_data", lua::wrap<l_set_chunk_data>},
|
{"set_chunk_data", lua::wrap<l_set_chunk_data>},
|
||||||
|
{"save_chunk_data", lua::wrap<l_save_chunk_data>},
|
||||||
{"count_chunks", lua::wrap<l_count_chunks>},
|
{"count_chunks", lua::wrap<l_count_chunks>},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
struct fnl_state;
|
struct fnl_state;
|
||||||
class Heightmap;
|
class Heightmap;
|
||||||
class VoxelFragment;
|
class VoxelFragment;
|
||||||
|
class Texture;
|
||||||
|
class ImageData;
|
||||||
|
|
||||||
namespace lua {
|
namespace lua {
|
||||||
class Userdata {
|
class Userdata {
|
||||||
@@ -90,4 +92,25 @@ namespace lua {
|
|||||||
inline static std::string TYPENAME = "VoxelFragment";
|
inline static std::string TYPENAME = "VoxelFragment";
|
||||||
};
|
};
|
||||||
static_assert(!std::is_abstract<LuaVoxelFragment>());
|
static_assert(!std::is_abstract<LuaVoxelFragment>());
|
||||||
|
|
||||||
|
class LuaCanvas : public Userdata {
|
||||||
|
public:
|
||||||
|
explicit LuaCanvas(std::shared_ptr<Texture> inTexture);
|
||||||
|
~LuaCanvas() override = default;
|
||||||
|
|
||||||
|
const std::string& getTypeName() const override {
|
||||||
|
return TYPENAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Texture& texture() const { return *mTexture; }
|
||||||
|
|
||||||
|
[[nodiscard]] ImageData& data() const { return *mData; }
|
||||||
|
|
||||||
|
static int createMetatable(lua::State*);
|
||||||
|
inline static std::string TYPENAME = "Canvas";
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Texture> mTexture;
|
||||||
|
std::unique_ptr<ImageData> mData;
|
||||||
|
};
|
||||||
|
static_assert(!std::is_abstract<LuaCanvas>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ void lua::init_state(State* L, StateType stateType) {
|
|||||||
newusertype<LuaBytearray>(L);
|
newusertype<LuaBytearray>(L);
|
||||||
newusertype<LuaHeightmap>(L);
|
newusertype<LuaHeightmap>(L);
|
||||||
newusertype<LuaVoxelFragment>(L);
|
newusertype<LuaVoxelFragment>(L);
|
||||||
|
newusertype<LuaCanvas>(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lua::initialize(const EnginePaths& paths, const CoreParameters& params) {
|
void lua::initialize(const EnginePaths& paths, const CoreParameters& params) {
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ namespace lua {
|
|||||||
inline int newuserdata(lua::State* L, Args&&... args) {
|
inline int newuserdata(lua::State* L, Args&&... args) {
|
||||||
const auto& found = usertypeNames.find(typeid(T));
|
const auto& found = usertypeNames.find(typeid(T));
|
||||||
void* ptr = lua_newuserdata(L, sizeof(T));
|
void* ptr = lua_newuserdata(L, sizeof(T));
|
||||||
new (ptr) T(args...);
|
new (ptr) T(std::forward<Args>(args)...);
|
||||||
|
|
||||||
if (found == usertypeNames.end()) {
|
if (found == usertypeNames.end()) {
|
||||||
log_error(
|
log_error(
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "graphics/core/ImageData.hpp"
|
||||||
|
#include "graphics/core/Texture.hpp"
|
||||||
|
#include "logic/scripting/lua/lua_custom_types.hpp"
|
||||||
|
#include "logic/scripting/lua/lua_util.hpp"
|
||||||
|
|
||||||
|
using namespace lua;
|
||||||
|
|
||||||
|
LuaCanvas::LuaCanvas(std::shared_ptr<Texture> inTexture)
|
||||||
|
: mTexture(std::move(inTexture)) {
|
||||||
|
mData = mTexture->readData();
|
||||||
|
}
|
||||||
|
|
||||||
|
union RGBA {
|
||||||
|
uint8_t rgba[4];
|
||||||
|
uint32_t raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
static RGBA* get_at(const ImageData& data, uint index) {
|
||||||
|
if (index >= data.getWidth() * data.getHeight()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<RGBA*>(data.getData() + index * sizeof(RGBA));
|
||||||
|
}
|
||||||
|
|
||||||
|
static RGBA* get_at(const ImageData& data, uint x, uint y) {
|
||||||
|
return get_at(data, y * data.getWidth() + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RGBA* get_at(State* L, uint x, uint y) {
|
||||||
|
if (auto texture = touserdata<LuaCanvas>(L, 1)) {
|
||||||
|
return get_at(texture->data(), x, y);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_at(State* L) {
|
||||||
|
auto x = static_cast<uint>(tointeger(L, 2));
|
||||||
|
auto y = static_cast<uint>(tointeger(L, 3));
|
||||||
|
|
||||||
|
if (auto pixel = get_at(L, x, y)) {
|
||||||
|
return pushinteger(L, pixel->raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set(State* L) {
|
||||||
|
auto x = static_cast<uint>(tointeger(L, 2));
|
||||||
|
auto y = static_cast<uint>(tointeger(L, 3));
|
||||||
|
|
||||||
|
if (auto pixel = get_at(L, x, y)) {
|
||||||
|
switch (gettop(L)) {
|
||||||
|
case 4:
|
||||||
|
pixel->raw = static_cast<uint>(tointeger(L, 4));
|
||||||
|
return 1;
|
||||||
|
case 6:
|
||||||
|
pixel->rgba[0] = static_cast<ubyte>(tointeger(L, 4));
|
||||||
|
pixel->rgba[1] = static_cast<ubyte>(tointeger(L, 5));
|
||||||
|
pixel->rgba[2] = static_cast<ubyte>(tointeger(L, 6));
|
||||||
|
pixel->rgba[3] = 255;
|
||||||
|
return 1;
|
||||||
|
case 7:
|
||||||
|
pixel->rgba[0] = static_cast<ubyte>(tointeger(L, 4));
|
||||||
|
pixel->rgba[1] = static_cast<ubyte>(tointeger(L, 5));
|
||||||
|
pixel->rgba[2] = static_cast<ubyte>(tointeger(L, 6));
|
||||||
|
pixel->rgba[3] = static_cast<ubyte>(tointeger(L, 7));
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_update(State* L) {
|
||||||
|
if (auto texture = touserdata<LuaCanvas>(L, 1)) {
|
||||||
|
texture->texture().reload(texture->data());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, lua_CFunction> methods {
|
||||||
|
{"at", lua::wrap<l_at>},
|
||||||
|
{"set", lua::wrap<l_set>},
|
||||||
|
{"update", lua::wrap<l_update>}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int l_meta_index(State* L) {
|
||||||
|
auto texture = touserdata<LuaCanvas>(L, 1);
|
||||||
|
if (texture == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto& data = texture->data();
|
||||||
|
if (isnumber(L, 2)) {
|
||||||
|
if (auto pixel = get_at(data, static_cast<uint>(tointeger(L, 2)))) {
|
||||||
|
return pushinteger(L, pixel->raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isstring(L, 2)) {
|
||||||
|
auto name = tostring(L, 2);
|
||||||
|
if (!strcmp(name, "width")) {
|
||||||
|
return pushinteger(L, data.getWidth());
|
||||||
|
}
|
||||||
|
if (!strcmp(name, "height")) {
|
||||||
|
return pushinteger(L, data.getHeight());
|
||||||
|
}
|
||||||
|
if (auto func = methods.find(tostring(L, 2)); func != methods.end()) {
|
||||||
|
return pushcfunction(L, func->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_meta_newindex(State* L) {
|
||||||
|
auto texture = touserdata<LuaCanvas>(L, 1);
|
||||||
|
if (texture == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto& data = texture->data();
|
||||||
|
if (isnumber(L, 2) && isnumber(L, 3)) {
|
||||||
|
if (auto pixel = get_at(data, static_cast<uint>(tointeger(L, 2)))) {
|
||||||
|
pixel->raw = static_cast<uint>(tointeger(L, 3));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LuaCanvas::createMetatable(State* L) {
|
||||||
|
createtable(L, 0, 3);
|
||||||
|
pushcfunction(L, lua::wrap<l_meta_index>);
|
||||||
|
setfield(L, "__index");
|
||||||
|
pushcfunction(L, lua::wrap<l_meta_newindex>);
|
||||||
|
setfield(L, "__newindex");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -2,27 +2,24 @@
|
|||||||
|
|
||||||
#include "coders/rle.hpp"
|
#include "coders/rle.hpp"
|
||||||
#include "coders/gzip.hpp"
|
#include "coders/gzip.hpp"
|
||||||
#include "coders/byte_utils.hpp"
|
|
||||||
#include "voxels/Chunk.hpp"
|
#include "files/WorldFiles.hpp"
|
||||||
|
|
||||||
inline constexpr int HAS_VOXELS = 0x1;
|
inline constexpr int HAS_VOXELS = 0x1;
|
||||||
inline constexpr int HAS_METADATA = 0x2;
|
inline constexpr int HAS_METADATA = 0x2;
|
||||||
|
|
||||||
std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
std::vector<ubyte> compressed_chunks::encode(
|
||||||
auto data = chunk.encode();
|
const ubyte* data,
|
||||||
|
const BlocksMetadata& metadata,
|
||||||
/// world.get_chunk_data is only available in the main Lua state
|
util::Buffer<ubyte>& rleBuffer
|
||||||
static util::Buffer<ubyte> rleBuffer;
|
) {
|
||||||
if (rleBuffer.size() < CHUNK_DATA_LEN * 2) {
|
|
||||||
rleBuffer = util::Buffer<ubyte>(CHUNK_DATA_LEN * 2);
|
|
||||||
}
|
|
||||||
size_t rleCompressedSize =
|
size_t rleCompressedSize =
|
||||||
extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data());
|
extrle::encode16(data, CHUNK_DATA_LEN, rleBuffer.data());
|
||||||
|
|
||||||
const auto gzipCompressedData = gzip::compress(
|
const auto gzipCompressedData = gzip::compress(
|
||||||
rleBuffer.data(), rleCompressedSize
|
rleBuffer.data(), rleCompressedSize
|
||||||
);
|
);
|
||||||
auto metadataBytes = chunk.blocksMetadata.serialize();
|
auto metadataBytes = metadata.serialize();
|
||||||
|
|
||||||
ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size());
|
ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size());
|
||||||
builder.put(HAS_VOXELS | HAS_METADATA); // flags
|
builder.put(HAS_VOXELS | HAS_METADATA); // flags
|
||||||
@@ -34,6 +31,23 @@ std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
||||||
|
auto data = chunk.encode();
|
||||||
|
|
||||||
|
/// world.get_chunk_data is only available in the main Lua state
|
||||||
|
static util::Buffer<ubyte> rleBuffer(CHUNK_DATA_LEN * 2);
|
||||||
|
return encode(data.get(), chunk.blocksMetadata, rleBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_voxel_data(ByteReader& reader, util::Buffer<ubyte>& dst) {
|
||||||
|
size_t gzipCompressedSize = reader.getInt32();
|
||||||
|
|
||||||
|
auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize);
|
||||||
|
reader.skip(gzipCompressedSize);
|
||||||
|
|
||||||
|
extrle::decode16(rleData.data(), rleData.size(), dst.data());
|
||||||
|
}
|
||||||
|
|
||||||
void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
||||||
ByteReader reader(src, size);
|
ByteReader reader(src, size);
|
||||||
|
|
||||||
@@ -41,14 +55,9 @@ void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
|||||||
reader.skip(1); // reserved byte
|
reader.skip(1); // reserved byte
|
||||||
|
|
||||||
if (flags & HAS_VOXELS) {
|
if (flags & HAS_VOXELS) {
|
||||||
size_t gzipCompressedSize = reader.getInt32();
|
|
||||||
|
|
||||||
auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize);
|
|
||||||
reader.skip(gzipCompressedSize);
|
|
||||||
|
|
||||||
/// world.get_chunk_data is only available in the main Lua state
|
/// world.get_chunk_data is only available in the main Lua state
|
||||||
static util::Buffer<ubyte> voxelData (CHUNK_DATA_LEN);
|
static util::Buffer<ubyte> voxelData (CHUNK_DATA_LEN);
|
||||||
extrle::decode16(rleData.data(), rleData.size(), voxelData.data());
|
read_voxel_data(reader, voxelData);
|
||||||
chunk.decode(voxelData.data());
|
chunk.decode(voxelData.data());
|
||||||
chunk.updateHeights();
|
chunk.updateHeights();
|
||||||
}
|
}
|
||||||
@@ -59,3 +68,30 @@ void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
|||||||
}
|
}
|
||||||
chunk.setModifiedAndUnsaved();
|
chunk.setModifiedAndUnsaved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void compressed_chunks::save(
|
||||||
|
int x, int z, std::vector<ubyte> bytes, WorldRegions& regions
|
||||||
|
) {
|
||||||
|
ByteReader reader(bytes.data(), bytes.size());
|
||||||
|
|
||||||
|
ubyte flags = reader.get();
|
||||||
|
reader.skip(1); // reserved byte
|
||||||
|
if (flags & HAS_VOXELS) {
|
||||||
|
util::Buffer<ubyte> voxelData (CHUNK_DATA_LEN);
|
||||||
|
read_voxel_data(reader, voxelData);
|
||||||
|
regions.put(
|
||||||
|
x, z, REGION_LAYER_VOXELS, voxelData.release(), CHUNK_DATA_LEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (flags & HAS_METADATA) {
|
||||||
|
size_t metadataSize = reader.getInt32();
|
||||||
|
regions.put(
|
||||||
|
x,
|
||||||
|
z,
|
||||||
|
REGION_LAYER_BLOCKS_DATA,
|
||||||
|
util::Buffer<ubyte>(reader.pointer(), metadataSize).release(),
|
||||||
|
metadataSize
|
||||||
|
);
|
||||||
|
reader.skip(metadataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "Chunk.hpp"
|
||||||
|
#include "coders/byte_utils.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Chunk;
|
class WorldRegions;
|
||||||
|
|
||||||
namespace compressed_chunks {
|
namespace compressed_chunks {
|
||||||
|
std::vector<ubyte> encode(
|
||||||
|
const ubyte* voxelData,
|
||||||
|
const BlocksMetadata& metadata,
|
||||||
|
util::Buffer<ubyte>& rleBuffer
|
||||||
|
);
|
||||||
std::vector<ubyte> encode(const Chunk& chunk);
|
std::vector<ubyte> encode(const Chunk& chunk);
|
||||||
void decode(Chunk& chunk, const ubyte* src, size_t size);
|
void decode(Chunk& chunk, const ubyte* src, size_t size);
|
||||||
|
void save(int x, int z, std::vector<ubyte> bytes, WorldRegions& regions);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user