Merge branch 'main' into curl
This commit is contained in:
@@ -43,7 +43,11 @@ void AssetsLoader::add(
|
||||
const std::string& alias,
|
||||
std::shared_ptr<AssetCfg> settings
|
||||
) {
|
||||
if (enqueued.find({tag, alias}) != enqueued.end()){
|
||||
return;
|
||||
}
|
||||
entries.push(aloader_entry {tag, filename, alias, std::move(settings)});
|
||||
enqueued.insert({tag, alias});
|
||||
}
|
||||
|
||||
bool AssetsLoader::hasNext() const {
|
||||
@@ -148,6 +152,16 @@ void AssetsLoader::processPreload(
|
||||
std::make_shared<SoundCfg>(map.at("keep-pcm").get(keepPCM)));
|
||||
break;
|
||||
}
|
||||
case AssetType::ATLAS: {
|
||||
std::string typeName = "atlas";
|
||||
map.at("type").get(typeName);
|
||||
auto type = AtlasType::ATLAS;
|
||||
if (typeName == "separate") {
|
||||
type = AtlasType::SEPARATE;
|
||||
}
|
||||
add(tag, path, name, std::make_shared<AtlasCfg>(type));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
add(tag, path, name);
|
||||
break;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
@@ -37,6 +38,17 @@ struct SoundCfg : AssetCfg {
|
||||
}
|
||||
};
|
||||
|
||||
enum class AtlasType {
|
||||
ATLAS, SEPARATE
|
||||
};
|
||||
|
||||
struct AtlasCfg : AssetCfg {
|
||||
AtlasType type;
|
||||
|
||||
AtlasCfg(AtlasType type) : type(type) {
|
||||
}
|
||||
};
|
||||
|
||||
using aloader_func = std::function<
|
||||
assetload::
|
||||
postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
||||
@@ -52,6 +64,7 @@ class AssetsLoader {
|
||||
Assets* assets;
|
||||
std::map<AssetType, aloader_func> loaders;
|
||||
std::queue<aloader_entry> entries;
|
||||
std::set<std::pair<AssetType, std::string>> enqueued;
|
||||
const ResPaths* paths;
|
||||
|
||||
void tryAddSound(const std::string& name);
|
||||
|
||||
@@ -103,12 +103,25 @@ static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::atlas(
|
||||
AssetsLoader*,
|
||||
AssetsLoader* loader,
|
||||
const ResPaths* paths,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>&
|
||||
const std::shared_ptr<AssetCfg>& config
|
||||
) {
|
||||
auto atlasConfig = std::dynamic_pointer_cast<AtlasCfg>(config);
|
||||
if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) {
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!imageio::is_read_supported(file.extension().u8string()))
|
||||
continue;
|
||||
loader->add(
|
||||
AssetType::TEXTURE,
|
||||
directory + "/" + file.stem().u8string(),
|
||||
name + "/" + file.stem().u8string()
|
||||
);
|
||||
}
|
||||
return [](auto){};
|
||||
}
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!imageio::is_read_supported(file.extension().u8string())) continue;
|
||||
@@ -172,7 +185,9 @@ assetload::postfunc assetload::layout(
|
||||
return [=](auto assets) {
|
||||
try {
|
||||
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
|
||||
assets->store(UiDocument::read(cfg->env, name, file), name);
|
||||
assets->store(
|
||||
UiDocument::read(cfg->env, name, file, "abs:" + file), name
|
||||
);
|
||||
} catch (const parsing_error& err) {
|
||||
throw std::runtime_error(
|
||||
"failed to parse layout XML '" + file + "':\n" + err.errorLog()
|
||||
|
||||
@@ -373,7 +373,7 @@ std::string BasicParser::parseString(char quote, bool closeRequired) {
|
||||
case 'b': ss << '\b'; break;
|
||||
case 't': ss << '\t'; break;
|
||||
case 'f': ss << '\f'; break;
|
||||
case '\'': ss << '\\'; break;
|
||||
case '\'': ss << '\''; break;
|
||||
case '"': ss << '"'; break;
|
||||
case '\\': ss << '\\'; break;
|
||||
case '/': ss << '/'; break;
|
||||
|
||||
+1
-1
@@ -265,5 +265,5 @@ dv::value json::parse(
|
||||
}
|
||||
|
||||
dv::value json::parse(std::string_view source) {
|
||||
return parse("<string>", source);
|
||||
return parse("[string]", source);
|
||||
}
|
||||
|
||||
+3
-2
@@ -250,7 +250,8 @@ std::string Parser::parseText() {
|
||||
}
|
||||
nextChar();
|
||||
}
|
||||
return std::string(source.substr(start, pos - start));
|
||||
return Parser("[string]", std::string(source.substr(start, pos - start)))
|
||||
.parseString('\0', false);
|
||||
}
|
||||
|
||||
inline bool is_xml_identifier_start(char c) {
|
||||
@@ -336,7 +337,7 @@ xmldocument Parser::parse() {
|
||||
return document;
|
||||
}
|
||||
|
||||
xmldocument xml::parse(const std::string& filename, const std::string& source) {
|
||||
xmldocument xml::parse(std::string_view filename, std::string_view source) {
|
||||
Parser parser(filename, source);
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
+1
-1
@@ -140,6 +140,6 @@ namespace xml {
|
||||
/// @param source xml source code string
|
||||
/// @return xml document
|
||||
extern xmldocument parse(
|
||||
const std::string& filename, const std::string& source
|
||||
std::string_view filename, std::string_view source
|
||||
);
|
||||
}
|
||||
|
||||
+5
-3
@@ -35,9 +35,6 @@ inline constexpr int CHUNK_D = 16;
|
||||
inline constexpr uint VOXEL_USER_BITS = 8;
|
||||
inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS;
|
||||
|
||||
/// @brief pixel size of an item inventory icon
|
||||
inline constexpr int ITEM_ICON_SIZE = 48;
|
||||
|
||||
/// @brief chunk volume (count of voxels per Chunk)
|
||||
inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D);
|
||||
|
||||
@@ -53,6 +50,11 @@ inline constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=C
|
||||
return (y * d + z) * w + x;
|
||||
}
|
||||
|
||||
/// @brief pixel size of an item inventory icon
|
||||
inline constexpr int ITEM_ICON_SIZE = 48;
|
||||
|
||||
inline constexpr int TRANSLUCENT_BLOCKS_SORT_INTERVAL = 8;
|
||||
|
||||
inline const std::string SHADERS_FOLDER = "shaders";
|
||||
inline const std::string TEXTURES_FOLDER = "textures";
|
||||
inline const std::string FONTS_FOLDER = "fonts";
|
||||
|
||||
@@ -333,6 +333,7 @@ void ContentLoader::loadBlock(
|
||||
root.at("inventory-size").get(def.inventorySize);
|
||||
root.at("tick-interval").get(def.tickInterval);
|
||||
root.at("overlay-texture").get(def.overlayTexture);
|
||||
root.at("translucent").get(def.translucent);
|
||||
|
||||
if (root.has("fields")) {
|
||||
def.dataStruct = std::make_unique<StructLayout>();
|
||||
@@ -484,7 +485,13 @@ void ContentLoader::loadBlock(
|
||||
|
||||
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_block_script(env, full, scriptfile, def.rt.funcsset);
|
||||
scripting::load_block_script(
|
||||
env,
|
||||
full,
|
||||
scriptfile,
|
||||
pack->id + ":scripts/" + def.scriptName + ".lua",
|
||||
def.rt.funcsset
|
||||
);
|
||||
}
|
||||
if (!def.hidden) {
|
||||
auto& item = builder.items.create(full + BLOCK_ITEM_SUFFIX);
|
||||
@@ -510,7 +517,13 @@ void ContentLoader::loadItem(
|
||||
|
||||
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_item_script(env, full, scriptfile, def.rt.funcsset);
|
||||
scripting::load_item_script(
|
||||
env,
|
||||
full,
|
||||
scriptfile,
|
||||
pack->id + ":scripts/" + def.scriptName + ".lua",
|
||||
def.rt.funcsset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,7 +732,11 @@ void ContentLoader::load() {
|
||||
fs::path scriptFile = folder / fs::path("scripts/world.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_world_script(
|
||||
env, pack->id, scriptFile, runtime->worldfuncsset
|
||||
env,
|
||||
pack->id,
|
||||
scriptFile,
|
||||
pack->id + ":scripts/world.lua",
|
||||
runtime->worldfuncsset
|
||||
);
|
||||
}
|
||||
|
||||
@@ -794,7 +811,11 @@ void ContentLoader::load() {
|
||||
fs::path componentsDir = folder / fs::u8path("scripts/components");
|
||||
foreach_file(componentsDir, [this](const fs::path& file) {
|
||||
auto name = pack->id + ":" + file.stem().u8string();
|
||||
scripting::load_entity_component(name, file);
|
||||
scripting::load_entity_component(
|
||||
name,
|
||||
file,
|
||||
pack->id + ":scripts/components/" + file.filename().u8string()
|
||||
);
|
||||
});
|
||||
|
||||
// Process content.json and load defined content units
|
||||
|
||||
@@ -99,6 +99,7 @@ struct world_funcs_set {
|
||||
bool onblockplaced : 1;
|
||||
bool onblockbroken : 1;
|
||||
bool onblockinteract : 1;
|
||||
bool onplayertick : 1;
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
|
||||
@@ -10,7 +10,6 @@ inline const std::string CORE_STRUCT_AIR = "core:struct_air";
|
||||
inline const std::string TEXTURE_NOTFOUND = "notfound";
|
||||
|
||||
// built-in bindings
|
||||
inline const std::string BIND_DEVTOOLS_CONSOLE = "devtools.console";
|
||||
inline const std::string BIND_CHUNKS_RELOAD = "chunks.reload";
|
||||
inline const std::string BIND_MOVE_FORWARD = "movement.forward";
|
||||
inline const std::string BIND_MOVE_BACK = "movement.back";
|
||||
|
||||
@@ -53,7 +53,12 @@ scriptenv UiDocument::getEnvironment() const {
|
||||
return env;
|
||||
}
|
||||
|
||||
std::unique_ptr<UiDocument> UiDocument::read(const scriptenv& penv, const std::string& name, const fs::path& file) {
|
||||
std::unique_ptr<UiDocument> UiDocument::read(
|
||||
const scriptenv& penv,
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
const std::string text = files::read_string(file);
|
||||
auto xmldoc = xml::parse(file.u8string(), text);
|
||||
|
||||
@@ -69,12 +74,16 @@ std::unique_ptr<UiDocument> UiDocument::read(const scriptenv& penv, const std::s
|
||||
uidocscript script {};
|
||||
auto scriptFile = fs::path(file.u8string()+".lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_layout_script(env, name, scriptFile, script);
|
||||
scripting::load_layout_script(
|
||||
env, name, scriptFile, fileName + ".lua", script
|
||||
);
|
||||
}
|
||||
return std::make_unique<UiDocument>(name, script, view, env);
|
||||
}
|
||||
|
||||
std::shared_ptr<gui::UINode> UiDocument::readElement(const fs::path& file) {
|
||||
auto document = read(nullptr, file.filename().u8string(), file);
|
||||
std::shared_ptr<gui::UINode> UiDocument::readElement(
|
||||
const fs::path& file, const std::string& fileName
|
||||
) {
|
||||
auto document = read(nullptr, file.filename().u8string(), file, fileName);
|
||||
return document->getRoot();
|
||||
}
|
||||
|
||||
@@ -45,6 +45,13 @@ public:
|
||||
const uidocscript& getScript() const;
|
||||
scriptenv getEnvironment() const;
|
||||
|
||||
static std::unique_ptr<UiDocument> read(const scriptenv& parent_env, const std::string& name, const fs::path& file);
|
||||
static std::shared_ptr<gui::UINode> readElement(const fs::path& file);
|
||||
static std::unique_ptr<UiDocument> read(
|
||||
const scriptenv& parent_env,
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
static std::shared_ptr<gui::UINode> readElement(
|
||||
const fs::path& file, const std::string& fileName
|
||||
);
|
||||
};
|
||||
|
||||
+17
-10
@@ -229,13 +229,9 @@ void Hud::processInput(bool visible) {
|
||||
setPause(true);
|
||||
}
|
||||
}
|
||||
if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
|
||||
showOverlay(assets->get<UiDocument>("core:console"), false);
|
||||
}
|
||||
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
||||
setPause(true);
|
||||
}
|
||||
|
||||
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
||||
if (inventoryOpen) {
|
||||
closeInventory();
|
||||
@@ -465,7 +461,9 @@ void Hud::showExchangeSlot() {
|
||||
|
||||
}
|
||||
|
||||
void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
||||
void Hud::showOverlay(
|
||||
UiDocument* doc, bool playerInventory, const dv::value& args
|
||||
) {
|
||||
if (isInventoryOpen()) {
|
||||
closeInventory();
|
||||
}
|
||||
@@ -476,7 +474,8 @@ void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
||||
showExchangeSlot();
|
||||
inventoryOpen = true;
|
||||
}
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false),
|
||||
args);
|
||||
}
|
||||
|
||||
void Hud::openPermanent(UiDocument* doc) {
|
||||
@@ -508,13 +507,18 @@ void Hud::closeInventory() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Hud::add(const HudElement& element) {
|
||||
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
||||
gui->add(element.getNode());
|
||||
auto document = element.getDocument();
|
||||
if (document) {
|
||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||
auto inventory = invview ? invview->getInventory() : nullptr;
|
||||
std::vector<dv::value> args;
|
||||
if (argsArray != nullptr) {
|
||||
for (const auto& arg : argsArray) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
args.emplace_back(inventory ? inventory.get()->getId() : 0);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
args.emplace_back(static_cast<integer_t>(blockPos[i]));
|
||||
@@ -571,7 +575,7 @@ void Hud::draw(const DrawContext& ctx){
|
||||
|
||||
// Crosshair
|
||||
if (!pause && !inventoryOpen && !player->debug) {
|
||||
DrawContext chctx = ctx.sub();
|
||||
DrawContext chctx = ctx.sub(batch);
|
||||
chctx.setBlendMode(BlendMode::inversion);
|
||||
auto texture = assets->get<Texture>("gui/crosshair");
|
||||
batch->texture(texture);
|
||||
@@ -615,8 +619,11 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
|
||||
}
|
||||
if (secondUI->getPositionFunc() == nullptr) {
|
||||
secondUI->setPos(glm::vec2(
|
||||
glm::min(width/2-invwidth/2, width-caWidth-(inventoryView ? 10 : 0)-invwidth),
|
||||
height/2-totalHeight/2
|
||||
glm::min(
|
||||
width / 2.f - invwidth / 2.f,
|
||||
width - caWidth - (inventoryView ? 10 : 0) - invwidth
|
||||
),
|
||||
height / 2.f - totalHeight / 2.f
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "util/ObjectsKeeper.hpp"
|
||||
#include "data/dv.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -173,7 +174,10 @@ public:
|
||||
/// @brief Show element in inventory-mode
|
||||
/// @param doc element layout
|
||||
/// @param playerInventory show player inventory too
|
||||
void showOverlay(UiDocument* doc, bool playerInventory);
|
||||
/// @param arg first argument passing to on_open
|
||||
void showOverlay(
|
||||
UiDocument* doc, bool playerInventory, const dv::value& arg = nullptr
|
||||
);
|
||||
|
||||
/// @brief Close all open inventories and overlay
|
||||
void closeInventory();
|
||||
@@ -182,7 +186,7 @@ public:
|
||||
/// @param doc element layout
|
||||
void openPermanent(UiDocument* doc);
|
||||
|
||||
void add(const HudElement& element);
|
||||
void add(const HudElement& element, const dv::value& arg=nullptr);
|
||||
void onRemove(const HudElement& element);
|
||||
void remove(const std::shared_ptr<gui::UINode>& node);
|
||||
|
||||
|
||||
@@ -62,7 +62,10 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) {
|
||||
auto fullname = "core:pages/"+name;
|
||||
|
||||
auto document_ptr = UiDocument::read(
|
||||
scripting::get_root_environment(), fullname, file
|
||||
scripting::get_root_environment(),
|
||||
fullname,
|
||||
file,
|
||||
"core:layouts/pages/" + name
|
||||
);
|
||||
auto document = document_ptr.get();
|
||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
||||
@@ -110,7 +113,7 @@ UiDocument* menus::show(Engine* engine, const std::string& name, std::vector<dv:
|
||||
auto fullname = "core:layouts/"+name;
|
||||
|
||||
auto document_ptr = UiDocument::read(
|
||||
scripting::get_root_environment(), fullname, file
|
||||
scripting::get_root_environment(), fullname, file, "core:layouts/"+name
|
||||
);
|
||||
auto document = document_ptr.get();
|
||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
||||
|
||||
@@ -83,7 +83,12 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
||||
const ContentPack& info = pack->getInfo();
|
||||
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile);
|
||||
scripting::load_hud_script(
|
||||
pack->getEnvironment(),
|
||||
info.id,
|
||||
scriptFile,
|
||||
pack->getId() + ":scripts/hud.lua"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
inline constexpr uint B2D_VERTEX_SIZE = 8;
|
||||
|
||||
Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
||||
const vattr attrs[] = {
|
||||
const VertexAttribute attrs[] = {
|
||||
{2}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ inline constexpr uint B3D_VERTEX_SIZE = 9;
|
||||
|
||||
Batch3D::Batch3D(size_t capacity)
|
||||
: capacity(capacity) {
|
||||
const vattr attrs[] = {
|
||||
const VertexAttribute attrs[] = {
|
||||
{3}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ DrawContext DrawContext::sub(Flushable* flushable) const {
|
||||
auto ctx = DrawContext(*this);
|
||||
ctx.parent = this;
|
||||
ctx.flushable = flushable;
|
||||
ctx.scissorsCount = 0;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ void DrawContext::setBlendMode(BlendMode mode) {
|
||||
set_blend_mode(mode);
|
||||
}
|
||||
|
||||
void DrawContext::setScissors(glm::vec4 area) {
|
||||
void DrawContext::setScissors(const glm::vec4& area) {
|
||||
Window::pushScissor(area);
|
||||
scissorsCount++;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,6 @@ public:
|
||||
void setDepthTest(bool flag);
|
||||
void setCullFace(bool flag);
|
||||
void setBlendMode(BlendMode mode);
|
||||
void setScissors(glm::vec4 area);
|
||||
void setScissors(const glm::vec4& area);
|
||||
void setLineWidth(float width);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
inline constexpr uint LB_VERTEX_SIZE = (3+4);
|
||||
|
||||
LineBatch::LineBatch(size_t capacity) : capacity(capacity) {
|
||||
const vattr attrs[] = { {3},{4}, {0} };
|
||||
const VertexAttribute attrs[] = { {3},{4}, {0} };
|
||||
buffer = std::make_unique<float[]>(capacity * LB_VERTEX_SIZE * 2);
|
||||
mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
|
||||
index = 0;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
int Mesh::meshesCount = 0;
|
||||
int Mesh::drawCalls = 0;
|
||||
|
||||
inline size_t calc_vertex_size(const vattr* attrs) {
|
||||
inline size_t calc_vertex_size(const VertexAttribute* attrs) {
|
||||
size_t vertexSize = 0;
|
||||
for (int i = 0; attrs[i].size; i++) {
|
||||
vertexSize += attrs[i].size;
|
||||
@@ -19,10 +19,10 @@ Mesh::Mesh(const MeshData& data)
|
||||
data.indices.size(),
|
||||
data.attrs.data()) {}
|
||||
|
||||
Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs) :
|
||||
Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const VertexAttribute* attrs) :
|
||||
ibo(0),
|
||||
vertices(vertices),
|
||||
indices(indices)
|
||||
vertices(0),
|
||||
indices(0)
|
||||
{
|
||||
meshesCount++;
|
||||
vertexSize = 0;
|
||||
@@ -58,10 +58,9 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
if (vertexBuffer != nullptr && vertices != 0) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STATIC_DRAW);
|
||||
}
|
||||
else {
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STREAM_DRAW);
|
||||
} else {
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW);
|
||||
}
|
||||
if (indexBuffer != nullptr && indices != 0) {
|
||||
if (ibo == 0) glGenBuffers(1, &ibo);
|
||||
@@ -75,7 +74,7 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu
|
||||
this->indices = indices;
|
||||
}
|
||||
|
||||
void Mesh::draw(unsigned int primitive){
|
||||
void Mesh::draw(unsigned int primitive) const {
|
||||
drawCalls++;
|
||||
glBindVertexArray(vao);
|
||||
if (ibo != 0) {
|
||||
@@ -87,6 +86,6 @@ void Mesh::draw(unsigned int primitive){
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Mesh::draw() {
|
||||
void Mesh::draw() const {
|
||||
draw(GL_TRIANGLES);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ class Mesh {
|
||||
size_t vertexSize;
|
||||
public:
|
||||
Mesh(const MeshData& data);
|
||||
Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs);
|
||||
Mesh(const float* vertexBuffer, size_t vertices, const vattr* attrs) :
|
||||
Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const VertexAttribute* attrs);
|
||||
Mesh(const float* vertexBuffer, size_t vertices, const VertexAttribute* attrs) :
|
||||
Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {};
|
||||
~Mesh();
|
||||
|
||||
@@ -28,10 +28,10 @@ public:
|
||||
|
||||
/// @brief Draw mesh with specified primitives type
|
||||
/// @param primitive primitives type
|
||||
void draw(unsigned int primitive);
|
||||
void draw(unsigned int primitive) const;
|
||||
|
||||
/// @brief Draw mesh as triangles
|
||||
void draw();
|
||||
void draw() const;
|
||||
|
||||
/// @brief Total numbers of alive mesh objects
|
||||
static int meshesCount;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
/// @brief Vertex attribute info
|
||||
struct vattr {
|
||||
struct VertexAttribute {
|
||||
ubyte size;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ struct vattr {
|
||||
struct MeshData {
|
||||
util::Buffer<float> vertices;
|
||||
util::Buffer<int> indices;
|
||||
util::Buffer<vattr> attrs;
|
||||
util::Buffer<VertexAttribute> attrs;
|
||||
|
||||
MeshData() = default;
|
||||
|
||||
@@ -24,7 +24,7 @@ struct MeshData {
|
||||
MeshData(
|
||||
util::Buffer<float> vertices,
|
||||
util::Buffer<int> indices,
|
||||
util::Buffer<vattr> attrs
|
||||
util::Buffer<VertexAttribute> attrs
|
||||
) : vertices(std::move(vertices)),
|
||||
indices(std::move(indices)),
|
||||
attrs(std::move(attrs)) {}
|
||||
|
||||
@@ -14,7 +14,7 @@ PostProcessing::PostProcessing() {
|
||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||
};
|
||||
vattr attrs[] {{2}, {0}};
|
||||
VertexAttribute attrs[] {{2}, {0}};
|
||||
quadMesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/render/MainBatch.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level)
|
||||
: assets(assets), level(level), batch(std::make_unique<MainBatch>(1024)) {
|
||||
}
|
||||
|
||||
BlockWrapsRenderer::~BlockWrapsRenderer() = default;
|
||||
|
||||
void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) {
|
||||
const auto& chunks = *level.chunks;
|
||||
|
||||
auto textureRegion = util::get_texture_region(assets, wrapper.texture, "");
|
||||
|
||||
auto& shader = assets.require<Shader>("entity");
|
||||
shader.use();
|
||||
shader.uniform1i("u_alphaClip", false);
|
||||
|
||||
const UVRegion& cracksRegion = textureRegion.region;
|
||||
UVRegion regions[6] {
|
||||
cracksRegion, cracksRegion, cracksRegion,
|
||||
cracksRegion, cracksRegion, cracksRegion
|
||||
};
|
||||
batch->setTexture(textureRegion.texture);
|
||||
|
||||
const voxel* vox = chunks.get(wrapper.position);
|
||||
if (vox == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (vox->id != BLOCK_VOID) {
|
||||
const auto& def =
|
||||
level.content->getIndices()->blocks.require(vox->id);
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
batch->cube(
|
||||
glm::vec3(wrapper.position) + glm::vec3(0.5f),
|
||||
glm::vec3(1.01f),
|
||||
regions,
|
||||
glm::vec4(0),
|
||||
false
|
||||
);
|
||||
break;
|
||||
case BlockModel::aabb: {
|
||||
const auto& aabb = def.rt.hitboxes[vox->state.rotation].at(0);
|
||||
const auto& size = aabb.size();
|
||||
regions[0].scale(size.z, size.y);
|
||||
regions[1].scale(size.z, size.y);
|
||||
regions[2].scale(size.x, size.z);
|
||||
regions[3].scale(size.x, size.z);
|
||||
regions[4].scale(size.x, size.y);
|
||||
regions[5].scale(size.x, size.y);
|
||||
batch->cube(
|
||||
glm::vec3(wrapper.position) + aabb.center(),
|
||||
size * glm::vec3(1.01f),
|
||||
regions,
|
||||
glm::vec4(0),
|
||||
false
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockWrapsRenderer::draw(const DrawContext& pctx, const Player& player) {
|
||||
auto ctx = pctx.sub();
|
||||
for (const auto& [_, wrapper] : wrappers) {
|
||||
draw(*wrapper);
|
||||
}
|
||||
batch->flush();
|
||||
}
|
||||
|
||||
u64id_t BlockWrapsRenderer::add(
|
||||
const glm::ivec3& position, const std::string& texture
|
||||
) {
|
||||
u64id_t id = nextWrapper++;
|
||||
wrappers[id] = std::make_unique<BlockWrapper>(
|
||||
BlockWrapper {position, texture}
|
||||
);
|
||||
return id;
|
||||
}
|
||||
|
||||
BlockWrapper* BlockWrapsRenderer::get(u64id_t id) const {
|
||||
const auto& found = wrappers.find(id);
|
||||
if (found == wrappers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
void BlockWrapsRenderer::remove(u64id_t id) {
|
||||
wrappers.erase(id);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "MainBatch.hpp"
|
||||
#include "typedefs.hpp"
|
||||
|
||||
class Assets;
|
||||
class Player;
|
||||
class Level;
|
||||
class DrawContext;
|
||||
|
||||
struct BlockWrapper {
|
||||
glm::ivec3 position;
|
||||
std::string texture;
|
||||
};
|
||||
|
||||
class BlockWrapsRenderer {
|
||||
const Assets& assets;
|
||||
const Level& level;
|
||||
std::unique_ptr<MainBatch> batch;
|
||||
|
||||
std::unordered_map<u64id_t, std::unique_ptr<BlockWrapper>> wrappers;
|
||||
u64id_t nextWrapper = 1;
|
||||
|
||||
void draw(const BlockWrapper& wrapper);
|
||||
public:
|
||||
BlockWrapsRenderer(const Assets& assets, const Level& level);
|
||||
~BlockWrapsRenderer();
|
||||
|
||||
void draw(const DrawContext& ctx, const Player& player);
|
||||
|
||||
u64id_t add(const glm::ivec3& position, const std::string& texture);
|
||||
|
||||
BlockWrapper* get(u64id_t id) const;
|
||||
|
||||
void remove(u64id_t id);
|
||||
};
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const uint BlocksRenderer::VERTEX_SIZE = 6;
|
||||
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
||||
|
||||
BlocksRenderer::BlocksRenderer(
|
||||
@@ -21,7 +20,7 @@ BlocksRenderer::BlocksRenderer(
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
) : content(content),
|
||||
vertexBuffer(std::make_unique<float[]>(capacity * VERTEX_SIZE)),
|
||||
vertexBuffer(std::make_unique<float[]>(capacity * CHUNK_VERTEX_SIZE)),
|
||||
indexBuffer(std::make_unique<int[]>(capacity)),
|
||||
vertexOffset(0),
|
||||
indexOffset(0),
|
||||
@@ -85,7 +84,7 @@ void BlocksRenderer::face(
|
||||
const glm::vec4(&lights)[4],
|
||||
const glm::vec4& tint
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +124,7 @@ void BlocksRenderer::faceAO(
|
||||
const UVRegion& region,
|
||||
bool lights
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +162,7 @@ void BlocksRenderer::face(
|
||||
glm::vec4 tint,
|
||||
bool lights
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -288,7 +287,7 @@ void BlocksRenderer::blockCustomModel(
|
||||
|
||||
const auto& model = cache.getModel(block->rt.id);
|
||||
for (const auto& mesh : model.meshes) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * mesh.vertices.size() > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * mesh.vertices.size() > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -433,21 +432,9 @@ glm::vec4 BlocksRenderer::pickSoftLight(
|
||||
right, up);
|
||||
}
|
||||
|
||||
void BlocksRenderer::render(const voxel* voxels) {
|
||||
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
||||
|
||||
int beginEnds[256][2] {};
|
||||
for (int i = totalBegin; i < totalEnd; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
const auto& def = *blockDefsCache[id];
|
||||
|
||||
if (beginEnds[def.drawGroup][0] == 0) {
|
||||
beginEnds[def.drawGroup][0] = i+1;
|
||||
}
|
||||
beginEnds[def.drawGroup][1] = i;
|
||||
}
|
||||
void BlocksRenderer::render(
|
||||
const voxel* voxels, int beginEnds[256][2]
|
||||
) {
|
||||
for (const auto drawGroup : *content.drawGroups) {
|
||||
int begin = beginEnds[drawGroup][0];
|
||||
if (begin == 0) {
|
||||
@@ -462,13 +449,13 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||
continue;
|
||||
}
|
||||
if (def.translucent) {
|
||||
continue;
|
||||
}
|
||||
const UVRegion texfaces[6] {
|
||||
cache.getRegion(id, 0),
|
||||
cache.getRegion(id, 1),
|
||||
cache.getRegion(id, 2),
|
||||
cache.getRegion(id, 3),
|
||||
cache.getRegion(id, 4),
|
||||
cache.getRegion(id, 5)
|
||||
cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||
cache.getRegion(id, 4), cache.getRegion(id, 5)
|
||||
};
|
||||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
@@ -503,43 +490,185 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
}
|
||||
}
|
||||
|
||||
SortingMeshData BlocksRenderer::renderTranslucent(
|
||||
const voxel* voxels, int beginEnds[256][2]
|
||||
) {
|
||||
SortingMeshData sortingMesh {{}};
|
||||
|
||||
AABB aabb {};
|
||||
bool aabbInit = false;
|
||||
size_t totalSize = 0;
|
||||
for (const auto drawGroup : *content.drawGroups) {
|
||||
int begin = beginEnds[drawGroup][0];
|
||||
if (begin == 0) {
|
||||
continue;
|
||||
}
|
||||
int end = beginEnds[drawGroup][1];
|
||||
for (int i = begin-1; i <= end; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
blockstate state = vox.state;
|
||||
const auto& def = *blockDefsCache[id];
|
||||
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||
continue;
|
||||
}
|
||||
if (!def.translucent) {
|
||||
continue;
|
||||
}
|
||||
const UVRegion texfaces[6] {
|
||||
cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||
cache.getRegion(id, 4), cache.getRegion(id, 5)
|
||||
};
|
||||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
int z = (i / CHUNK_D) % CHUNK_W;
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
||||
def.ambientOcclusion);
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
blockXSprite(x, y, z, glm::vec3(1.0f),
|
||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||
break;
|
||||
}
|
||||
case BlockModel::aabb: {
|
||||
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
||||
!def.shadeless, def.ambientOcclusion);
|
||||
break;
|
||||
}
|
||||
case BlockModel::custom: {
|
||||
blockCustomModel({x, y, z}, &def, vox.state.rotation,
|
||||
!def.shadeless, def.ambientOcclusion);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (vertexOffset == 0) {
|
||||
continue;
|
||||
}
|
||||
SortingMeshEntry entry {
|
||||
glm::vec3(
|
||||
x + chunk->x * CHUNK_W + 0.5f,
|
||||
y + 0.5f,
|
||||
z + chunk->z * CHUNK_D + 0.5f
|
||||
),
|
||||
util::Buffer<float>(indexSize * CHUNK_VERTEX_SIZE)};
|
||||
|
||||
totalSize += entry.vertexData.size();
|
||||
|
||||
for (int j = 0; j < indexSize; j++) {
|
||||
std::memcpy(
|
||||
entry.vertexData.data() + j * CHUNK_VERTEX_SIZE,
|
||||
vertexBuffer.get() + indexBuffer[j] * CHUNK_VERTEX_SIZE,
|
||||
sizeof(float) * CHUNK_VERTEX_SIZE
|
||||
);
|
||||
float& vx = entry.vertexData[j * CHUNK_VERTEX_SIZE + 0];
|
||||
float& vy = entry.vertexData[j * CHUNK_VERTEX_SIZE + 1];
|
||||
float& vz = entry.vertexData[j * CHUNK_VERTEX_SIZE + 2];
|
||||
|
||||
if (!aabbInit) {
|
||||
aabbInit = true;
|
||||
aabb.a = aabb.b = {vx, vy, vz};
|
||||
} else {
|
||||
aabb.addPoint(glm::vec3(vx, vy, vz));
|
||||
}
|
||||
vx += chunk->x * CHUNK_W + 0.5f;
|
||||
vy += 0.5f;
|
||||
vz += chunk->z * CHUNK_D + 0.5f;
|
||||
}
|
||||
sortingMesh.entries.push_back(std::move(entry));
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// additional powerful optimization
|
||||
auto size = aabb.size();
|
||||
if ((size.y < 0.01f || size.x < 0.01f || size.z < 0.01f) &&
|
||||
sortingMesh.entries.size() > 1) {
|
||||
SortingMeshEntry newEntry {
|
||||
sortingMesh.entries[0].position,
|
||||
util::Buffer<float>(totalSize)
|
||||
};
|
||||
size_t offset = 0;
|
||||
for (const auto& entry : sortingMesh.entries) {
|
||||
std::memcpy(
|
||||
newEntry.vertexData.data() + offset,
|
||||
entry.vertexData.data(), entry.vertexData.size() * sizeof(float)
|
||||
);
|
||||
offset += entry.vertexData.size();
|
||||
}
|
||||
return SortingMeshData {{std::move(newEntry)}};
|
||||
}
|
||||
return sortingMesh;
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
|
||||
this->chunk = chunk;
|
||||
voxelsBuffer->setPosition(
|
||||
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
||||
chunk->z * CHUNK_D - voxelBufferPadding);
|
||||
chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get());
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
|
||||
if (voxelsBuffer->pickBlockId(
|
||||
chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D
|
||||
) == BLOCK_VOID) {
|
||||
cancelled = true;
|
||||
return;
|
||||
}
|
||||
cancelled = false;
|
||||
const voxel* voxels = chunk->voxels;
|
||||
render(voxels);
|
||||
|
||||
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
||||
|
||||
int beginEnds[256][2] {};
|
||||
for (int i = totalBegin; i < totalEnd; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
const auto& def = *blockDefsCache[id];
|
||||
|
||||
if (beginEnds[def.drawGroup][0] == 0) {
|
||||
beginEnds[def.drawGroup][0] = i+1;
|
||||
}
|
||||
beginEnds[def.drawGroup][1] = i;
|
||||
}
|
||||
cancelled = false;
|
||||
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
|
||||
sortingMesh = std::move(renderTranslucent(voxels, beginEnds));
|
||||
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
|
||||
render(voxels, beginEnds);
|
||||
}
|
||||
|
||||
MeshData BlocksRenderer::createMesh() {
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
return MeshData(
|
||||
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
|
||||
util::Buffer<int>(indexBuffer.get(), indexSize),
|
||||
util::Buffer<vattr>({{3}, {2}, {1}, {0}})
|
||||
);
|
||||
ChunkMeshData BlocksRenderer::createMesh() {
|
||||
return ChunkMeshData {
|
||||
MeshData(
|
||||
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
|
||||
util::Buffer<int>(indexBuffer.get(), indexSize),
|
||||
util::Buffer<VertexAttribute>(
|
||||
CHUNK_VATTRS, sizeof(CHUNK_VATTRS) / sizeof(VertexAttribute)
|
||||
)
|
||||
),
|
||||
std::move(sortingMesh)};
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
|
||||
ChunkMesh BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
|
||||
build(chunk, chunks);
|
||||
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
||||
return std::make_shared<Mesh>(
|
||||
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs
|
||||
);
|
||||
size_t vcount = vertexOffset / CHUNK_VERTEX_SIZE;
|
||||
return ChunkMesh{std::make_unique<Mesh>(
|
||||
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, CHUNK_VATTRS
|
||||
), std::move(sortingMesh)};
|
||||
}
|
||||
|
||||
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "voxels/VoxelsVolume.hpp"
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "commons.hpp"
|
||||
|
||||
class Content;
|
||||
class Mesh;
|
||||
@@ -26,7 +27,6 @@ struct UVRegion;
|
||||
|
||||
class BlocksRenderer {
|
||||
static const glm::vec3 SUN_VECTOR;
|
||||
static const uint VERTEX_SIZE;
|
||||
const Content& content;
|
||||
std::unique_ptr<float[]> vertexBuffer;
|
||||
std::unique_ptr<int[]> indexBuffer;
|
||||
@@ -45,6 +45,8 @@ class BlocksRenderer {
|
||||
|
||||
util::PseudoRandom randomizer;
|
||||
|
||||
SortingMeshData sortingMesh;
|
||||
|
||||
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
|
||||
void index(int a, int b, int c, int d, int e, int f);
|
||||
|
||||
@@ -115,7 +117,6 @@ class BlocksRenderer {
|
||||
|
||||
bool isOpenForLight(int x, int y, int z) const;
|
||||
|
||||
|
||||
// Does block allow to see other blocks sides (is it transparent)
|
||||
inline bool isOpen(const glm::ivec3& pos, ubyte group) const {
|
||||
auto id = voxelsBuffer->pickBlockId(
|
||||
@@ -135,7 +136,9 @@ class BlocksRenderer {
|
||||
glm::vec4 pickLight(const glm::ivec3& coord) const;
|
||||
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
void render(const voxel* voxels);
|
||||
|
||||
void render(const voxel* voxels, int beginEnds[256][2]);
|
||||
SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
|
||||
public:
|
||||
BlocksRenderer(
|
||||
size_t capacity,
|
||||
@@ -146,8 +149,8 @@ public:
|
||||
virtual ~BlocksRenderer();
|
||||
|
||||
void build(const Chunk* chunk, const Chunks* chunks);
|
||||
std::shared_ptr<Mesh> render(const Chunk* chunk, const Chunks* chunks);
|
||||
MeshData createMesh();
|
||||
ChunkMesh render(const Chunk* chunk, const Chunks* chunks);
|
||||
ChunkMeshData createMesh();
|
||||
VoxelsVolume* getVoxelsBuffer() const;
|
||||
|
||||
bool isCancelled() const {
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
#include "util/listutil.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
|
||||
static debug::Logger logger("chunks-render");
|
||||
|
||||
size_t ChunksRenderer::visibleChunks = 0;
|
||||
@@ -62,7 +58,11 @@ ChunksRenderer::ChunksRenderer(
|
||||
[&](){return std::make_shared<RendererWorker>(*level, cache, settings);},
|
||||
[&](RendererResult& result){
|
||||
if (!result.cancelled) {
|
||||
meshes[result.key] = std::make_shared<Mesh>(result.meshData);
|
||||
auto meshData = std::move(result.meshData);
|
||||
meshes[result.key] = ChunkMesh {
|
||||
std::make_unique<Mesh>(meshData.mesh),
|
||||
std::move(meshData.sortingMesh)
|
||||
};
|
||||
}
|
||||
inwork.erase(result.key);
|
||||
}, settings.graphics.chunkMaxRenderers.get())
|
||||
@@ -78,12 +78,16 @@ ChunksRenderer::ChunksRenderer(
|
||||
ChunksRenderer::~ChunksRenderer() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::render(const std::shared_ptr<Chunk>& chunk, bool important) {
|
||||
const Mesh* ChunksRenderer::render(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
) {
|
||||
chunk->flags.modified = false;
|
||||
if (important) {
|
||||
auto mesh = renderer->render(chunk.get(), level.chunks.get());
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
|
||||
return mesh;
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
||||
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
||||
};
|
||||
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
||||
}
|
||||
glm::ivec2 key(chunk->x, chunk->z);
|
||||
if (inwork.find(key) != inwork.end()) {
|
||||
@@ -107,7 +111,9 @@ void ChunksRenderer::clear() {
|
||||
threadPool.clearQueue();
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>& chunk, bool important) {
|
||||
const Mesh* ChunksRenderer::getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found == meshes.end()) {
|
||||
return render(chunk, important);
|
||||
@@ -115,19 +121,19 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>&
|
||||
if (chunk->flags.modified) {
|
||||
render(chunk, important);
|
||||
}
|
||||
return found->second;
|
||||
return found->second.mesh.get();
|
||||
}
|
||||
|
||||
void ChunksRenderer::update() {
|
||||
threadPool.update();
|
||||
}
|
||||
|
||||
bool ChunksRenderer::drawChunk(
|
||||
const Mesh* ChunksRenderer::retrieveChunk(
|
||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||
) {
|
||||
auto chunk = level.chunks->getChunks()[index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
float distance = glm::distance(
|
||||
camera.position,
|
||||
@@ -139,7 +145,7 @@ bool ChunksRenderer::drawChunk(
|
||||
);
|
||||
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
||||
if (mesh == nullptr) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
if (culling) {
|
||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||
@@ -149,13 +155,9 @@ bool ChunksRenderer::drawChunk(
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustum.isBoxVisible(min, max)) return false;
|
||||
if (!frustum.isBoxVisible(min, max)) return nullptr;
|
||||
}
|
||||
glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f);
|
||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||
shader.uniformMatrix("u_model", model);
|
||||
mesh->draw();
|
||||
return true;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void ChunksRenderer::drawChunks(
|
||||
@@ -191,11 +193,109 @@ void ChunksRenderer::drawChunks(
|
||||
bool culling = settings.graphics.frustumCulling.get();
|
||||
|
||||
visibleChunks = 0;
|
||||
//if (GLEW_ARB_multi_draw_indirect && false) {
|
||||
// TODO: implement Multi Draw Indirect chunks draw
|
||||
//} else {
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
visibleChunks += drawChunk(indices[i].index, camera, shader, culling);
|
||||
shader.uniform1i("u_alphaClip", true);
|
||||
|
||||
// TODO: minimize draw calls number
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
auto chunk = chunks.getChunks()[indices[i].index];
|
||||
auto mesh = retrieveChunk(indices[i].index, camera, shader, culling);
|
||||
|
||||
if (mesh) {
|
||||
glm::vec3 coord(
|
||||
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
|
||||
);
|
||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||
shader.uniformMatrix("u_model", model);
|
||||
mesh->draw();
|
||||
visibleChunks++;
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void write_sorting_mesh_entries(
|
||||
float* buffer, const std::vector<SortingMeshEntry>& chunkEntries
|
||||
) {
|
||||
for (const auto& entry : chunkEntries) {
|
||||
const auto& vertexData = entry.vertexData;
|
||||
std::memcpy(
|
||||
buffer,
|
||||
vertexData.data(),
|
||||
vertexData.size() * sizeof(float)
|
||||
);
|
||||
buffer += vertexData.size();
|
||||
}
|
||||
}
|
||||
|
||||
void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||
const int sortInterval = TRANSLUCENT_BLOCKS_SORT_INTERVAL;
|
||||
static int frameid = 0;
|
||||
frameid++;
|
||||
|
||||
bool culling = settings.graphics.frustumCulling.get();
|
||||
const auto& chunks = level.chunks->getChunks();
|
||||
const auto& cameraPos = camera.position;
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
shader.use();
|
||||
atlas.getTexture()->bind();
|
||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||
shader.uniform1i("u_alphaClip", false);
|
||||
|
||||
for (const auto& index : indices) {
|
||||
const auto& chunk = chunks[index.index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
continue;
|
||||
}
|
||||
const auto& found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found == meshes.end() || found->second.sortingMeshData.entries.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||
glm::vec3 max(
|
||||
chunk->x * CHUNK_W + CHUNK_W,
|
||||
chunk->top,
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustum.isBoxVisible(min, max)) continue;
|
||||
|
||||
auto& chunkEntries = found->second.sortingMeshData.entries;
|
||||
|
||||
if (chunkEntries.size() == 1) {
|
||||
auto& entry = chunkEntries.at(0);
|
||||
if (found->second.sortedMesh == nullptr) {
|
||||
found->second.sortedMesh = std::make_unique<Mesh>(
|
||||
entry.vertexData.data(),
|
||||
entry.vertexData.size() / CHUNK_VERTEX_SIZE,
|
||||
CHUNK_VATTRS
|
||||
);
|
||||
}
|
||||
found->second.sortedMesh->draw();
|
||||
continue;
|
||||
}
|
||||
for (auto& entry : chunkEntries) {
|
||||
entry.distance = static_cast<long long>(
|
||||
glm::distance2(entry.position, cameraPos)
|
||||
);
|
||||
}
|
||||
if (found->second.sortedMesh == nullptr ||
|
||||
(frameid + chunk->x) % sortInterval == 0) {
|
||||
std::sort(chunkEntries.begin(), chunkEntries.end());
|
||||
size_t size = 0;
|
||||
for (const auto& entry : chunkEntries) {
|
||||
size += entry.vertexData.size();
|
||||
}
|
||||
|
||||
static util::Buffer<float> buffer;
|
||||
if (buffer.size() < size) {
|
||||
buffer = util::Buffer<float>(size);
|
||||
}
|
||||
write_sorting_mesh_entries(buffer.data(), chunkEntries);
|
||||
found->second.sortedMesh = std::make_unique<Mesh>(
|
||||
buffer.data(), size / CHUNK_VERTEX_SIZE, CHUNK_VATTRS
|
||||
);
|
||||
}
|
||||
found->second.sortedMesh->draw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,21 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/hash.hpp>
|
||||
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/ChunksStorage.hpp"
|
||||
#include "util/ThreadPool.hpp"
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "commons.hpp"
|
||||
|
||||
class Mesh;
|
||||
class Chunk;
|
||||
class Level;
|
||||
class Camera;
|
||||
class Shader;
|
||||
class Chunks;
|
||||
class Assets;
|
||||
class Frustum;
|
||||
class BlocksRenderer;
|
||||
@@ -35,7 +37,7 @@ struct ChunksSortEntry {
|
||||
struct RendererResult {
|
||||
glm::ivec2 key;
|
||||
bool cancelled;
|
||||
MeshData meshData;
|
||||
ChunkMeshData meshData;
|
||||
};
|
||||
|
||||
class ChunksRenderer {
|
||||
@@ -45,12 +47,11 @@ class ChunksRenderer {
|
||||
const EngineSettings& settings;
|
||||
|
||||
std::unique_ptr<BlocksRenderer> renderer;
|
||||
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
||||
std::unordered_map<glm::ivec2, ChunkMesh> meshes;
|
||||
std::unordered_map<glm::ivec2, bool> inwork;
|
||||
std::vector<ChunksSortEntry> indices;
|
||||
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
|
||||
|
||||
bool drawChunk(
|
||||
const Mesh* retrieveChunk(
|
||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||
);
|
||||
public:
|
||||
@@ -63,17 +64,19 @@ public:
|
||||
);
|
||||
virtual ~ChunksRenderer();
|
||||
|
||||
std::shared_ptr<Mesh> render(
|
||||
const Mesh* render(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
);
|
||||
void unload(const Chunk* chunk);
|
||||
void clear();
|
||||
|
||||
std::shared_ptr<Mesh> getOrRender(
|
||||
const Mesh* getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
);
|
||||
void drawChunks(const Camera& camera, Shader& shader);
|
||||
|
||||
void drawSortedMeshes(const Camera& camera, Shader& shader);
|
||||
|
||||
void update();
|
||||
|
||||
static size_t visibleChunks;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
|
||||
static const vattr attrs[] = {
|
||||
static const VertexAttribute attrs[] = {
|
||||
{3}, {2}, {3}, {1}, {0}
|
||||
};
|
||||
|
||||
@@ -97,38 +97,38 @@ void MainBatch::cube(
|
||||
const glm::vec3 Z(0.0f, 0.0f, 1.0f);
|
||||
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, 0.0f, 0.0f),
|
||||
coord + Z * size.z * 0.5f,
|
||||
X, Y, glm::vec2(size.x, size.y),
|
||||
(shading ? do_tint(0.8) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[5]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(size.x, 0.0f, -size.z),
|
||||
coord - Z * size.z * 0.5f,
|
||||
-X, Y, glm::vec2(size.x, size.y),
|
||||
(shading ? do_tint(0.8) * tint : tint),
|
||||
(shading ? do_tint(0.9f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[4]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, size.y, 0.0f),
|
||||
X, -Z, glm::vec2(size.x, size.z),
|
||||
coord + Y * size.y * 0.5f,
|
||||
-X, Z, glm::vec2(size.x, size.z),
|
||||
(shading ? do_tint(1.0f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[3]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, 0.0f, -size.z),
|
||||
coord - Y * size.y * 0.5f,
|
||||
X, Z, glm::vec2(size.x, size.z),
|
||||
(shading ? do_tint(0.7f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[2]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(0.0f, 0.0f, -size.z),
|
||||
Z, Y, glm::vec2(size.z, size.y),
|
||||
(shading ? do_tint(0.9f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[0]
|
||||
coord + X * size.x * 0.5f,
|
||||
-Z, Y, glm::vec2(size.z, size.y),
|
||||
(shading ? do_tint(0.8f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[1]
|
||||
);
|
||||
quad(
|
||||
coord + glm::vec3(size.x, 0.0f, 0.0f),
|
||||
-Z, Y, glm::vec2(size.z, size.y),
|
||||
coord - X * size.x * 0.5f,
|
||||
Z, Y, glm::vec2(size.z, size.y),
|
||||
(shading ? do_tint(0.9f) * tint : tint),
|
||||
glm::vec3(1.0f), texfaces[1]
|
||||
);
|
||||
|
||||
@@ -89,11 +89,13 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
);
|
||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||
}
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
batch->quad(
|
||||
particle.position,
|
||||
right,
|
||||
preset.globalUpVector ? glm::vec3(0, 1, 0) : up,
|
||||
preset.size,
|
||||
preset.size * scale,
|
||||
light,
|
||||
glm::vec3(1.0f),
|
||||
particle.region
|
||||
|
||||
@@ -39,7 +39,7 @@ Skybox::Skybox(uint size, Shader& shader)
|
||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||
};
|
||||
vattr attrs[] {{2}, {0}};
|
||||
VertexAttribute attrs[] {{2}, {0}};
|
||||
mesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||
|
||||
sprites.push_back(skysprite {
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
#include "ParticlesRenderer.hpp"
|
||||
#include "TextsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
@@ -80,7 +81,8 @@ WorldRenderer::WorldRenderer(
|
||||
*frustumCulling,
|
||||
frontend.getContentGfxCache(),
|
||||
engine->getSettings()
|
||||
)) {
|
||||
)),
|
||||
blockWraps(std::make_unique<BlockWrapsRenderer>(assets, level)) {
|
||||
auto& settings = engine->getSettings();
|
||||
level.events->listen(
|
||||
EVT_CHUNK_HIDDEN,
|
||||
@@ -153,6 +155,7 @@ void WorldRenderer::renderLevel(
|
||||
frustumCulling->update(camera.getProjView());
|
||||
}
|
||||
|
||||
entityShader.uniform1i("u_alphaClip", true);
|
||||
level.entities->render(
|
||||
assets,
|
||||
*modelBatch,
|
||||
@@ -164,9 +167,18 @@ void WorldRenderer::renderLevel(
|
||||
particles->render(camera, delta * !pause);
|
||||
|
||||
auto& shader = assets.require<Shader>("main");
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
|
||||
setupWorldShader(shader, camera, settings, fogFactor);
|
||||
|
||||
chunks->drawChunks(camera, shader);
|
||||
blockWraps->draw(ctx, *player);
|
||||
|
||||
if (hudVisible) {
|
||||
renderLines(camera, linesShader, ctx);
|
||||
}
|
||||
shader.use();
|
||||
chunks->drawSortedMeshes(camera, shader);
|
||||
|
||||
if (!pause) {
|
||||
scripting::on_frontend_render();
|
||||
@@ -326,7 +338,6 @@ void WorldRenderer::draw(
|
||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||
);
|
||||
}
|
||||
renderLines(camera, linesShader, ctx);
|
||||
if (player->currentCamera == player->fpCamera) {
|
||||
renderHands(camera, delta * !pause);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class Batch3D;
|
||||
class LineBatch;
|
||||
class ChunksRenderer;
|
||||
class ParticlesRenderer;
|
||||
class BlockWrapsRenderer;
|
||||
class GuidesRenderer;
|
||||
class TextsRenderer;
|
||||
class Shader;
|
||||
@@ -68,6 +69,7 @@ class WorldRenderer {
|
||||
public:
|
||||
std::unique_ptr<TextsRenderer> texts;
|
||||
std::unique_ptr<ParticlesRenderer> particles;
|
||||
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||
|
||||
static bool showChunkBorders;
|
||||
static bool showEntitiesDebug;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
/// @brief Chunk mesh vertex attributes
|
||||
inline const VertexAttribute CHUNK_VATTRS[]{ {3}, {2}, {1}, {0} };
|
||||
/// @brief Chunk mesh vertex size divided by sizeof(float)
|
||||
inline constexpr int CHUNK_VERTEX_SIZE = 6;
|
||||
|
||||
class Mesh;
|
||||
|
||||
struct SortingMeshEntry {
|
||||
glm::vec3 position;
|
||||
util::Buffer<float> vertexData;
|
||||
long long distance;
|
||||
|
||||
inline bool operator<(const SortingMeshEntry& o) const noexcept {
|
||||
return distance > o.distance;
|
||||
}
|
||||
};
|
||||
|
||||
struct SortingMeshData {
|
||||
std::vector<SortingMeshEntry> entries;
|
||||
};
|
||||
|
||||
struct ChunkMeshData {
|
||||
MeshData mesh;
|
||||
SortingMeshData sortingMesh;
|
||||
};
|
||||
|
||||
struct ChunkMesh {
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
SortingMeshData sortingMeshData;
|
||||
std::unique_ptr<Mesh> sortedMesh = nullptr;
|
||||
};
|
||||
@@ -90,7 +90,7 @@ void Container::draw(const DrawContext* pctx, Assets* assets) {
|
||||
if (!nodes.empty()) {
|
||||
batch->flush();
|
||||
DrawContext ctx = pctx->sub();
|
||||
ctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
|
||||
ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)));
|
||||
for (const auto& node : nodes) {
|
||||
if (node->isVisible())
|
||||
node->draw(pctx, assets);
|
||||
@@ -108,7 +108,7 @@ void Container::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->setColor(color);
|
||||
batch->rect(pos.x, pos.y, size.x, size.y);
|
||||
batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y));
|
||||
}
|
||||
|
||||
void Container::add(const std::shared_ptr<UINode> &node) {
|
||||
@@ -165,6 +165,14 @@ void Container::setSize(glm::vec2 size) {
|
||||
}
|
||||
}
|
||||
|
||||
int Container::getScrollStep() const {
|
||||
return scrollStep;
|
||||
}
|
||||
|
||||
void Container::setScrollStep(int step) {
|
||||
scrollStep = step;
|
||||
}
|
||||
|
||||
void Container::refresh() {
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
||||
return a->getZIndex() < b->getZIndex();
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace gui {
|
||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||
virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);};
|
||||
virtual void setSize(glm::vec2 size) override;
|
||||
virtual int getScrollStep() const;
|
||||
virtual void setScrollStep(int step);
|
||||
virtual void refresh() override;
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "TextBox.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -14,24 +15,39 @@
|
||||
|
||||
using namespace gui;
|
||||
|
||||
inline constexpr int LINE_NUMBERS_PANE_WIDTH = 40;
|
||||
|
||||
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
|
||||
: Panel(glm::vec2(200,32), padding, 0),
|
||||
: Container(glm::vec2(200,32)),
|
||||
padding(padding),
|
||||
input(L""),
|
||||
placeholder(std::move(placeholder))
|
||||
{
|
||||
setOnUpPressed(nullptr);
|
||||
setOnDownPressed(nullptr);
|
||||
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
|
||||
label = std::make_shared<Label>(L"");
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
label->setPos(glm::vec2(
|
||||
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
|
||||
));
|
||||
add(label);
|
||||
|
||||
lineNumbersLabel = std::make_shared<Label>(L"");
|
||||
lineNumbersLabel->setMultiline(true);
|
||||
lineNumbersLabel->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
lineNumbersLabel->setVerticalAlign(Align::top);
|
||||
add(lineNumbersLabel);
|
||||
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
|
||||
|
||||
textInitX = label->getPos().x;
|
||||
scrollable = true;
|
||||
scrollStep = 0;
|
||||
}
|
||||
|
||||
void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
||||
Panel::draw(pctx, assets);
|
||||
Container::draw(pctx, assets);
|
||||
|
||||
font = assets->get<Font>(label->getFontName());
|
||||
|
||||
@@ -76,6 +92,44 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
||||
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFocused() && multiline) {
|
||||
auto selectionCtx = subctx.sub(batch);
|
||||
selectionCtx.setBlendMode(BlendMode::addition);
|
||||
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
|
||||
|
||||
uint line = label->getLineByTextIndex(caret);
|
||||
while (label->isFakeLine(line)) {
|
||||
line--;
|
||||
}
|
||||
do {
|
||||
int lineY = label->getLineYOffset(line);
|
||||
int lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
|
||||
if (showLineNumbers) {
|
||||
batch->rect(
|
||||
lcoord.x - 8,
|
||||
lcoord.y + lineY,
|
||||
label->getSize().x,
|
||||
lineHeight
|
||||
);
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.10f));
|
||||
batch->rect(
|
||||
lcoord.x - LINE_NUMBERS_PANE_WIDTH,
|
||||
lcoord.y + lineY,
|
||||
LINE_NUMBERS_PANE_WIDTH - 8,
|
||||
lineHeight
|
||||
);
|
||||
} else {
|
||||
batch->rect(
|
||||
lcoord.x, lcoord.y + lineY, label->getSize().x, lineHeight
|
||||
);
|
||||
}
|
||||
line++;
|
||||
} while (line < label->getLinesNumber() && label->isFakeLine(line));
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
@@ -103,31 +157,31 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
if (!isFocused() && supplier) {
|
||||
input = supplier();
|
||||
}
|
||||
|
||||
if (isFocused() && multiline) {
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
|
||||
glm::vec2 lcoord = label->calcPos();
|
||||
lcoord.y -= 2;
|
||||
|
||||
uint line = label->getLineByTextIndex(caret);
|
||||
while (label->isFakeLine(line)) {
|
||||
line--;
|
||||
}
|
||||
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
|
||||
do {
|
||||
int lineY = label->getLineYOffset(line);
|
||||
int lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||
|
||||
batch->rect(lcoord.x, lcoord.y+lineY, label->getSize().x, lineHeight);
|
||||
line++;
|
||||
} while (line < label->getLinesNumber() && label->isFakeLine(line));
|
||||
}
|
||||
refreshLabel();
|
||||
}
|
||||
|
||||
void TextBox::refreshLabel() {
|
||||
label->setColor(glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||
label->setText(input.empty() && !hint.empty() ? hint : getText());
|
||||
|
||||
if (showLineNumbers) {
|
||||
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
|
||||
std::wstringstream ss;
|
||||
int n = 1;
|
||||
for (int i = 1; i <= label->getLinesNumber(); i++) {
|
||||
if (!label->isFakeLine(i-1)) {
|
||||
ss << n;
|
||||
n++;
|
||||
}
|
||||
if (i + 1 <= label->getLinesNumber()) {
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
lineNumbersLabel->setText(ss.str());
|
||||
}
|
||||
lineNumbersLabel->setPos(padding);
|
||||
lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f));
|
||||
}
|
||||
|
||||
if (autoresize && font) {
|
||||
auto size = getSize();
|
||||
@@ -293,7 +347,7 @@ bool TextBox::isAutoResize() const {
|
||||
}
|
||||
|
||||
void TextBox::onFocus(GUI* gui) {
|
||||
Panel::onFocus(gui);
|
||||
Container::onFocus(gui);
|
||||
if (onEditStart){
|
||||
setCaret(input.size());
|
||||
onEditStart();
|
||||
@@ -302,8 +356,11 @@ void TextBox::onFocus(GUI* gui) {
|
||||
}
|
||||
|
||||
void TextBox::refresh() {
|
||||
Panel::refresh();
|
||||
Container::refresh();
|
||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||
label->setPos(glm::vec2(
|
||||
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
|
||||
));
|
||||
}
|
||||
|
||||
/// @brief Clamp index to range [0, input.length()]
|
||||
@@ -567,6 +624,14 @@ void TextBox::select(int start, int end) {
|
||||
setCaret(selectionEnd);
|
||||
}
|
||||
|
||||
uint TextBox::getLineAt(size_t position) const {
|
||||
return label->getLineByTextIndex(position);
|
||||
}
|
||||
|
||||
size_t TextBox::getLinePos(uint line) const {
|
||||
return label->getTextLineOffset(line);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
@@ -619,6 +684,15 @@ glm::vec4 TextBox::getFocusedColor() const {
|
||||
return focusedColor;
|
||||
}
|
||||
|
||||
|
||||
void TextBox::setTextColor(glm::vec4 color) {
|
||||
this->textColor = color;
|
||||
}
|
||||
|
||||
glm::vec4 TextBox::getTextColor() const {
|
||||
return textColor;
|
||||
}
|
||||
|
||||
void TextBox::setErrorColor(glm::vec4 color) {
|
||||
this->invalidColor = color;
|
||||
}
|
||||
@@ -673,9 +747,11 @@ void TextBox::setCaret(size_t position) {
|
||||
uint line = label->getLineByTextIndex(caret);
|
||||
int offset = label->getLineYOffset(line) + getContentOffset().y;
|
||||
uint lineHeight = font->getLineHeight()*label->getLineInterval();
|
||||
scrollStep = lineHeight;
|
||||
if (scrollStep == 0) {
|
||||
scrollStep = lineHeight;
|
||||
}
|
||||
if (offset < 0) {
|
||||
scrolled(1);
|
||||
scrolled(-glm::floor(offset/static_cast<double>(scrollStep)+0.5f));
|
||||
} else if (offset >= getSize().y) {
|
||||
offset -= getSize().y;
|
||||
scrolled(-glm::ceil(offset/static_cast<double>(scrollStep)+0.5f));
|
||||
@@ -696,3 +772,20 @@ void TextBox::setCaret(ptrdiff_t position) {
|
||||
setCaret(static_cast<size_t>(position));
|
||||
}
|
||||
}
|
||||
|
||||
void TextBox::setPadding(glm::vec4 padding) {
|
||||
this->padding = padding;
|
||||
refresh();
|
||||
}
|
||||
|
||||
glm::vec4 TextBox::getPadding() const {
|
||||
return padding;
|
||||
}
|
||||
|
||||
void TextBox::setShowLineNumbers(bool flag) {
|
||||
showLineNumbers = flag;
|
||||
}
|
||||
|
||||
bool TextBox::isShowLineNumbers() const {
|
||||
return showLineNumbers;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,14 @@ class Font;
|
||||
namespace gui {
|
||||
class Label;
|
||||
|
||||
class TextBox : public Panel {
|
||||
class TextBox : public Container {
|
||||
protected:
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||
glm::vec4 textColor {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
glm::vec4 padding {2};
|
||||
std::shared_ptr<Label> label;
|
||||
std::shared_ptr<Label> lineNumbersLabel;
|
||||
/// @brief Current user input
|
||||
std::wstring input;
|
||||
/// @brief Text will be used if nothing entered
|
||||
@@ -52,6 +55,7 @@ namespace gui {
|
||||
bool multiline = false;
|
||||
bool editable = true;
|
||||
bool autoresize = false;
|
||||
bool showLineNumbers = false;
|
||||
|
||||
void stepLeft(bool shiftPressed, bool breakSelection);
|
||||
void stepRight(bool shiftPressed, bool breakSelection);
|
||||
@@ -106,6 +110,9 @@ namespace gui {
|
||||
virtual void setFocusedColor(glm::vec4 color);
|
||||
virtual glm::vec4 getFocusedColor() const;
|
||||
|
||||
virtual void setTextColor(glm::vec4 color);
|
||||
virtual glm::vec4 getTextColor() const;
|
||||
|
||||
/// @brief Set color of textbox marked by validator as invalid
|
||||
virtual void setErrorColor(glm::vec4 color);
|
||||
|
||||
@@ -152,6 +159,16 @@ namespace gui {
|
||||
/// @param end index of the last selected character + 1
|
||||
virtual void select(int start, int end);
|
||||
|
||||
/// @brief Get number of line at specific position in text
|
||||
/// @param position target position
|
||||
/// @return line number
|
||||
virtual uint getLineAt(size_t position) const;
|
||||
|
||||
/// @brief Get specific line text position
|
||||
/// @param line target line
|
||||
/// @return line position in text
|
||||
virtual size_t getLinePos(uint line) const;
|
||||
|
||||
/// @brief Check text with validator set with setTextValidator
|
||||
/// @return true if text is valid
|
||||
virtual bool validate();
|
||||
@@ -177,12 +194,18 @@ namespace gui {
|
||||
/// @brief Check if text editing feature is enabled
|
||||
virtual bool isEditable() const;
|
||||
|
||||
virtual void setPadding(glm::vec4 padding);
|
||||
glm::vec4 getPadding() const;
|
||||
|
||||
/// @brief Set runnable called on textbox focus
|
||||
virtual void setOnEditStart(runnable oneditstart);
|
||||
|
||||
virtual void setAutoResize(bool flag);
|
||||
virtual bool isAutoResize() const;
|
||||
|
||||
virtual void setShowLineNumbers(bool flag);
|
||||
virtual bool isShowLineNumbers() const;
|
||||
|
||||
virtual void onFocus(GUI*) override;
|
||||
virtual void refresh() override;
|
||||
virtual void doubleClick(GUI*, int x, int y) override;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace gui {
|
||||
/// @brief element color when clicked
|
||||
glm::vec4 pressedColor {1.0f};
|
||||
/// @brief element margin (only supported for Panel sub-nodes)
|
||||
glm::vec4 margin {1.0f};
|
||||
glm::vec4 margin {0.0f};
|
||||
/// @brief is element visible
|
||||
bool visible = true;
|
||||
/// @brief is mouse over the element
|
||||
|
||||
@@ -20,7 +20,7 @@ std::shared_ptr<gui::UINode> guiutil::create(const std::string& source, scripten
|
||||
env = scripting::get_root_environment();
|
||||
}
|
||||
UiXmlReader reader(env);
|
||||
return reader.readXML("<string>", source);
|
||||
return reader.readXML("[string]", source);
|
||||
}
|
||||
|
||||
void guiutil::alert(GUI* gui, const std::wstring& text, const runnable& on_hidden) {
|
||||
|
||||
@@ -172,6 +172,9 @@ static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element,
|
||||
if (element->has("scrollable")) {
|
||||
container.setScrollable(element->attr("scrollable").asBool());
|
||||
}
|
||||
if (element->has("scroll-step")) {
|
||||
container.setScrollStep(element->attr("scroll-step").asInt());
|
||||
}
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->isText())
|
||||
continue;
|
||||
@@ -342,7 +345,16 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
|
||||
textbox->setHint(hint);
|
||||
|
||||
_readPanel(reader, element, *textbox);
|
||||
_readContainer(reader, element, *textbox);
|
||||
if (element->has("padding")) {
|
||||
glm::vec4 padding = element->attr("padding").asVec4();
|
||||
textbox->setPadding(padding);
|
||||
glm::vec2 size = textbox->getSize();
|
||||
textbox->setSize(glm::vec2(
|
||||
size.x + padding.x + padding.z,
|
||||
size.y + padding.y + padding.w
|
||||
));
|
||||
}
|
||||
textbox->setText(text);
|
||||
|
||||
if (element->has("multiline")) {
|
||||
@@ -357,6 +369,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
if (element->has("autoresize")) {
|
||||
textbox->setAutoResize(element->attr("autoresize").asBool());
|
||||
}
|
||||
if (element->has("line-numbers")) {
|
||||
textbox->setShowLineNumbers(element->attr("line-numbers").asBool());
|
||||
}
|
||||
if (element->has("consumer")) {
|
||||
textbox->setTextConsumer(scripting::create_wstring_consumer(
|
||||
reader.getEnvironment(),
|
||||
@@ -384,6 +399,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
||||
if (element->has("error-color")) {
|
||||
textbox->setErrorColor(element->attr("error-color").asColor());
|
||||
}
|
||||
if (element->has("text-color")) {
|
||||
textbox->setTextColor(element->attr("text-color").asColor());
|
||||
}
|
||||
if (element->has("validator")) {
|
||||
textbox->setTextValidator(scripting::create_wstring_validator(
|
||||
reader.getEnvironment(),
|
||||
|
||||
@@ -45,6 +45,10 @@ void Inventory::move(
|
||||
}
|
||||
}
|
||||
|
||||
void Inventory::resize(uint newSize) {
|
||||
slots.resize(newSize);
|
||||
}
|
||||
|
||||
void Inventory::deserialize(const dv::value& src) {
|
||||
id = src["id"].asInteger(1);
|
||||
auto& slotsarr = src["slots"];
|
||||
|
||||
@@ -35,6 +35,8 @@ public:
|
||||
size_t end = -1
|
||||
);
|
||||
|
||||
void resize(uint newSize);
|
||||
|
||||
void deserialize(const dv::value& src) override;
|
||||
|
||||
dv::value serialize() const override;
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
|
||||
BlocksController::BlocksController(Level* level, uint padding)
|
||||
BlocksController::BlocksController(const Level& level, uint padding)
|
||||
: level(level),
|
||||
chunks(level->chunks.get()),
|
||||
lighting(level->lighting.get()),
|
||||
chunks(*level.chunks),
|
||||
lighting(*level.lighting),
|
||||
randTickClock(20, 3),
|
||||
blocksTickClock(20, 1),
|
||||
worldTickClock(20, 1),
|
||||
@@ -34,8 +34,8 @@ void BlocksController::updateSides(int x, int y, int z) {
|
||||
}
|
||||
|
||||
void BlocksController::updateSides(int x, int y, int z, int w, int h, int d) {
|
||||
voxel* vox = chunks->get(x, y, z);
|
||||
const auto& def = level->content->getIndices()->blocks.require(vox->id);
|
||||
voxel* vox = chunks.get(x, y, z);
|
||||
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||
const auto& rot = def.rotations.variants[vox->state.rotation];
|
||||
const auto& xaxis = rot.axisX;
|
||||
const auto& yaxis = rot.axisY;
|
||||
@@ -62,8 +62,8 @@ void BlocksController::breakBlock(
|
||||
onBlockInteraction(
|
||||
player, glm::ivec3(x, y, z), def, BlockInteraction::destruction
|
||||
);
|
||||
chunks->set(x, y, z, 0, {});
|
||||
lighting->onBlockSet(x, y, z, 0);
|
||||
chunks.set(x, y, z, 0, {});
|
||||
lighting.onBlockSet(x, y, z, 0);
|
||||
scripting::on_block_broken(player, def, glm::ivec3(x, y, z));
|
||||
if (def.rt.extended) {
|
||||
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
||||
@@ -78,8 +78,8 @@ void BlocksController::placeBlock(
|
||||
onBlockInteraction(
|
||||
player, glm::ivec3(x, y, z), def, BlockInteraction::placing
|
||||
);
|
||||
chunks->set(x, y, z, def.rt.id, state);
|
||||
lighting->onBlockSet(x, y, z, def.rt.id);
|
||||
chunks.set(x, y, z, def.rt.id, state);
|
||||
lighting.onBlockSet(x, y, z, def.rt.id);
|
||||
scripting::on_block_placed(player, def, glm::ivec3(x, y, z));
|
||||
if (def.rt.extended) {
|
||||
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
||||
@@ -89,12 +89,12 @@ void BlocksController::placeBlock(
|
||||
}
|
||||
|
||||
void BlocksController::updateBlock(int x, int y, int z) {
|
||||
voxel* vox = chunks->get(x, y, z);
|
||||
voxel* vox = chunks.get(x, y, z);
|
||||
if (vox == nullptr) return;
|
||||
const auto& def = level->content->getIndices()->blocks.require(vox->id);
|
||||
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||
if (def.grounded) {
|
||||
const auto& vec = get_ground_direction(def, vox->state.rotation);
|
||||
if (!chunks->isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) {
|
||||
if (!chunks.isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) {
|
||||
breakBlock(nullptr, def, x, y, z);
|
||||
return;
|
||||
}
|
||||
@@ -117,12 +117,11 @@ void BlocksController::update(float delta) {
|
||||
}
|
||||
|
||||
void BlocksController::onBlocksTick(int tickid, int parts) {
|
||||
auto content = level->content;
|
||||
auto indices = content->getIndices();
|
||||
const auto& indices = level.content->getIndices()->blocks;
|
||||
int tickRate = blocksTickClock.getTickRate();
|
||||
for (size_t id = 0; id < indices->blocks.count(); id++) {
|
||||
for (size_t id = 0; id < indices.count(); id++) {
|
||||
if ((id + tickid) % parts != 0) continue;
|
||||
auto& def = indices->blocks.require(id);
|
||||
auto& def = indices.require(id);
|
||||
auto interval = def.tickInterval;
|
||||
if (def.rt.funcsset.onblockstick && tickid / parts % interval == 0) {
|
||||
scripting::on_blocks_tick(def, tickRate / interval);
|
||||
@@ -155,9 +154,9 @@ void BlocksController::randomTick(
|
||||
}
|
||||
|
||||
void BlocksController::randomTick(int tickid, int parts) {
|
||||
auto indices = level->content->getIndices();
|
||||
int width = chunks->getWidth();
|
||||
int height = chunks->getHeight();
|
||||
auto indices = level.content->getIndices();
|
||||
int width = chunks.getWidth();
|
||||
int height = chunks.getHeight();
|
||||
int segments = 4;
|
||||
|
||||
for (uint z = padding; z < height - padding; z++) {
|
||||
@@ -166,7 +165,7 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
if ((index + tickid) % parts != 0) {
|
||||
continue;
|
||||
}
|
||||
auto& chunk = chunks->getChunks()[index];
|
||||
auto& chunk = chunks.getChunks()[index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
continue;
|
||||
}
|
||||
@@ -176,7 +175,7 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
}
|
||||
|
||||
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
@@ -184,21 +183,20 @@ int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
auto inv = chunk->getBlockInventory(lx, y, lz);
|
||||
if (inv == nullptr) {
|
||||
auto indices = level->content->getIndices();
|
||||
auto& def =
|
||||
indices->blocks.require(chunk->voxels[vox_index(lx, y, lz)].id);
|
||||
const auto& indices = level.content->getIndices()->blocks;
|
||||
auto& def = indices.require(chunk->voxels[vox_index(lx, y, lz)].id);
|
||||
int invsize = def.inventorySize;
|
||||
if (invsize == 0) {
|
||||
return 0;
|
||||
}
|
||||
inv = level->inventories->create(invsize);
|
||||
inv = level.inventories->create(invsize);
|
||||
chunk->addBlockInventory(inv, lx, y, lz);
|
||||
}
|
||||
return inv->getId();
|
||||
}
|
||||
|
||||
void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
@@ -207,11 +205,11 @@ void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
chunk->addBlockInventory(level->inventories->get(invid), lx, y, lz);
|
||||
chunk->addBlockInventory(level.inventories->get(invid), lx, y, lz);
|
||||
}
|
||||
|
||||
void BlocksController::unbindInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
throw std::runtime_error("block does not exists");
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ using on_block_interaction = std::function<
|
||||
|
||||
/// BlocksController manages block updates and data (inventories, metadata)
|
||||
class BlocksController {
|
||||
Level* level;
|
||||
Chunks* chunks;
|
||||
Lighting* lighting;
|
||||
const Level& level;
|
||||
Chunks& chunks;
|
||||
Lighting& lighting;
|
||||
util::Clock randTickClock;
|
||||
util::Clock blocksTickClock;
|
||||
util::Clock worldTickClock;
|
||||
@@ -34,7 +34,7 @@ class BlocksController {
|
||||
FastRandom random {};
|
||||
std::vector<on_block_interaction> blockInteractionCallbacks;
|
||||
public:
|
||||
BlocksController(Level* level, uint padding);
|
||||
BlocksController(const Level& level, uint padding);
|
||||
|
||||
void updateSides(int x, int y, int z);
|
||||
void updateSides(int x, int y, int z, int w, int h, int d);
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
const uint MAX_WORK_PER_FRAME = 128;
|
||||
const uint MIN_SURROUNDING = 9;
|
||||
|
||||
ChunksController::ChunksController(Level* level, uint padding)
|
||||
ChunksController::ChunksController(Level& level, uint padding)
|
||||
: level(level),
|
||||
chunks(level->chunks.get()),
|
||||
lighting(level->lighting.get()),
|
||||
chunks(*level.chunks),
|
||||
lighting(*level.lighting),
|
||||
padding(padding),
|
||||
generator(std::make_unique<WorldGenerator>(
|
||||
level->content->generators.require(level->getWorld()->getGenerator()),
|
||||
level->content,
|
||||
level->getWorld()->getSeed()
|
||||
level.content->generators.require(level.getWorld()->getGenerator()),
|
||||
level.content,
|
||||
level.getWorld()->getSeed()
|
||||
)) {}
|
||||
|
||||
ChunksController::~ChunksController() = default;
|
||||
@@ -56,8 +56,8 @@ void ChunksController::update(
|
||||
}
|
||||
|
||||
bool ChunksController::loadVisible() {
|
||||
int sizeX = chunks->getWidth();
|
||||
int sizeY = chunks->getHeight();
|
||||
int sizeX = chunks.getWidth();
|
||||
int sizeY = chunks.getHeight();
|
||||
|
||||
int nearX = 0;
|
||||
int nearZ = 0;
|
||||
@@ -66,7 +66,7 @@ bool ChunksController::loadVisible() {
|
||||
for (uint z = padding; z < sizeY - padding; z++) {
|
||||
for (uint x = padding; x < sizeX - padding; x++) {
|
||||
int index = z * sizeX + x;
|
||||
auto& chunk = chunks->getChunks()[index];
|
||||
auto& chunk = chunks.getChunks()[index];
|
||||
if (chunk != nullptr) {
|
||||
if (chunk->flags.loaded && !chunk->flags.lighted) {
|
||||
if (buildLights(chunk)) {
|
||||
@@ -87,12 +87,12 @@ bool ChunksController::loadVisible() {
|
||||
}
|
||||
}
|
||||
|
||||
const auto& chunk = chunks->getChunks()[nearZ * sizeX + nearX];
|
||||
const auto& chunk = chunks.getChunks()[nearZ * sizeX + nearX];
|
||||
if (chunk != nullptr || !assigned) {
|
||||
return false;
|
||||
}
|
||||
int offsetX = chunks->getOffsetX();
|
||||
int offsetY = chunks->getOffsetY();
|
||||
int offsetX = chunks.getOffsetX();
|
||||
int offsetY = chunks.getOffsetY();
|
||||
createChunk(nearX + offsetX, nearZ + offsetY);
|
||||
return true;
|
||||
}
|
||||
@@ -101,15 +101,15 @@ bool ChunksController::buildLights(const std::shared_ptr<Chunk>& chunk) {
|
||||
int surrounding = 0;
|
||||
for (int oz = -1; oz <= 1; oz++) {
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
if (chunks->getChunk(chunk->x + ox, chunk->z + oz)) surrounding++;
|
||||
if (chunks.getChunk(chunk->x + ox, chunk->z + oz)) surrounding++;
|
||||
}
|
||||
}
|
||||
if (surrounding == MIN_SURROUNDING) {
|
||||
bool lightsCache = chunk->flags.loadedLights;
|
||||
if (!lightsCache) {
|
||||
lighting->buildSkyLight(chunk->x, chunk->z);
|
||||
lighting.buildSkyLight(chunk->x, chunk->z);
|
||||
}
|
||||
lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
||||
lighting.onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
||||
chunk->flags.lighted = true;
|
||||
return true;
|
||||
}
|
||||
@@ -117,8 +117,8 @@ bool ChunksController::buildLights(const std::shared_ptr<Chunk>& chunk) {
|
||||
}
|
||||
|
||||
void ChunksController::createChunk(int x, int z) {
|
||||
auto chunk = level->chunksStorage->create(x, z);
|
||||
chunks->putChunk(chunk);
|
||||
auto chunk = level.chunksStorage->create(x, z);
|
||||
chunks.putChunk(chunk);
|
||||
auto& chunkFlags = chunk->flags;
|
||||
|
||||
if (!chunkFlags.loaded) {
|
||||
@@ -128,7 +128,7 @@ void ChunksController::createChunk(int x, int z) {
|
||||
chunk->updateHeights();
|
||||
|
||||
if (!chunkFlags.loadedLights) {
|
||||
Lighting::prebuildSkyLight(chunk.get(), level->content->getIndices());
|
||||
Lighting::prebuildSkyLight(chunk.get(), level.content->getIndices());
|
||||
}
|
||||
chunkFlags.loaded = true;
|
||||
chunkFlags.ready = true;
|
||||
|
||||
@@ -13,9 +13,9 @@ class WorldGenerator;
|
||||
/// @brief ChunksController manages chunks dynamic loading/unloading
|
||||
class ChunksController {
|
||||
private:
|
||||
Level* level;
|
||||
Chunks* chunks;
|
||||
Lighting* lighting;
|
||||
Level& level;
|
||||
Chunks& chunks;
|
||||
Lighting& lighting;
|
||||
uint padding;
|
||||
std::unique_ptr<WorldGenerator> generator;
|
||||
|
||||
@@ -24,7 +24,7 @@ private:
|
||||
bool buildLights(const std::shared_ptr<Chunk>& chunk);
|
||||
void createChunk(int x, int y);
|
||||
public:
|
||||
ChunksController(Level* level, uint padding);
|
||||
ChunksController(Level& level, uint padding);
|
||||
~ChunksController();
|
||||
|
||||
/// @param maxDuration milliseconds reserved for chunks loading
|
||||
|
||||
@@ -418,7 +418,7 @@ Command Command::create(
|
||||
std::string_view description,
|
||||
executor_func executor
|
||||
) {
|
||||
return CommandParser("<string>", scheme)
|
||||
return CommandParser("[string]", scheme)
|
||||
.parseScheme(std::move(executor), description);
|
||||
}
|
||||
|
||||
@@ -440,5 +440,5 @@ Command* CommandsRepository::get(const std::string& name) {
|
||||
}
|
||||
|
||||
Prompt CommandsInterpreter::parse(std::string_view text) {
|
||||
return CommandParser("<string>", text).parsePrompt(this);
|
||||
return CommandParser("[string]", text).parsePrompt(this);
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
static debug::Logger logger("level-control");
|
||||
|
||||
LevelController::LevelController(Engine* engine, std::unique_ptr<Level> level)
|
||||
LevelController::LevelController(Engine* engine, std::unique_ptr<Level> levelPtr)
|
||||
: settings(engine->getSettings()),
|
||||
level(std::move(level)),
|
||||
level(std::move(levelPtr)),
|
||||
blocks(std::make_unique<BlocksController>(
|
||||
this->level.get(), settings.chunks.padding.get()
|
||||
*level, settings.chunks.padding.get()
|
||||
)),
|
||||
chunks(std::make_unique<ChunksController>(
|
||||
this->level.get(), settings.chunks.padding.get()
|
||||
*level, settings.chunks.padding.get()
|
||||
)),
|
||||
player(std::make_unique<PlayerController>(
|
||||
settings, this->level.get(), blocks.get()
|
||||
|
||||
@@ -195,7 +195,8 @@ PlayerController::PlayerController(
|
||||
: settings(settings), level(level),
|
||||
player(level->getObject<Player>(0)),
|
||||
camControl(player, settings.camera),
|
||||
blocksController(blocksController) {
|
||||
blocksController(blocksController),
|
||||
playerTickClock(20, 3) {
|
||||
}
|
||||
|
||||
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
||||
@@ -249,6 +250,13 @@ void PlayerController::update(float delta, bool input, bool pause) {
|
||||
resetKeyboard();
|
||||
}
|
||||
updatePlayer(delta);
|
||||
|
||||
if (playerTickClock.update(delta)) {
|
||||
if (player->getId() % playerTickClock.getParts() ==
|
||||
playerTickClock.getPart()) {
|
||||
scripting::on_player_tick(player.get(), playerTickClock.getTickRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +310,7 @@ void PlayerController::updatePlayer(float delta) {
|
||||
}
|
||||
|
||||
static int determine_rotation(
|
||||
const Block* def, const glm::ivec3& norm, glm::vec3& camDir
|
||||
const Block* def, const glm::ivec3& norm, const glm::vec3& camDir
|
||||
) {
|
||||
if (def && def->rotatable) {
|
||||
const std::string& name = def->rotations.name;
|
||||
@@ -461,6 +469,10 @@ void PlayerController::processRightClick(const Block& def, const Block& target)
|
||||
}
|
||||
}
|
||||
if (chosenBlock != vox->id && chosenBlock) {
|
||||
if (!player->isInfiniteItems()) {
|
||||
auto& slot = player->getInventory()->getSlot(player->getChosenSlot());
|
||||
slot.setCount(slot.getCount()-1);
|
||||
}
|
||||
blocksController->placeBlock(
|
||||
player.get(), def, state, coord.x, coord.y, coord.z
|
||||
);
|
||||
@@ -522,16 +534,18 @@ void PlayerController::updateInteraction(float delta) {
|
||||
auto iend = selection.position;
|
||||
if (lclick && !input.shift && item.rt.funcsset.on_block_break_by) {
|
||||
if (scripting::on_item_break_block(
|
||||
player.get(), item, iend.x, iend.y, iend.z
|
||||
)) {
|
||||
player.get(), item, iend.x, iend.y, iend.z
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto& target = indices->blocks.require(vox->id);
|
||||
if (lclick && target.breakable) {
|
||||
blocksController->breakBlock(
|
||||
player.get(), target, iend.x, iend.y, iend.z
|
||||
);
|
||||
if (lclick) {
|
||||
if (player->isInstantDestruction() && target.breakable) {
|
||||
blocksController->breakBlock(
|
||||
player.get(), target, iend.x, iend.y, iend.z
|
||||
);
|
||||
}
|
||||
}
|
||||
if (rclick && !input.shift) {
|
||||
bool preventDefault = false;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "objects/Player.hpp"
|
||||
#include "util/Clock.hpp"
|
||||
|
||||
class Engine;
|
||||
class Camera;
|
||||
@@ -53,6 +54,7 @@ class PlayerController {
|
||||
PlayerInput input {};
|
||||
CameraControl camControl;
|
||||
BlocksController* blocksController;
|
||||
util::Clock playerTickClock;
|
||||
|
||||
float interactionTimer = 0.0f;
|
||||
void updateKeyboard();
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
|
||||
// Libraries
|
||||
extern const luaL_Reg audiolib[];
|
||||
extern const luaL_Reg base64lib[];
|
||||
extern const luaL_Reg bjsonlib[];
|
||||
extern const luaL_Reg blocklib[];
|
||||
extern const luaL_Reg blockwrapslib[]; // gfx.blockwraps
|
||||
extern const luaL_Reg cameralib[];
|
||||
extern const luaL_Reg consolelib[];
|
||||
extern const luaL_Reg corelib[];
|
||||
@@ -31,10 +33,10 @@ extern const luaL_Reg itemlib[];
|
||||
extern const luaL_Reg jsonlib[];
|
||||
extern const luaL_Reg mat4lib[];
|
||||
extern const luaL_Reg packlib[];
|
||||
extern const luaL_Reg particleslib[];
|
||||
extern const luaL_Reg particleslib[]; // gfx.particles
|
||||
extern const luaL_Reg playerlib[];
|
||||
extern const luaL_Reg quatlib[];
|
||||
extern const luaL_Reg text3dlib[];
|
||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||
extern const luaL_Reg timelib[];
|
||||
extern const luaL_Reg tomllib[];
|
||||
extern const luaL_Reg utf8lib[];
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "api_lua.hpp"
|
||||
|
||||
#include "util/stringutil.hpp"
|
||||
|
||||
static int l_encode(lua::State* L) {
|
||||
if (lua::istable(L, 1)) {
|
||||
lua::pushvalue(L, 1);
|
||||
size_t size = lua::objlen(L, 1);
|
||||
util::Buffer<char> buffer(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
lua::rawgeti(L, i + 1);
|
||||
buffer[i] = lua::tointeger(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
return lua::pushstring(L, util::base64_encode(
|
||||
reinterpret_cast<const ubyte*>(buffer.data()), buffer.size()
|
||||
));
|
||||
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
|
||||
return lua::pushstring(
|
||||
L,
|
||||
util::base64_encode(
|
||||
bytes->data().data(),
|
||||
bytes->data().size()
|
||||
)
|
||||
);
|
||||
}
|
||||
throw std::runtime_error("array or ByteArray expected");
|
||||
}
|
||||
|
||||
static int l_decode(lua::State* L) {
|
||||
auto buffer = util::base64_decode(lua::require_lstring(L, 1));
|
||||
if (lua::toboolean(L, 2)) {
|
||||
lua::createtable(L, buffer.size(), 0);
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
lua::pushinteger(L, buffer[i] & 0xFF);
|
||||
lua::rawseti(L, i+1);
|
||||
}
|
||||
} else {
|
||||
lua::newuserdata<lua::LuaBytearray>(L, buffer.size());
|
||||
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
|
||||
bytearray->data().reserve(buffer.size());
|
||||
std::memcpy(bytearray->data().data(), buffer.data(), buffer.size());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg base64lib[] = {
|
||||
{"encode", lua::wrap<l_encode>},
|
||||
{"decode", lua::wrap<l_decode>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "api_lua.hpp"
|
||||
|
||||
#include "logic/scripting/scripting_hud.hpp"
|
||||
#include "graphics/render/WorldRenderer.hpp"
|
||||
#include "graphics/render/BlockWrapsRenderer.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static int l_wrap(lua::State* L) {
|
||||
auto position = lua::tovec3(L, 1);
|
||||
std::string texture = lua::require_string(L, 2);
|
||||
|
||||
return lua::pushinteger(
|
||||
L, renderer->blockWraps->add(position, std::move(texture))
|
||||
);
|
||||
}
|
||||
|
||||
static int l_unwrap(lua::State* L) {
|
||||
renderer->blockWraps->remove(lua::tointeger(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_pos(lua::State* L) {
|
||||
if (auto wrapper = renderer->blockWraps->get(lua::tointeger(L, 1))) {
|
||||
wrapper->position = lua::tovec3(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_texture(lua::State* L) {
|
||||
if (auto wrapper = renderer->blockWraps->get(lua::tointeger(L, 1))) {
|
||||
wrapper->texture = lua::require_string(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg blockwrapslib[] = {
|
||||
{"wrap", lua::wrap<l_wrap>},
|
||||
{"unwrap", lua::wrap<l_unwrap>},
|
||||
{"set_pos", lua::wrap<l_set_pos>},
|
||||
{"set_texture", lua::wrap<l_set_texture>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@@ -57,11 +57,12 @@ static fs::path get_writeable_path(lua::State* L) {
|
||||
fs::path path = resolve_path(rawpath);
|
||||
auto entryPoint = rawpath.substr(0, rawpath.find(':'));
|
||||
if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) {
|
||||
lua::emit_event(L, "core:warning", [=](auto L) {
|
||||
if (lua::getglobal(L, "__vc_warning")) {
|
||||
lua::pushstring(L, "writing to read-only entry point");
|
||||
lua::pushstring(L, entryPoint);
|
||||
return 2;
|
||||
});
|
||||
lua::pushinteger(L, 1);
|
||||
lua::call_nothrow(L, 3);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,24 @@ static int l_move_into(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_line_at(lua::State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
auto position = lua::tointeger(L, 2);
|
||||
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
|
||||
return lua::pushinteger(L, box->getLineAt(position));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_line_pos(lua::State* L) {
|
||||
auto node = getDocumentNode(L, 1);
|
||||
auto line = lua::tointeger(L, 2);
|
||||
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
|
||||
return lua::pushinteger(L, box->getLinePos(line));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_inventory(UINode* node, lua::State* L) {
|
||||
if (auto inventory = dynamic_cast<InventoryView*>(node)) {
|
||||
auto inv = inventory->getInventory();
|
||||
@@ -221,6 +239,13 @@ static int p_get_track_color(UINode* node, lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_text_color(UINode* node, lua::State* L) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
return lua::pushcolor(L, box->getTextColor());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_is_valid(UINode* node, lua::State* L) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
return lua::pushboolean(L, box->validate());
|
||||
@@ -267,6 +292,13 @@ static int p_get_editable(UINode* node, lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_line_numbers(UINode* node, lua::State* L) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
return lua::pushboolean(L, box->isShowLineNumbers());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p_get_src(UINode* node, lua::State* L) {
|
||||
if (auto image = dynamic_cast<Image*>(node)) {
|
||||
return lua::pushstring(L, image->getTexture());
|
||||
@@ -342,6 +374,12 @@ static int p_move_into(UINode*, lua::State* L) {
|
||||
static int p_get_focused(UINode* node, lua::State* L) {
|
||||
return lua::pushboolean(L, node->isFocused());
|
||||
}
|
||||
static int p_get_line_at(UINode*, lua::State* L) {
|
||||
return lua::pushcfunction(L, l_get_line_at);
|
||||
}
|
||||
static int p_get_line_pos(UINode*, lua::State* L) {
|
||||
return lua::pushcfunction(L, l_get_line_pos);
|
||||
}
|
||||
|
||||
static int l_gui_getattr(lua::State* L) {
|
||||
auto docname = lua::require_string(L, 1);
|
||||
@@ -376,6 +414,9 @@ static int l_gui_getattr(lua::State* L) {
|
||||
{"caret", p_get_caret},
|
||||
{"text", p_get_text},
|
||||
{"editable", p_get_editable},
|
||||
{"lineNumbers", p_get_line_numbers},
|
||||
{"lineAt", p_get_line_at},
|
||||
{"linePos", p_get_line_pos},
|
||||
{"src", p_get_src},
|
||||
{"value", p_get_value},
|
||||
{"min", p_get_min},
|
||||
@@ -383,6 +424,7 @@ static int l_gui_getattr(lua::State* L) {
|
||||
{"step", p_get_step},
|
||||
{"trackWidth", p_get_track_width},
|
||||
{"trackColor", p_get_track_color},
|
||||
{"textColor", p_get_text_color},
|
||||
{"checked", p_is_checked},
|
||||
{"page", p_get_page},
|
||||
{"back", p_get_back},
|
||||
@@ -462,6 +504,11 @@ static void p_set_editable(UINode* node, lua::State* L, int idx) {
|
||||
box->setEditable(lua::toboolean(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_line_numbers(UINode* node, lua::State* L, int idx) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
box->setShowLineNumbers(lua::toboolean(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_src(UINode* node, lua::State* L, int idx) {
|
||||
if (auto image = dynamic_cast<Image*>(node)) {
|
||||
image->setTexture(lua::require_string(L, idx));
|
||||
@@ -497,6 +544,11 @@ static void p_set_track_color(UINode* node, lua::State* L, int idx) {
|
||||
bar->setTrackColor(lua::tocolor(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_text_color(UINode* node, lua::State* L, int idx) {
|
||||
if (auto box = dynamic_cast<TextBox*>(node)) {
|
||||
box->setTextColor(lua::tocolor(L, idx));
|
||||
}
|
||||
}
|
||||
static void p_set_checked(UINode* node, lua::State* L, int idx) {
|
||||
if (auto box = dynamic_cast<CheckBox*>(node)) {
|
||||
box->setChecked(lua::toboolean(L, idx));
|
||||
@@ -556,6 +608,7 @@ static int l_gui_setattr(lua::State* L) {
|
||||
{"hint", p_set_hint},
|
||||
{"text", p_set_text},
|
||||
{"editable", p_set_editable},
|
||||
{"lineNumbers", p_set_line_numbers},
|
||||
{"src", p_set_src},
|
||||
{"caret", p_set_caret},
|
||||
{"value", p_set_value},
|
||||
@@ -564,6 +617,7 @@ static int l_gui_setattr(lua::State* L) {
|
||||
{"step", p_set_step},
|
||||
{"trackWidth", p_set_track_width},
|
||||
{"trackColor", p_set_track_color},
|
||||
{"textColor", p_set_text_color},
|
||||
{"checked", p_set_checked},
|
||||
{"page", p_set_page},
|
||||
{"inventory", p_set_inventory},
|
||||
|
||||
@@ -98,7 +98,8 @@ static int l_show_overlay(lua::State* L) {
|
||||
if (layout == nullptr) {
|
||||
throw std::runtime_error("there is no ui layout " + util::quote(name));
|
||||
}
|
||||
hud->showOverlay(layout, playerInventory);
|
||||
auto args = lua::tovalue(L, 3);
|
||||
hud->showOverlay(layout, playerInventory, std::move(args));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -188,4 +189,5 @@ const luaL_Reg hudlib[] = {
|
||||
{"_is_content_access", lua::wrap<l_is_content_access>},
|
||||
{"_set_content_access", lua::wrap<l_set_content_access>},
|
||||
{"_set_debug_cheats", lua::wrap<l_set_debug_cheats>},
|
||||
{NULL, NULL}};
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ static int l_json_stringify(lua::State* L) {
|
||||
|
||||
static int l_json_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto element = json::parse("<string>", string);
|
||||
auto element = json::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,34 @@ static int l_set_noclip(lua::State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_infinite_items(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
return lua::pushboolean(L, player->isInfiniteItems());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_infinite_items(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
player->setInfiniteItems(lua::toboolean(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_instant_destruction(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
return lua::pushboolean(L, player->isInstantDestruction());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_instant_destruction(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
player->setInstantDestruction(lua::toboolean(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_selected_block(lua::State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
if (player->selection.vox.id == BLOCK_VOID) {
|
||||
@@ -220,6 +248,10 @@ const luaL_Reg playerlib[] = {
|
||||
{"set_flight", lua::wrap<l_set_flight>},
|
||||
{"is_noclip", lua::wrap<l_is_noclip>},
|
||||
{"set_noclip", lua::wrap<l_set_noclip>},
|
||||
{"is_infinite_items", lua::wrap<l_is_infinite_items>},
|
||||
{"set_infinite_items", lua::wrap<l_set_infinite_items>},
|
||||
{"is_instant_destruction", lua::wrap<l_is_instant_destruction>},
|
||||
{"set_instant_destruction", lua::wrap<l_set_instant_destruction>},
|
||||
{"get_selected_block", lua::wrap<l_get_selected_block>},
|
||||
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
|
||||
{"set_spawnpoint", lua::wrap<l_set_spawnpoint>},
|
||||
@@ -228,4 +260,5 @@ const luaL_Reg playerlib[] = {
|
||||
{"set_entity", lua::wrap<l_set_entity>},
|
||||
{"get_camera", lua::wrap<l_get_camera>},
|
||||
{"set_camera", lua::wrap<l_set_camera>},
|
||||
{NULL, NULL}};
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ static int l_toml_stringify(lua::State* L) {
|
||||
|
||||
static int l_toml_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto element = toml::parse("<string>", string);
|
||||
auto element = toml::parse("[string]", string);
|
||||
return lua::pushvalue(L, element);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@ static int l_encode(lua::State* L) {
|
||||
return lua::pushlstring(L, bytes, count);
|
||||
}
|
||||
|
||||
static int l_escape(lua::State* L) {
|
||||
auto string = lua::require_lstring(L, 1);
|
||||
return lua::pushstring(L, util::escape(string));
|
||||
}
|
||||
|
||||
const luaL_Reg utf8lib[] = {
|
||||
{"tobytes", lua::wrap<l_tobytes>},
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
@@ -103,5 +108,6 @@ const luaL_Reg utf8lib[] = {
|
||||
{"upper", lua::wrap<l_upper>},
|
||||
{"lower", lua::wrap<l_lower>},
|
||||
{"encode", lua::wrap<l_encode>},
|
||||
{"escape", lua::wrap<l_escape>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ static void remove_lib_funcs(
|
||||
}
|
||||
|
||||
static void create_libs(State* L, StateType stateType) {
|
||||
openlib(L, "base64", base64lib);
|
||||
openlib(L, "bjson", bjsonlib);
|
||||
openlib(L, "block", blocklib);
|
||||
openlib(L, "core", corelib);
|
||||
@@ -143,7 +144,8 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) {
|
||||
init_state(L, stateType);
|
||||
|
||||
auto resDir = paths.getResourcesFolder();
|
||||
auto src = files::read_string(resDir / fs::u8path("scripts/stdmin.lua"));
|
||||
lua::pop(L, lua::execute(L, 0, src, "<stdmin>"));
|
||||
auto file = resDir / fs::u8path("scripts/stdmin.lua");
|
||||
auto src = files::read_string(file);
|
||||
lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua"));
|
||||
return L;
|
||||
}
|
||||
|
||||
@@ -145,10 +145,14 @@ static int l_error_handler(lua_State* L) {
|
||||
if (!isstring(L, 1)) { // 'message' not a string?
|
||||
return 1; // keep it intact
|
||||
}
|
||||
if (get_from(L, "debug", "traceback")) {
|
||||
if (getglobal(L, "__vc__error")) {
|
||||
lua_pushvalue(L, 1); // pass error message
|
||||
lua_pushinteger(L, 2); // skip this function and traceback
|
||||
lua_call(L, 2, 1); // call debug.traceback
|
||||
} if (get_from(L, "debug", "traceback")) {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 2);
|
||||
lua_call(L, 2, 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -172,8 +176,13 @@ int lua::call_nothrow(State* L, int argc, int nresults) {
|
||||
pushcfunction(L, l_error_handler);
|
||||
insert(L, handler_pos);
|
||||
if (lua_pcall(L, argc, LUA_MULTRET, handler_pos)) {
|
||||
log_error(tostring(L, -1));
|
||||
pop(L);
|
||||
auto errorstr = tostring(L, -1);
|
||||
if (errorstr) {
|
||||
log_error(errorstr);
|
||||
pop(L);
|
||||
} else {
|
||||
log_error("");
|
||||
}
|
||||
remove(L, handler_pos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -240,6 +240,11 @@ namespace lua {
|
||||
inline const char* tostring(lua::State* L, int idx) {
|
||||
return lua_tostring(L, idx);
|
||||
}
|
||||
inline std::string_view tolstring(lua::State* L, int idx) {
|
||||
size_t len = 0;
|
||||
auto string = lua_tolstring(L, idx, &len);
|
||||
return std::string_view(string, len);
|
||||
}
|
||||
inline const void* topointer(lua::State* L, int idx) {
|
||||
return lua_topointer(L, idx);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ void scripting::load_script(const fs::path& name, bool throwable) {
|
||||
fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name;
|
||||
std::string src = files::read_string(file);
|
||||
auto L = lua::get_main_state();
|
||||
lua::loadbuffer(L, 0, src, file.u8string());
|
||||
lua::loadbuffer(L, 0, src, "core:scripts/"+name.u8string());
|
||||
if (throwable) {
|
||||
lua::call(L, 0, 0);
|
||||
} else {
|
||||
@@ -53,11 +53,14 @@ void scripting::load_script(const fs::path& name, bool throwable) {
|
||||
}
|
||||
|
||||
int scripting::load_script(
|
||||
int env, const std::string& type, const fs::path& file
|
||||
int env,
|
||||
const std::string& type,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
std::string src = files::read_string(file);
|
||||
logger.info() << "script (" << type << ") " << file.u8string();
|
||||
return lua::execute(lua::get_main_state(), env, src, file.u8string());
|
||||
return lua::execute(lua::get_main_state(), env, src, fileName);
|
||||
}
|
||||
|
||||
void scripting::initialize(Engine* engine) {
|
||||
@@ -320,6 +323,21 @@ bool scripting::on_block_interact(
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_player_tick(Player* player, int tps) {
|
||||
auto args = [=](lua::State* L) {
|
||||
lua::pushinteger(L, player ? player->getId() : -1);
|
||||
lua::pushinteger(L, tps);
|
||||
return 2;
|
||||
};
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
if (pack->worldfuncsset.onplayertick) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(), packid + ":.playertick", args
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool scripting::on_item_use(Player* player, const ItemDef& item) {
|
||||
std::string name = item.name + ".use";
|
||||
return lua::emit_event(
|
||||
@@ -657,10 +675,11 @@ void scripting::load_block_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "block", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "block", file, fileName));
|
||||
funcsset.init = register_event(env, "init", prefix + ".init");
|
||||
funcsset.update = register_event(env, "on_update", prefix + ".update");
|
||||
funcsset.randupdate =
|
||||
@@ -677,10 +696,11 @@ void scripting::load_item_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "item", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "item", file, fileName));
|
||||
funcsset.init = register_event(env, "init", prefix + ".init");
|
||||
funcsset.on_use = register_event(env, "on_use", prefix + ".use");
|
||||
funcsset.on_use_on_block =
|
||||
@@ -690,12 +710,12 @@ void scripting::load_item_script(
|
||||
}
|
||||
|
||||
void scripting::load_entity_component(
|
||||
const std::string& name, const fs::path& file
|
||||
const std::string& name, const fs::path& file, const std::string& fileName
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
std::string src = files::read_string(file);
|
||||
logger.info() << "script (component) " << file.u8string();
|
||||
lua::loadbuffer(L, 0, src, "C!" + name);
|
||||
lua::loadbuffer(L, 0, src, fileName);
|
||||
lua::store_in(L, lua::CHUNKS_TABLE, name);
|
||||
}
|
||||
|
||||
@@ -703,10 +723,11 @@ void scripting::load_world_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
) {
|
||||
int env = *senv;
|
||||
lua::pop(lua::get_main_state(), load_script(env, "world", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "world", file, fileName));
|
||||
register_event(env, "init", prefix + ".init");
|
||||
register_event(env, "on_world_open", prefix + ":.worldopen");
|
||||
register_event(env, "on_world_tick", prefix + ":.worldtick");
|
||||
@@ -718,17 +739,20 @@ void scripting::load_world_script(
|
||||
register_event(env, "on_block_broken", prefix + ":.blockbroken");
|
||||
funcsset.onblockinteract =
|
||||
register_event(env, "on_block_interact", prefix + ":.blockinteract");
|
||||
funcsset.onplayertick =
|
||||
register_event(env, "on_player_tick", prefix + ":.playertick");
|
||||
}
|
||||
|
||||
void scripting::load_layout_script(
|
||||
const scriptenv& senv,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
) {
|
||||
int env = *senv;
|
||||
|
||||
lua::pop(lua::get_main_state(), load_script(env, "layout", file));
|
||||
lua::pop(lua::get_main_state(), load_script(env, "layout", file, fileName));
|
||||
script.onopen = register_event(env, "on_open", prefix + ".open");
|
||||
script.onprogress =
|
||||
register_event(env, "on_progress", prefix + ".progress");
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace scripting {
|
||||
Player* player, const Block& block, const glm::ivec3& pos
|
||||
);
|
||||
bool on_block_interact(Player* player, const Block& block, const glm::ivec3& pos);
|
||||
void on_player_tick(Player* player, int tps);
|
||||
|
||||
/// @brief Called on RMB click with the item selected
|
||||
/// @return true if prevents default action
|
||||
@@ -125,11 +126,13 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param prefix pack id
|
||||
/// @param file item script file
|
||||
/// @param fileName script file path using the engine format
|
||||
/// @param funcsset block callbacks set
|
||||
void load_block_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
);
|
||||
|
||||
@@ -137,15 +140,25 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param prefix pack id
|
||||
/// @param file item script file
|
||||
/// @param fileName script file path using the engine format
|
||||
/// @param funcsset item callbacks set
|
||||
void load_item_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
);
|
||||
|
||||
void load_entity_component(const std::string& name, const fs::path& file);
|
||||
/// @brief Load component script
|
||||
/// @param name component full name (packid:name)
|
||||
/// @param file component script file path
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_entity_component(
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
|
||||
std::unique_ptr<GeneratorScript> load_generator(
|
||||
const GeneratorDef& def,
|
||||
@@ -157,10 +170,12 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param packid content-pack id
|
||||
/// @param file script file path
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_world_script(
|
||||
const scriptenv& env,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
);
|
||||
|
||||
@@ -168,11 +183,13 @@ namespace scripting {
|
||||
/// @param env environment
|
||||
/// @param prefix pack id
|
||||
/// @param file item script file
|
||||
/// @param fileName script file path using the engine format
|
||||
/// @param script document script info
|
||||
void load_layout_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
);
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
namespace scripting {
|
||||
void load_script(const std::filesystem::path& name, bool throwable);
|
||||
|
||||
[[nodiscard]]
|
||||
int load_script(int env, const std::string& type, const std::filesystem::path& file);
|
||||
[[nodiscard]] int load_script(
|
||||
int env,
|
||||
const std::string& type,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,66 +14,66 @@ namespace scripting {
|
||||
runnable create_runnable(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
wstringconsumer create_wstring_consumer(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
wstringsupplier create_wstring_supplier(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
wstringchecker create_wstring_validator(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
boolconsumer create_bool_consumer(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
boolsupplier create_bool_supplier(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
doubleconsumer create_number_consumer(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
doublesupplier create_number_supplier(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
int_array_consumer create_int_array_consumer(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
vec2supplier create_vec2_supplier(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
|
||||
value_to_string_func create_tostring(
|
||||
const scriptenv& env,
|
||||
const std::string& src,
|
||||
const std::string& file = "<string>"
|
||||
const std::string& file = "[string]"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,12 +32,13 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
|
||||
auto L = lua::get_main_state();
|
||||
|
||||
lua::openlib(L, "hud", hudlib);
|
||||
lua::openlib(L, "gfx", "blockwraps", blockwrapslib);
|
||||
lua::openlib(L, "gfx", "particles", particleslib);
|
||||
lua::openlib(L, "gfx", "text3d", text3dlib);
|
||||
|
||||
load_script("hud_classes.lua");
|
||||
|
||||
if (lua::getglobal(L, "__vc_create_hud_rules")) {
|
||||
if (lua::getglobal(L, "__vc_on_hud_open")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
|
||||
@@ -76,13 +77,16 @@ void scripting::on_frontend_close() {
|
||||
}
|
||||
|
||||
void scripting::load_hud_script(
|
||||
const scriptenv& senv, const std::string& packid, const fs::path& file
|
||||
const scriptenv& senv,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
int env = *senv;
|
||||
std::string src = files::read_string(file);
|
||||
logger.info() << "loading script " << file.u8string();
|
||||
|
||||
lua::execute(lua::get_main_state(), env, src, file.u8string());
|
||||
lua::execute(lua::get_main_state(), env, src, fileName);
|
||||
|
||||
register_event(env, "init", packid + ":.init");
|
||||
register_event(env, "on_hud_open", packid + ":.hudopen");
|
||||
|
||||
@@ -22,7 +22,11 @@ namespace scripting {
|
||||
/// @param env environment id
|
||||
/// @param packid content-pack id
|
||||
/// @param file script file path
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_hud_script(
|
||||
const scriptenv &env, const std::string &packid, const fs::path &file
|
||||
const scriptenv& env,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,4 +40,15 @@ struct UVRegion {
|
||||
float h = getHeight();
|
||||
return glm::vec2(u1 + uv.x * w, v1 + uv.y * h);
|
||||
}
|
||||
|
||||
void scale(float x, float y) {
|
||||
float w = getWidth();
|
||||
float h = getHeight();
|
||||
float cx = (u1 + u2) * 0.5f;
|
||||
float cy = (v1 + v2) * 0.5f;
|
||||
u1 = cx - w * 0.5f * x;
|
||||
v1 = cy - h * 0.5f * y;
|
||||
u2 = cx + w * 0.5f * x;
|
||||
v2 = cy + h * 0.5f * y;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,6 +240,22 @@ void Player::setNoclip(bool flag) {
|
||||
this->noclip = flag;
|
||||
}
|
||||
|
||||
bool Player::isInfiniteItems() const {
|
||||
return infiniteItems;
|
||||
}
|
||||
|
||||
void Player::setInfiniteItems(bool flag) {
|
||||
infiniteItems = flag;
|
||||
}
|
||||
|
||||
bool Player::isInstantDestruction() const {
|
||||
return instantDestruction;
|
||||
}
|
||||
|
||||
void Player::setInstantDestruction(bool flag) {
|
||||
instantDestruction = flag;
|
||||
}
|
||||
|
||||
entityid_t Player::getEntity() const {
|
||||
return eid;
|
||||
}
|
||||
@@ -273,6 +289,8 @@ dv::value Player::serialize() const {
|
||||
|
||||
root["flight"] = flight;
|
||||
root["noclip"] = noclip;
|
||||
root["infinite-items"] = infiniteItems;
|
||||
root["instant-destruction"] = instantDestruction;
|
||||
root["chosen-slot"] = chosenSlot;
|
||||
root["entity"] = eid;
|
||||
root["inventory"] = inventory->serialize();
|
||||
@@ -300,6 +318,9 @@ void Player::deserialize(const dv::value& src) {
|
||||
|
||||
flight = src["flight"].asBoolean();
|
||||
noclip = src["noclip"].asBoolean();
|
||||
src.at("infinite-items").get(infiniteItems);
|
||||
src.at("instant-destruction").get(instantDestruction);
|
||||
|
||||
setChosenSlot(src["chosen-slot"].asInteger());
|
||||
eid = src["entity"].asNumber();
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ class Player : public Object, public Serializable {
|
||||
std::shared_ptr<Inventory> inventory;
|
||||
bool flight = false;
|
||||
bool noclip = false;
|
||||
bool infiniteItems = true;
|
||||
bool instantDestruction = true;
|
||||
entityid_t eid;
|
||||
entityid_t selectedEid;
|
||||
public:
|
||||
@@ -86,6 +88,12 @@ public:
|
||||
bool isNoclip() const;
|
||||
void setNoclip(bool flag);
|
||||
|
||||
bool isInfiniteItems() const;
|
||||
void setInfiniteItems(bool flag);
|
||||
|
||||
bool isInstantDestruction() const;
|
||||
void setInstantDestruction(bool flag);
|
||||
|
||||
entityid_t getEntity() const;
|
||||
void setEntity(entityid_t eid);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ dv::value ParticlesPreset::serialize() const {
|
||||
root["acceleration"] = dv::to_value(acceleration);
|
||||
root["explosion"] = dv::to_value(explosion);
|
||||
root["size"] = dv::to_value(size);
|
||||
root["size_spread"] = sizeSpread;
|
||||
root["spawn_spread"] = dv::to_value(size);
|
||||
root["spawn_shape"] = to_string(spawnShape);
|
||||
root["random_sub_uv"] = randomSubUV;
|
||||
@@ -67,6 +68,7 @@ void ParticlesPreset::deserialize(const dv::value& src) {
|
||||
if (src.has("size")) {
|
||||
dv::get_vec(src["size"], size);
|
||||
}
|
||||
src.at("size_spread").get(sizeSpread);
|
||||
if (src.has("spawn_spread")) {
|
||||
dv::get_vec(src["spawn_spread"], spawnSpread);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ struct ParticlesPreset : public Serializable {
|
||||
glm::vec3 explosion {2.0f};
|
||||
/// @brief Particle size
|
||||
glm::vec3 size {0.1f};
|
||||
/// @brief Particles size spread
|
||||
float sizeSpread = 0.2f;
|
||||
/// @brief Spawn spread shape
|
||||
ParticleSpawnShape spawnShape = BALL;
|
||||
/// @brief Spawn spread
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
// TODO: finish
|
||||
std::string util::escape(const std::string& s) {
|
||||
std::string util::escape(std::string_view s) {
|
||||
std::stringstream ss;
|
||||
ss << '"';
|
||||
size_t pos = 0;
|
||||
@@ -318,7 +317,7 @@ std::string util::base64_encode(const ubyte* data, size_t size) {
|
||||
ending[i - fullsegments] = data[i];
|
||||
}
|
||||
size_t trailing = size - fullsegments;
|
||||
{
|
||||
if (trailing) {
|
||||
char output[] = "====";
|
||||
output[0] = B64ABC[(ending[0] & 0b11111100) >> 2];
|
||||
output[1] =
|
||||
@@ -364,8 +363,8 @@ util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
util::Buffer<ubyte> util::base64_decode(const std::string& str) {
|
||||
return base64_decode(str.c_str(), str.size());
|
||||
util::Buffer<ubyte> util::base64_decode(std::string_view str) {
|
||||
return base64_decode(str.data(), str.size());
|
||||
}
|
||||
|
||||
int util::replaceAll(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace util {
|
||||
/// @brief Function used for string serialization in text formats
|
||||
std::string escape(const std::string& s);
|
||||
std::string escape(std::string_view s);
|
||||
|
||||
/// @brief Function used for error messages
|
||||
std::string quote(const std::string& s);
|
||||
@@ -63,7 +63,7 @@ namespace util {
|
||||
|
||||
std::string base64_encode(const ubyte* data, size_t size);
|
||||
util::Buffer<ubyte> base64_decode(const char* str, size_t size);
|
||||
util::Buffer<ubyte> base64_decode(const std::string& str);
|
||||
util::Buffer<ubyte> base64_decode(std::string_view str);
|
||||
|
||||
std::string tohex(uint64_t value);
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ void Block::cloneTo(Block& dst) {
|
||||
dst.inventorySize = inventorySize;
|
||||
dst.tickInterval = tickInterval;
|
||||
dst.overlayTexture = overlayTexture;
|
||||
dst.translucent = translucent;
|
||||
if (particles) {
|
||||
dst.particles = std::make_unique<ParticlesPreset>(*particles);
|
||||
}
|
||||
|
||||
@@ -172,6 +172,9 @@ public:
|
||||
/// @brief Turns off block item generation
|
||||
bool hidden = false;
|
||||
|
||||
/// @brief Block has semi-transparent texture
|
||||
bool translucent = false;
|
||||
|
||||
/// @brief Set of block physical hitboxes
|
||||
std::vector<AABB> hitboxes;
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ void Chunks::set(
|
||||
// block finalization
|
||||
voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
||||
const auto& prevdef = indices->blocks.require(vox.id);
|
||||
if (prevdef.inventorySize == 0) {
|
||||
if (prevdef.inventorySize != 0) {
|
||||
chunk->removeBlockInventory(lx, y, lz);
|
||||
}
|
||||
if (prevdef.rt.extended && !vox.state.segment) {
|
||||
@@ -438,7 +438,7 @@ voxel* Chunks::rayCast(
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter
|
||||
) {
|
||||
) const {
|
||||
float px = start.x;
|
||||
float py = start.y;
|
||||
float pz = start.z;
|
||||
@@ -571,7 +571,7 @@ voxel* Chunks::rayCast(
|
||||
|
||||
glm::vec3 Chunks::rayCastToObstacle(
|
||||
const glm::vec3& start, const glm::vec3& dir, float maxDist
|
||||
) {
|
||||
) const {
|
||||
const float px = start.x;
|
||||
const float py = start.y;
|
||||
const float pz = start.z;
|
||||
|
||||
@@ -102,11 +102,11 @@ public:
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter = {}
|
||||
);
|
||||
) const;
|
||||
|
||||
glm::vec3 rayCastToObstacle(
|
||||
const glm::vec3& start, const glm::vec3& dir, float maxDist
|
||||
);
|
||||
) const;
|
||||
|
||||
const AABB* isObstacleAt(float x, float y, float z) const;
|
||||
|
||||
|
||||
@@ -39,10 +39,10 @@ void ChunksStorage::remove(int x, int z) {
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyLoadedChunk(ContentIndices* indices, Chunk* chunk) {
|
||||
static void check_voxels(const ContentIndices& indices, Chunk* chunk) {
|
||||
for (size_t i = 0; i < CHUNK_VOL; i++) {
|
||||
blockid_t id = chunk->voxels[i].id;
|
||||
if (indices->blocks.get(id) == nullptr) {
|
||||
if (indices.blocks.get(id) == nullptr) {
|
||||
auto logline = logger.error();
|
||||
logline << "corruped block detected at " << i << " of chunk ";
|
||||
logline << chunk->x << "x" << chunk->z;
|
||||
@@ -59,9 +59,26 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
|
||||
auto chunk = std::make_shared<Chunk>(x, z);
|
||||
store(chunk);
|
||||
if (auto data = regions.getVoxels(chunk->x, chunk->z)) {
|
||||
const auto& indices = *level->content->getIndices();
|
||||
|
||||
chunk->decode(data.get());
|
||||
check_voxels(indices, chunk.get());
|
||||
|
||||
auto invs = regions.fetchInventories(chunk->x, chunk->z);
|
||||
auto iterator = invs.begin();
|
||||
while (iterator != invs.end()) {
|
||||
uint index = iterator->first;
|
||||
const auto& def = indices.blocks.require(chunk->voxels[index].id);
|
||||
if (def.inventorySize == 0) {
|
||||
iterator = invs.erase(iterator);
|
||||
continue;
|
||||
}
|
||||
auto& inventory = iterator->second;
|
||||
if (def.inventorySize != inventory->size()) {
|
||||
inventory->resize(def.inventorySize);
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
chunk->setBlockInventories(std::move(invs));
|
||||
|
||||
auto entitiesData = regions.fetchEntities(chunk->x, chunk->z);
|
||||
@@ -74,7 +91,6 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
|
||||
for (auto& entry : chunk->inventories) {
|
||||
level->inventories->store(entry.second);
|
||||
}
|
||||
verifyLoadedChunk(level->content->getIndices(), chunk.get());
|
||||
}
|
||||
if (auto lights = regions.getLights(chunk->x, chunk->z)) {
|
||||
chunk->lightmap.set(lights.get());
|
||||
|
||||
@@ -258,14 +258,14 @@ void Window::pushScissor(glm::vec4 area) {
|
||||
}
|
||||
scissorStack.push(scissorArea);
|
||||
|
||||
area.z += area.x;
|
||||
area.w += area.y;
|
||||
area.z += glm::ceil(area.x);
|
||||
area.w += glm::ceil(area.y);
|
||||
|
||||
area.x = fmax(area.x, scissorArea.x);
|
||||
area.y = fmax(area.y, scissorArea.y);
|
||||
area.x = glm::max(area.x, scissorArea.x);
|
||||
area.y = glm::max(area.y, scissorArea.y);
|
||||
|
||||
area.z = fmin(area.z, scissorArea.z);
|
||||
area.w = fmin(area.w, scissorArea.w);
|
||||
area.z = glm::min(area.z, scissorArea.z);
|
||||
area.w = glm::min(area.w, scissorArea.w);
|
||||
|
||||
if (area.z < 0.0f || area.w < 0.0f) {
|
||||
glScissor(0, 0, 0, 0);
|
||||
@@ -273,8 +273,8 @@ void Window::pushScissor(glm::vec4 area) {
|
||||
glScissor(
|
||||
area.x,
|
||||
Window::height - area.w,
|
||||
std::max(0, int(area.z - area.x)),
|
||||
std::max(0, int(area.w - area.y))
|
||||
std::max(0, static_cast<int>(glm::ceil(area.z - area.x))),
|
||||
std::max(0, static_cast<int>(glm::ceil(area.w - area.y)))
|
||||
);
|
||||
}
|
||||
scissorArea = area;
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ public:
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::shared_ptr<T> getObject(uint64_t id) {
|
||||
std::shared_ptr<T> getObject(uint64_t id) const {
|
||||
static_assert(
|
||||
std::is_base_of<Object, T>::value,
|
||||
"T must be a derived of Object class"
|
||||
|
||||
@@ -148,6 +148,11 @@ std::unique_ptr<Level> World::load(
|
||||
0
|
||||
);
|
||||
player->deserialize(playerMap);
|
||||
auto& inventory = player->getInventory();
|
||||
// invalid inventory id pre 0.25
|
||||
if (inventory->getId() == 0) {
|
||||
inventory->setId(level->getWorld()->getNextInventoryId());
|
||||
}
|
||||
level->inventories->store(player->getInventory());
|
||||
}
|
||||
} else {
|
||||
|
||||
+2
-4
@@ -27,7 +27,7 @@ struct WorldInfo : public Serializable {
|
||||
std::string name;
|
||||
std::string generator;
|
||||
uint64_t seed;
|
||||
int64_t nextInventoryId = 0;
|
||||
int64_t nextInventoryId = 1;
|
||||
|
||||
/// @brief Day/night loop timer in range 0..1 where
|
||||
/// 0.0 - is midnight and
|
||||
@@ -58,8 +58,6 @@ class World {
|
||||
const Content* const content;
|
||||
std::vector<ContentPack> packs;
|
||||
|
||||
int64_t nextInventoryId = 0;
|
||||
|
||||
void writeResources(const Content* content);
|
||||
public:
|
||||
std::shared_ptr<WorldFiles> wfile;
|
||||
@@ -155,7 +153,7 @@ public:
|
||||
/// @brief Get next inventory id and increment it's counter
|
||||
/// @return integer >= 1
|
||||
int64_t getNextInventoryId() {
|
||||
return nextInventoryId++;
|
||||
return info.nextInventoryId++;
|
||||
}
|
||||
|
||||
/// @brief Get current world Content instance
|
||||
|
||||
Reference in New Issue
Block a user