Merge branch 'main' into curl
This commit is contained in:
commit
14b769f6ff
@ -74,6 +74,7 @@ Properties:
|
||||
| ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ |
|
||||
| text | string | yes | yes | entered text or placeholder |
|
||||
| placeholder | string | yes | yes | placeholder (used if nothing has been entered) |
|
||||
| hint | string | yes | yes | text to display when nothing is entered |
|
||||
| caret | int | yes | yes | carriage position. `textbox.caret = -1` will set the position to the end of the text |
|
||||
| editable | bool | yes | yes | text mutability |
|
||||
| multiline | bool | yes | yes | multiline support |
|
||||
|
||||
@ -72,17 +72,20 @@ Fragments used by the generator must present in the directory:
|
||||
|
||||
## Structures
|
||||
|
||||
A structure is a set of rules for inserting a fragment into the world by the generator. It currently has no properties, being created as empty objects in the `generators/generator_name.files/structures.toml` file. Example:
|
||||
A structure is a set of rules for inserting a fragment into the world by the generator. Structures are declared as objects in the file `generators/generator_name.files/structures.toml`. Example:
|
||||
```toml
|
||||
tree0 = {}
|
||||
tree1 = {}
|
||||
tree2 = {}
|
||||
tower = {}
|
||||
tower = {lowering=2}
|
||||
coal_ore0 = {}
|
||||
```
|
||||
|
||||
Currently, the name of the structure must match the name of the fragment used.
|
||||
|
||||
Available properties:
|
||||
- lowering - depth of structure lowering.
|
||||
|
||||
## Biomes
|
||||
|
||||
A biome defines what blocks and layers the terrain is generated from, as well as a set of plants and structures.
|
||||
|
||||
@ -74,6 +74,7 @@ document["worlds-panel"]:clear()
|
||||
| ----------- | ------ | ------ | ------ | ---------------------------------------------------------------------- |
|
||||
| text | string | да | да | введенный текст или заполнитель |
|
||||
| placeholder | string | да | да | заполнитель (используется если ничего не было введено) |
|
||||
| hint | string | да | да | текст, отображаемый, когда ничего не введено |
|
||||
| caret | int | да | да | позиция каретки. `textbox.caret = -1` установит позицию в конец текста |
|
||||
| editable | bool | да | да | изменяемость текста |
|
||||
| multiline | bool | да | да | поддержка многострочности |
|
||||
|
||||
@ -72,17 +72,20 @@
|
||||
|
||||
## Структуры
|
||||
|
||||
Структура - набор правил по вставке фрагмента в мир генератором. На данный момент не имеет свойств, создаваясь в виде пустых объектов в файле `generators/имя_генератора.files/structures.toml`. Пример:
|
||||
Структура - набор правил по вставке фрагмента в мир генератором. Структуры объявляются в виде объектов в файле `generators/имя_генератора.files/structures.toml`. Пример:
|
||||
```toml
|
||||
tree0 = {}
|
||||
tree1 = {}
|
||||
tree2 = {}
|
||||
tower = {}
|
||||
tower = {lowering=-2}
|
||||
coal_ore0 = {}
|
||||
```
|
||||
|
||||
На данный момент, имя структуры должно совпадать с именем использованного фрагмента.
|
||||
|
||||
Доступные свойства:
|
||||
- lowering - глубина погружения структуры под поверхность.
|
||||
|
||||
## Биомы
|
||||
|
||||
Биом определяет то, из каких блоков и какими слоями генерируется ландшафт, а так же набор растений, структур.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
tree0 = {}
|
||||
tree1 = {}
|
||||
tree2 = {}
|
||||
tower = {}
|
||||
tower = {lowering=2}
|
||||
coal_ore0 = {}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "base",
|
||||
"title": "Base",
|
||||
"version": "0.24",
|
||||
"version": "0.25",
|
||||
"description": "basic content package"
|
||||
}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
<container size='1000,480' color='#0F1E2DB2' padding='8' interval='5' context='menu'>
|
||||
<panel id='contents' pos='15,15' size='440,390' color='0' max-length='406' scrollable='true'>
|
||||
<container size='1000,580' color='#0F1E2DB2' interval='5' context='menu'>
|
||||
<panel id='contents' pos='15,15' size='440,490' color='0' max-length='455' scrollable='true'>
|
||||
<!-- content is generated in script -->
|
||||
</panel>
|
||||
<button pos='15,430' size='440,40' onclick='menu:back()'>@Back</button>
|
||||
<button pos='485,430' size='500,40' onclick='core.open_folder("user:content")'>@Open content folder</button>
|
||||
<button pos='15,525' size='440,40' onclick='menu:back()'>@Back</button>
|
||||
<button pos='485,525' size='500,40' onclick='core.open_folder("user:content")'>@Open content folder</button>
|
||||
|
||||
<panel id='search_panel' size='440,35' pos='15,485' interval='1' color='#0000004C'>
|
||||
<textbox id='search_textbox' multiline='false' size='440,25' sub-consumer='function(x) refresh_search() end'></textbox>
|
||||
</panel>
|
||||
|
||||
<panel id='content_info' pos='485,15' size='440,406' color='0' max-length='406' scrollable='true'>
|
||||
<label>@Creator</label>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
local packs_installed = {}
|
||||
|
||||
function on_open(params)
|
||||
refresh()
|
||||
end
|
||||
@ -13,11 +15,33 @@ function place_pack(panel, packinfo, callback)
|
||||
end
|
||||
packinfo.callback = callback
|
||||
panel:add(gui.template("pack", packinfo))
|
||||
if not callback then
|
||||
document["pack_"..packinfo.id].enabled = false
|
||||
end
|
||||
|
||||
function refresh_search()
|
||||
local search_text = document.search_textbox.text:lower()
|
||||
local visible = 0
|
||||
local interval = 4
|
||||
local step = -1
|
||||
|
||||
for i, v in ipairs(packs_installed) do
|
||||
local id = v[1]
|
||||
local title = v[2]
|
||||
local content = document["pack_" .. id]
|
||||
local pos = content.pos
|
||||
local size = content.size
|
||||
|
||||
if title:lower():find(search_text) or search_text == '' then
|
||||
content.enabled = true
|
||||
content.pos = {pos[1], visible * (size[2] + interval) - step}
|
||||
visible = visible + 1
|
||||
else
|
||||
content.enabled = false
|
||||
content.pos = {pos[1], (visible + #packs_installed - i) * (size[2] + interval) - step}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function open_pack(id)
|
||||
local packinfo = pack.get_info(id)
|
||||
|
||||
@ -28,8 +52,8 @@ function open_pack(id)
|
||||
end
|
||||
|
||||
function refresh()
|
||||
local packs_installed = pack.get_installed()
|
||||
local packs_available = pack.get_available()
|
||||
packs_installed = pack.get_installed()
|
||||
|
||||
for i,k in ipairs(packs_available) do
|
||||
table.insert(packs_installed, k)
|
||||
@ -41,7 +65,8 @@ function refresh()
|
||||
for i,id in ipairs(packs_installed) do
|
||||
local packinfo = pack.get_info(id)
|
||||
|
||||
packinfo.index = i
|
||||
packinfo.id = id
|
||||
packs_installed[i] = {packinfo.id, packinfo.title}
|
||||
local callback = string.format('open_pack("%s")', id)
|
||||
place_pack(contents, packinfo, callback)
|
||||
end
|
||||
|
||||
18
res/scripts/hud_classes.lua
Normal file
18
res/scripts/hud_classes.lua
Normal file
@ -0,0 +1,18 @@
|
||||
local Text3D = {__index={
|
||||
hide=function(self) return gfx.text3d.hide(self.id) end,
|
||||
get_pos=function(self) return gfx.text3d.get_pos(self.id) end,
|
||||
set_pos=function(self, v) return gfx.text3d.set_pos(self.id, v) end,
|
||||
get_axis_x=function(self) return gfx.text3d.get_axis_x(self.id) end,
|
||||
set_axis_x=function(self, v) return gfx.text3d.set_axis_x(self.id, v) end,
|
||||
get_axis_y=function(self) return gfx.text3d.get_axis_y(self.id) end,
|
||||
set_axis_y=function(self, v) return gfx.text3d.set_axis_y(self.id, v) end,
|
||||
set_rotation=function(self, m) return gfx.text3d.set_rotation(self.id, m) end,
|
||||
get_text=function(self) return gfx.text3d.get_text(self.id) end,
|
||||
set_text=function(self, s) return gfx.text3d.set_text(self.id, s) end,
|
||||
update_settings=function(self, t) return gfx.text3d.update_settings(self.id, t) end,
|
||||
}}
|
||||
|
||||
gfx.text3d.new = function(pos, text, preset, extension)
|
||||
local id = gfx.text3d.show(pos, text, preset, extension)
|
||||
return setmetatable({id=id}, Text3D)
|
||||
end
|
||||
@ -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
|
||||
@ -316,11 +338,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 +358,7 @@ inline bool contains(
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool animation(
|
||||
static bool load_animation(
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string& atlasName,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -127,7 +127,7 @@ static VoxelStructureMeta load_structure_meta(
|
||||
) {
|
||||
VoxelStructureMeta meta;
|
||||
meta.name = name;
|
||||
|
||||
config.at("lowering").get(meta.lowering);
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
@ -273,9 +272,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 +297,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 +320,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 +340,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 +374,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;
|
||||
@ -401,8 +399,8 @@ void Hud::openInventory(
|
||||
if (isInventoryOpen()) {
|
||||
closeInventory();
|
||||
}
|
||||
auto level = frontend->getLevel();
|
||||
auto content = level->content;
|
||||
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'");
|
||||
@ -415,7 +413,7 @@ void Hud::openInventory(
|
||||
inventoryOpen = true;
|
||||
}
|
||||
if (inv == nullptr) {
|
||||
inv = level->inventories->createVirtual(secondInvView->getSlotsCount());
|
||||
inv = level.inventories->createVirtual(secondInvView->getSlotsCount());
|
||||
}
|
||||
secondInvView->bind(inv, content);
|
||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
|
||||
@ -430,8 +428,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'");
|
||||
@ -443,19 +441,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)
|
||||
);
|
||||
@ -487,7 +485,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));
|
||||
}
|
||||
@ -682,7 +680,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);
|
||||
|
||||
@ -73,7 +73,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
|
||||
@ -130,7 +130,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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#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"
|
||||
@ -17,9 +17,9 @@ 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)),
|
||||
indexBuffer(std::make_unique<int[]>(capacity)),
|
||||
@ -34,7 +34,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() {
|
||||
@ -286,7 +286,7 @@ 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) {
|
||||
overflow = true;
|
||||
@ -434,10 +434,27 @@ glm::vec4 BlocksRenderer::pickSoftLight(
|
||||
}
|
||||
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
@ -446,12 +463,12 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
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);
|
||||
@ -486,12 +503,12 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
}
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
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());
|
||||
chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get());
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
@ -515,7 +532,7 @@ MeshData BlocksRenderer::createMesh() {
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
|
||||
build(chunk, chunks);
|
||||
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
|
||||
@ -19,7 +19,7 @@ class Block;
|
||||
class Chunk;
|
||||
class Chunks;
|
||||
class VoxelsVolume;
|
||||
class ChunksStorage;
|
||||
class Chunks;
|
||||
class ContentGfxCache;
|
||||
struct EngineSettings;
|
||||
struct UVRegion;
|
||||
@ -27,7 +27,7 @@ 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,8 +40,8 @@ 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;
|
||||
|
||||
@ -137,11 +137,16 @@ class BlocksRenderer {
|
||||
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
void render(const voxel* voxels);
|
||||
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);
|
||||
void build(const Chunk* chunk, const Chunks* chunks);
|
||||
std::shared_ptr<Mesh> render(const Chunk* chunk, const Chunks* chunks);
|
||||
MeshData createMesh();
|
||||
VoxelsVolume* getVoxelsBuffer() const;
|
||||
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
#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>
|
||||
@ -12,21 +20,23 @@
|
||||
|
||||
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 +48,29 @@ 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);
|
||||
}
|
||||
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";
|
||||
}
|
||||
@ -66,7 +81,7 @@ ChunksRenderer::~ChunksRenderer() {
|
||||
std::shared_ptr<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());
|
||||
auto mesh = renderer->render(chunk.get(), level.chunks.get());
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
|
||||
return mesh;
|
||||
}
|
||||
@ -103,14 +118,84 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>&
|
||||
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;
|
||||
}
|
||||
|
||||
void ChunksRenderer::update() {
|
||||
threadPool.update();
|
||||
}
|
||||
|
||||
bool ChunksRenderer::drawChunk(
|
||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||
) {
|
||||
auto chunk = level.chunks->getChunks()[index];
|
||||
if (chunk == nullptr || !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 = 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 (!frustum.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 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;
|
||||
//if (GLEW_ARB_multi_draw_indirect && false) {
|
||||
// TODO: implement Multi Draw Indirect chunks draw
|
||||
//} else {
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
visibleChunks += drawChunk(indices[i].index, camera, shader, culling);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
@ -14,10 +14,24 @@
|
||||
class Mesh;
|
||||
class Chunk;
|
||||
class Level;
|
||||
class Camera;
|
||||
class Shader;
|
||||
class Chunks;
|
||||
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;
|
||||
@ -25,26 +39,42 @@ struct RendererResult {
|
||||
};
|
||||
|
||||
class ChunksRenderer {
|
||||
Level* level;
|
||||
const Level& level;
|
||||
const Assets& assets;
|
||||
const Frustum& frustum;
|
||||
const EngineSettings& settings;
|
||||
|
||||
std::unique_ptr<BlocksRenderer> renderer;
|
||||
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
||||
std::unordered_map<glm::ivec2, bool> inwork;
|
||||
|
||||
std::vector<ChunksSortEntry> indices;
|
||||
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
|
||||
|
||||
bool drawChunk(
|
||||
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);
|
||||
std::shared_ptr<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);
|
||||
std::shared_ptr<Mesh> getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
);
|
||||
void drawChunks(const Camera& camera, Shader& shader);
|
||||
|
||||
void update();
|
||||
|
||||
static size_t visibleChunks;
|
||||
};
|
||||
|
||||
109
src/graphics/render/GuidesRenderer.cpp
Normal file
109
src/graphics/render/GuidesRenderer.cpp
Normal 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
src/graphics/render/GuidesRenderer.hpp
Normal file
28
src/graphics/render/GuidesRenderer.hpp
Normal 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
|
||||
);
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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) {}
|
||||
|
||||
@ -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))
|
||||
@ -43,14 +44,14 @@ Skybox::Skybox(uint size, Shader* shader)
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
src/graphics/render/TextNote.cpp
Normal file
47
src/graphics/render/TextNote.cpp
Normal 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
src/graphics/render/TextNote.hpp
Normal file
32
src/graphics/render/TextNote.hpp
Normal 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
src/graphics/render/TextsRenderer.cpp
Normal file
146
src/graphics/render/TextsRenderer.cpp
Normal 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
src/graphics/render/TextsRenderer.hpp
Normal file
49
src/graphics/render/TextsRenderer.hpp
Normal 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);
|
||||
};
|
||||
@ -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,37 @@ 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->get<Shader>("main");
|
||||
auto& shader = assets.require<Shader>("main");
|
||||
setupWorldShader(shader, camera, settings, fogFactor);
|
||||
|
||||
drawChunks(level->chunks.get(), camera, shader);
|
||||
chunks->drawChunks(camera, shader);
|
||||
|
||||
if (!pause) {
|
||||
scripting::on_frontend_render();
|
||||
@ -238,7 +177,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 +193,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 +205,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 +293,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 +303,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 +313,30 @@ 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) {
|
||||
if (player->debug) {
|
||||
guides->renderDebugLines(
|
||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||
);
|
||||
}
|
||||
renderLines(camera, linesShader, ctx);
|
||||
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 +347,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 +370,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 +392,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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
||||
|
||||
void TextBox::refreshLabel() {
|
||||
label->setColor(glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||
label->setText(getText());
|
||||
label->setText(input.empty() && !hint.empty() ? hint : getText());
|
||||
|
||||
if (autoresize && font) {
|
||||
auto size = getSize();
|
||||
@ -505,7 +505,7 @@ void TextBox::performEditingKeyboardEvents(keycode key) {
|
||||
} else {
|
||||
defocus();
|
||||
if (validate() && consumer) {
|
||||
consumer(label->getText());
|
||||
consumer(getText());
|
||||
}
|
||||
}
|
||||
} else if (key == keycode::TAB) {
|
||||
@ -627,7 +627,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 +638,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 +646,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);
|
||||
}
|
||||
|
||||
@ -13,23 +13,35 @@ namespace gui {
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||
std::shared_ptr<Label> label;
|
||||
/// @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;
|
||||
|
||||
@ -101,17 +113,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;
|
||||
|
||||
@ -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());
|
||||
}
|
||||
@ -331,8 +337,11 @@ 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));
|
||||
textbox->setHint(hint);
|
||||
|
||||
_readPanel(reader, element, *textbox);
|
||||
textbox->setText(text);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,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[];
|
||||
|
||||
@ -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>},
|
||||
|
||||
@ -242,6 +242,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());
|
||||
@ -364,6 +371,7 @@ 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},
|
||||
@ -430,6 +438,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));
|
||||
@ -540,6 +553,7 @@ 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},
|
||||
{"src", p_set_src},
|
||||
|
||||
115
src/logic/scripting/lua/libs/libtext3d.cpp
Normal file
115
src/logic/scripting/lua/libs/libtext3d.cpp
Normal 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}
|
||||
};
|
||||
@ -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; }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -550,7 +550,7 @@ void Entities::renderDebug(
|
||||
}
|
||||
|
||||
void Entities::render(
|
||||
Assets* assets,
|
||||
const Assets& assets,
|
||||
ModelBatch& batch,
|
||||
const Frustum* frustum,
|
||||
float delta,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
src/presets/NotePreset.cpp
Normal file
54
src/presets/NotePreset.cpp
Normal 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
src/presets/NotePreset.hpp
Normal file
29
src/presets/NotePreset.hpp
Normal 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;
|
||||
};
|
||||
@ -5,6 +5,24 @@
|
||||
#include <vector>
|
||||
|
||||
namespace util {
|
||||
template<typename Iter>
|
||||
inline void insertion_sort(Iter first, Iter last) {
|
||||
for (Iter it = first; it != last; ++it) {
|
||||
std::rotate(
|
||||
std::upper_bound(first, it, *it), it, std::next(it)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Iter, typename Compare>
|
||||
inline void insertion_sort(Iter first, Iter last, Compare compare) {
|
||||
for (Iter it = first; it != last; ++it) {
|
||||
std::rotate(
|
||||
std::upper_bound(first, it, *it, compare), it, std::next(it)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool contains(const std::vector<T>& vec, const T& value) {
|
||||
return std::find(vec.begin(), vec.end(), value) != vec.end();
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "objects/Entities.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "world/LevelEvents.hpp"
|
||||
#include "VoxelsVolume.hpp"
|
||||
#include "Block.hpp"
|
||||
#include "Chunk.hpp"
|
||||
#include "voxel.hpp"
|
||||
@ -161,7 +162,7 @@ light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) const {
|
||||
return chunk->lightmap.get(lx, y, lz);
|
||||
}
|
||||
|
||||
Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) {
|
||||
Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) const {
|
||||
if (y < 0 || y >= CHUNK_H) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -173,7 +174,7 @@ Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Chunk* Chunks::getChunk(int x, int z) {
|
||||
Chunk* Chunks::getChunk(int x, int z) const {
|
||||
if (auto ptr = areaMap.getIf(x, z)) {
|
||||
return ptr->get();
|
||||
}
|
||||
@ -675,6 +676,95 @@ bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
|
||||
return areaMap.set(chunk->x, chunk->z, chunk);
|
||||
}
|
||||
|
||||
// reduce nesting on next modification
|
||||
// 25.06.2024: not now
|
||||
// 11.11.2024: not now
|
||||
void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const {
|
||||
const Content* content = level->content;
|
||||
auto indices = content->getIndices();
|
||||
voxel* voxels = volume->getVoxels();
|
||||
light_t* lights = volume->getLights();
|
||||
int x = volume->getX();
|
||||
int y = volume->getY();
|
||||
int z = volume->getZ();
|
||||
|
||||
int w = volume->getW();
|
||||
int h = volume->getH();
|
||||
int d = volume->getD();
|
||||
|
||||
int scx = floordiv(x, CHUNK_W);
|
||||
int scz = floordiv(z, CHUNK_D);
|
||||
|
||||
int ecx = floordiv(x + w, CHUNK_W);
|
||||
int ecz = floordiv(z + d, CHUNK_D);
|
||||
|
||||
int cw = ecx - scx + 1;
|
||||
int cd = ecz - scz + 1;
|
||||
|
||||
// cw*cd chunks will be scanned
|
||||
for (int cz = scz; cz < scz + cd; cz++) {
|
||||
for (int cx = scx; cx < scx + cw; cx++) {
|
||||
const auto chunk = getChunk(cx, cz);
|
||||
if (chunk == nullptr) {
|
||||
// no chunk loaded -> filling with BLOCK_VOID
|
||||
for (int ly = y; ly < y + h; ly++) {
|
||||
for (int lz = std::max(z, cz * CHUNK_D);
|
||||
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(x, cx * CHUNK_W);
|
||||
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint idx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||
voxels[idx].id = BLOCK_VOID;
|
||||
lights[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const voxel* cvoxels = chunk->voxels;
|
||||
const light_t* clights = chunk->lightmap.getLights();
|
||||
for (int ly = y; ly < y + h; ly++) {
|
||||
for (int lz = std::max(z, cz * CHUNK_D);
|
||||
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(x, cx * CHUNK_W);
|
||||
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint vidx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||
uint cidx = vox_index(
|
||||
lx - cx * CHUNK_W,
|
||||
ly,
|
||||
lz - cz * CHUNK_D,
|
||||
CHUNK_W,
|
||||
CHUNK_D
|
||||
);
|
||||
voxels[vidx] = cvoxels[cidx];
|
||||
light_t light = clights[cidx];
|
||||
if (backlight) {
|
||||
const auto block =
|
||||
indices->blocks.get(voxels[vidx].id);
|
||||
if (block && block->lightPassing) {
|
||||
light = Lightmap::combine(
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 0) + 1),
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 1) + 1),
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 2) + 1),
|
||||
std::min(15,
|
||||
static_cast<int>(Lightmap::extract(light, 3)))
|
||||
);
|
||||
}
|
||||
}
|
||||
lights[vidx] = light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunks::saveAndClear() {
|
||||
areaMap.clear();
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ class WorldFiles;
|
||||
class LevelEvents;
|
||||
class Block;
|
||||
class Level;
|
||||
class VoxelsVolume;
|
||||
|
||||
/// Player-centred chunks matrix
|
||||
class Chunks {
|
||||
@ -39,10 +40,8 @@ class Chunks {
|
||||
);
|
||||
|
||||
util::AreaMap2D<std::shared_ptr<Chunk>, int32_t> areaMap;
|
||||
public:
|
||||
size_t visible = 0;
|
||||
WorldFiles* worldFiles;
|
||||
|
||||
public:
|
||||
Chunks(
|
||||
int32_t w,
|
||||
int32_t d,
|
||||
@ -55,8 +54,8 @@ public:
|
||||
|
||||
bool putChunk(const std::shared_ptr<Chunk>& chunk);
|
||||
|
||||
Chunk* getChunk(int32_t x, int32_t z);
|
||||
Chunk* getChunkByVoxel(int32_t x, int32_t y, int32_t z);
|
||||
Chunk* getChunk(int32_t x, int32_t z) const;
|
||||
Chunk* getChunkByVoxel(int32_t x, int32_t y, int32_t z) const;
|
||||
voxel* get(int32_t x, int32_t y, int32_t z) const;
|
||||
voxel& require(int32_t x, int32_t y, int32_t z) const;
|
||||
|
||||
@ -119,6 +118,8 @@ public:
|
||||
bool isReplaceableBlock(int32_t x, int32_t y, int32_t z);
|
||||
bool isObstacleBlock(int32_t x, int32_t y, int32_t z);
|
||||
|
||||
void getVoxels(VoxelsVolume* volume, bool backlight = false) const;
|
||||
|
||||
void setCenter(int32_t x, int32_t z);
|
||||
void resize(uint32_t newW, uint32_t newD);
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#include "world/World.hpp"
|
||||
#include "Block.hpp"
|
||||
#include "Chunk.hpp"
|
||||
#include "VoxelsVolume.hpp"
|
||||
|
||||
static debug::Logger logger("chunks-storage");
|
||||
|
||||
@ -84,93 +83,3 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
|
||||
chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
// reduce nesting on next modification
|
||||
// 25.06.2024: not now
|
||||
// TODO: move to Chunks for performance improvement
|
||||
void ChunksStorage::getVoxels(VoxelsVolume* volume, bool backlight) const {
|
||||
const Content* content = level->content;
|
||||
auto indices = content->getIndices();
|
||||
voxel* voxels = volume->getVoxels();
|
||||
light_t* lights = volume->getLights();
|
||||
int x = volume->getX();
|
||||
int y = volume->getY();
|
||||
int z = volume->getZ();
|
||||
|
||||
int w = volume->getW();
|
||||
int h = volume->getH();
|
||||
int d = volume->getD();
|
||||
|
||||
int scx = floordiv(x, CHUNK_W);
|
||||
int scz = floordiv(z, CHUNK_D);
|
||||
|
||||
int ecx = floordiv(x + w, CHUNK_W);
|
||||
int ecz = floordiv(z + d, CHUNK_D);
|
||||
|
||||
int cw = ecx - scx + 1;
|
||||
int cd = ecz - scz + 1;
|
||||
|
||||
// cw*cd chunks will be scanned
|
||||
for (int cz = scz; cz < scz + cd; cz++) {
|
||||
for (int cx = scx; cx < scx + cw; cx++) {
|
||||
const auto& found = chunksMap.find(glm::ivec2(cx, cz));
|
||||
if (found == chunksMap.end()) {
|
||||
// no chunk loaded -> filling with BLOCK_VOID
|
||||
for (int ly = y; ly < y + h; ly++) {
|
||||
for (int lz = std::max(z, cz * CHUNK_D);
|
||||
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(x, cx * CHUNK_W);
|
||||
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint idx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||
voxels[idx].id = BLOCK_VOID;
|
||||
lights[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto& chunk = found->second;
|
||||
const voxel* cvoxels = chunk->voxels;
|
||||
const light_t* clights = chunk->lightmap.getLights();
|
||||
for (int ly = y; ly < y + h; ly++) {
|
||||
for (int lz = std::max(z, cz * CHUNK_D);
|
||||
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(x, cx * CHUNK_W);
|
||||
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint vidx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||
uint cidx = vox_index(
|
||||
lx - cx * CHUNK_W,
|
||||
ly,
|
||||
lz - cz * CHUNK_D,
|
||||
CHUNK_W,
|
||||
CHUNK_D
|
||||
);
|
||||
voxels[vidx] = cvoxels[cidx];
|
||||
light_t light = clights[cidx];
|
||||
if (backlight) {
|
||||
const auto block =
|
||||
indices->blocks.get(voxels[vidx].id);
|
||||
if (block && block->lightPassing) {
|
||||
light = Lightmap::combine(
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 0) + 1),
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 1) + 1),
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 2) + 1),
|
||||
std::min(15,
|
||||
static_cast<int>(Lightmap::extract(light, 3)))
|
||||
);
|
||||
}
|
||||
}
|
||||
lights[vidx] = light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
|
||||
class Chunk;
|
||||
class Level;
|
||||
class VoxelsVolume;
|
||||
|
||||
class ChunksStorage {
|
||||
Level* level;
|
||||
@ -23,6 +22,5 @@ public:
|
||||
std::shared_ptr<Chunk> get(int x, int z) const;
|
||||
void store(const std::shared_ptr<Chunk>& chunk);
|
||||
void remove(int x, int y);
|
||||
void getVoxels(VoxelsVolume* volume, bool backlight = false) const;
|
||||
std::shared_ptr<Chunk> create(int x, int z);
|
||||
};
|
||||
|
||||
@ -66,3 +66,7 @@ void Camera::setFov(float fov) {
|
||||
float Camera::getFov() const {
|
||||
return fov;
|
||||
}
|
||||
|
||||
float Camera::getAspectRatio() const {
|
||||
return aspect;
|
||||
}
|
||||
|
||||
@ -35,4 +35,6 @@ public:
|
||||
|
||||
void setFov(float fov);
|
||||
float getFov() const;
|
||||
|
||||
float getAspectRatio() const;
|
||||
};
|
||||
|
||||
@ -211,9 +211,12 @@ int Window::initialize(DisplaySettings* settings) {
|
||||
setFramerate(settings->framerate.get());
|
||||
const GLubyte* vendor = glGetString(GL_VENDOR);
|
||||
const GLubyte* renderer = glGetString(GL_RENDERER);
|
||||
logger.info() << "GL Vendor: " << (char*)vendor;
|
||||
logger.info() << "GL Renderer: " << (char*)renderer;
|
||||
logger.info() << "GL Vendor: " << reinterpret_cast<const char*>(vendor);
|
||||
logger.info() << "GL Renderer: " << reinterpret_cast<const char*>(renderer);
|
||||
logger.info() << "GLFW: " << glfwGetVersionString();
|
||||
glm::vec2 scale;
|
||||
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &scale.x, &scale.y);
|
||||
logger.info() << "monitor content scale: " << scale.x << "x" << scale.y;
|
||||
|
||||
input_util::initialize();
|
||||
return 0;
|
||||
|
||||
@ -96,6 +96,10 @@ World* Level::getWorld() {
|
||||
return world.get();
|
||||
}
|
||||
|
||||
const World* Level::getWorld() const {
|
||||
return world.get();
|
||||
}
|
||||
|
||||
void Level::onSave() {
|
||||
auto& cameraIndices = content->getIndices(ResourceType::CAMERA);
|
||||
for (size_t i = 0; i < cameraIndices.size(); i++) {
|
||||
|
||||
@ -50,6 +50,8 @@ public:
|
||||
|
||||
World* getWorld();
|
||||
|
||||
const World* getWorld() const;
|
||||
|
||||
/// Spawns object of class T and returns pointer to it.
|
||||
/// @param T class that derives the Object class
|
||||
/// @param args pass arguments needed for T class constructor
|
||||
|
||||
@ -15,7 +15,10 @@ class VoxelFragment;
|
||||
struct GeneratorDef;
|
||||
|
||||
struct VoxelStructureMeta {
|
||||
/// @brief Structure name
|
||||
std::string name;
|
||||
/// @brief Structure lowering on placement
|
||||
int lowering = 0;
|
||||
};
|
||||
|
||||
struct BlocksLayer {
|
||||
|
||||
@ -26,7 +26,7 @@ std::unique_ptr<VoxelFragment> VoxelFragment::create(
|
||||
if (crop) {
|
||||
VoxelsVolume volume(size.x, size.y, size.z);
|
||||
volume.setPosition(start.x, start.y, start.z);
|
||||
level->chunksStorage->getVoxels(&volume);
|
||||
level->chunks->getVoxels(&volume);
|
||||
|
||||
auto end = start + size;
|
||||
|
||||
@ -51,7 +51,7 @@ std::unique_ptr<VoxelFragment> VoxelFragment::create(
|
||||
|
||||
VoxelsVolume volume(size.x, size.y, size.z);
|
||||
volume.setPosition(start.x, start.y, start.z);
|
||||
level->chunksStorage->getVoxels(&volume);
|
||||
level->chunks->getVoxels(&volume);
|
||||
|
||||
auto volVoxels = volume.getVoxels();
|
||||
std::vector<voxel> voxels(size.x * size.y * size.z);
|
||||
|
||||
@ -270,10 +270,11 @@ void WorldGenerator::generateStructures(
|
||||
if (height < def.seaLevel) {
|
||||
continue;
|
||||
}
|
||||
auto& structure = *def.structures[structureId]->fragments[rotation];
|
||||
glm::ivec3 position {x, height, z};
|
||||
position.x -= structure.getSize().x / 2;
|
||||
position.z -= structure.getSize().z / 2;
|
||||
auto& structure = *def.structures[structureId];
|
||||
auto& fragment = *structure.fragments[rotation];
|
||||
glm::ivec3 position {x, height-structure.meta.lowering, z};
|
||||
position.x -= fragment.getSize().x / 2;
|
||||
position.z -= fragment.getSize().z / 2;
|
||||
placeStructure(
|
||||
StructurePlacement {
|
||||
structureId,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user