Merge branch 'main' into update-particles

This commit is contained in:
MihailRis
2024-11-19 10:15:42 +03:00
163 changed files with 3071 additions and 1037 deletions
+39 -17
View File
@@ -32,7 +32,7 @@ static debug::Logger logger("assetload-funcs");
namespace fs = std::filesystem;
static bool animation(
static bool load_animation(
Assets* assets,
const ResPaths* paths,
const std::string& atlasName,
@@ -102,8 +102,13 @@ static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
return true;
}
assetload::postfunc assetload::
atlas(AssetsLoader*, const ResPaths* paths, const std::string& directory, const std::string& name, const std::shared_ptr<AssetCfg>&) {
assetload::postfunc assetload::atlas(
AssetsLoader*,
const ResPaths* paths,
const std::string& directory,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
AtlasBuilder builder;
for (const auto& file : paths->listdir(directory)) {
if (!imageio::is_read_supported(file.extension().u8string())) continue;
@@ -115,24 +120,41 @@ assetload::postfunc assetload::
atlas->prepare();
assets->store(std::unique_ptr<Atlas>(atlas), name);
for (const auto& file : names) {
animation(assets, paths, name, directory, file, atlas);
load_animation(assets, paths, name, directory, file, atlas);
}
};
}
assetload::postfunc assetload::
font(AssetsLoader*, const ResPaths* paths, const std::string& filename, const std::string& name, const std::shared_ptr<AssetCfg>&) {
assetload::postfunc assetload::font(
AssetsLoader*,
const ResPaths* paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
auto pages = std::make_shared<std::vector<std::unique_ptr<ImageData>>>();
for (size_t i = 0; i <= 4; i++) {
for (size_t i = 0; i <= 1024; i++) {
std::string pagefile = filename + "_" + std::to_string(i) + ".png";
pagefile = paths->find(pagefile).string();
pages->push_back(imageio::read(pagefile));
auto file = paths->find(pagefile);
if (fs::exists(file)) {
pages->push_back(imageio::read(file.u8string()));
} else if (i == 0) {
throw std::runtime_error("font must have page 0");
} else {
pages->push_back(nullptr);
}
}
return [=](auto assets) {
int res = pages->at(0)->getHeight() / 16;
std::vector<std::unique_ptr<Texture>> textures;
for (auto& page : *pages) {
textures.emplace_back(Texture::from(page.get()));
if (page == nullptr) {
textures.emplace_back(nullptr);
} else {
auto texture = Texture::from(page.get());
texture->setMipMapping(false);
textures.emplace_back(std::move(texture));
}
}
assets->store(
std::make_unique<Font>(std::move(textures), res, 4), name
@@ -150,7 +172,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()
@@ -316,11 +340,9 @@ static TextureAnimation create_animation(
if (elem.second > 0) {
frame.duration = static_cast<float>(elem.second) / 1000.0f;
}
frame.srcPos =
glm::ivec2(
region.u1 * srcWidth, srcHeight - region.v2 * srcHeight
) -
extension;
frame.srcPos = glm::ivec2(
region.u1 * srcWidth, srcHeight - region.v2 * srcHeight
) - extension;
animation.addFrame(frame);
}
return animation;
@@ -338,7 +360,7 @@ inline bool contains(
return false;
}
static bool animation(
static bool load_animation(
Assets* assets,
const ResPaths* paths,
const std::string& atlasName,
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
);
}
+7 -5
View File
@@ -6,7 +6,7 @@
#include <string>
inline constexpr int ENGINE_VERSION_MAJOR = 0;
inline constexpr int ENGINE_VERSION_MINOR = 24;
inline constexpr int ENGINE_VERSION_MINOR = 25;
#ifdef NDEBUG
inline constexpr bool ENGINE_DEBUG_BUILD = false;
@@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false;
inline constexpr bool ENGINE_DEBUG_BUILD = true;
#endif // NDEBUG
inline const std::string ENGINE_VERSION_STRING = "0.24";
inline const std::string ENGINE_VERSION_STRING = "0.25";
/// @brief world regions format version
inline constexpr uint REGION_FORMAT_VERSION = 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";
+25 -4
View File
@@ -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
+12 -1
View File
@@ -76,8 +76,19 @@ ContentPack ContentPack::read(const fs::path& folder) {
root.at("id").get(pack.id);
root.at("title").get(pack.title);
root.at("version").get(pack.version);
root.at("creator").get(pack.creator);
if (root.has("creators")) {
const auto& creators = root["creators"];
for (int i = 0; i < creators.size(); i++) {
if (i > 0) {
pack.creator += ", ";
}
pack.creator += creators[i].asString();
}
} else {
root.at("creator").get(pack.creator);
}
root.at("description").get(pack.description);
root.at("source").get(pack.source);
pack.folder = folder;
if (auto found = root.at("dependencies")) {
+1
View File
@@ -44,6 +44,7 @@ struct ContentPack {
std::string description = "no description";
fs::path folder;
std::vector<DependencyPack> dependencies;
std::string source = "";
fs::path getContentFile() const;
+1 -1
View File
@@ -127,7 +127,7 @@ static VoxelStructureMeta load_structure_meta(
) {
VoxelStructureMeta meta;
meta.name = name;
config.at("lowering").get(meta.lowering);
return meta;
}
+8 -8
View File
@@ -11,25 +11,25 @@
#include "maths/UVRegion.hpp"
#include "voxels/Block.hpp"
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets)
ContentGfxCache::ContentGfxCache(const Content* content, const Assets& assets)
: content(content) {
auto indices = content->getIndices();
sideregions = std::make_unique<UVRegion[]>(indices->blocks.count() * 6);
auto atlas = assets->get<Atlas>("blocks");
const auto& atlas = assets.require<Atlas>("blocks");
const auto& blocks = indices->blocks.getIterable();
for (blockid_t i = 0; i < blocks.size(); i++) {
auto def = blocks[i];
for (uint side = 0; side < 6; side++) {
const std::string& tex = def->textureFaces[side];
if (atlas->has(tex)) {
sideregions[i * 6 + side] = atlas->get(tex);
} else if (atlas->has(TEXTURE_NOTFOUND)) {
sideregions[i * 6 + side] = atlas->get(TEXTURE_NOTFOUND);
if (atlas.has(tex)) {
sideregions[i * 6 + side] = atlas.get(tex);
} else if (atlas.has(TEXTURE_NOTFOUND)) {
sideregions[i * 6 + side] = atlas.get(TEXTURE_NOTFOUND);
}
}
if (def->model == BlockModel::custom) {
auto model = assets->require<model::Model>(def->modelName);
auto model = assets.require<model::Model>(def->modelName);
// temporary dirty fix tbh
if (def->modelName.find(':') == std::string::npos) {
for (auto& mesh : model.meshes) {
@@ -37,7 +37,7 @@ ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets)
if (pos == std::string::npos) {
continue;
}
if (auto region = atlas->getIf(mesh.texture.substr(pos+1))) {
if (auto region = atlas.getIf(mesh.texture.substr(pos+1))) {
for (auto& vertex : mesh.vertices) {
vertex.uv = region->apply(vertex.uv);
}
+2 -2
View File
@@ -22,7 +22,7 @@ class ContentGfxCache {
std::unique_ptr<UVRegion[]> sideregions;
std::unordered_map<blockid_t, model::Model> models;
public:
ContentGfxCache(const Content* content, Assets* assets);
ContentGfxCache(const Content* content, const Assets& assets);
~ContentGfxCache();
inline const UVRegion& getRegion(blockid_t id, int side) const {
@@ -30,6 +30,6 @@ public:
}
const model::Model& getModel(blockid_t id) const;
const Content* getContent() const;
};
+21 -14
View File
@@ -14,25 +14,28 @@
#include "world/Level.hpp"
LevelFrontend::LevelFrontend(
Player* currentPlayer, LevelController* controller, Assets* assets
) : level(controller->getLevel()),
Player* currentPlayer, LevelController* controller, Assets& assets
) : level(*controller->getLevel()),
controller(controller),
assets(assets),
contentCache(std::make_unique<ContentGfxCache>(level->content, assets))
contentCache(std::make_unique<ContentGfxCache>(level.content, assets))
{
assets->store(
BlocksPreview::build(contentCache.get(), assets, level->content),
assets.store(
BlocksPreview::build(
*contentCache, assets, *level.content->getIndices()
),
"block-previews"
);
controller->getBlocksController()->listenBlockInteraction(
[=](auto player, const auto& pos, const auto& def, BlockInteraction type) {
auto material = level->content->findBlockMaterial(def.material);
[currentPlayer, controller, &assets](auto player, const auto& pos, const auto& def, BlockInteraction type) {
const auto& level = *controller->getLevel();
auto material = level.content->findBlockMaterial(def.material);
if (material == nullptr) {
return;
}
if (type == BlockInteraction::step) {
auto sound = assets->get<audio::Sound>(material->stepsSound);
auto sound = assets.get<audio::Sound>(material->stepsSound);
glm::vec3 pos {};
auto soundsCamera = currentPlayer->currentCamera.get();
if (soundsCamera == currentPlayer->spCamera.get() ||
@@ -58,10 +61,10 @@ LevelFrontend::LevelFrontend(
audio::Sound* sound = nullptr;
switch (type) {
case BlockInteraction::placing:
sound = assets->get<audio::Sound>(material->placeSound);
sound = assets.get<audio::Sound>(material->placeSound);
break;
case BlockInteraction::destruction:
sound = assets->get<audio::Sound>(material->breakSound);
sound = assets.get<audio::Sound>(material->breakSound);
break;
default:
break;
@@ -83,16 +86,20 @@ LevelFrontend::LevelFrontend(
LevelFrontend::~LevelFrontend() = default;
Level* LevelFrontend::getLevel() const {
Level& LevelFrontend::getLevel() {
return level;
}
Assets* LevelFrontend::getAssets() const {
const Level& LevelFrontend::getLevel() const {
return level;
}
const Assets& LevelFrontend::getAssets() const {
return assets;
}
ContentGfxCache* LevelFrontend::getContentGfxCache() const {
return contentCache.get();
const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
return *contentCache;
}
LevelController* LevelFrontend::getController() const {
+7 -6
View File
@@ -9,16 +9,17 @@ class ContentGfxCache;
class LevelController;
class LevelFrontend {
Level* level;
Level& level;
LevelController* controller;
Assets* assets;
const Assets& assets;
std::unique_ptr<ContentGfxCache> contentCache;
public:
LevelFrontend(Player* currentPlayer, LevelController* controller, Assets* assets);
LevelFrontend(Player* currentPlayer, LevelController* controller, Assets& assets);
~LevelFrontend();
Level* getLevel() const;
Assets* getAssets() const;
ContentGfxCache* getContentGfxCache() const;
Level& getLevel();
const Level& getLevel() const;
const Assets& getAssets() const;
const ContentGfxCache& getContentGfxCache() const;
LevelController* getController() const;
};
+13 -4
View File
@@ -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();
}
+9 -2
View File
@@ -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
);
};
+34 -32
View File
@@ -11,6 +11,7 @@
#include "graphics/ui/elements/InputBindBox.hpp"
#include "graphics/render/WorldRenderer.hpp"
#include "graphics/render/ParticlesRenderer.hpp"
#include "graphics/render/ChunksRenderer.hpp"
#include "logic/scripting/scripting.hpp"
#include "objects/Player.hpp"
#include "objects/Entities.hpp"
@@ -41,8 +42,8 @@ static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
// TODO: move to xml finally
std::shared_ptr<UINode> create_debug_panel(
Engine* engine,
Level* level,
Player* player,
Level& level,
Player& player,
bool allowDebugCheats
) {
auto panel = std::make_shared<Panel>(glm::vec2(300, 200), glm::vec4(5.0f), 2.0f);
@@ -93,57 +94,58 @@ std::shared_ptr<UINode> create_debug_panel(
L" emitters: " +
std::to_wstring(ParticlesRenderer::aliveEmitters);
}));
panel->add(create_label([=]() {
return L"chunks: "+std::to_wstring(level->chunks->getChunksCount())+
L" visible: "+std::to_wstring(level->chunks->visible);
panel->add(create_label([&]() {
return L"chunks: "+std::to_wstring(level.chunks->getChunksCount())+
L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks);
}));
panel->add(create_label([=]() {
return L"entities: "+std::to_wstring(level->entities->size())+L" next: "+
std::to_wstring(level->entities->peekNextID());
panel->add(create_label([&]() {
return L"entities: "+std::to_wstring(level.entities->size())+L" next: "+
std::to_wstring(level.entities->peekNextID());
}));
panel->add(create_label([=]() {
const auto& vox = player->selection.vox;
panel->add(create_label([&]() -> std::wstring {
const auto& vox = player.selection.vox;
std::wstringstream stream;
stream << "r:" << vox.state.rotation << " s:"
<< std::bitset<3>(vox.state.segment) << " u:"
<< std::bitset<8>(vox.state.userbits);
if (vox.id == BLOCK_VOID) {
return std::wstring {L"block: -"};
return L"block: -";
} else {
return L"block: "+std::to_wstring(vox.id)+
L" "+stream.str();
}
}));
panel->add(create_label([=]() -> std::wstring {
const auto& vox = player->selection.vox;
panel->add(create_label([&]() -> std::wstring {
const auto& selection = player.selection;
const auto& vox = selection.vox;
if (vox.id == BLOCK_VOID) {
return L"x: - y: - z: -";
}
return L"x: " + std::to_wstring(player->selection.actualPosition.x) +
L" y: " + std::to_wstring(player->selection.actualPosition.y) +
L" z: " + std::to_wstring(player->selection.actualPosition.z);
return L"x: " + std::to_wstring(selection.actualPosition.x) +
L" y: " + std::to_wstring(selection.actualPosition.y) +
L" z: " + std::to_wstring(selection.actualPosition.z);
}));
panel->add(create_label([=]() {
auto eid = player->getSelectedEntity();
panel->add(create_label([&]() {
auto eid = player.getSelectedEntity();
if (eid == ENTITY_NONE) {
return std::wstring {L"entity: -"};
} else if (auto entity = level->entities->get(eid)) {
} else if (auto entity = level.entities->get(eid)) {
return L"entity: "+util::str2wstr_utf8(entity->getDef().name)+
L" uid: "+std::to_wstring(entity->getUID());
} else {
return std::wstring {L"entity: error (invalid UID)"};
}
}));
panel->add(create_label([=](){
auto* indices = level->content->getIndices();
if (auto def = indices->blocks.get(player->selection.vox.id)) {
panel->add(create_label([&](){
auto* indices = level.content->getIndices();
if (auto def = indices->blocks.get(player.selection.vox.id)) {
return L"name: " + util::str2wstr_utf8(def->name);
} else {
return std::wstring {L"name: void"};
}
}));
panel->add(create_label([=](){
return L"seed: "+std::to_wstring(level->getWorld()->getSeed());
panel->add(create_label([&](){
return L"seed: "+std::to_wstring(level.getWorld()->getSeed());
}));
for (int ax = 0; ax < 3; ax++) {
@@ -160,22 +162,22 @@ std::shared_ptr<UINode> create_debug_panel(
// Coord input
auto box = std::make_shared<TextBox>(L"");
auto boxRef = box.get();
box->setTextSupplier([=]() {
return util::to_wstring(player->getPosition()[ax], 2);
box->setTextSupplier([&player, ax]() {
return util::to_wstring(player.getPosition()[ax], 2);
});
if (allowDebugCheats) {
box->setTextConsumer([=](const std::wstring& text) {
box->setTextConsumer([&player, ax](const std::wstring& text) {
try {
glm::vec3 position = player->getPosition();
glm::vec3 position = player.getPosition();
position[ax] = std::stoi(text);
player->teleport(position);
player.teleport(position);
} catch (std::exception& _){
}
});
}
box->setOnEditStart([=]() {
box->setOnEditStart([&player, boxRef, ax]() {
boxRef->setText(
std::to_wstring(static_cast<int>(player->getPosition()[ax]))
std::to_wstring(static_cast<int>(player.getPosition()[ax]))
);
});
box->setSize(glm::vec2(230, 27));
@@ -183,7 +185,7 @@ std::shared_ptr<UINode> create_debug_panel(
sub->add(box, glm::vec2(20, 0));
panel->add(sub);
}
auto& worldInfo = level->getWorld()->getInfo();
auto& worldInfo = level.getWorld()->getInfo();
panel->add(create_label([&](){
int hour, minute, second;
timeutil::from_value(worldInfo.daytime, hour, minute, second);
+74 -33
View File
@@ -61,8 +61,8 @@ bool Hud::showGeneratorMinimap = false;
// implemented in debug_panel.cpp
extern std::shared_ptr<UINode> create_debug_panel(
Engine* engine,
Level* level,
Player* player,
Level& level,
Player& player,
bool allowDebugCheats
);
@@ -104,8 +104,7 @@ std::shared_ptr<UINode> HudElement::getNode() const {
}
std::shared_ptr<InventoryView> Hud::createContentAccess() {
auto level = frontend->getLevel();
auto content = level->content;
auto content = frontend.getLevel().content;
auto indices = content->getIndices();
auto inventory = player->getInventory();
@@ -134,7 +133,7 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
std::shared_ptr<InventoryView> Hud::createHotbar() {
auto inventory = player->getInventory();
auto content = frontend->getLevel()->content;
auto content = frontend.getLevel().content;
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
InventoryBuilder builder;
@@ -149,7 +148,7 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
static constexpr uint WORLDGEN_IMG_SIZE = 128U;
Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player)
: engine(engine),
assets(engine->getAssets()),
gui(engine->getGUI()),
@@ -178,7 +177,7 @@ Hud::Hud(Engine* engine, LevelFrontend* frontend, Player* player)
uicamera->flipped = true;
debugPanel = create_debug_panel(
engine, frontend->getLevel(), player, allowDebugCheats
engine, frontend.getLevel(), *player, allowDebugCheats
);
debugPanel->setZIndex(2);
gui->add(debugPanel);
@@ -231,7 +230,11 @@ void Hud::processInput(bool visible) {
}
}
if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
showOverlay(assets->get<UiDocument>("core:console"), false);
showOverlay(
assets->get<UiDocument>("core:console"),
false,
std::string("console")
);
}
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
setPause(true);
@@ -273,9 +276,9 @@ void Hud::updateHotbarControl() {
}
void Hud::updateWorldGenDebugVisualization() {
auto level = frontend->getLevel();
auto& level = frontend.getLevel();
auto generator =
frontend->getController()->getChunksController()->getGenerator();
frontend.getController()->getChunksController()->getGenerator();
auto debugInfo = generator->createDebugInfo();
int width = debugImgWorldGen->getWidth();
@@ -298,9 +301,9 @@ void Hud::updateWorldGenDebugVisualization() {
int az = z - (height - areaHeight) / 2;
data[(flippedZ * width + x) * 4 + 1] =
level->chunks->getChunk(ax + ox, az + oz) ? 255 : 0;
level.chunks->getChunk(ax + ox, az + oz) ? 255 : 0;
data[(flippedZ * width + x) * 4 + 0] =
level->chunksStorage->get(ax + ox, az + oz) ? 255 : 0;
level.chunksStorage->get(ax + ox, az + oz) ? 255 : 0;
if (ax < 0 || az < 0 ||
ax >= areaWidth || az >= areaHeight) {
@@ -321,7 +324,7 @@ void Hud::updateWorldGenDebugVisualization() {
}
void Hud::update(bool visible) {
auto level = frontend->getLevel();
const auto& level = frontend.getLevel();
auto menu = gui->getMenu();
debugPanel->setVisible(player->debug && visible);
@@ -341,7 +344,7 @@ void Hud::update(bool visible) {
}
if (blockUI) {
voxel* vox = level->chunks->get(blockPos.x, blockPos.y, blockPos.z);
voxel* vox = level.chunks->get(blockPos.x, blockPos.y, blockPos.z);
if (vox == nullptr || vox->id != currentblockid) {
closeInventory();
}
@@ -375,8 +378,7 @@ void Hud::update(bool visible) {
/// @brief Show inventory on the screen and turn on inventory mode blocking movement
void Hud::openInventory() {
auto level = frontend->getLevel();
auto content = level->content;
auto content = frontend.getLevel().content;
showExchangeSlot();
inventoryOpen = true;
@@ -388,6 +390,39 @@ void Hud::openInventory() {
add(HudElement(hud_element_mode::inventory_bound, nullptr, exchangeSlot, false));
}
void Hud::openInventory(
UiDocument* doc,
std::shared_ptr<Inventory> inv,
bool playerInventory
) {
if (inv == nullptr) {
// why try to open nox-existent inventory??
return;
}
if (isInventoryOpen()) {
closeInventory();
}
const auto& level = frontend.getLevel();
auto content = level.content;
secondInvView = std::dynamic_pointer_cast<InventoryView>(doc->getRoot());
if (secondInvView == nullptr) {
throw std::runtime_error("secondary UI root element must be 'inventory'");
}
secondUI = secondInvView;
if (playerInventory) {
openInventory();
} else {
inventoryOpen = true;
}
if (inv == nullptr) {
inv = level.inventories->createVirtual(secondInvView->getSlotsCount());
}
secondInvView->bind(inv, content);
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
}
void Hud::openInventory(
glm::ivec3 block,
UiDocument* doc,
@@ -397,8 +432,8 @@ void Hud::openInventory(
if (isInventoryOpen()) {
closeInventory();
}
auto level = frontend->getLevel();
auto content = level->content;
auto& level = frontend.getLevel();
auto content = level.content;
blockUI = std::dynamic_pointer_cast<InventoryView>(doc->getRoot());
if (blockUI == nullptr) {
throw std::runtime_error("block UI root element must be 'inventory'");
@@ -410,19 +445,19 @@ void Hud::openInventory(
inventoryOpen = true;
}
if (blockinv == nullptr) {
blockinv = level->inventories->createVirtual(blockUI->getSlotsCount());
blockinv = level.inventories->createVirtual(blockUI->getSlotsCount());
}
level->chunks->getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true;
level.chunks->getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true;
blockUI->bind(blockinv, content);
blockPos = block;
currentblockid = level->chunks->get(block.x, block.y, block.z)->id;
currentblockid = level.chunks->get(block.x, block.y, block.z)->id;
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
}
void Hud::showExchangeSlot() {
auto level = frontend->getLevel();
auto content = level->content;
exchangeSlotInv = level->inventories->createVirtual(1);
auto& level = frontend.getLevel();
auto content = level.content;
exchangeSlotInv = level.inventories->createVirtual(1);
exchangeSlot = std::make_shared<SlotView>(
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr)
);
@@ -434,7 +469,9 @@ void Hud::showExchangeSlot() {
}
void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
void Hud::showOverlay(
UiDocument* doc, bool playerInventory, const dv::value& arg
) {
if (isInventoryOpen()) {
closeInventory();
}
@@ -445,7 +482,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),
arg);
}
void Hud::openPermanent(UiDocument* doc) {
@@ -454,7 +492,7 @@ void Hud::openPermanent(UiDocument* doc) {
auto invview = std::dynamic_pointer_cast<InventoryView>(root);
if (invview) {
invview->bind(player->getInventory(), frontend->getLevel()->content);
invview->bind(player->getInventory(), frontend.getLevel().content);
}
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
}
@@ -477,13 +515,13 @@ void Hud::closeInventory() {
cleanup();
}
void Hud::add(const HudElement& element) {
void Hud::add(const HudElement& element, const dv::value& arg) {
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;
std::vector<dv::value> args {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]));
@@ -540,7 +578,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);
@@ -584,8 +622,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
));
}
}
@@ -649,7 +690,7 @@ void Hud::setDebugCheats(bool flag) {
gui->remove(debugPanel);
debugPanel = create_debug_panel(
engine, frontend->getLevel(), player, allowDebugCheats
engine, frontend.getLevel(), *player, allowDebugCheats
);
debugPanel->setZIndex(2);
gui->add(debugPanel);
+20 -4
View File
@@ -2,6 +2,7 @@
#include "typedefs.hpp"
#include "util/ObjectsKeeper.hpp"
#include "data/dv.hpp"
#include <string>
#include <memory>
@@ -73,7 +74,7 @@ class Hud : public util::ObjectsKeeper {
Assets* assets;
std::unique_ptr<Camera> uicamera;
gui::GUI* gui;
LevelFrontend* frontend;
LevelFrontend& frontend;
Player* player;
/// @brief Is any overlay/inventory open
@@ -102,6 +103,8 @@ class Hud : public util::ObjectsKeeper {
std::shared_ptr<gui::InventoryView> inventoryView = nullptr;
/// @brief Block inventory view
std::shared_ptr<gui::InventoryView> blockUI = nullptr;
/// @brief Secondary inventory view
std::shared_ptr<gui::InventoryView> secondInvView = nullptr;
/// @brief Position of the block open
glm::ivec3 blockPos {};
/// @brief Id of the block open (used to detect block destruction or replacement)
@@ -128,7 +131,7 @@ class Hud : public util::ObjectsKeeper {
void showExchangeSlot();
void updateWorldGenDebugVisualization();
public:
Hud(Engine* engine, LevelFrontend* frontend, Player* player);
Hud(Engine* engine, LevelFrontend& frontend, Player* player);
~Hud();
void update(bool hudVisible);
@@ -145,6 +148,16 @@ public:
/// @brief Show player inventory in inventory-mode
void openInventory();
/// @brief Show inventory in inventory-mode
/// @param doc ui layout
/// @param inv inventory
/// @param playerInventory show player inventory too
void openInventory(
UiDocument* doc,
std::shared_ptr<Inventory> inv,
bool playerInventory
);
/// @brief Show block inventory in inventory-mode
/// @param block block position
@@ -161,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();
@@ -170,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);
+5 -2
View File
@@ -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);
+16 -8
View File
@@ -36,18 +36,21 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> levelPtr)
Level* level = levelPtr.get();
auto& settings = engine->getSettings();
auto assets = engine->getAssets();
auto& assets = *engine->getAssets();
auto menu = engine->getGUI()->getMenu();
menu->reset();
controller = std::make_unique<LevelController>(engine, std::move(levelPtr));
frontend = std::make_unique<LevelFrontend>(controller->getPlayer(), controller.get(), assets);
worldRenderer = std::make_unique<WorldRenderer>(engine, frontend.get(), controller->getPlayer());
hud = std::make_unique<Hud>(engine, frontend.get(), controller->getPlayer());
frontend = std::make_unique<LevelFrontend>(
controller->getPlayer(), controller.get(), assets
);
worldRenderer = std::make_unique<WorldRenderer>(
engine, *frontend, controller->getPlayer()
);
hud = std::make_unique<Hud>(engine, *frontend, controller->getPlayer());
decorator = std::make_unique<Decorator>(
*controller, *worldRenderer->particles, *assets
*controller, *worldRenderer->particles, assets
);
keepAlive(settings.graphics.backlight.observe([=](bool) {
@@ -63,7 +66,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> levelPtr)
}));
animator = std::make_unique<TextureAnimator>();
animator->addAnimations(assets->getAnimations());
animator->addAnimations(assets.getAnimations());
initializeContent();
}
@@ -80,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"
);
}
}
+1 -1
View File
@@ -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}
};
+25 -1
View File
@@ -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}
};
@@ -117,6 +117,22 @@ void Batch3D::texture(const Texture* new_texture){
new_texture->bind();
}
void Batch3D::sprite(
const glm::vec3& pos,
const glm::vec3& up,
const glm::vec3& right,
float w,
float h,
int atlasRes,
int index,
const glm::vec4& tint
) {
float scale = 1.0f / static_cast<float>(atlasRes);
float u = (index % atlasRes) * scale;
float v = 1.0f - ((index / atlasRes) * scale) - scale;
sprite(pos, up, right, w, h, UVRegion(u, v, u+scale, v+scale), tint);
}
void Batch3D::sprite(
const glm::vec3& pos,
const glm::vec3& up,
@@ -272,3 +288,11 @@ void Batch3D::flushPoints() {
mesh->draw(GL_POINTS);
index = 0;
}
void Batch3D::setColor(const glm::vec4& color) {
tint = color;
}
const glm::vec4& Batch3D::getColor() const {
return tint;
}
+14
View File
@@ -17,6 +17,7 @@ class Batch3D : public Flushable {
std::unique_ptr<Mesh> mesh;
std::unique_ptr<Texture> blank;
size_t index;
glm::vec4 tint {1.0f};
const Texture* currentTexture;
@@ -57,6 +58,16 @@ public:
const UVRegion& uv,
const glm::vec4& tint
);
void sprite(
const glm::vec3& pos,
const glm::vec3& up,
const glm::vec3& right,
float w,
float h,
int atlasRes,
int index,
const glm::vec4& tint
);
void xSprite(
float w,
float h,
@@ -81,4 +92,7 @@ public:
void point(const glm::vec3& pos, const glm::vec4& tint);
void flush() override;
void flushPoints();
void setColor(const glm::vec4& color);
const glm::vec4& getColor() const;
};
+2 -1
View File
@@ -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++;
}
+1 -1
View File
@@ -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);
};
+99 -42
View File
@@ -3,6 +3,8 @@
#include <utility>
#include "Texture.hpp"
#include "Batch2D.hpp"
#include "Batch3D.hpp"
#include "window/Camera.hpp"
inline constexpr uint GLYPH_SIZE = 16;
inline constexpr uint MAX_CODEPAGES = 10000; // idk ho many codepages unicode has
@@ -35,71 +37,126 @@ bool Font::isPrintableChar(uint codepoint) const {
}
}
int Font::calcWidth(const std::wstring& text, size_t length) {
int Font::calcWidth(const std::wstring& text, size_t length) const {
return calcWidth(text, 0, length);
}
int Font::calcWidth(const std::wstring& text, size_t offset, size_t length) {
return std::min(text.length()-offset, length) * 8;
int Font::calcWidth(const std::wstring& text, size_t offset, size_t length) const {
return std::min(text.length()-offset, length) * glyphInterval;
}
void Font::draw(Batch2D* batch, std::wstring text, int x, int y) {
draw(batch, std::move(text), x, y, FontStyle::none);
static inline void draw_glyph(
Batch2D& batch,
const glm::vec3& pos,
const glm::vec2& offset,
uint c,
const glm::vec3& right,
const glm::vec3& up,
float glyphInterval
) {
batch.sprite(
pos.x + offset.x * right.x,
pos.y + offset.y * right.y,
right.x / glyphInterval,
up.y,
16,
c,
batch.getColor()
);
}
static inline void drawGlyph(Batch2D* batch, int x, int y, uint c, FontStyle style) {
switch (style){
case FontStyle::none:
break;
case FontStyle::shadow:
batch->sprite(x+1, y+1, GLYPH_SIZE, GLYPH_SIZE, 16, c, SHADOW_TINT);
break;
case FontStyle::outline:
for (int oy = -1; oy <= 1; oy++){
for (int ox = -1; ox <= 1; ox++){
if (ox || oy) {
batch->sprite(x+ox, y+oy, GLYPH_SIZE, GLYPH_SIZE, 16, c, SHADOW_TINT);
}
}
}
break;
}
batch->sprite(x, y, GLYPH_SIZE, GLYPH_SIZE, 16, c, batch->getColor());
static inline void draw_glyph(
Batch3D& batch,
const glm::vec3& pos,
const glm::vec2& offset,
uint c,
const glm::vec3& right,
const glm::vec3& up,
float glyphInterval
) {
batch.sprite(
pos + right * offset.x + up * offset.y,
up, right / glyphInterval,
0.5f,
0.5f,
16,
c,
batch.getColor()
);
}
void Font::draw(Batch2D* batch, const std::wstring& text, int x, int y, FontStyle style) {
draw(batch, std::wstring_view(text.c_str(), text.length()), x, y, style);
}
void Font::draw(Batch2D* batch, std::wstring_view text, int x, int y, FontStyle style) {
template <class Batch>
static inline void draw_text(
const Font& font,
Batch& batch,
std::wstring_view text,
const glm::vec3& pos,
const glm::vec3& right,
const glm::vec3& up,
float glyphInterval
) {
uint page = 0;
uint next = MAX_CODEPAGES;
int init_x = x;
int x = 0;
int y = 0;
do {
for (uint c : text){
if (!isPrintableChar(c)) {
x += 8;
if (!font.isPrintableChar(c)) {
x++;
continue;
}
uint charpage = c >> 8;
if (charpage == page){
Texture* texture = nullptr;
if (charpage < pages.size()) {
texture = pages[charpage].get();
}
if (texture == nullptr){
texture = pages[0].get();
}
batch->texture(texture);
drawGlyph(batch, x, y, c, style);
batch.texture(font.getPage(charpage));
draw_glyph(
batch, pos, glm::vec2(x, y), c, right, up, glyphInterval
);
}
else if (charpage > page && charpage < next){
next = charpage;
}
x += 8;//getGlyphWidth(c);
x++;
}
page = next;
next = MAX_CODEPAGES;
x = init_x;
x = 0;
} while (page < MAX_CODEPAGES);
}
const Texture* Font::getPage(int charpage) const {
Texture* texture = nullptr;
if (charpage < pages.size()) {
texture = pages[charpage].get();
}
if (texture == nullptr){
texture = pages[0].get();
}
return texture;
}
void Font::draw(
Batch2D& batch, std::wstring_view text, int x, int y, float scale
) const {
draw_text(
*this, batch, text,
glm::vec3(x, y, 0),
glm::vec3(glyphInterval*scale, 0, 0),
glm::vec3(0, lineHeight*scale, 0),
glyphInterval/static_cast<float>(lineHeight)
);
}
void Font::draw(
Batch3D& batch,
std::wstring_view text,
const glm::vec3& pos,
const glm::vec3& right,
const glm::vec3& up
) const {
draw_text(
*this, batch, text, pos,
right * static_cast<float>(glyphInterval),
up * static_cast<float>(lineHeight),
glyphInterval/static_cast<float>(lineHeight)
);
}
+19 -6
View File
@@ -3,10 +3,13 @@
#include <memory>
#include <string>
#include <vector>
#include <glm/glm.hpp>
#include "typedefs.hpp"
class Texture;
class Batch2D;
class Batch3D;
class Camera;
enum class FontStyle {
none,
@@ -17,8 +20,9 @@ enum class FontStyle {
class Font {
int lineHeight;
int yoffset;
public:
int glyphInterval = 8;
std::vector<std::unique_ptr<Texture>> pages;
public:
Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset);
~Font();
@@ -29,19 +33,28 @@ public:
/// @param text selected text
/// @param length max substring length (default: no limit)
/// @return pixel width of the substring
int calcWidth(const std::wstring& text, size_t length=-1);
int calcWidth(const std::wstring& text, size_t length=-1) const;
/// @brief Calculate text width in pixels
/// @param text selected text
/// @param offset start of the substring
/// @param length max substring length
/// @return pixel width of the substring
int calcWidth(const std::wstring& text, size_t offset, size_t length);
int calcWidth(const std::wstring& text, size_t offset, size_t length) const;
/// @brief Check if character is visible (non-whitespace)
/// @param codepoint character unicode codepoint
bool isPrintableChar(uint codepoint) const;
void draw(Batch2D* batch, std::wstring text, int x, int y);
void draw(Batch2D* batch, const std::wstring& text, int x, int y, FontStyle style);
void draw(Batch2D* batch, std::wstring_view text, int x, int y, FontStyle style);
void draw(Batch2D& batch, std::wstring_view text, int x, int y, float scale=1) const;
void draw(
Batch3D& batch,
std::wstring_view text,
const glm::vec3& pos,
const glm::vec3& right={1, 0, 0},
const glm::vec3& up={0, 1, 0}
) const;
const Texture* getPage(int page) const;
};
+15 -1
View File
@@ -59,7 +59,9 @@ std::unique_ptr<ImageData> GLTexture::readData() {
glBindTexture(GL_TEXTURE_2D, id);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.get());
glBindTexture(GL_TEXTURE_2D, 0);
return std::make_unique<ImageData>(ImageFormat::rgba8888, width, height, data.release());
return std::make_unique<ImageData>(
ImageFormat::rgba8888, width, height, data.release()
);
}
void GLTexture::setNearestFilter() {
@@ -69,6 +71,18 @@ void GLTexture::setNearestFilter() {
glBindTexture(GL_TEXTURE_2D, 0);
}
void GLTexture::setMipMapping(bool flag) {
bind();
if (flag) {
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST
);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
std::unique_ptr<GLTexture> GLTexture::from(const ImageData* image) {
uint width = image->getWidth();
uint height = image->getHeight();
+2
View File
@@ -18,6 +18,8 @@ public:
virtual void reload(const ImageData& image) override;
virtual void setMipMapping(bool flag) override;
virtual std::unique_ptr<ImageData> readData() override;
virtual uint getId() const override;
+1 -1
View File
@@ -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;
+9 -10
View File
@@ -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);
}
+4 -4
View File
@@ -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;
+3 -3
View File
@@ -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)) {}
+1 -1
View File
@@ -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);
}
+2
View File
@@ -34,5 +34,7 @@ public:
virtual uint getId() const = 0;
virtual void setMipMapping(bool flag) = 0;
static std::unique_ptr<Texture> from(const ImageData* image);
};
+33 -34
View File
@@ -18,18 +18,18 @@
#include <glm/ext.hpp>
std::unique_ptr<ImageData> BlocksPreview::draw(
const ContentGfxCache* cache,
Shader* shader,
Framebuffer* fbo,
Batch3D* batch,
const ContentGfxCache& cache,
Shader& shader,
const Framebuffer& fbo,
Batch3D& batch,
const Block& def,
int size
){
Window::clear();
blockid_t id = def.rt.id;
const UVRegion texfaces[6]{cache->getRegion(id, 0), cache->getRegion(id, 1),
cache->getRegion(id, 2), cache->getRegion(id, 3),
cache->getRegion(id, 4), cache->getRegion(id, 5)};
const UVRegion texfaces[6]{cache.getRegion(id, 0), cache.getRegion(id, 1),
cache.getRegion(id, 2), cache.getRegion(id, 3),
cache.getRegion(id, 4), cache.getRegion(id, 5)};
glm::vec3 offset(0.1f, 0.5f, 0.1f);
switch (def.model) {
@@ -37,10 +37,10 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
// something went wrong...
break;
case BlockModel::block:
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
batch->blockCube(glm::vec3(size * 0.63f), texfaces,
glm::vec4(1.0f), !def.rt.emissive);
batch->flush();
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
batch.blockCube(glm::vec3(size * 0.63f), texfaces,
glm::vec4(1.0f), !def.rt.emissive);
batch.flush();
break;
case BlockModel::aabb:
{
@@ -49,39 +49,39 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
hitbox = glm::max(hitbox, box.size());
}
offset = glm::vec3(1, 1, 0.0f);
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
glm::vec3 scaledSize = glm::vec3(size * 0.63f);
batch->cube(
batch.cube(
-hitbox * scaledSize * 0.5f * glm::vec3(1,1,-1),
hitbox * scaledSize,
texfaces, glm::vec4(1.0f),
!def.rt.emissive
);
}
batch->flush();
batch.flush();
break;
case BlockModel::custom:{
glm::vec3 pmul = glm::vec3(size * 0.63f);
glm::vec3 hitbox = glm::vec3(1.0f);
glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f);
offset.y += (1.0f - hitbox).y * 0.5f;
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
const auto& model = cache->getModel(def.rt.id);
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
const auto& model = cache.getModel(def.rt.id);
for (const auto& mesh : model.meshes) {
for (const auto& vertex : mesh.vertices) {
float d = glm::dot(glm::normalize(vertex.normal), glm::vec3(0.2, 0.8, 0.4));
d = 0.8f + d * 0.2f;
batch->vertex((vertex.coord - poff)*pmul, vertex.uv, glm::vec4(d, d, d, 1.0f));
batch.vertex((vertex.coord - poff)*pmul, vertex.uv, glm::vec4(d, d, d, 1.0f));
}
batch->flush();
batch.flush();
}
break;
}
case BlockModel::xsprite: {
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
glm::vec3 right = glm::normalize(glm::vec3(1.f, 0.f, -1.f));
batch->sprite(
batch.sprite(
right*float(size)*0.43f+glm::vec3(0, size*0.4f, 0),
glm::vec3(0.f, 1.f, 0.f),
right,
@@ -89,24 +89,23 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
texfaces[0],
glm::vec4(1.0f)
);
batch->flush();
batch.flush();
break;
}
}
return fbo->getTexture()->readData();
return fbo.getTexture()->readData();
}
std::unique_ptr<Atlas> BlocksPreview::build(
const ContentGfxCache* cache,
Assets* assets,
const Content* content
const ContentGfxCache& cache,
const Assets& assets,
const ContentIndices& indices
) {
auto indices = content->getIndices();
size_t count = indices->blocks.count();
size_t count = indices.blocks.count();
size_t iconSize = ITEM_ICON_SIZE;
auto shader = assets->get<Shader>("ui3d");
auto atlas = assets->get<Atlas>("blocks");
auto& shader = assets.require<Shader>("ui3d");
const auto& atlas = assets.require<Atlas>("blocks");
Viewport viewport(iconSize, iconSize);
DrawContext pctx(nullptr, viewport, nullptr);
@@ -118,8 +117,8 @@ std::unique_ptr<Atlas> BlocksPreview::build(
Batch3D batch(1024);
batch.begin();
shader->use();
shader->uniformMatrix("u_projview",
shader.use();
shader.uniformMatrix("u_projview",
glm::ortho(0.0f, float(iconSize), 0.0f, float(iconSize),
-100.0f, 100.0f) *
glm::lookAt(glm::vec3(0.57735f),
@@ -132,9 +131,9 @@ std::unique_ptr<Atlas> BlocksPreview::build(
fbo.bind();
for (size_t i = 0; i < count; i++) {
auto& def = indices->blocks.require(i);
atlas->getTexture()->bind();
builder.add(def.name, draw(cache, shader, &fbo, &batch, def, iconSize));
auto& def = indices.blocks.require(i);
atlas.getTexture()->bind();
builder.add(def.name, draw(cache, shader, fbo, batch, def, iconSize));
}
fbo.unbind();
+8 -8
View File
@@ -11,23 +11,23 @@ class Atlas;
class Framebuffer;
class Batch3D;
class Block;
class Content;
class ContentIndices;
class Shader;
class ContentGfxCache;
class BlocksPreview {
static std::unique_ptr<ImageData> draw(
const ContentGfxCache* cache,
Shader* shader,
Framebuffer* framebuffer,
Batch3D* batch,
const ContentGfxCache& cache,
Shader& shader,
const Framebuffer& framebuffer,
Batch3D& batch,
const Block& block,
int size
);
public:
static std::unique_ptr<Atlas> build(
const ContentGfxCache* cache,
Assets* assets,
const Content* content
const ContentGfxCache& cache,
const Assets& assets,
const ContentIndices& indices
);
};
+212 -60
View File
@@ -5,23 +5,22 @@
#include "maths/UVRegion.hpp"
#include "constants.hpp"
#include "content/Content.hpp"
#include "voxels/ChunksStorage.hpp"
#include "voxels/Chunks.hpp"
#include "lighting/Lightmap.hpp"
#include "frontend/ContentGfxCache.hpp"
#include "settings.hpp"
#include <glm/glm.hpp>
const uint BlocksRenderer::VERTEX_SIZE = 6;
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
BlocksRenderer::BlocksRenderer(
size_t capacity,
const Content* content,
const ContentGfxCache* cache,
const EngineSettings* settings
const Content& content,
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),
@@ -34,7 +33,7 @@ BlocksRenderer::BlocksRenderer(
CHUNK_W + voxelBufferPadding*2,
CHUNK_H,
CHUNK_D + voxelBufferPadding*2);
blockDefsCache = content->getIndices()->blocks.getDefs();
blockDefsCache = content.getIndices()->blocks.getDefs();
}
BlocksRenderer::~BlocksRenderer() {
@@ -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;
}
@@ -286,29 +285,35 @@ void BlocksRenderer::blockCustomModel(
Z = orient.axisZ;
}
const auto& model = cache->getModel(block->rt.id);
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;
}
int i = 0;
for (const auto& vertex : mesh.vertices) {
auto n =
vertex.normal.x * X + vertex.normal.y * Y + vertex.normal.z * Z;
float d = glm::dot(glm::normalize(n), SUN_VECTOR);
d = 0.8f + d * 0.2f;
const auto& vcoord = vertex.coord - 0.5f;
vertexAO(
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
vertex.uv.x,
vertex.uv.y,
glm::vec4(1, 1, 1, 1),
glm::vec3(1, 0, 0),
glm::vec3(0, 1, 0),
n
);
indexBuffer[indexSize++] = indexOffset++;
for (int triangle = 0; triangle < mesh.vertices.size() / 3; triangle++) {
auto r = mesh.vertices[triangle * 3 + (triangle % 2) * 2].coord -
mesh.vertices[triangle * 3 + 1].coord;
r = glm::normalize(r);
for (int i = 0; i < 3; i++) {
const auto& vertex = mesh.vertices[triangle * 3 + i];
auto n = vertex.normal.x * X + vertex.normal.y * Y +
vertex.normal.z * Z;
float d = glm::dot(n, SUN_VECTOR);
d = 0.8f + d * 0.2f;
const auto& vcoord = vertex.coord - 0.5f;
vertexAO(
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
vertex.uv.x,
vertex.uv.y,
glm::vec4(d, d, d, d),
glm::cross(r, n),
r,
n
);
indexBuffer[indexSize++] = indexOffset++;
}
}
}
}
@@ -427,11 +432,16 @@ glm::vec4 BlocksRenderer::pickSoftLight(
right, up);
}
void BlocksRenderer::render(const voxel* voxels) {
int begin = chunk->bottom * (CHUNK_W * CHUNK_D);
int end = chunk->top * (CHUNK_W * CHUNK_D);
for (const auto drawGroup : *content->drawGroups) {
for (int i = begin; i < end; i++) {
void BlocksRenderer::render(
const voxel* voxels, int beginEnds[256][2]
) {
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;
@@ -439,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);
@@ -480,43 +490,185 @@ void BlocksRenderer::render(const voxel* voxels) {
}
}
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
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;
chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get());
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 ChunksStorage* 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 {
+19 -11
View File
@@ -12,6 +12,7 @@
#include "voxels/VoxelsVolume.hpp"
#include "graphics/core/MeshData.hpp"
#include "maths/util.hpp"
#include "commons.hpp"
class Content;
class Mesh;
@@ -19,15 +20,14 @@ class Block;
class Chunk;
class Chunks;
class VoxelsVolume;
class ChunksStorage;
class Chunks;
class ContentGfxCache;
struct EngineSettings;
struct UVRegion;
class BlocksRenderer {
static const glm::vec3 SUN_VECTOR;
static const uint VERTEX_SIZE;
const Content* const content;
const Content& content;
std::unique_ptr<float[]> vertexBuffer;
std::unique_ptr<int[]> indexBuffer;
size_t vertexOffset;
@@ -40,11 +40,13 @@ class BlocksRenderer {
std::unique_ptr<VoxelsVolume> voxelsBuffer;
const Block* const* blockDefsCache;
const ContentGfxCache* const cache;
const EngineSettings* settings;
const ContentGfxCache& cache;
const EngineSettings& settings;
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,14 +136,21 @@ 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, const Content* content, const ContentGfxCache* cache, const EngineSettings* settings);
BlocksRenderer(
size_t capacity,
const Content& content,
const ContentGfxCache& cache,
const EngineSettings& settings
);
virtual ~BlocksRenderer();
void build(const Chunk* chunk, const ChunksStorage* chunks);
std::shared_ptr<Mesh> render(const Chunk* chunk, const ChunksStorage* chunks);
MeshData createMesh();
void build(const Chunk* chunk, const Chunks* chunks);
ChunkMesh render(const Chunk* chunk, const Chunks* chunks);
ChunkMeshData createMesh();
VoxelsVolume* getVoxelsBuffer() const;
bool isCancelled() const {
+219 -35
View File
@@ -1,32 +1,38 @@
#include "ChunksRenderer.hpp"
#include "BlocksRenderer.hpp"
#include "debug/Logger.hpp"
#include "assets/Assets.hpp"
#include "graphics/core/Mesh.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/Atlas.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp"
#include "world/Level.hpp"
#include "window/Camera.hpp"
#include "maths/FrustumCulling.hpp"
#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;
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
Level* level;
const Level& level;
BlocksRenderer renderer;
public:
RendererWorker(
Level* level,
const ContentGfxCache* cache,
const EngineSettings* settings
const Level& level,
const ContentGfxCache& cache,
const EngineSettings& settings
) : level(level),
renderer(settings->graphics.chunkMaxVertices.get(),
level->content, cache, settings)
renderer(settings.graphics.chunkMaxVertices.get(),
*level.content, cache, settings)
{}
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
renderer.build(chunk.get(), level->chunksStorage.get());
renderer.build(chunk.get(), level.chunks.get());
if (renderer.isCancelled()) {
return RendererResult {
glm::ivec2(chunk->x, chunk->z), true, MeshData()};
@@ -38,24 +44,33 @@ public:
};
ChunksRenderer::ChunksRenderer(
Level* level,
const ContentGfxCache* cache,
const EngineSettings* settings
) : level(level),
const Level* level,
const Assets& assets,
const Frustum& frustum,
const ContentGfxCache& cache,
const EngineSettings& settings
) : level(*level),
assets(assets),
frustum(frustum),
settings(settings),
threadPool(
"chunks-render-pool",
[=](){return std::make_shared<RendererWorker>(level, cache, settings);},
[=](RendererResult& result){
[&](){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())
}, settings.graphics.chunkMaxRenderers.get())
{
threadPool.setStopOnFail(false);
renderer = std::make_unique<BlocksRenderer>(
settings->graphics.chunkMaxVertices.get(),
level->content, cache, settings
settings.graphics.chunkMaxVertices.get(),
*level->content, cache, settings
);
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
}
@@ -63,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->chunksStorage.get());
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
return mesh;
auto mesh = renderer->render(chunk.get(), level.chunks.get());
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()) {
@@ -92,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);
@@ -100,17 +121,180 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>&
if (chunk->flags.modified) {
render(chunk, important);
}
return found->second;
}
std::shared_ptr<Mesh> ChunksRenderer::get(Chunk* chunk) {
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
if (found != meshes.end()) {
return found->second;
}
return nullptr;
return found->second.mesh.get();
}
void ChunksRenderer::update() {
threadPool.update();
}
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 nullptr;
}
float distance = glm::distance(
camera.position,
glm::vec3(
(chunk->x + 0.5f) * CHUNK_W,
camera.position.y,
(chunk->z + 0.5f) * CHUNK_D
)
);
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
if (mesh == nullptr) {
return nullptr;
}
if (culling) {
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
glm::vec3 max(
chunk->x * CHUNK_W + CHUNK_W,
chunk->top,
chunk->z * CHUNK_D + CHUNK_D
);
if (!frustum.isBoxVisible(min, max)) return nullptr;
}
return mesh;
}
void ChunksRenderer::drawChunks(
const Camera& camera, Shader& shader
) {
const auto& chunks = *level.chunks;
const auto& atlas = assets.require<Atlas>("blocks");
atlas.getTexture()->bind();
update();
// [warning] this whole method is not thread-safe for chunks
int chunksWidth = chunks.getWidth();
int chunksOffsetX = chunks.getOffsetX();
int chunksOffsetY = chunks.getOffsetY();
if (indices.size() != chunks.getVolume()) {
indices.clear();
for (int i = 0; i < chunks.getVolume(); i++) {
indices.push_back(ChunksSortEntry {i, 0});
}
}
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
for (auto& index : indices) {
float x = index.index % chunksWidth + chunksOffsetX - px;
float z = index.index / chunksWidth + chunksOffsetY - pz;
index.d = (x * x + z * z) * 1024;
}
util::insertion_sort(indices.begin(), indices.end());
bool culling = settings.graphics.frustumCulling.get();
visibleChunks = 0;
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");
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();
}
}
+45 -12
View File
@@ -4,47 +4,80 @@
#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 Assets;
class Frustum;
class BlocksRenderer;
class ContentGfxCache;
struct EngineSettings;
struct ChunksSortEntry {
int index;
int d;
inline bool operator<(const ChunksSortEntry& o) const noexcept {
return d > o.d;
}
};
struct RendererResult {
glm::ivec2 key;
bool cancelled;
MeshData meshData;
ChunkMeshData meshData;
};
class ChunksRenderer {
Level* level;
std::unique_ptr<BlocksRenderer> renderer;
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
std::unordered_map<glm::ivec2, bool> inwork;
const Level& level;
const Assets& assets;
const Frustum& frustum;
const EngineSettings& settings;
std::unique_ptr<BlocksRenderer> renderer;
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;
const Mesh* retrieveChunk(
size_t index, const Camera& camera, Shader& shader, bool culling
);
public:
ChunksRenderer(
Level* level,
const ContentGfxCache* cache,
const EngineSettings* settings
const Level* level,
const Assets& assets,
const Frustum& frustum,
const ContentGfxCache& cache,
const EngineSettings& settings
);
virtual ~ChunksRenderer();
std::shared_ptr<Mesh> render(const std::shared_ptr<Chunk>& chunk, bool important);
const Mesh* render(
const std::shared_ptr<Chunk>& chunk, bool important
);
void unload(const Chunk* chunk);
void clear();
std::shared_ptr<Mesh> getOrRender(const std::shared_ptr<Chunk>& chunk, bool important);
std::shared_ptr<Mesh> get(Chunk* chunk);
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;
};
+1 -1
View File
@@ -64,6 +64,7 @@ void Emitter::update(
return;
}
}
timer += delta;
const float maxDistance = preset.maxDistance;
if (glm::distance2(position, cameraPosition) > maxDistance * maxDistance) {
if (count > 0) {
@@ -77,7 +78,6 @@ void Emitter::update(
}
return;
}
timer += delta;
while (count && timer > spawnInterval) {
// spawn particle
Particle particle = prototype;
+109
View File
@@ -0,0 +1,109 @@
#include "GuidesRenderer.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include "graphics/core/Shader.hpp"
#include "graphics/core/LineBatch.hpp"
#include "graphics/core/DrawContext.hpp"
#include "maths/voxmaths.hpp"
#include "window/Camera.hpp"
#include "constants.hpp"
void GuidesRenderer::drawBorders(
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
) {
int ww = ex - sx;
int dd = ez - sz;
/*corner*/ {
batch.line(sx, sy, sz, sx, ey, sz, 0.8f, 0, 0.8f, 1);
batch.line(sx, sy, ez, sx, ey, ez, 0.8f, 0, 0.8f, 1);
batch.line(ex, sy, sz, ex, ey, sz, 0.8f, 0, 0.8f, 1);
batch.line(ex, sy, ez, ex, ey, ez, 0.8f, 0, 0.8f, 1);
}
for (int i = 2; i < ww; i += 2) {
batch.line(sx + i, sy, sz, sx + i, ey, sz, 0, 0, 0.8f, 1);
batch.line(sx + i, sy, ez, sx + i, ey, ez, 0, 0, 0.8f, 1);
}
for (int i = 2; i < dd; i += 2) {
batch.line(sx, sy, sz + i, sx, ey, sz + i, 0.8f, 0, 0, 1);
batch.line(ex, sy, sz + i, ex, ey, sz + i, 0.8f, 0, 0, 1);
}
for (int i = sy; i < ey; i += 2) {
batch.line(sx, i, sz, sx, i, ez, 0, 0.8f, 0, 1);
batch.line(sx, i, ez, ex, i, ez, 0, 0.8f, 0, 1);
batch.line(ex, i, ez, ex, i, sz, 0, 0.8f, 0, 1);
batch.line(ex, i, sz, sx, i, sz, 0, 0.8f, 0, 1);
}
batch.flush();
}
void GuidesRenderer::drawCoordSystem(
LineBatch& batch, const DrawContext& pctx, float length
) {
auto ctx = pctx.sub();
ctx.setDepthTest(false);
batch.lineWidth(4.0f);
batch.line(0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
batch.line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 1.f);
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 1.f);
batch.flush();
ctx.setDepthTest(true);
batch.lineWidth(2.0f);
batch.line(0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f);
batch.line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 1.f);
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
}
void GuidesRenderer::renderDebugLines(
const DrawContext& pctx,
const Camera& camera,
LineBatch& batch,
Shader& linesShader,
bool showChunkBorders
) {
DrawContext ctx = pctx.sub(&batch);
const auto& viewport = ctx.getViewport();
uint displayWidth = viewport.getWidth();
uint displayHeight = viewport.getHeight();
ctx.setDepthTest(true);
linesShader.use();
if (showChunkBorders) {
linesShader.uniformMatrix("u_projview", camera.getProjView());
glm::vec3 coord = camera.position;
if (coord.x < 0) coord.x--;
if (coord.z < 0) coord.z--;
int cx = floordiv(static_cast<int>(coord.x), CHUNK_W);
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
drawBorders(
batch,
cx * CHUNK_W,
0,
cz * CHUNK_D,
(cx + 1) * CHUNK_W,
CHUNK_H,
(cz + 1) * CHUNK_D
);
}
float length = 40.f;
glm::vec3 tsl(displayWidth / 2, displayHeight / 2, 0.f);
glm::mat4 model(glm::translate(glm::mat4(1.f), tsl));
linesShader.uniformMatrix(
"u_projview",
glm::ortho(
0.f,
static_cast<float>(displayWidth),
0.f,
static_cast<float>(displayHeight),
-length,
length
) * model *
glm::inverse(camera.rotation)
);
drawCoordSystem(batch, ctx, length);
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
class LineBatch;
class DrawContext;
class Camera;
class Shader;
class GuidesRenderer {
public:
void drawBorders(
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
);
void drawCoordSystem(
LineBatch& batch, const DrawContext& pctx, float length
);
/// @brief Render all debug lines (chunks borders, coord system guides)
/// @param context graphics context
/// @param camera active camera
/// @param linesShader shader used
void renderDebugLines(
const DrawContext& context,
const Camera& camera,
LineBatch& batch,
Shader& linesShader,
bool showChunkBorders
);
};
+1 -1
View File
@@ -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}
};
+6 -6
View File
@@ -47,9 +47,9 @@ static glm::mat4 extract_rotation(glm::mat4 matrix) {
ModelBatch::ModelBatch(
size_t capacity,
Assets* assets,
Chunks* chunks,
const EngineSettings* settings
const Assets& assets,
const Chunks& chunks,
const EngineSettings& settings
)
: batch(std::make_unique<MainBatch>(capacity)),
assets(assets),
@@ -73,7 +73,7 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix,
if (mesh.lighting) {
glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
gpos += lightsOffset;
lights = MainBatch::sampleLight(gpos, *chunks, backlight);
lights = MainBatch::sampleLight(gpos, chunks, backlight);
}
for (size_t i = 0; i < vcount / 3; i++) {
batch->prepare(3);
@@ -107,7 +107,7 @@ void ModelBatch::render() {
return a.mesh->texture < b.mesh->texture;
}
);
bool backlight = settings->graphics.backlight.get();
bool backlight = settings.graphics.backlight.get();
for (auto& entry : entries) {
draw(
*entry.mesh,
@@ -136,6 +136,6 @@ void ModelBatch::setTexture(const std::string& name,
return setTexture(found->second, varTextures);
}
}
auto region = util::get_texture_region(*assets, name, "blocks:notfound");
auto region = util::get_texture_region(assets, name, "blocks:notfound");
batch->setTexture(region.texture, region.region);
}
+7 -6
View File
@@ -23,10 +23,10 @@ namespace model {
using texture_names_map = std::unordered_map<std::string, std::string>;
class ModelBatch {
Assets* assets;
Chunks* chunks;
const Assets& assets;
const Chunks& chunks;
const EngineSettings* settings;
const EngineSettings& settings;
glm::vec3 lightsOffset {};
static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f};
@@ -39,6 +39,7 @@ class ModelBatch {
glm::vec3 tint,
const texture_names_map* varTextures,
bool backlight);
void setTexture(const std::string& name,
const texture_names_map* varTextures);
@@ -53,9 +54,9 @@ class ModelBatch {
public:
ModelBatch(
size_t capacity,
Assets* assets,
Chunks* chunks,
const EngineSettings* settings
const Assets& assets,
const Chunks& chunks,
const EngineSettings& settings
);
~ModelBatch();
+12 -13
View File
@@ -60,43 +60,42 @@ model::Model ModelsGenerator::fromCustom(
auto& mesh = model.addMesh("blocks:" + modelTextures[i * 6]);
mesh.lighting = lighting;
const UVRegion boxtexfaces[6] = {
get_region_for(modelTextures[i * 6], assets),
get_region_for(modelTextures[i * 6 + 1], assets),
get_region_for(modelTextures[i * 6 + 2], assets),
get_region_for(modelTextures[i * 6 + 3], assets),
get_region_for(modelTextures[i * 6 + 5], assets),
get_region_for(modelTextures[i * 6 + 4], assets),
get_region_for(modelTextures[i * 6 + 5], assets)
get_region_for(modelTextures[i * 6 + 3], assets),
get_region_for(modelTextures[i * 6 + 2], assets),
get_region_for(modelTextures[i * 6 + 1], assets),
get_region_for(modelTextures[i * 6 + 0], assets)
};
mesh.addBox(
modelBoxes[i].center(), modelBoxes[i].size() * 0.5f, boxtexfaces
);
}
glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 norm {0, 1, 0};
for (size_t i = 0; i < points.size() / 4; i++) {
auto texture = "blocks:" + modelTextures[modelBoxes.size() * 6 + i];
auto texture = modelTextures[modelBoxes.size() * 6 + i];
auto& mesh = model.addMesh(texture);
mesh.lighting = lighting;
auto reg = get_region_for(texture, assets);
mesh.vertices.push_back(
{points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v1), norm}
{points[i * 4 + 0], glm::vec2(reg.u1, reg.v1), norm}
);
mesh.vertices.push_back(
{points[i * 4 + 1] - poff, glm::vec2(reg.u2, reg.v1), norm}
{points[i * 4 + 1], glm::vec2(reg.u2, reg.v1), norm}
);
mesh.vertices.push_back(
{points[i * 4 + 2] - poff, glm::vec2(reg.u2, reg.v2), norm}
{points[i * 4 + 2], glm::vec2(reg.u2, reg.v2), norm}
);
mesh.vertices.push_back(
{points[i * 4 + 3] - poff, glm::vec2(reg.u1, reg.v1), norm}
{points[i * 4 + 0], glm::vec2(reg.u1, reg.v1), norm}
);
mesh.vertices.push_back(
{points[i * 4 + 4] - poff, glm::vec2(reg.u2, reg.v2), norm}
{points[i * 4 + 2], glm::vec2(reg.u2, reg.v2), norm}
);
mesh.vertices.push_back(
{points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v2), norm}
{points[i * 4 + 3], glm::vec2(reg.u1, reg.v2), norm}
);
}
return model;
+1 -1
View File
@@ -18,7 +18,7 @@ size_t ParticlesRenderer::aliveEmitters = 0;
ParticlesRenderer::ParticlesRenderer(
const Assets& assets, const Level& level, const GraphicsSettings* settings
)
: batch(std::make_unique<MainBatch>(1024)),
: batch(std::make_unique<MainBatch>(4096)),
level(level),
assets(assets),
settings(settings) {}
+24 -17
View File
@@ -15,6 +15,7 @@
#include <iostream>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#ifndef M_PI
#define M_PI 3.141592
@@ -23,7 +24,7 @@
const int STARS_COUNT = 3000;
const int STARS_SEED = 632;
Skybox::Skybox(uint size, Shader* shader)
Skybox::Skybox(uint size, Shader& shader)
: size(size),
shader(shader),
batch3d(std::make_unique<Batch3D>(4096))
@@ -38,19 +39,19 @@ 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 {
"misc/moon",
M_PI*0.5f,
glm::pi<float>()*0.5f,
4.0f,
false
});
sprites.push_back(skysprite {
"misc/sun",
M_PI*1.5f,
glm::pi<float>()*1.5f,
4.0f,
true
});
@@ -115,13 +116,13 @@ void Skybox::draw(
p_shader->uniformMatrix("u_apply", glm::mat4(1.0f));
batch3d->begin();
float angle = daytime * float(M_PI) * 2.0f;
float angle = daytime * glm::pi<float>() * 2.0f;
float opacity = glm::pow(1.0f-fog, 7.0f);
for (auto& sprite : sprites) {
batch3d->texture(assets.get<Texture>(sprite.texture));
float sangle = daytime * float(M_PI)*2.0 + sprite.phase;
float sangle = daytime * glm::pi<float>()*2.0 + sprite.phase;
float distance = sprite.distance;
glm::vec3 pos(-std::cos(sangle)*distance, std::sin(sangle)*distance, 0);
@@ -152,15 +153,15 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
ready = true;
glActiveTexture(GL_TEXTURE1);
cubemap->bind();
shader->use();
t *= M_PI*2.0f;
shader.use();
t *= glm::pi<float>()*2.0f;
lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f));
shader->uniform1i("u_quality", quality);
shader->uniform1f("u_mie", mie);
shader->uniform1f("u_fog", mie - 1.0f);
shader->uniform3f("u_lightDir", lightDir);
shader->uniform1f("u_dayTime", dayTime);
shader.uniform1i("u_quality", quality);
shader.uniform1f("u_mie", mie);
shader.uniform1f("u_fog", mie - 1.0f);
shader.uniform3f("u_lightDir", lightDir);
shader.uniform1f("u_dayTime", dayTime);
if (glm::abs(mie-prevMie) + glm::abs(t-prevT) >= 0.01) {
for (uint face = 0; face < 6; face++) {
@@ -206,10 +207,16 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) {
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, 1.0f},
};
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0);
shader->uniform3f("u_xaxis", xaxs[face]);
shader->uniform3f("u_yaxis", yaxs[face]);
shader->uniform3f("u_zaxis", zaxs[face]);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
cubemap->getId(),
0
);
shader.uniform3f("u_xaxis", xaxs[face]);
shader.uniform3f("u_yaxis", yaxs[face]);
shader.uniform3f("u_zaxis", zaxs[face]);
mesh->draw();
}
+2 -2
View File
@@ -27,7 +27,7 @@ struct skysprite {
class Skybox {
std::unique_ptr<Framebuffer> fbo;
uint size;
Shader* shader;
Shader& shader;
bool ready = false;
FastRandom random;
glm::vec3 lightDir;
@@ -46,7 +46,7 @@ class Skybox {
);
void refreshFace(uint face, Cubemap* cubemap);
public:
Skybox(uint size, Shader* shader);
Skybox(uint size, Shader& shader);
~Skybox();
void draw(
+47
View File
@@ -0,0 +1,47 @@
#include "TextNote.hpp"
TextNote::TextNote(std::wstring text, NotePreset preset, glm::vec3 position)
: text(std::move(text)),
preset(std::move(preset)),
position(std::move(position)) {
}
void TextNote::setText(std::wstring_view text) {
this->text = text;
}
const std::wstring& TextNote::getText() const {
return text;
}
const NotePreset& TextNote::getPreset() const {
return preset;
}
void TextNote::updatePreset(const dv::value& data) {
preset.deserialize(data);
}
void TextNote::setPosition(const glm::vec3& position) {
this->position = position;
}
const glm::vec3& TextNote::getPosition() const {
return position;
}
const glm::vec3& TextNote::getAxisX() const {
return xAxis;
}
const glm::vec3& TextNote::getAxisY() const {
return yAxis;
}
void TextNote::setAxisX(const glm::vec3& vec) {
xAxis = vec;
}
void TextNote::setAxisY(const glm::vec3& vec) {
yAxis = vec;
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include "presets/NotePreset.hpp"
/// @brief 3D text instance
class TextNote {
std::wstring text;
NotePreset preset;
glm::vec3 position;
glm::vec3 xAxis {1, 0, 0};
glm::vec3 yAxis {0, 1, 0};
public:
TextNote(std::wstring text, NotePreset preset, glm::vec3 position);
void setText(std::wstring_view text);
const std::wstring& getText() const;
const NotePreset& getPreset() const;
void updatePreset(const dv::value& data);
void setPosition(const glm::vec3& position);
const glm::vec3& getPosition() const;
const glm::vec3& getAxisX() const;
const glm::vec3& getAxisY() const;
void setAxisX(const glm::vec3& vec);
void setAxisY(const glm::vec3& vec);
};
+146
View File
@@ -0,0 +1,146 @@
#include "TextsRenderer.hpp"
#include "TextNote.hpp"
#include "maths/util.hpp"
#include "assets/Assets.hpp"
#include "window/Camera.hpp"
#include "window/Window.hpp"
#include "maths/FrustumCulling.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/Batch3D.hpp"
#include "graphics/core/Shader.hpp"
#include "presets/NotePreset.hpp"
TextsRenderer::TextsRenderer(
Batch3D& batch, const Assets& assets, const Frustum& frustum
)
: batch(batch), assets(assets), frustum(frustum) {
}
void TextsRenderer::renderNote(
const TextNote& note,
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
bool hudVisible,
bool frontLayer,
bool projected
) {
const auto& text = note.getText();
const auto& preset = note.getPreset();
auto pos = note.getPosition();
if (util::distance2(pos, camera.position) >
util::sqr(preset.renderDistance / camera.zoom)) {
return;
}
// Projected notes are displayed on the front layer only
if ((preset.displayMode == NoteDisplayMode::PROJECTED) != projected) {
return;
}
float opacity = 1.0f;
if (frontLayer && preset.displayMode != NoteDisplayMode::PROJECTED) {
if (preset.xrayOpacity <= 0.0001f) {
return;
}
opacity = preset.xrayOpacity;
}
const auto& font = assets.require<Font>("normal");
glm::vec3 xvec = note.getAxisX();
glm::vec3 yvec = note.getAxisY();
int width = font.calcWidth(text, text.length());
if (preset.displayMode == NoteDisplayMode::Y_FREE_BILLBOARD ||
preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) {
xvec = camera.position - pos;
xvec.y = 0;
std::swap(xvec.x, xvec.z);
xvec.z *= -1;
xvec = glm::normalize(xvec);
if (preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) {
yvec = camera.up;
}
}
if (preset.displayMode != NoteDisplayMode::PROJECTED) {
if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f),
pos + xvec * (width * 0.5f))) {
return;
}
} else {
float scale = 1.0f;
if (glm::abs(preset.perspective) > 0.0001f) {
float scale2 = scale /
(glm::distance(camera.position, pos) *
util::sqr(camera.zoom) *
glm::sqrt(glm::tan(camera.getFov() * 0.5f)));
scale = scale2 * preset.perspective +
scale * (1.0f - preset.perspective);
}
auto projpos = camera.getProjView() * glm::vec4(pos, 1.0f);
pos = projpos;
if (pos.z < 0.0f) {
return;
}
pos /= pos.z;
pos.z = 0;
xvec = {2.0f/Window::width*scale, 0, 0};
yvec = {0, 2.0f/Window::height*scale, 0};
}
auto color = preset.color;
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
font.draw(
batch,
text,
pos - xvec * (width * 0.5f) * preset.scale,
xvec * preset.scale,
yvec * preset.scale
);
}
void TextsRenderer::render(
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
bool hudVisible,
bool frontLayer
) {
auto& shader = assets.require<Shader>("ui3d");
shader.use();
shader.uniformMatrix("u_projview", camera.getProjView());
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
batch.begin();
for (const auto& [_, note] : notes) {
renderNote(*note, context, camera, settings, hudVisible, frontLayer, false);
}
batch.flush();
if (frontLayer) {
shader.uniformMatrix(
"u_projview",
glm::mat4(1.0f)
);
for (const auto& [_, note] : notes) {
renderNote(*note, context, camera, settings, hudVisible, true, true);
}
batch.flush();
}
}
u64id_t TextsRenderer::add(std::unique_ptr<TextNote> note) {
u64id_t uid = nextNote++;
notes[uid] = std::move(note);
return uid;
}
TextNote* TextsRenderer::get(u64id_t id) const {
const auto& found = notes.find(id);
if (found == notes.end()) {
return nullptr;
}
return found->second.get();
}
void TextsRenderer::remove(u64id_t id) {
notes.erase(id);
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <unordered_map>
#include <memory>
#include "typedefs.hpp"
class DrawContext;
class Camera;
class Assets;
class Batch3D;
class Frustum;
class TextNote;
struct EngineSettings;
class TextsRenderer {
Batch3D& batch;
const Assets& assets;
const Frustum& frustum;
std::unordered_map<u64id_t, std::unique_ptr<TextNote>> notes;
u64id_t nextNote = 1;
void renderNote(
const TextNote& note,
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
bool hudVisible,
bool frontLayer,
bool projected
);
public:
TextsRenderer(Batch3D& batch, const Assets& assets, const Frustum& frustum);
void render(
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
bool hudVisible,
bool frontLayer
);
u64id_t add(std::unique_ptr<TextNote> note);
TextNote* get(u64id_t id) const;
void remove(u64id_t id);
};
+91 -229
View File
@@ -40,160 +40,94 @@
#include "graphics/core/PostProcessing.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/Font.hpp"
#include "ParticlesRenderer.hpp"
#include "TextsRenderer.hpp"
#include "ChunksRenderer.hpp"
#include "GuidesRenderer.hpp"
#include "ModelBatch.hpp"
#include "Skybox.hpp"
#include "Emitter.hpp"
#include "TextNote.hpp"
inline constexpr size_t BATCH3D_CAPACITY = 4096;
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
bool WorldRenderer::showChunkBorders = false;
bool WorldRenderer::showEntitiesDebug = false;
WorldRenderer::WorldRenderer(
Engine* engine, LevelFrontend* frontend, Player* player
Engine* engine, LevelFrontend& frontend, Player* player
)
: engine(engine),
level(frontend->getLevel()),
level(frontend.getLevel()),
player(player),
assets(*engine->getAssets()),
frustumCulling(std::make_unique<Frustum>()),
lineBatch(std::make_unique<LineBatch>()),
batch3d(std::make_unique<Batch3D>(BATCH3D_CAPACITY)),
modelBatch(std::make_unique<ModelBatch>(
20'000,
engine->getAssets(),
level->chunks.get(),
&engine->getSettings()
MODEL_BATCH_CAPACITY, assets, *level.chunks, engine->getSettings()
)),
particles(std::make_unique<ParticlesRenderer>(
*engine->getAssets(),
*frontend->getLevel(),
&engine->getSettings().graphics
assets, level, &engine->getSettings().graphics
)),
texts(std::make_unique<TextsRenderer>(*batch3d, assets, *frustumCulling)),
guides(std::make_unique<GuidesRenderer>()),
chunks(std::make_unique<ChunksRenderer>(
&level,
assets,
*frustumCulling,
frontend.getContentGfxCache(),
engine->getSettings()
)) {
renderer = std::make_unique<ChunksRenderer>(
level, frontend->getContentGfxCache(), &engine->getSettings()
);
batch3d = std::make_unique<Batch3D>(4096);
auto& settings = engine->getSettings();
level->events->listen(
level.events->listen(
EVT_CHUNK_HIDDEN,
[this](lvl_event_type, Chunk* chunk) { renderer->unload(chunk); }
[this](lvl_event_type, Chunk* chunk) { chunks->unload(chunk); }
);
auto assets = engine->getAssets();
skybox = std::make_unique<Skybox>(
settings.graphics.skyboxResolution.get(),
assets->get<Shader>("skybox_gen")
assets->require<Shader>("skybox_gen")
);
}
WorldRenderer::~WorldRenderer() = default;
bool WorldRenderer::drawChunk(
size_t index, const Camera& camera, Shader* shader, bool culling
) {
auto chunk = level->chunks->getChunks()[index];
if (!chunk->flags.lighted) {
return false;
}
float distance = glm::distance(
camera.position,
glm::vec3(
(chunk->x + 0.5f) * CHUNK_W,
camera.position.y,
(chunk->z + 0.5f) * CHUNK_D
)
);
auto mesh = renderer->getOrRender(chunk, distance < CHUNK_W * 1.5f);
if (mesh == nullptr) {
return false;
}
if (culling) {
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
glm::vec3 max(
chunk->x * CHUNK_W + CHUNK_W,
chunk->top,
chunk->z * CHUNK_D + CHUNK_D
);
if (!frustumCulling->isBoxVisible(min, max)) return false;
}
glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f);
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
shader->uniformMatrix("u_model", model);
mesh->draw();
return true;
}
void WorldRenderer::drawChunks(
Chunks* chunks, const Camera& camera, Shader* shader
) {
auto assets = engine->getAssets();
auto atlas = assets->get<Atlas>("blocks");
atlas->getTexture()->bind();
renderer->update();
// [warning] this whole method is not thread-safe for chunks
std::vector<size_t> indices;
for (size_t i = 0; i < chunks->getVolume(); i++) {
if (chunks->getChunks()[i] == nullptr) continue;
indices.emplace_back(i);
}
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
std::sort(indices.begin(), indices.end(), [chunks, px, pz](auto i, auto j) {
const auto& chunksBuffer = chunks->getChunks();
const auto a = chunksBuffer[i].get();
const auto b = chunksBuffer[j].get();
auto adx = (a->x - px);
auto adz = (a->z - pz);
auto bdx = (b->x - px);
auto bdz = (b->z - pz);
return (adx * adx + adz * adz > bdx * bdx + bdz * bdz);
});
bool culling = engine->getSettings().graphics.frustumCulling.get();
if (culling) {
frustumCulling->update(camera.getProjView());
}
chunks->visible = 0;
for (size_t i = 0; i < indices.size(); i++) {
chunks->visible += drawChunk(indices[i], camera, shader, culling);
}
}
void WorldRenderer::setupWorldShader(
Shader* shader,
Shader& shader,
const Camera& camera,
const EngineSettings& settings,
float fogFactor
) {
shader->use();
shader->uniformMatrix("u_model", glm::mat4(1.0f));
shader->uniformMatrix("u_proj", camera.getProjection());
shader->uniformMatrix("u_view", camera.getView());
shader->uniform1f("u_timer", timer);
shader->uniform1f("u_gamma", settings.graphics.gamma.get());
shader->uniform1f("u_fogFactor", fogFactor);
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
shader->uniform1f("u_dayTime", level->getWorld()->getInfo().daytime);
shader->uniform2f("u_lightDir", skybox->getLightDir());
shader->uniform3f("u_cameraPos", camera.position);
shader->uniform1i("u_cubemap", 1);
shader.use();
shader.uniformMatrix("u_model", glm::mat4(1.0f));
shader.uniformMatrix("u_proj", camera.getProjection());
shader.uniformMatrix("u_view", camera.getView());
shader.uniform1f("u_timer", timer);
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
shader.uniform1f("u_fogFactor", fogFactor);
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
shader.uniform2f("u_lightDir", skybox->getLightDir());
shader.uniform3f("u_cameraPos", camera.position);
shader.uniform1i("u_cubemap", 1);
auto indices = level->content->getIndices();
auto indices = level.content->getIndices();
// Light emission when an emissive item is chosen
{
auto inventory = player->getInventory();
ItemStack& stack = inventory->getSlot(player->getChosenSlot());
auto& item = indices->items.require(stack.getItemId());
float multiplier = 0.5f;
shader->uniform3f(
shader.uniform3f(
"u_torchlightColor",
item.emission[0] / 15.0f * multiplier,
item.emission[1] / 15.0f * multiplier,
item.emission[2] / 15.0f * multiplier
);
shader->uniform1f("u_torchlightDistance", 6.0f);
shader.uniform1f("u_torchlightDistance", 6.0f);
}
}
@@ -202,32 +136,45 @@ void WorldRenderer::renderLevel(
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause
bool pause,
bool hudVisible
) {
auto assets = engine->getAssets();
texts->render(ctx, camera, settings, hudVisible, false);
bool culling = engine->getSettings().graphics.frustumCulling.get();
float fogFactor =
15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
auto entityShader = assets->get<Shader>("entity");
auto& entityShader = assets.require<Shader>("entity");
setupWorldShader(entityShader, camera, settings, fogFactor);
skybox->bind();
level->entities->render(
if (culling) {
frustumCulling->update(camera.getProjView());
}
level.entities->render(
assets,
*modelBatch,
culling ? frustumCulling.get() : nullptr,
delta,
pause
);
particles->render(camera, delta * !pause);
modelBatch->render();
particles->render(camera, delta * !pause);
auto& shader = assets.require<Shader>("main");
auto& linesShader = assets.require<Shader>("lines");
auto shader = assets->get<Shader>("main");
setupWorldShader(shader, camera, settings, fogFactor);
drawChunks(level->chunks.get(), camera, shader);
chunks->drawChunks(camera, shader);
if (hudVisible) {
renderLines(camera, linesShader, ctx);
}
shader.use();
chunks->drawSortedMeshes(camera, shader);
if (!pause) {
scripting::on_frontend_render();
@@ -238,7 +185,7 @@ void WorldRenderer::renderLevel(
void WorldRenderer::renderBlockSelection() {
const auto& selection = player->selection;
auto indices = level->content->getIndices();
auto indices = level.content->getIndices();
blockid_t id = selection.vox.id;
auto& block = indices->blocks.require(id);
const glm::ivec3 pos = player->selection.position;
@@ -254,7 +201,7 @@ void WorldRenderer::renderBlockSelection() {
const glm::vec3 center = glm::vec3(pos) + hitbox.center();
const glm::vec3 size = hitbox.size();
lineBatch->box(
center, size + glm::vec3(0.02), glm::vec4(0.f, 0.f, 0.f, 0.5f)
center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 0.5f)
);
if (player->debug) {
lineBatch->line(
@@ -266,87 +213,27 @@ void WorldRenderer::renderBlockSelection() {
}
void WorldRenderer::renderLines(
const Camera& camera, Shader* linesShader, const DrawContext& pctx
const Camera& camera, Shader& linesShader, const DrawContext& pctx
) {
linesShader->use();
linesShader->uniformMatrix("u_projview", camera.getProjView());
linesShader.use();
linesShader.uniformMatrix("u_projview", camera.getProjView());
if (player->selection.vox.id != BLOCK_VOID) {
renderBlockSelection();
}
if (player->debug && showEntitiesDebug) {
auto ctx = pctx.sub(lineBatch.get());
bool culling = engine->getSettings().graphics.frustumCulling.get();
level->entities->renderDebug(
level.entities->renderDebug(
*lineBatch, culling ? frustumCulling.get() : nullptr, ctx
);
}
}
void WorldRenderer::renderDebugLines(
const DrawContext& pctx, const Camera& camera, Shader* linesShader
) {
DrawContext ctx = pctx.sub(lineBatch.get());
const auto& viewport = ctx.getViewport();
uint displayWidth = viewport.getWidth();
uint displayHeight = viewport.getHeight();
ctx.setDepthTest(true);
linesShader->use();
if (showChunkBorders) {
linesShader->uniformMatrix("u_projview", camera.getProjView());
glm::vec3 coord = player->fpCamera->position;
if (coord.x < 0) coord.x--;
if (coord.z < 0) coord.z--;
int cx = floordiv(static_cast<int>(coord.x), CHUNK_W);
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
drawBorders(
cx * CHUNK_W,
0,
cz * CHUNK_D,
(cx + 1) * CHUNK_W,
CHUNK_H,
(cz + 1) * CHUNK_D
);
}
float length = 40.f;
glm::vec3 tsl(displayWidth / 2, displayHeight / 2, 0.f);
glm::mat4 model(glm::translate(glm::mat4(1.f), tsl));
linesShader->uniformMatrix(
"u_projview",
glm::ortho(
0.f,
static_cast<float>(displayWidth),
0.f,
static_cast<float>(displayHeight),
-length,
length
) * model *
glm::inverse(camera.rotation)
);
ctx.setDepthTest(false);
lineBatch->lineWidth(4.0f);
lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 1.f);
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 1.f);
lineBatch->flush();
ctx.setDepthTest(true);
lineBatch->lineWidth(2.0f);
lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f);
lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 1.f);
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
}
void WorldRenderer::renderHands(
const Camera& camera, const Assets& assets, float delta
const Camera& camera, float delta
) {
auto entityShader = assets.get<Shader>("entity");
auto indices = level->content->getIndices();
auto& entityShader = assets.require<Shader>("entity");
auto indices = level.content->getIndices();
// get current chosen item
const auto& inventory = player->getInventory();
@@ -414,7 +301,7 @@ void WorldRenderer::draw(
PostProcessing* postProcessing
) {
timer += delta * !pause;
auto world = level->getWorld();
auto world = level.getWorld();
const Viewport& vp = pctx.getViewport();
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
@@ -424,10 +311,9 @@ void WorldRenderer::draw(
skybox->refresh(pctx, worldInfo.daytime, 1.0f + worldInfo.fog * 2.0f, 4);
const auto& assets = *engine->getAssets();
auto linesShader = assets.get<Shader>("lines");
auto& linesShader = assets.require<Shader>("lines");
// World render scope with diegetic HUD included
{
/* World render scope with diegetic HUD included */ {
DrawContext wctx = pctx.sub();
postProcessing->use(wctx);
@@ -435,25 +321,29 @@ void WorldRenderer::draw(
// Drawing background sky plane
skybox->draw(pctx, camera, assets, worldInfo.daytime, worldInfo.fog);
// Actually world render with depth buffer on
{
/* Actually world render with depth buffer on */ {
DrawContext ctx = wctx.sub();
ctx.setDepthTest(true);
ctx.setCullFace(true);
renderLevel(ctx, camera, settings, delta, pause);
renderLevel(ctx, camera, settings, delta, pause, hudVisible);
// Debug lines
if (hudVisible) {
renderLines(camera, linesShader, ctx);
if (player->debug) {
guides->renderDebugLines(
ctx, camera, *lineBatch, linesShader, showChunkBorders
);
}
if (player->currentCamera == player->fpCamera) {
renderHands(camera, assets, delta * !pause);
renderHands(camera, delta * !pause);
}
}
}
if (hudVisible && player->debug) {
renderDebugLines(wctx, camera, linesShader);
{
DrawContext ctx = wctx.sub();
texts->render(ctx, camera, settings, hudVisible, true);
}
renderBlockOverlay(wctx, assets);
renderBlockOverlay(wctx);
}
// Rendering fullscreen quad with
@@ -464,14 +354,14 @@ void WorldRenderer::draw(
postProcessing->render(pctx, screenShader);
}
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& assets) {
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
int x = std::floor(player->currentCamera->position.x);
int y = std::floor(player->currentCamera->position.y);
int z = std::floor(player->currentCamera->position.z);
auto block = level->chunks->get(x, y, z);
auto block = level.chunks->get(x, y, z);
if (block && block->id) {
const auto& def =
level->content->getIndices()->blocks.require(block->id);
level.content->getIndices()->blocks.require(block->id);
if (def.overlayTexture.empty()) {
return;
}
@@ -487,7 +377,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& as
batch3d->begin();
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
auto light = level->chunks->getLight(x, y, z);
auto light = level.chunks->getLight(x, y, z);
float s = Lightmap::extract(light, 3) / 15.0f;
glm::vec4 tint(
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
@@ -509,34 +399,6 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& as
}
}
void WorldRenderer::drawBorders(
int sx, int sy, int sz, int ex, int ey, int ez
) {
int ww = ex - sx;
int dd = ez - sz;
/*corner*/ {
lineBatch->line(sx, sy, sz, sx, ey, sz, 0.8f, 0, 0.8f, 1);
lineBatch->line(sx, sy, ez, sx, ey, ez, 0.8f, 0, 0.8f, 1);
lineBatch->line(ex, sy, sz, ex, ey, sz, 0.8f, 0, 0.8f, 1);
lineBatch->line(ex, sy, ez, ex, ey, ez, 0.8f, 0, 0.8f, 1);
}
for (int i = 2; i < ww; i += 2) {
lineBatch->line(sx + i, sy, sz, sx + i, ey, sz, 0, 0, 0.8f, 1);
lineBatch->line(sx + i, sy, ez, sx + i, ey, ez, 0, 0, 0.8f, 1);
}
for (int i = 2; i < dd; i += 2) {
lineBatch->line(sx, sy, sz + i, sx, ey, sz + i, 0.8f, 0, 0, 1);
lineBatch->line(ex, sy, sz + i, ex, ey, sz + i, 0.8f, 0, 0, 1);
}
for (int i = sy; i < ey; i += 2) {
lineBatch->line(sx, i, sz, sx, i, ez, 0, 0.8f, 0, 1);
lineBatch->line(sx, i, ez, ex, i, ez, 0, 0.8f, 0, 1);
lineBatch->line(ex, i, ez, ex, i, sz, 0, 0.8f, 0, 1);
lineBatch->line(ex, i, sz, sx, i, sz, 0, 0.8f, 0, 1);
}
lineBatch->flush();
}
void WorldRenderer::clear() {
renderer->clear();
chunks->clear();
}
+15 -29
View File
@@ -17,76 +17,62 @@ class Batch3D;
class LineBatch;
class ChunksRenderer;
class ParticlesRenderer;
class GuidesRenderer;
class TextsRenderer;
class Shader;
class Frustum;
class Engine;
class Chunks;
class LevelFrontend;
class Skybox;
class PostProcessing;
class DrawContext;
class ModelBatch;
class Assets;
class Emitter;
struct EngineSettings;
namespace model {
struct Model;
}
class WorldRenderer {
Engine* engine;
Level* level;
const Level& level;
Player* player;
const Assets& assets;
std::unique_ptr<Frustum> frustumCulling;
std::unique_ptr<LineBatch> lineBatch;
std::unique_ptr<ChunksRenderer> renderer;
std::unique_ptr<Skybox> skybox;
std::unique_ptr<Batch3D> batch3d;
std::unique_ptr<ChunksRenderer> chunks;
std::unique_ptr<GuidesRenderer> guides;
std::unique_ptr<Skybox> skybox;
std::unique_ptr<ModelBatch> modelBatch;
float timer = 0.0f;
bool drawChunk(size_t index, const Camera& camera, Shader* shader, bool culling);
void drawChunks(Chunks* chunks, const Camera& camera, Shader* shader);
/// @brief Render block selection lines
void renderBlockSelection();
void renderHands(const Camera& camera, const Assets& assets, float delta);
void renderHands(const Camera& camera, float delta);
/// @brief Render lines (selection and debug)
/// @param camera active camera
/// @param linesShader shader used
void renderLines(
const Camera& camera, Shader* linesShader, const DrawContext& pctx
const Camera& camera, Shader& linesShader, const DrawContext& pctx
);
/// @brief Render all debug lines (chunks borders, coord system guides)
/// @param context graphics context
/// @param camera active camera
/// @param linesShader shader used
void renderDebugLines(
const DrawContext& context,
const Camera& camera,
Shader* linesShader
);
void renderBlockOverlay(const DrawContext& context, const Assets& assets);
void renderBlockOverlay(const DrawContext& context);
void setupWorldShader(
Shader* shader,
Shader& shader,
const Camera& camera,
const EngineSettings& settings,
float fogFactor
);
public:
std::unique_ptr<TextsRenderer> texts;
std::unique_ptr<ParticlesRenderer> particles;
static bool showChunkBorders;
static bool showEntitiesDebug;
WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player);
WorldRenderer(Engine* engine, LevelFrontend& frontend, Player* player);
~WorldRenderer();
void draw(
@@ -97,7 +83,6 @@ public:
float delta,
PostProcessing* postProcessing
);
void drawBorders(int sx, int sy, int sz, int ex, int ey, int ez);
/// @brief Render level without diegetic interface
/// @param context graphics context
@@ -108,7 +93,8 @@ public:
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause
bool pause,
bool hudVisible
);
void clear();
+40
View File
@@ -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;
};
+10 -2
View File
@@ -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();
+2
View File
@@ -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;
+2 -2
View File
@@ -194,9 +194,9 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
int y = pos.y+slotSize-16;
batch->setColor({0, 0, 0, 1.0f});
font->draw(batch, text, x+1, y+1);
font->draw(*batch, text, x+1, y+1);
batch->setColor(glm::vec4(1.0f));
font->draw(batch, text, x, y);
font->draw(*batch, text, x, y);
}
}
+2 -2
View File
@@ -201,10 +201,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) {
if (i < cache.lines.size()-1) {
view = std::wstring_view(text.c_str()+offset, cache.lines.at(i+1).offset-offset);
}
font->draw(batch, view, pos.x, pos.y + i * totalLineHeight, FontStyle::none);
font->draw(*batch, view, pos.x, pos.y + i * totalLineHeight);
}
} else {
font->draw(batch, text, pos.x, pos.y, FontStyle::none);
font->draw(*batch, text, pos.x, pos.y);
}
}
+1 -1
View File
@@ -47,6 +47,6 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) {
batch->setColor({1,1,1,0.2f});
string = util::to_wstring(y / multiplier, 3);
}
font->draw(batch, string, pos.x+dmwidth+2, pos.y+dmheight-y-labelsInterval);
font->draw(*batch, string, pos.x+dmwidth+2, pos.y+dmheight-y-labelsInterval);
}
}
+132 -30
View File
@@ -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->setText(getText());
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()]
@@ -505,7 +562,7 @@ void TextBox::performEditingKeyboardEvents(keycode key) {
} else {
defocus();
if (validate() && consumer) {
consumer(label->getText());
consumer(getText());
}
}
} else if (key == keycode::TAB) {
@@ -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;
}
@@ -627,7 +701,7 @@ glm::vec4 TextBox::getErrorColor() const {
return invalidColor;
}
std::wstring TextBox::getText() const {
const std::wstring& TextBox::getText() const {
if (input.empty())
return placeholder;
return input;
@@ -638,7 +712,7 @@ void TextBox::setText(const std::wstring& value) {
input.erase(std::remove(input.begin(), input.end(), '\r'), input.end());
}
std::wstring TextBox::getPlaceholder() const {
const std::wstring& TextBox::getPlaceholder() const {
return placeholder;
}
@@ -646,6 +720,15 @@ void TextBox::setPlaceholder(const std::wstring& placeholder) {
this->placeholder = placeholder;
}
const std::wstring& TextBox::getHint() const {
return hint;
}
void TextBox::setHint(const std::wstring& text) {
this->hint = text;
}
std::wstring TextBox::getSelection() const {
return input.substr(selectionStart, selectionEnd-selectionStart);
}
@@ -664,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));
@@ -687,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;
}
+48 -6
View File
@@ -8,28 +8,43 @@ 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
std::wstring placeholder;
/// @brief Text will be shown when nothing entered
std::wstring hint;
/// @brief Text supplier called every frame when not focused
wstringsupplier supplier = nullptr;
/// @brief Text supplier called on Enter pressed
wstringconsumer consumer = nullptr;
/// @brief Text supplier called while input
wstringconsumer subconsumer = nullptr;
/// @brief Text validator returning boolean value
wstringchecker validator = nullptr;
/// @brief Function called on focus
runnable onEditStart = nullptr;
/// @brief Function called on up arrow pressed
runnable onUpPressed;
/// @brief Function called on down arrow pressed
runnable onDownPressed;
/// @brief Is current input valid
bool valid = true;
/// @brief text input pointer, value may be greather than text length
/// @brief Text input pointer, value may be greather than text length
size_t caret = 0;
/// @brief actual local (line) position of the caret on vertical move
/// @brief Actual local (line) position of the caret on vertical move
size_t maxLocalCaret = 0;
size_t textOffset = 0;
int textInitX;
/// @brief last time of the caret was moved (used for blink animation)
/// @brief Last time of the caret was moved (used for blink animation)
double caretLastMove = 0.0;
Font* font = nullptr;
@@ -40,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);
@@ -94,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);
@@ -101,17 +120,24 @@ namespace gui {
virtual glm::vec4 getErrorColor() const;
/// @brief Get TextBox content text or placeholder if empty
virtual std::wstring getText() const;
virtual const std::wstring& getText() const;
/// @brief Set TextBox content text
virtual void setText(const std::wstring &value);
/// @brief Get text placeholder
virtual std::wstring getPlaceholder() const;
virtual const std::wstring& getPlaceholder() const;
/// @brief Set text placeholder
/// @param text will be used instead of empty
virtual void setPlaceholder(const std::wstring& text);
/// @brief Get textbox hint
virtual const std::wstring& getHint() const;
/// @brief Set textbox hint
/// @param text will be shown instead of empty
virtual void setHint(const std::wstring& text);
/// @brief Get selected text
virtual std::wstring getSelection() const;
@@ -133,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();
@@ -158,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;
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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) {
+30 -3
View File
@@ -67,7 +67,11 @@ static runnable create_runnable(
return nullptr;
}
static onaction create_action(UiXmlReader& reader, const xml::xmlelement& element, const std::string& name) {
static onaction create_action(
const UiXmlReader& reader,
const xml::xmlelement& element,
const std::string& name
) {
auto callback = create_runnable(reader, element, name);
if (callback == nullptr) {
return nullptr;
@@ -76,7 +80,9 @@ static onaction create_action(UiXmlReader& reader, const xml::xmlelement& elemen
}
/* Read basic UINode properties */
static void _readUINode(UiXmlReader& reader, const xml::xmlelement& element, UINode& node) {
static void _readUINode(
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
) {
if (element->has("id")) {
node.setId(element->attr("id").getText());
}
@@ -166,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;
@@ -331,9 +340,21 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmle
static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto placeholder = util::str2wstr_utf8(element->attr("placeholder", "").getText());
auto hint = util::str2wstr_utf8(element->attr("hint", "").getText());
auto text = readAndProcessInnerText(element, reader.getContext());
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
_readPanel(reader, element, *textbox);
textbox->setHint(hint);
_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")) {
@@ -348,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(),
@@ -375,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(),
+4
View File
@@ -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"];
+2
View File
@@ -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;
+2 -2
View File
@@ -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);
}
+9 -7
View File
@@ -253,14 +253,16 @@ void EngineController::reconfigPacks(
bool hasIndices = false;
std::stringstream ss;
for (const auto& id : packsToRemove) {
auto runtime = content->getPackRuntime(id);
if (runtime && runtime->getStats().hasSavingContent()) {
if (hasIndices) {
ss << ", ";
if (content) {
for (const auto& id : packsToRemove) {
auto runtime = content->getPackRuntime(id);
if (runtime && runtime->getStats().hasSavingContent()) {
if (hasIndices) {
ss << ", ";
}
hasIndices = true;
ss << id;
}
hasIndices = true;
ss << id;
}
}
+3 -1
View File
@@ -15,6 +15,7 @@
// 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 cameralib[];
@@ -33,7 +34,8 @@ extern const luaL_Reg mat4lib[];
extern const luaL_Reg packlib[];
extern const luaL_Reg particleslib[];
extern const luaL_Reg playerlib[];
extern const luaL_Reg quatlib[]; // quat.cpp
extern const luaL_Reg quatlib[];
extern const luaL_Reg text3dlib[];
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}
};
+2 -2
View File
@@ -53,7 +53,7 @@ static int l_get_def(lua::State* L) {
return 0;
}
static int l_spawn(lua::State* L) {
static int l_show(lua::State* L) {
auto level = controller->getLevel();
auto defname = lua::tostring(L, 1);
auto& def = content->entities.require(defname);
@@ -213,7 +213,7 @@ const luaL_Reg entitylib[] = {
{"def_hitbox", lua::wrap<l_def_hitbox>},
{"get_def", lua::wrap<l_get_def>},
{"defs_count", lua::wrap<l_defs_count>},
{"spawn", lua::wrap<l_spawn>},
{"spawn", lua::wrap<l_show>},
{"despawn", lua::wrap<l_despawn>},
{"get_skeleton", lua::wrap<l_get_skeleton>},
{"set_skeleton", lua::wrap<l_set_skeleton>},
+4 -3
View File
@@ -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;
}
+68
View File
@@ -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());
@@ -242,6 +267,13 @@ static int p_get_placeholder(UINode* node, lua::State* L) {
return 0;
}
static int p_get_hint(UINode* node, lua::State* L) {
if (auto box = dynamic_cast<TextBox*>(node)) {
return lua::pushwstring(L, box->getHint());
}
return 0;
}
static int p_get_text(UINode* node, lua::State* L) {
if (auto button = dynamic_cast<Button*>(node)) {
return lua::pushwstring(L, button->getText());
@@ -260,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());
@@ -335,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);
@@ -364,10 +409,14 @@ static int l_gui_getattr(lua::State* L) {
{"clear", p_get_clear},
{"setInterval", p_set_interval},
{"placeholder", p_get_placeholder},
{"hint", p_get_hint},
{"valid", p_is_valid},
{"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},
@@ -375,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},
@@ -430,6 +480,11 @@ static void p_set_placeholder(UINode* node, lua::State* L, int idx) {
box->setPlaceholder(lua::require_wstring(L, idx));
}
}
static void p_set_hint(UINode* node, lua::State* L, int idx) {
if (auto box = dynamic_cast<TextBox*>(node)) {
box->setHint(lua::require_wstring(L, idx));
}
}
static void p_set_text(UINode* node, lua::State* L, int idx) {
if (auto label = dynamic_cast<Label*>(node)) {
label->setText(lua::require_wstring(L, idx));
@@ -449,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));
@@ -484,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));
@@ -540,8 +605,10 @@ static int l_gui_setattr(lua::State* L) {
{"visible", p_set_visible},
{"enabled", p_set_enabled},
{"placeholder", p_set_placeholder},
{"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},
@@ -550,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},
+21
View File
@@ -36,6 +36,26 @@ static int l_close_inventory(lua::State*) {
return 0;
}
static int l_open(lua::State* L) {
auto invid = lua::tointeger(L, 1);
auto layoutid = lua::require_string(L, 2);
bool playerInventory = !lua::toboolean(L, 3);
auto assets = engine->getAssets();
auto layout = assets->get<UiDocument>(layoutid);
if (layout == nullptr) {
throw std::runtime_error("there is no ui layout " + util::quote(layoutid));
}
hud->openInventory(
layout,
level->inventories->get(invid),
playerInventory
);
return 0;
}
static int l_open_block(lua::State* L) {
auto x = lua::tointeger(L, 1);
auto y = lua::tointeger(L, 2);
@@ -154,6 +174,7 @@ static int l_set_debug_cheats(lua::State* L) {
const luaL_Reg hudlib[] = {
{"open_inventory", lua::wrap<l_open_inventory>},
{"close_inventory", lua::wrap<l_close_inventory>},
{"open", lua::wrap<l_open>},
{"open_block", lua::wrap<l_open_block>},
{"open_permanent", lua::wrap<l_open_permanent>},
{"show_overlay", lua::wrap<l_show_overlay>},
@@ -109,6 +109,26 @@ static int l_inventory_unbind_block(lua::State* L) {
return 0;
}
static int l_inventory_create(lua::State* L) {
auto invsize = lua::tointeger(L, 1);
auto inv = level->inventories->create(invsize);
if (inv == nullptr) {
return lua::pushinteger(L, 0);
}
return lua::pushinteger(L, inv->getId());
}
static int l_inventory_remove(lua::State* L) {
auto invid = lua::tointeger(L, 1);
auto inv = get_inventory(invid);
if (inv == nullptr) {
return 0;
}
level->inventories->remove(invid);
return 0;
}
static int l_inventory_clone(lua::State* L) {
auto id = lua::tointeger(L, 1);
auto clone = level->inventories->clone(id);
@@ -145,5 +165,7 @@ const luaL_Reg inventorylib[] = {
{"get_block", lua::wrap<l_inventory_get_block>},
{"bind_block", lua::wrap<l_inventory_bind_block>},
{"unbind_block", lua::wrap<l_inventory_unbind_block>},
{"create", lua::wrap<l_inventory_create>},
{"remove", lua::wrap<l_inventory_remove>},
{"clone", lua::wrap<l_inventory_clone>},
{NULL, NULL}};
+1 -1
View File
@@ -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);
}
+115
View File
@@ -0,0 +1,115 @@
#include "api_lua.hpp"
#include "logic/scripting/scripting_hud.hpp"
#include "graphics/render/WorldRenderer.hpp"
#include "graphics/render/TextsRenderer.hpp"
#include "graphics/render/TextNote.hpp"
#include "engine.hpp"
using namespace scripting;
static int l_show(lua::State* L) {
auto position = lua::tovec3(L, 1);
auto text = lua::require_wstring(L, 2);
auto preset = lua::tovalue(L, 3);
auto extension = lua::tovalue(L, 4);
NotePreset notePreset {};
notePreset.deserialize(preset);
if (extension != nullptr) {
notePreset.deserialize(extension);
}
auto note = std::make_unique<TextNote>(text, notePreset, position);
return lua::pushinteger(L, renderer->texts->add(std::move(note)));
}
static int l_hide(lua::State* L) {
renderer->texts->remove(lua::touinteger(L, 1));
return 0;
}
static int l_get_text(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
return lua::pushwstring(L, note->getText());
}
return 0;
}
static int l_set_text(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
note->setText(lua::require_wstring(L, 2));
}
return 0;
}
static int l_get_pos(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
return lua::pushvec(L, note->getPosition());
}
return 0;
}
static int l_set_pos(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
note->setPosition(lua::tovec3(L, 2));
}
return 0;
}
static int l_get_axis_x(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
return lua::pushvec(L, note->getAxisX());
}
return 0;
}
static int l_set_axis_x(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
note->setAxisX(lua::tovec3(L, 2));
}
return 0;
}
static int l_get_axis_y(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
return lua::pushvec(L, note->getAxisY());
}
return 0;
}
static int l_set_axis_y(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
note->setAxisY(lua::tovec3(L, 2));
}
return 0;
}
static int l_update_settings(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
note->updatePreset(lua::tovalue(L, 2));
}
return 0;
}
static int l_set_rotation(lua::State* L) {
if (auto note = renderer->texts->get(lua::tointeger(L, 1))) {
auto matrix = lua::tomat4(L, 2);
note->setAxisX(matrix * glm::vec4(1, 0, 0, 1));
note->setAxisY(matrix * glm::vec4(0, 1, 0, 1));
}
return 0;
}
const luaL_Reg text3dlib[] = {
{"show", lua::wrap<l_show>},
{"hide", lua::wrap<l_hide>},
{"get_text", lua::wrap<l_get_text>},
{"set_text", lua::wrap<l_set_text>},
{"get_pos", lua::wrap<l_get_pos>},
{"set_pos", lua::wrap<l_set_pos>},
{"get_axis_x", lua::wrap<l_get_axis_x>},
{"set_axis_x", lua::wrap<l_set_axis_x>},
{"get_axis_y", lua::wrap<l_get_axis_y>},
{"set_axis_y", lua::wrap<l_set_axis_y>},
{"set_rotation", lua::wrap<l_set_rotation>},
{"update_settings", lua::wrap<l_update_settings>},
{NULL, NULL}
};
+1 -1
View File
@@ -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);
}
+6
View File
@@ -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}
};
+4 -2
View File
@@ -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;
}
+12 -3
View File
@@ -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;
}
+5
View File
@@ -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);
}
+16 -9
View File
@@ -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) {
@@ -657,10 +660,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 +681,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 +695,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 +708,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");
@@ -724,11 +730,12 @@ 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");
+17 -1
View File
@@ -125,11 +125,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 +139,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 +169,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 +182,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 -2
View File
@@ -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
);
}
+11 -11
View File
@@ -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]"
);
}
+17 -3
View File
@@ -17,6 +17,14 @@ static debug::Logger logger("scripting-hud");
Hud* scripting::hud = nullptr;
WorldRenderer* scripting::renderer = nullptr;
static void load_script(const std::string& name) {
auto file = engine->getPaths()->getResourcesFolder() / "scripts" / name;
std::string src = files::read_string(file);
logger.info() << "loading script " << file.u8string();
lua::execute(lua::get_main_state(), 0, src, file.u8string());
}
void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
scripting::hud = hud;
scripting::renderer = renderer;
@@ -25,6 +33,9 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
lua::openlib(L, "hud", hudlib);
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")) {
lua::call_nothrow(L, 0, 0);
@@ -46,7 +57,7 @@ void scripting::on_frontend_render() {
lua::emit_event(
lua::get_main_state(),
pack.id + ":.hudrender",
[&](lua::State* L) { return 0; }
[](lua::State* L) { return 0; }
);
}
}
@@ -65,13 +76,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");
+5 -1
View File
@@ -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
);
}
+5
View File
@@ -70,6 +70,11 @@ namespace util {
}
};
template<typename T>
inline T sqr(T value) {
return value * value;
}
/// @return integer square of distance between two points
/// @note glm::distance2 does not support integer vectors
inline int distance2(const glm::ivec3& a, const glm::ivec3& b) {
+1 -1
View File
@@ -550,7 +550,7 @@ void Entities::renderDebug(
}
void Entities::render(
Assets* assets,
const Assets& assets,
ModelBatch& batch,
const Frustum* frustum,
float delta,
+1 -1
View File
@@ -194,7 +194,7 @@ public:
LineBatch& batch, const Frustum* frustum, const DrawContext& ctx
);
void render(
Assets* assets,
const Assets& assets,
ModelBatch& batch,
const Frustum* frustum,
float delta,
+3 -3
View File
@@ -12,9 +12,9 @@
using namespace rigging;
void ModelReference::refresh(const Assets* assets) {
void ModelReference::refresh(const Assets& assets) {
if (updateFlag) {
model = assets->get<model::Model>(name);
model = assets.get<model::Model>(name);
updateFlag = false;
}
}
@@ -95,7 +95,7 @@ void SkeletonConfig::update(Skeleton& skeleton, glm::mat4 matrix) const {
}
void SkeletonConfig::render(
Assets* assets,
const Assets& assets,
ModelBatch& batch,
Skeleton& skeleton,
const glm::mat4& matrix
+2 -2
View File
@@ -32,7 +32,7 @@ namespace rigging {
model::Model* model;
bool updateFlag;
void refresh(const Assets* assets);
void refresh(const Assets& assets);
};
class Bone {
@@ -111,7 +111,7 @@ namespace rigging {
void update(Skeleton& skeleton, glm::mat4 matrix) const;
void render(
Assets* assets,
const Assets& assets,
ModelBatch& batch,
Skeleton& skeleton,
const glm::mat4& matrix
+54
View File
@@ -0,0 +1,54 @@
#include "NotePreset.hpp"
#include <map>
#include <vector>
#include "data/dv_util.hpp"
std::string to_string(NoteDisplayMode mode) {
static std::vector<std::string> names = {
"static_billboard",
"y_free_billboard",
"xy_free_billboard",
"projected"
};
return names.at(static_cast<int>(mode));
}
std::optional<NoteDisplayMode> NoteDisplayMode_from(std::string_view s) {
static std::map<std::string_view, NoteDisplayMode, std::less<>> map {
{"static_billboard", NoteDisplayMode::STATIC_BILLBOARD},
{"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD},
{"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD},
{"projected", NoteDisplayMode::PROJECTED}
};
const auto& found = map.find(s);
if (found == map.end()) {
return std::nullopt;
}
return found->second;
}
dv::value NotePreset::serialize() const {
return dv::object({
{"display", to_string(displayMode)},
{"color", dv::to_value(color)},
{"scale", scale},
{"render_distance", renderDistance},
{"xray_opacity", xrayOpacity},
{"perspective", perspective},
});
}
void NotePreset::deserialize(const dv::value& src) {
if (src.has("display")) {
displayMode = NoteDisplayMode_from(src["display"].asString()).value();
}
if (src.has("color")) {
dv::get_vec(src["color"], color);
}
src.at("scale").get(scale);
src.at("render_distance").get(renderDistance);
src.at("xray_opacity").get(xrayOpacity);
src.at("perspective").get(perspective);
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <optional>
#include "interfaces/Serializable.hpp"
enum class NoteDisplayMode {
STATIC_BILLBOARD,
Y_FREE_BILLBOARD,
XY_FREE_BILLBOARD,
PROJECTED
};
std::string to_string(NoteDisplayMode mode);
std::optional<NoteDisplayMode> NoteDisplayMode_from(std::string_view s);
struct NotePreset : public Serializable {
NoteDisplayMode displayMode = NoteDisplayMode::STATIC_BILLBOARD;
glm::vec4 color {1.0f};
float scale = 1.0f;
float renderDistance = 10.0f;
float xrayOpacity = 0.0f;
float perspective = 0.0f;
dv::value serialize() const override;
void deserialize(const dv::value& src) override;
};

Some files were not shown because too many files have changed in this diff Show More