Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blockwraps enhancements #396

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions doc/en/scripting/builtins/libgfx-blockwraps.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Library for working with *block wrappers*.
Block wrappers are introduced to implement block destruction animation and can be used for other purposes.

```lua
-- Creates a wrapper at the specified position, with the specified texture.
-- Creates a wrapper at the specified position, with the specified texture, removing the existing one at the position.
-- Returns the wrapper id.
gfx.blockwraps.wrap(position: vec3, texture: str) --> int

Expand All @@ -17,6 +17,22 @@ gfx.blockwraps.set_pos(id: int, position: vec3)

-- Changes the texture of the wrapper, if it exists.
gfx.blockwraps.set_texture(id: int, texture: str)

-- Gets the wrapper position if it exists.
-- Returns the wrapper position.
gfx.blockwraps.get_texture(id: int)

-- Gets the texture of the wrapper, if it exists.
-- Returns the wrapper texture.
gfx.blockwraps.get_texture(id: int)

-- Checks the existence of the wrapper.
-- Returns true/false depending on the wrapper existence.
gfx.blockwraps.is_alive(id: int)

-- Searches for wrapper at the specified position.
-- Returns the wrapper id.
gfx.block wrap s.get_on_pos(position: vec3)
```

Wrappers are not automatically removed without calling `unwrap`.
Wrappers are removed automatically when the block is broken/replaced.
20 changes: 18 additions & 2 deletions doc/ru/scripting/builtins/libgfx-blockwraps.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
использоваться для иных задач.

```lua
-- Создаёт обертку на указанной позиции, с указанной текстурой.
-- Создаёт обертку на указанной позиции, с указанной текстурой, удаляя существующую на позиции.
-- Возвращает id обёртки.
gfx.blockwraps.wrap(position: vec3, texture: str) --> int

Expand All @@ -18,6 +18,22 @@ gfx.blockwraps.set_pos(id: int, position: vec3)

-- Меняет текстуру обёртки, если она существует.
gfx.blockwraps.set_texture(id: int, texture: str)

-- Получает позицию обёртки, если она существует.
-- Возвращает позицию обёртки.
gfx.blockwraps.get_texture(id: int)

-- Получает текстуру обёртки, если она существует.
-- Возвращает текстуру обёртки.
gfx.blockwraps.get_texture(id: int)

-- Проверяет существование обёртки.
-- Возвращает true/false в зависимости от существования обёртки.
gfx.blockwraps.is_alive(id: int)

-- Ищет обёртку на указанной позиции.
-- Возвращает id обёртки.
gfx.blockwraps.get_on_pos(position: vec3)
```

Обертки не удаляются автоматически без вызова `unwrap`.
Обертки удаляются автоматически при ломании/замене блока.
31 changes: 25 additions & 6 deletions src/graphics/render/BlockWrapsRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@
#include "voxels/Chunks.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
#include "world/LevelEvents.hpp"

BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level)
: assets(assets), level(level), batch(std::make_unique<MainBatch>(1024)) {
this->level.events->listen(
EVT_BLOCK_SET,
[this](lvl_event_type, void* pos) {
if (const u64id_t existingId = get_id_by_pos(*static_cast<glm::ivec3*>(pos))) {
remove(existingId);
}
}
);
}

BlockWrapsRenderer::~BlockWrapsRenderer() = default;
Expand Down Expand Up @@ -88,21 +97,31 @@ void BlockWrapsRenderer::draw(const DrawContext& pctx, const Player& player) {
u64id_t BlockWrapsRenderer::add(
const glm::ivec3& position, const std::string& texture
) {
if (const u64id_t existingId = get_id_by_pos(position)) {
remove(existingId);
}

u64id_t id = nextWrapper++;
wrappers[id] = std::make_unique<BlockWrapper>(
BlockWrapper {position, texture}
);
positionIndex[position] = id;
return id;
}

BlockWrapper* BlockWrapsRenderer::get(u64id_t id) const {
const auto& found = wrappers.find(id);
if (found == wrappers.end()) {
return nullptr;
}
return found->second.get();
return (found != wrappers.end()) ? found->second.get() : nullptr;
}

void BlockWrapsRenderer::remove(u64id_t id) {
wrappers.erase(id);
u64id_t BlockWrapsRenderer::get_id_by_pos(const glm::ivec3& position) const {
const auto& found = positionIndex.find(position);
return (found != positionIndex.end()) ? found->second : 0;
}

void BlockWrapsRenderer::remove(u64id_t id) {
if (const auto& found = wrappers.find(id); found != wrappers.end()) {
positionIndex.erase(found->second->position);
wrappers.erase(found);
}
}
12 changes: 12 additions & 0 deletions src/graphics/render/BlockWrapsRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <string>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <glm/glm.hpp>
#include <functional>

#include "MainBatch.hpp"
#include "typedefs.hpp"
Expand All @@ -12,6 +15,13 @@ class Player;
class Level;
class DrawContext;

template <>
struct std::hash<glm::ivec3> {
size_t operator()(const glm::ivec3& vec) const noexcept {
return std::hash<int>()(vec.x) ^ (std::hash<int>()(vec.y) << 1) ^ (std::hash<int>()(vec.z) << 2);
}
};

struct BlockWrapper {
glm::ivec3 position;
std::string texture;
Expand All @@ -23,6 +33,7 @@ class BlockWrapsRenderer {
std::unique_ptr<MainBatch> batch;

std::unordered_map<u64id_t, std::unique_ptr<BlockWrapper>> wrappers;
std::unordered_map<glm::ivec3, u64id_t> positionIndex;
u64id_t nextWrapper = 1;

void draw(const BlockWrapper& wrapper);
Expand All @@ -35,6 +46,7 @@ class BlockWrapsRenderer {
u64id_t add(const glm::ivec3& position, const std::string& texture);

BlockWrapper* get(u64id_t id) const;
u64id_t get_id_by_pos(const glm::ivec3& position) const;

void remove(u64id_t id);
};
2 changes: 1 addition & 1 deletion src/graphics/render/WorldRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ WorldRenderer::WorldRenderer(
auto& settings = engine->getSettings();
level.events->listen(
EVT_CHUNK_HIDDEN,
[this](lvl_event_type, Chunk* chunk) { chunks->unload(chunk); }
[this](lvl_event_type, void* chunk) { chunks->unload(static_cast<Chunk*>(chunk)); }
);
auto assets = engine->getAssets();
skybox = std::make_unique<Skybox>(
Expand Down
31 changes: 31 additions & 0 deletions src/logic/scripting/lua/libs/libblockwraps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,41 @@ static int l_set_texture(lua::State* L) {
return 0;
}

static int l_get_pos(lua::State* L) {
if (const auto wrapper = renderer->blockWraps->get(lua::tointeger(L, 1))) {
return lua::pushvec3(L, wrapper->position);
}
return 0;
}

static int l_get_texture(lua::State* L) {
if (const auto wrapper = renderer->blockWraps->get(lua::tointeger(L, 1))) {
return lua::pushstring(L, wrapper->texture);
}
return 0;
}

static int l_is_alive(lua::State* L) {
return lua::pushboolean(
L, renderer->blockWraps->get(lua::tointeger(L, 1)) != nullptr
);
}

static int l_get_on_pos(lua::State* L) {
if (const auto id = renderer->blockWraps->get_id_by_pos(lua::tovec3(L, 1))) {
return lua::pushinteger(L, id);
}
return 0;
}

const luaL_Reg blockwrapslib[] = {
{"wrap", lua::wrap<l_wrap>},
{"unwrap", lua::wrap<l_unwrap>},
{"set_pos", lua::wrap<l_set_pos>},
{"set_texture", lua::wrap<l_set_texture>},
{"get_pos", lua::wrap<l_get_pos>},
{"get_texture", lua::wrap<l_get_texture>},
{"is_alive", lua::wrap<l_is_alive>},
{"get_on_pos", lua::wrap<l_get_on_pos>},
{NULL, NULL}
};
3 changes: 3 additions & 0 deletions src/voxels/Chunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ void Chunks::set(
if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) {
chunk->flags.modified = true;
}

glm::ivec3 pos(x, y, z);
level->events->trigger(EVT_BLOCK_SET, &pos);
}

voxel* Chunks::rayCast(
Expand Down
4 changes: 2 additions & 2 deletions src/world/Level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ Level::Level(
);
lighting = std::make_unique<Lighting>(content, chunks.get());

events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type, Chunk* chunk) {
this->chunksStorage->remove(chunk->x, chunk->z);
events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type, void* chunk) {
this->chunksStorage->remove(static_cast<Chunk*>(chunk)->x, static_cast<Chunk*>(chunk)->z);
});

inventories = std::make_unique<Inventories>(*this);
Expand Down
14 changes: 6 additions & 8 deletions src/world/LevelEvents.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
#include "LevelEvents.hpp"

#include "voxels/Chunk.hpp"

using std::vector;

void LevelEvents::listen(lvl_event_type type, const chunk_event_func& func) {
auto& callbacks = chunk_callbacks[type];
void LevelEvents::listen(lvl_event_type type, const event_func& func) {
auto& callbacks = this->callbacks[type];
callbacks.push_back(func);
}

void LevelEvents::trigger(lvl_event_type type, Chunk* chunk) {
const auto& callbacks = chunk_callbacks[type];
for (const chunk_event_func& func : callbacks) {
func(type, chunk);
void LevelEvents::trigger(lvl_event_type type, void* data) {
const auto& callbacks = this->callbacks[type];
for (const event_func& func : callbacks) {
func(type, data);
}
}
12 changes: 5 additions & 7 deletions src/world/LevelEvents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@
#include <unordered_map>
#include <vector>

class Chunk;

enum lvl_event_type {
EVT_CHUNK_HIDDEN,
EVT_BLOCK_SET,
};

using chunk_event_func = std::function<void(lvl_event_type, Chunk*)>;
using event_func = std::function<void(lvl_event_type, void*)>;

class LevelEvents {
std::unordered_map<lvl_event_type, std::vector<chunk_event_func>>
chunk_callbacks;
std::unordered_map<lvl_event_type, std::vector<event_func>> callbacks;
public:
void listen(lvl_event_type type, const chunk_event_func& func);
void trigger(lvl_event_type type, Chunk* chunk);
void listen(lvl_event_type type, const event_func& func);
void trigger(lvl_event_type type, void* data);
};
Loading