summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruntodesu <kirill@untode.su>2025-09-12 13:33:52 +0500
committeruntodesu <kirill@untode.su>2025-09-12 13:33:52 +0500
commite9076f22fe2a49d1cd8933e54b7b00c5dd943269 (patch)
tree89b8a4ca1861196e067dcba218fad1d7f889b860
parent68694a9c9d7d27d3b79c7b96bb67f56db2f75c45 (diff)
downloadvoxelius-e9076f22fe2a49d1cd8933e54b7b00c5dd943269.tar.bz2
voxelius-e9076f22fe2a49d1cd8933e54b7b00c5dd943269.zip
It compiles
-rw-r--r--game/client/experiments.cc6
-rw-r--r--game/client/game.cc36
-rw-r--r--game/client/session.cc10
-rw-r--r--game/client/world/chunk_mesher.cc192
-rw-r--r--game/client/world/chunk_quad.hh6
-rw-r--r--game/client/world/player_target.cc15
-rw-r--r--game/client/world/player_target.hh3
-rw-r--r--game/client/world/voxel_sounds.cc53
-rw-r--r--game/client/world/voxel_sounds.hh6
-rw-r--r--game/server/receive.cc5
-rw-r--r--game/server/sessions.cc4
-rw-r--r--game/server/world/overworld.cc12
-rw-r--r--game/shared/entity/collision.cc36
-rw-r--r--game/shared/entity/grounded.hh2
-rw-r--r--game/shared/game_voxels.cc193
-rw-r--r--game/shared/game_voxels.hh1
-rw-r--r--game/shared/world/CMakeLists.txt4
-rw-r--r--game/shared/world/feature.cc28
-rw-r--r--game/shared/world/feature.hh2
-rw-r--r--game/shared/world/item_registry.cc9
-rw-r--r--game/shared/world/item_registry.hh9
-rw-r--r--game/shared/world/voxel.cc95
-rw-r--r--game/shared/world/voxel.hh130
-rw-r--r--game/shared/world/voxel_registry.cc22
-rw-r--r--game/shared/world/voxel_registry.hh4
-rw-r--r--game/shared/world/voxels/generic.hh27
26 files changed, 524 insertions, 386 deletions
diff --git a/game/client/experiments.cc b/game/client/experiments.cc
index 2b9fe89..b947012 100644
--- a/game/client/experiments.cc
+++ b/game/client/experiments.cc
@@ -22,7 +22,7 @@
static void on_glfw_mouse_button(const io::GlfwMouseButtonEvent& event)
{
if(!globals::gui_screen && session::is_ingame()) {
- if((event.action == GLFW_PRESS) && (world::player_target::voxel != NULL_VOXEL_ID)) {
+ if((event.action == GLFW_PRESS) && world::player_target::voxel) {
if(event.button == GLFW_MOUSE_BUTTON_LEFT) {
experiments::attack();
return;
@@ -68,13 +68,13 @@ void experiments::update_late(void)
void experiments::attack(void)
{
- globals::dimension->set_voxel(NULL_VOXEL_ID, world::player_target::coord);
+ globals::dimension->set_voxel(nullptr, world::player_target::coord);
}
void experiments::interact(void)
{
if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) {
- if(info->place_voxel != NULL_VOXEL_ID) {
+ if(info->place_voxel) {
globals::dimension->set_voxel(info->place_voxel, world::player_target::coord + world::player_target::normal);
}
}
diff --git a/game/client/game.cc b/game/client/game.cc
index a59aec3..257bd2d 100644
--- a/game/client/game.cc
+++ b/game/client/game.cc
@@ -372,24 +372,40 @@ void client_game::init_late(void)
// NOTE: this is very debug, early and a quite
// conservative limit choice; there must be a better
// way to make this limit way smaller than it currently is
- for(const std::shared_ptr<world::VoxelInfo>& info : world::voxel_registry::voxels) {
- for(const world::VoxelTexture& vtex : info->textures) {
- max_texture_count += vtex.paths.size();
- }
+ for(const auto& voxel : world::voxel_registry::voxels) {
+ max_texture_count += voxel->get_default_textures().size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_NORTH).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_SOUTH).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_EAST).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_WEST).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_TOP).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_BOTTOM).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_CROSS_NWSE).size();
+ max_texture_count += voxel->get_face_textures(world::VFACE_CROSS_NESW).size();
}
// UNDONE: asset packs for non-16x16 stuff
world::voxel_atlas::create(16, 16, max_texture_count);
- for(std::shared_ptr<world::VoxelInfo>& info : world::voxel_registry::voxels) {
- for(world::VoxelTexture& vtex : info->textures) {
- if(auto strip = world::voxel_atlas::find_or_load(vtex.paths)) {
- vtex.cached_offset = strip->offset;
- vtex.cached_plane = strip->plane;
+ for(auto& voxel : world::voxel_registry::voxels) {
+ constexpr std::array faces = {
+ world::VFACE_NORTH,
+ world::VFACE_SOUTH,
+ world::VFACE_EAST,
+ world::VFACE_WEST,
+ world::VFACE_TOP,
+ world::VFACE_BOTTOM,
+ world::VFACE_CROSS_NWSE,
+ world::VFACE_CROSS_NESW,
+ };
+
+ for(auto face : faces) {
+ if(auto strip = world::voxel_atlas::find_or_load(voxel->get_face_textures(face))) {
+ voxel->set_face_cache(face, strip->offset, strip->plane);
continue;
}
- spdlog::critical("client_gl: {}: failed to load atlas strips", info->name);
+ spdlog::critical("client_gl: {}: failed to load atlas strips", voxel->get_name());
std::terminate();
}
}
diff --git a/game/client/session.cc b/game/client/session.cc
index 7e0d36c..c33b4d9 100644
--- a/game/client/session.cc
+++ b/game/client/session.cc
@@ -98,8 +98,10 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet)
auto index = coord::to_index(lpos);
if(auto chunk = globals::dimension->find_chunk(cpos)) {
- if(chunk->get_voxel(index) != packet.voxel) {
- chunk->set_voxel(packet.voxel, index);
+ auto packet_voxel = world::voxel_registry::find(packet.voxel);
+
+ if(chunk->get_voxel(index) != packet_voxel) {
+ chunk->set_voxel(packet_voxel, index);
world::ChunkUpdateEvent event;
event.dimension = globals::dimension;
@@ -125,7 +127,7 @@ static void on_voxel_set(const world::VoxelSetEvent& event)
// FIXME: should we also validate things here or wait for the server to do so
protocol::SetVoxel packet;
packet.vpos = coord::to_voxel(event.cpos, event.lpos);
- packet.voxel = event.voxel;
+ packet.voxel = event.voxel->get_id();
protocol::send(session::peer, protocol::encode(packet));
}
@@ -283,7 +285,7 @@ void session::send_login_request(void)
{
protocol::LoginRequest packet;
packet.version = protocol::VERSION;
- packet.voxel_registry_checksum = world::voxel_registry::calculate_checksum();
+ packet.voxel_registry_checksum = world::voxel_registry::get_checksum();
packet.item_registry_checksum = world::item_registry::calculate_checksum();
packet.password_hash = server_password_hash;
packet.username = client_game::username.get();
diff --git a/game/client/world/chunk_mesher.cc b/game/client/world/chunk_mesher.cc
index a8ee817..bc90a03 100644
--- a/game/client/world/chunk_mesher.cc
+++ b/game/client/world/chunk_mesher.cc
@@ -8,6 +8,7 @@
#include "shared/world/chunk.hh"
#include "shared/world/dimension.hh"
+#include "shared/world/voxel.hh"
#include "shared/world/voxel_registry.hh"
#include "shared/coord.hh"
@@ -56,37 +57,6 @@ static const CachedChunkCoord get_cached_cpos(const chunk_pos& pivot, const chun
return CPOS_ITSELF;
}
-static world::voxel_facing get_facing(world::voxel_face face, world::voxel_type type)
-{
- if(type == world::voxel_type::CROSS) {
- switch(face) {
- case world::voxel_face::CROSS_NESW:
- return world::voxel_facing::NESW;
- case world::voxel_face::CROSS_NWSE:
- return world::voxel_facing::NWSE;
- default:
- return world::voxel_facing::NORTH;
- }
- }
-
- switch(face) {
- case world::voxel_face::CUBE_NORTH:
- return world::voxel_facing::NORTH;
- case world::voxel_face::CUBE_SOUTH:
- return world::voxel_facing::SOUTH;
- case world::voxel_face::CUBE_EAST:
- return world::voxel_facing::EAST;
- case world::voxel_face::CUBE_WEST:
- return world::voxel_facing::WEST;
- case world::voxel_face::CUBE_TOP:
- return world::voxel_facing::UP;
- case world::voxel_face::CUBE_BOTTOM:
- return world::voxel_facing::DOWN;
- default:
- return world::voxel_facing::NORTH;
- }
-}
-
class GL_MeshingTask final : public Task {
public:
explicit GL_MeshingTask(entt::entity entity, const chunk_pos& cpos);
@@ -95,11 +65,10 @@ public:
virtual void finalize(void) override;
private:
- bool vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const;
- void push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face);
- void push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face,
- std::size_t entropy);
- void make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, std::size_t entropy);
+ bool vis_test(const world::Voxel* voxel, const local_pos& lpos) const;
+ void push_quad_a(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face);
+ void push_quad_v(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face, std::size_t entropy);
+ void make_cube(const world::Voxel* voxel, const local_pos& lpos, world::VoxelVisBits vis, std::size_t entropy);
void cache_chunk(const chunk_pos& cpos);
private:
@@ -138,41 +107,39 @@ void GL_MeshingTask::process(void)
return;
}
- const auto voxel = voxels[i];
const auto lpos = coord::to_local(i);
+ const auto voxel = world::voxel_registry::find(voxels[i]);
- const auto info = world::voxel_registry::find(voxel);
-
- if(info == nullptr) {
+ if(voxel == nullptr) {
// Either a NULL_VOXEL_ID or something went
// horribly wrong and we don't what this is
continue;
}
- world::voxel_vis vis = 0;
+ unsigned int vis = 0U;
- if(vis_test(voxel, info, lpos + DIR_NORTH<local_pos::value_type>)) {
- vis |= world::VIS_NORTH;
+ if(vis_test(voxel, lpos + DIR_NORTH<local_pos::value_type>)) {
+ vis |= world::VVIS_NORTH;
}
- if(vis_test(voxel, info, lpos + DIR_SOUTH<local_pos::value_type>)) {
- vis |= world::VIS_SOUTH;
+ if(vis_test(voxel, lpos + DIR_SOUTH<local_pos::value_type>)) {
+ vis |= world::VVIS_SOUTH;
}
- if(vis_test(voxel, info, lpos + DIR_EAST<local_pos::value_type>)) {
- vis |= world::VIS_EAST;
+ if(vis_test(voxel, lpos + DIR_EAST<local_pos::value_type>)) {
+ vis |= world::VVIS_EAST;
}
- if(vis_test(voxel, info, lpos + DIR_WEST<local_pos::value_type>)) {
- vis |= world::VIS_WEST;
+ if(vis_test(voxel, lpos + DIR_WEST<local_pos::value_type>)) {
+ vis |= world::VVIS_WEST;
}
- if(vis_test(voxel, info, lpos + DIR_UP<local_pos::value_type>)) {
- vis |= world::VIS_UP;
+ if(vis_test(voxel, lpos + DIR_UP<local_pos::value_type>)) {
+ vis |= world::VVIS_UP;
}
- if(vis_test(voxel, info, lpos + DIR_DOWN<local_pos::value_type>)) {
- vis |= world::VIS_DOWN;
+ if(vis_test(voxel, lpos + DIR_DOWN<local_pos::value_type>)) {
+ vis |= world::VVIS_DOWN;
}
const auto vpos = coord::to_voxel(m_cpos, lpos);
@@ -180,7 +147,7 @@ void GL_MeshingTask::process(void)
const auto entropy = math::crc64(&entropy_src, sizeof(entropy_src));
// FIXME: handle different voxel types
- make_cube(voxel, info, lpos, vis, entropy);
+ make_cube(voxel, lpos, world::VoxelVisBits(vis), entropy);
}
}
@@ -254,7 +221,7 @@ void GL_MeshingTask::finalize(void)
}
}
-bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const
+bool GL_MeshingTask::vis_test(const world::Voxel* voxel, const local_pos& lpos) const
{
const auto pvpos = coord::to_voxel(m_cpos, lpos);
const auto pcpos = coord::to_chunk(pvpos);
@@ -263,26 +230,18 @@ bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, cons
const auto cached_cpos = get_cached_cpos(m_cpos, pcpos);
const auto& voxels = m_cache.at(cached_cpos);
- const auto neighbour = voxels[index];
+ const auto neighbour = world::voxel_registry::find(voxels[index]);
bool result;
- if(neighbour == NULL_VOXEL_ID) {
+ if(neighbour == nullptr) {
result = true;
}
else if(neighbour == voxel) {
result = false;
}
- else if(auto neighbour_info = world::voxel_registry::find(neighbour)) {
- if(neighbour_info->blending != info->blending) {
- // Voxel types that use blending are semi-transparent;
- // this means they're rendered using a different setup
- // and they must have visible faces with opaque voxels
- result = neighbour_info->blending;
- }
- else {
- result = false;
- }
+ else if(neighbour->get_render_mode() != voxel->get_render_mode()) {
+ result = true;
}
else {
result = false;
@@ -291,88 +250,95 @@ bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, cons
return result;
}
-void GL_MeshingTask::push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face)
+void GL_MeshingTask::push_quad_a(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face)
{
- const world::voxel_facing facing = get_facing(face, info->type);
- const world::VoxelTexture& vtex = info->textures[static_cast<std::size_t>(face)];
-
- if(info->blending) {
- m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size()));
- }
- else {
- m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size()));
+ auto cached_offset = voxel->get_cached_face_offset(face);
+ auto cached_plane = voxel->get_cached_face_plane(face);
+ auto& textures = voxel->get_face_textures(face);
+
+ switch(voxel->get_render_mode()) {
+ case world::VRENDER_OPAQUE:
+ m_quads_s[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset, textures.size()));
+ break;
+
+ case world::VRENDER_BLEND:
+ m_quads_b[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset, textures.size()));
+ break;
}
}
-void GL_MeshingTask::push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face,
+void GL_MeshingTask::push_quad_v(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face,
std::size_t entropy)
{
- const world::voxel_facing facing = get_facing(face, info->type);
- const world::VoxelTexture& vtex = info->textures[static_cast<std::size_t>(face)];
- const std::size_t entropy_mod = entropy % vtex.paths.size();
-
- if(info->blending) {
- m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0));
- }
- else {
- m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0));
+ auto cached_offset = voxel->get_cached_face_offset(face);
+ auto cached_plane = voxel->get_cached_face_plane(face);
+ auto& textures = voxel->get_face_textures(face);
+ auto index = entropy % textures.size();
+
+ switch(voxel->get_render_mode()) {
+ case world::VRENDER_OPAQUE:
+ m_quads_s[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset + index, 0));
+ break;
+
+ case world::VRENDER_BLEND:
+ m_quads_b[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset + index, 0));
+ break;
}
}
-void GL_MeshingTask::make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis,
- std::size_t entropy)
+void GL_MeshingTask::make_cube(const world::Voxel* voxel, const local_pos& lpos, world::VoxelVisBits vis, std::size_t entropy)
{
const glm::fvec3 fpos = glm::fvec3(lpos);
const glm::fvec2 fsize = glm::fvec2(1.0f, 1.0f);
- if(info->animated) {
- if(vis & world::VIS_NORTH) {
- push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_NORTH);
+ if(voxel->is_animated()) {
+ if(vis & world::VVIS_NORTH) {
+ push_quad_a(voxel, fpos, fsize, world::VFACE_NORTH);
}
- if(vis & world::VIS_SOUTH) {
- push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_SOUTH);
+ if(vis & world::VVIS_SOUTH) {
+ push_quad_a(voxel, fpos, fsize, world::VFACE_SOUTH);
}
- if(vis & world::VIS_EAST) {
- push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_EAST);
+ if(vis & world::VVIS_EAST) {
+ push_quad_a(voxel, fpos, fsize, world::VFACE_EAST);
}
- if(vis & world::VIS_WEST) {
- push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_WEST);
+ if(vis & world::VVIS_WEST) {
+ push_quad_a(voxel, fpos, fsize, world::VFACE_WEST);
}
- if(vis & world::VIS_UP) {
- push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_TOP);
+ if(vis & world::VVIS_UP) {
+ push_quad_a(voxel, fpos, fsize, world::VFACE_TOP);
}
- if(vis & world::VIS_DOWN) {
- push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM);
+ if(vis & world::VVIS_DOWN) {
+ push_quad_a(voxel, fpos, fsize, world::VFACE_BOTTOM);
}
}
else {
- if(vis & world::VIS_NORTH) {
- push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_NORTH, entropy);
+ if(vis & world::VVIS_NORTH) {
+ push_quad_v(voxel, fpos, fsize, world::VFACE_NORTH, entropy);
}
- if(vis & world::VIS_SOUTH) {
- push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_SOUTH, entropy);
+ if(vis & world::VVIS_SOUTH) {
+ push_quad_v(voxel, fpos, fsize, world::VFACE_SOUTH, entropy);
}
- if(vis & world::VIS_EAST) {
- push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_EAST, entropy);
+ if(vis & world::VVIS_EAST) {
+ push_quad_v(voxel, fpos, fsize, world::VFACE_EAST, entropy);
}
- if(vis & world::VIS_WEST) {
- push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_WEST, entropy);
+ if(vis & world::VVIS_WEST) {
+ push_quad_v(voxel, fpos, fsize, world::VFACE_WEST, entropy);
}
- if(vis & world::VIS_UP) {
- push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_TOP, entropy);
+ if(vis & world::VVIS_UP) {
+ push_quad_v(voxel, fpos, fsize, world::VFACE_TOP, entropy);
}
- if(vis & world::VIS_DOWN) {
- push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM, entropy);
+ if(vis & world::VVIS_DOWN) {
+ push_quad_v(voxel, fpos, fsize, world::VFACE_BOTTOM, entropy);
}
}
}
diff --git a/game/client/world/chunk_quad.hh b/game/client/world/chunk_quad.hh
index c15bb7a..d68977e 100644
--- a/game/client/world/chunk_quad.hh
+++ b/game/client/world/chunk_quad.hh
@@ -13,8 +13,8 @@ using ChunkQuad = std::array<std::uint32_t, 2>;
namespace world
{
-constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, const glm::fvec2& size, voxel_facing facing,
- std::size_t texture, std::size_t frames)
+constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, const glm::fvec2& size, VoxelFace face, std::size_t texture,
+ std::size_t frames)
{
ChunkQuad result = {};
result[0] = 0x00000000;
@@ -30,7 +30,7 @@ constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, co
result[0] |= (0x0000000FU & static_cast<std::uint32_t>(size.y * 16.0f - 1.0f));
// [1] FFFF----------------------------
- result[1] |= (0x0000000FU & static_cast<std::uint32_t>(facing)) << 28U;
+ result[1] |= (0x0000000FU & static_cast<std::uint32_t>(face)) << 28U;
// [1] ----TTTTTTTTTTTAAAAA------------
result[1] |= (0x000007FFU & static_cast<std::uint32_t>(texture)) << 17U;
diff --git a/game/client/world/player_target.cc b/game/client/world/player_target.cc
index f0550c0..3ede47e 100644
--- a/game/client/world/player_target.cc
+++ b/game/client/world/player_target.cc
@@ -16,17 +16,15 @@
constexpr static float MAX_REACH = 16.0f;
-voxel_id world::player_target::voxel;
voxel_pos world::player_target::coord;
voxel_pos world::player_target::normal;
-const world::VoxelInfo* world::player_target::info;
+const world::Voxel* world::player_target::voxel;
void world::player_target::init(void)
{
- world::player_target::voxel = NULL_VOXEL_ID;
world::player_target::coord = voxel_pos();
world::player_target::normal = voxel_pos();
- world::player_target::info = nullptr;
+ world::player_target::voxel = nullptr;
}
void world::player_target::update(void)
@@ -37,29 +35,26 @@ void world::player_target::update(void)
do {
world::player_target::voxel = ray.step();
- if(world::player_target::voxel != NULL_VOXEL_ID) {
+ if(world::player_target::voxel) {
world::player_target::coord = ray.vpos;
world::player_target::normal = ray.vnormal;
- world::player_target::info = world::voxel_registry::find(world::player_target::voxel);
break;
}
world::player_target::coord = voxel_pos();
world::player_target::normal = voxel_pos();
- world::player_target::info = nullptr;
} while(ray.distance < MAX_REACH);
}
else {
- world::player_target::voxel = NULL_VOXEL_ID;
+ world::player_target::voxel = nullptr;
world::player_target::coord = voxel_pos();
world::player_target::normal = voxel_pos();
- world::player_target::info = nullptr;
}
}
void world::player_target::render(void)
{
- if((world::player_target::voxel != NULL_VOXEL_ID) && !client_game::hide_hud) {
+ if(world::player_target::voxel && !client_game::hide_hud) {
auto cpos = coord::to_chunk(world::player_target::coord);
auto fpos = coord::to_local(world::player_target::coord);
diff --git a/game/client/world/player_target.hh b/game/client/world/player_target.hh
index f0db9be..34532c3 100644
--- a/game/client/world/player_target.hh
+++ b/game/client/world/player_target.hh
@@ -4,10 +4,9 @@
namespace world::player_target
{
-extern voxel_id voxel;
extern voxel_pos coord;
extern voxel_pos normal;
-extern const VoxelInfo* info;
+extern const Voxel* voxel;
} // namespace world::player_target
namespace world::player_target
diff --git a/game/client/world/voxel_sounds.cc b/game/client/world/voxel_sounds.cc
index 481e615..fe91f01 100644
--- a/game/client/world/voxel_sounds.cc
+++ b/game/client/world/voxel_sounds.cc
@@ -4,24 +4,21 @@
#include "client/resource/sound_effect.hh"
-constexpr static std::size_t NUM_SURFACES = static_cast<std::size_t>(world::voxel_surface::COUNT);
-
-static std::vector<resource_ptr<SoundEffect>> footsteps_sounds[NUM_SURFACES];
+static std::vector<resource_ptr<SoundEffect>> footsteps_sounds[world::VMAT_COUNT];
static std::mt19937_64 randomizer;
-static void add_footsteps_effect(world::voxel_surface surface, std::string_view name)
+static void add_footsteps_effect(world::VoxelMaterial material, std::string_view name)
{
if(auto effect = resource::load<SoundEffect>(name)) {
- auto surface_index = static_cast<std::size_t>(surface);
- footsteps_sounds[surface_index].push_back(effect);
+ footsteps_sounds[material].push_back(effect);
}
}
-static resource_ptr<SoundEffect> get_footsteps_effect(world::voxel_surface surface)
+static resource_ptr<SoundEffect> get_footsteps_effect(world::VoxelMaterial material)
{
- auto surface_index = static_cast<std::size_t>(surface);
+ auto surface_index = static_cast<std::size_t>(material);
- if(surface_index >= NUM_SURFACES) {
+ if(surface_index >= world::VMAT_COUNT) {
// Surface index out of range
return nullptr;
}
@@ -39,48 +36,48 @@ static resource_ptr<SoundEffect> get_footsteps_effect(world::voxel_surface surfa
void world::voxel_sounds::init(void)
{
- add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default1.wav");
- add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default2.wav");
- add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default3.wav");
- add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default4.wav");
+ add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default1.wav");
+ add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default2.wav");
+ add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default3.wav");
+ add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default4.wav");
- add_footsteps_effect(voxel_surface::DIRT, "sounds/surface/dirt1.wav");
+ add_footsteps_effect(VMAT_DIRT, "sounds/surface/dirt1.wav");
- add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass1.wav");
- add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass2.wav");
- add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass3.wav");
+ add_footsteps_effect(VMAT_GRASS, "sounds/surface/grass1.wav");
+ add_footsteps_effect(VMAT_GRASS, "sounds/surface/grass2.wav");
+ add_footsteps_effect(VMAT_GRASS, "sounds/surface/grass3.wav");
- add_footsteps_effect(voxel_surface::GRAVEL, "sounds/surface/gravel1.wav");
+ add_footsteps_effect(VMAT_GRAVEL, "sounds/surface/gravel1.wav");
- add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand1.wav");
- add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand2.wav");
+ add_footsteps_effect(VMAT_SAND, "sounds/surface/sand1.wav");
+ add_footsteps_effect(VMAT_SAND, "sounds/surface/sand2.wav");
- add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood1.wav");
- add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood2.wav");
- add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood3.wav");
+ add_footsteps_effect(VMAT_WOOD, "sounds/surface/wood1.wav");
+ add_footsteps_effect(VMAT_WOOD, "sounds/surface/wood2.wav");
+ add_footsteps_effect(VMAT_WOOD, "sounds/surface/wood3.wav");
}
void world::voxel_sounds::shutdown(void)
{
- for(std::size_t i = 0; i < NUM_SURFACES; ++i) {
+ for(std::size_t i = 0; i < world::VMAT_COUNT; ++i) {
footsteps_sounds[i].clear();
}
}
-resource_ptr<SoundEffect> world::voxel_sounds::get_footsteps(voxel_surface surface)
+resource_ptr<SoundEffect> world::voxel_sounds::get_footsteps(world::VoxelMaterial material)
{
- if(auto effect = get_footsteps_effect(surface)) {
+ if(auto effect = get_footsteps_effect(material)) {
return effect;
}
- if(auto effect = get_footsteps_effect(voxel_surface::DEFAULT)) {
+ if(auto effect = get_footsteps_effect(VMAT_DEFAULT)) {
return effect;
}
return nullptr;
}
-resource_ptr<SoundEffect> world::voxel_sounds::get_placebreak(voxel_surface surface)
+resource_ptr<SoundEffect> world::voxel_sounds::get_placebreak(world::VoxelMaterial material)
{
return nullptr;
}
diff --git a/game/client/world/voxel_sounds.hh b/game/client/world/voxel_sounds.hh
index 09f5e2e..d0f3e07 100644
--- a/game/client/world/voxel_sounds.hh
+++ b/game/client/world/voxel_sounds.hh
@@ -2,7 +2,7 @@
#include "core/resource/resource.hh"
-#include "shared/world/voxel_registry.hh"
+#include "shared/world/voxel.hh"
struct SoundEffect;
@@ -14,6 +14,6 @@ void shutdown(void);
namespace world::voxel_sounds
{
-resource_ptr<SoundEffect> get_footsteps(voxel_surface surface);
-resource_ptr<SoundEffect> get_placebreak(voxel_surface surface);
+resource_ptr<SoundEffect> get_footsteps(VoxelMaterial material);
+resource_ptr<SoundEffect> get_placebreak(VoxelMaterial material);
} // namespace world::voxel_sounds
diff --git a/game/server/receive.cc b/game/server/receive.cc
index 75ac3a6..612674e 100644
--- a/game/server/receive.cc
+++ b/game/server/receive.cc
@@ -10,6 +10,7 @@
#include "shared/world/chunk_aabb.hh"
#include "shared/world/dimension.hh"
+#include "shared/world/voxel_registry.hh"
#include "shared/coord.hh"
#include "shared/protocol.hh"
@@ -83,7 +84,7 @@ static void on_entity_head_packet(const protocol::EntityHead& packet)
static void on_set_voxel_packet(const protocol::SetVoxel& packet)
{
if(auto session = sessions::find(packet.peer)) {
- if(session->dimension && !session->dimension->set_voxel(packet.voxel, packet.vpos)) {
+ if(session->dimension && !session->dimension->set_voxel(world::voxel_registry::find(packet.voxel), packet.vpos)) {
auto cpos = coord::to_chunk(packet.vpos);
auto lpos = coord::to_local(packet.vpos);
auto index = coord::to_index(lpos);
@@ -102,7 +103,7 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet)
return;
}
- chunk->set_voxel(packet.voxel, index);
+ chunk->set_voxel(world::voxel_registry::find(packet.voxel), index);
session->dimension->chunks.emplace_or_replace<world::Inhabited>(chunk->get_entity());
diff --git a/game/server/sessions.cc b/game/server/sessions.cc
index c06ec3e..32b96a3 100644
--- a/game/server/sessions.cc
+++ b/game/server/sessions.cc
@@ -65,7 +65,7 @@ static void on_login_request_packet(const protocol::LoginRequest& packet)
// FIXME: calculate voxel registry checksum ahead of time
// instead of figuring it out every time a new player connects
- if(packet.voxel_registry_checksum != world::voxel_registry::calculate_checksum()) {
+ if(packet.voxel_registry_checksum != world::voxel_registry::get_checksum()) {
protocol::Disconnect response;
response.reason = "protocol.voxel_registry_checksum";
protocol::send(packet.peer, protocol::encode(response));
@@ -241,7 +241,7 @@ static void on_voxel_set(const world::VoxelSetEvent& event)
{
protocol::SetVoxel packet;
packet.vpos = coord::to_voxel(event.cpos, event.lpos);
- packet.voxel = event.voxel;
+ packet.voxel = event.voxel ? event.voxel->get_id() : NULL_VOXEL_ID;
packet.flags = 0U; // UNDONE
protocol::broadcast(globals::server_host, protocol::encode(packet));
}
diff --git a/game/server/world/overworld.cc b/game/server/world/overworld.cc
index eb801de..43059d8 100644
--- a/game/server/world/overworld.cc
+++ b/game/server/world/overworld.cc
@@ -4,13 +4,15 @@
#include "core/math/vectors.hh"
+#include "shared/world/voxel.hh"
#include "shared/world/voxel_storage.hh"
#include "shared/coord.hh"
#include "shared/game_voxels.hh"
// FIXME: load these from a file
-static void compute_tree_feature(unsigned int height, world::Feature& feature, voxel_id log_voxel, voxel_id leaves_voxel)
+static void compute_tree_feature(unsigned int height, world::Feature& feature, const world::Voxel* log_voxel,
+ const world::Voxel* leaves_voxel)
{
// Ensure the tree height is too small
height = math::max<unsigned int>(height, 4U);
@@ -251,12 +253,12 @@ void world::Overworld::generate_terrain(const chunk_pos& cpos, VoxelStorage& vox
}
if(vpos.y < -variation) {
- voxels[i] = game_voxels::stone;
+ voxels[i] = game_voxels::stone->get_id();
continue;
}
if(is_inside_terrain(vpos)) {
- voxels[i] = game_voxels::stone;
+ voxels[i] = game_voxels::stone->get_id();
continue;
}
}
@@ -308,10 +310,10 @@ void world::Overworld::generate_surface(const chunk_pos& cpos, VoxelStorage& vox
if(depth < 5U) {
if(depth == 0U) {
- voxels[i] = game_voxels::grass;
+ voxels[i] = game_voxels::grass->get_id();
}
else {
- voxels[i] = game_voxels::dirt;
+ voxels[i] = game_voxels::dirt->get_id();
}
}
}
diff --git a/game/shared/entity/collision.cc b/game/shared/entity/collision.cc
index 24f5d49..6b9f063 100644
--- a/game/shared/entity/collision.cc
+++ b/game/shared/entity/collision.cc
@@ -16,7 +16,7 @@
#include "shared/globals.hh"
static int vgrid_collide(const world::Dimension* dimension, int d, entity::Collision& collision, entity::Transform& transform,
- entity::Velocity& velocity, world::voxel_surface& touch_surface)
+ entity::Velocity& velocity, world::VoxelMaterial& touch_surface)
{
const auto move = globals::fixed_frametime * velocity.value[d];
const auto move_sign = math::sign<int>(move);
@@ -57,9 +57,9 @@ static int vgrid_collide(const world::Dimension* dimension, int d, entity::Colli
dmax = lpos_min[d];
}
- world::voxel_touch latch_touch = world::voxel_touch::NOTHING;
+ world::VoxelTouch latch_touch = world::VTOUCH_NONE;
glm::fvec3 latch_values = glm::fvec3(0.0f, 0.0f, 0.0f);
- world::voxel_surface latch_surface = world::voxel_surface::UNKNOWN;
+ world::VoxelMaterial latch_surface = world::VMAT_UNKNOWN;
math::AABBf latch_vbox;
for(auto i = dmin; i != dmax; i += ddir) {
@@ -70,18 +70,16 @@ static int vgrid_collide(const world::Dimension* dimension, int d, entity::Colli
lpos[u] = j;
lpos[v] = k;
- const auto vpos = coord::to_voxel(transform.chunk, lpos);
- const auto info = world::voxel_registry::find(dimension->get_voxel(vpos));
+ auto vpos = coord::to_voxel(transform.chunk, lpos);
+ auto voxel = dimension->get_voxel(vpos);
- if(info == nullptr) {
+ if(voxel == nullptr) {
// Don't collide with something
// that we assume to be nothing
continue;
}
- math::AABBf vbox;
- vbox.min = glm::fvec3(lpos);
- vbox.max = glm::fvec3(lpos) + 1.0f;
+ math::AABBf vbox(voxel->get_collision().push(lpos));
if(!next_aabb.intersect(vbox)) {
// No intersection between the voxel
@@ -89,29 +87,29 @@ static int vgrid_collide(const world::Dimension* dimension, int d, entity::Colli
continue;
}
- if(info->touch_type == world::voxel_touch::SOLID) {
+ if(voxel->is_touch_type<world::VTOUCH_SOLID>()) {
// Solid touch type makes a collision
// response whenever it is encountered
velocity.value[d] = 0.0f;
- touch_surface = info->surface;
+ touch_surface = voxel->get_surface_material();
return move_sign;
}
// In case of other touch types, they
// are latched and the last ever touch
// type is then responded to
- if(info->touch_type != world::voxel_touch::NOTHING) {
- latch_touch = info->touch_type;
- latch_values = info->touch_values;
- latch_surface = info->surface;
+ if(voxel->get_touch_type() != world::VTOUCH_NONE) {
+ latch_touch = voxel->get_touch_type();
+ latch_values = voxel->get_touch_values();
+ latch_surface = voxel->get_surface_material();
latch_vbox = vbox;
continue;
}
}
}
- if(latch_touch != world::voxel_touch::NOTHING) {
- if(latch_touch == world::voxel_touch::BOUNCE) {
+ if(latch_touch != world::VTOUCH_NONE) {
+ if(latch_touch == world::VTOUCH_BOUNCE) {
const auto move_distance = math::abs(current_aabb.min[d] - next_aabb.min[d]);
const auto threshold = 2.0f * globals::fixed_frametime;
@@ -127,7 +125,7 @@ static int vgrid_collide(const world::Dimension* dimension, int d, entity::Colli
return move_sign;
}
- if(latch_touch == world::voxel_touch::SINK) {
+ if(latch_touch == world::VTOUCH_SINK) {
velocity.value[d] *= latch_values[d];
touch_surface = latch_surface;
return move_sign;
@@ -151,7 +149,7 @@ void entity::Collision::fixed_update(world::Dimension* dimension)
auto group = dimension->entities.group<entity::Collision>(entt::get<entity::Transform, entity::Velocity>);
for(auto [entity, collision, transform, velocity] : group.each()) {
- auto surface = world::voxel_surface::UNKNOWN;
+ auto surface = world::VMAT_UNKNOWN;
auto vertical_move = vgrid_collide(dimension, 1, collision, transform, velocity, surface);
if(dimension->entities.any_of<entity::Gravity>(entity)) {
diff --git a/game/shared/entity/grounded.hh b/game/shared/entity/grounded.hh
index e86e3a8..940cebe 100644
--- a/game/shared/entity/grounded.hh
+++ b/game/shared/entity/grounded.hh
@@ -9,7 +9,7 @@ namespace entity
// Assigned to entities which are grounded
// according to the collision and gravity system
struct Grounded final {
- world::voxel_surface surface;
+ world::VoxelMaterial surface;
};
} // namespace entity
diff --git a/game/shared/game_voxels.cc b/game/shared/game_voxels.cc
index 59d035d..75c56c7 100644
--- a/game/shared/game_voxels.cc
+++ b/game/shared/game_voxels.cc
@@ -2,10 +2,11 @@
#include "shared/game_voxels.hh"
-#include "shared/world/voxels/generic.hh"
-
+#include "shared/world/dimension.hh"
#include "shared/world/voxel_registry.hh"
+#include "shared/const.hh"
+
const world::Voxel* game_voxels::cobblestone = nullptr;
const world::Voxel* game_voxels::dirt = nullptr;
const world::Voxel* game_voxels::grass = nullptr;
@@ -17,78 +18,124 @@ const world::Voxel* game_voxels::oak_planks = nullptr;
const world::Voxel* game_voxels::oak_log = nullptr;
const world::Voxel* game_voxels::glass = nullptr;
const world::Voxel* game_voxels::slime = nullptr;
-const world::Voxel* game_voxels::mud = nullptr;
+
+static void dirt_tick(world::Dimension* dimension, const voxel_pos& vpos)
+{
+ auto grass_found = false;
+ auto air_above = false;
+
+ for(voxel_pos::value_type dx = -1; dx <= 1 && !grass_found; ++dx) {
+ for(voxel_pos::value_type dy = -1; dy <= 1 && !grass_found; ++dy) {
+ for(voxel_pos::value_type dz = -1; dz <= 1 && !grass_found; ++dz) {
+ if(dx == 0 && dy == 0 && dz == 0) {
+ // Skip self
+ continue;
+ }
+
+ auto neighbour_vpos = vpos + voxel_pos(dx, dy, dz);
+ auto neighbour_voxel = dimension->get_voxel(neighbour_vpos);
+
+ // Voxel pointers returned by get_voxel() are the exact same
+ // returned by the voxel registry, so we can compare pointers directly
+ // and not bother with voxel_id property comparisons
+ if(neighbour_voxel == game_voxels::grass) {
+ grass_found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ auto above_vpos = vpos + voxel_pos(0, 1, 0);
+ auto above_voxel = dimension->get_voxel(above_vpos);
+
+ if(above_voxel == nullptr || above_voxel->is_surface_material<world::VMAT_GLASS>()) {
+ air_above = true;
+ }
+
+ if(grass_found && air_above) {
+ // Replace itself with the grass voxel
+ dimension->set_voxel(game_voxels::grass, vpos);
+ }
+}
void game_voxels::populate(void)
{
- using namespace world;
- using namespace world::voxels;
- using world::voxel_registry::register_voxel;
-
- stone = register_voxel(GenericCube("stone", VRENDER_OPAQUE, false, VMAT_STONE, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/stone_01.png", "textures/voxel/stone_02.png", "textures/voxel/stone_03.png", "textures/voxel/stone_04.png"));
-
- cobblestone = register_voxel(GenericCube("cobblestone", VRENDER_OPAQUE, false, VMAT_STONE, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/cobblestone_01.png", "textures/voxel/cobblestone_02.png"));
-
- vtest = register_voxel(GenericCube("vtest", VRENDER_OPAQUE, true, VMAT_DEFAULT, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/vtest_F1.png", "textures/voxel/vtest_F2.png", "textures/voxel/vtest_F3.png", "textures/voxel/vtest_F4.png"));
-
- vtest_ck = register_voxel(GenericCube("vtest_ck", VRENDER_OPAQUE, false, VMAT_DEFAULT, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/chromakey.png"));
-
- oak_leaves = register_voxel(GenericCube("oak_leaves", VRENDER_BLEND, false, VMAT_GRASS, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/oak_leaves.png"));
-
- oak_planks = register_voxel(GenericCube("oak_planks", VRENDER_OPAQUE, false, VMAT_WOOD, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/oak_planks_01.png", "textures/voxel/oak_planks_02.png"));
-
- glass = register_voxel(GenericCube("glass", VRENDER_BLEND, false, VMAT_GLASS, VTOUCH_SOLID, glm::fvec3(0.0f, 0.0f, 0.0f),
- "textures/voxel/glass_01.png"));
-
- slime = register_voxel(GenericCube("slime", VRENDER_BLEND, false, VMAT_SLOSH, VTOUCH_BOUNCE, glm::fvec3(0.00f, 0.60f, 0.00f),
- "textures/voxel/slime_01.png"));
-
-#if 0
-
- // Oak logs; greenery. TODO: add trees as surface features
- game_voxels::oak_log =
- world::voxel_registry::construct("oak_log", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/oak_wood_01.png")
- .add_texture_default("textures/voxel/oak_wood_02.png")
- .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/oak_wood_top.png")
- .add_texture(world::voxel_face::CUBE_TOP, "textures/voxel/oak_wood_top.png")
- .set_surface(world::voxel_surface::WOOD)
- .build();
-
- // Glass; blend rendering test
- game_voxels::glass = world::voxel_registry::construct("glass", world::voxel_type::CUBE, false, true)
- .add_texture_default("textures/voxel/glass_01.png")
- .set_surface(world::voxel_surface::GLASS)
- .build();
-
- // Dirt with a grass layer on top; the top layer of plains biome
- game_voxels::grass =
- world::voxel_registry::construct("grass", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/grass_side_01.png")
- .add_texture_default("textures/voxel/grass_side_02.png")
- .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_01.png")
- .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_02.png")
- .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_03.png")
- .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_04.png")
- .add_texture(world::voxel_face::CUBE_TOP, "textures/voxel/grass_01.png")
- .add_texture(world::voxel_face::CUBE_TOP, "textures/voxel/grass_02.png")
- .set_surface(world::voxel_surface::GRASS)
- .build();
-
- // Dirt; the under-surface layer of some biomes
- game_voxels::dirt =
- world::voxel_registry::construct("dirt", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/dirt_01.png")
- .add_texture_default("textures/voxel/dirt_02.png")
- .add_texture_default("textures/voxel/dirt_03.png")
- .add_texture_default("textures/voxel/dirt_04.png")
- .set_surface(world::voxel_surface::DIRT)
- .build();
-#endif
+ world::VoxelBuilder builder;
+
+ builder = world::VoxelBuilder("stone");
+ builder.add_default_texture("textures/voxel/stone_01.png")
+ .add_default_texture("textures/voxel/stone_02.png")
+ .add_default_texture("textures/voxel/stone_03.png")
+ .add_default_texture("textures/voxel/stone_04.png");
+ stone = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("cobblestone");
+ builder.add_default_texture("textures/voxel/cobblestone_01.png").add_default_texture("textures/voxel/cobblestone_02.png");
+ cobblestone = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("dirt");
+ builder.add_default_texture("textures/voxel/dirt_01.png")
+ .add_default_texture("textures/voxel/dirt_02.png")
+ .add_default_texture("textures/voxel/dirt_03.png")
+ .add_default_texture("textures/voxel/dirt_04.png");
+ builder.set_surface_material(world::VMAT_DIRT);
+ builder.set_on_tick(&dirt_tick);
+ dirt = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("grass");
+ builder.add_default_texture("textures/voxel/grass_side_01.png")
+ .add_default_texture("textures/voxel/grass_side_02.png")
+ .add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_01.png")
+ .add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_02.png")
+ .add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_03.png")
+ .add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_04.png")
+ .add_face_texture(world::VFACE_TOP, "textures/voxel/grass_01.png")
+ .add_face_texture(world::VFACE_TOP, "textures/voxel/grass_02.png");
+ builder.set_surface_material(world::VMAT_GRASS);
+ grass = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("vtest");
+ builder.add_default_texture("textures/voxel/vtest_F1.png")
+ .add_default_texture("textures/voxel/vtest_F2.png")
+ .add_default_texture("textures/voxel/vtest_F3.png")
+ .add_default_texture("textures/voxel/vtest_F4.png");
+ builder.set_animated(true);
+ vtest = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("vtest_ck");
+ builder.add_default_texture("textures/voxel/chromakey.png");
+ vtest_ck = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("oak_leaves");
+ builder.add_default_texture("textures/voxel/oak_leaves.png");
+ builder.set_render_mode(world::VRENDER_BLEND);
+ builder.set_surface_material(world::VMAT_GRASS);
+ oak_leaves = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("oak_planks");
+ builder.add_default_texture("textures/voxel/oak_planks_01.png").add_default_texture("textures/voxel/oak_planks_02.png");
+ builder.set_surface_material(world::VMAT_WOOD);
+ oak_planks = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("oak_log");
+ builder.add_default_texture("textures/voxel/oak_wood_01.png")
+ .add_default_texture("textures/voxel/oak_wood_02.png")
+ .add_face_texture(world::VFACE_BOTTOM, "textures/voxel/oak_wood_top.png")
+ .add_face_texture(world::VFACE_TOP, "textures/voxel/oak_wood_top.png");
+ builder.set_surface_material(world::VMAT_WOOD);
+ oak_log = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("glass");
+ builder.add_default_texture("textures/voxel/glass_01.png");
+ builder.set_render_mode(world::VRENDER_BLEND);
+ builder.set_surface_material(world::VMAT_GLASS);
+ glass = world::voxel_registry::register_voxel(builder);
+
+ builder = world::VoxelBuilder("slime");
+ builder.add_default_texture("textures/voxel/slime_01.png");
+ builder.set_render_mode(world::VRENDER_BLEND);
+ builder.set_surface_material(world::VMAT_SLOSH);
+ builder.set_touch_type(world::VTOUCH_BOUNCE).set_touch_values({ 0.00f, 0.60f, 0.00f });
+ slime = world::voxel_registry::register_voxel(builder);
}
diff --git a/game/shared/game_voxels.hh b/game/shared/game_voxels.hh
index 7b24ec4..68e599e 100644
--- a/game/shared/game_voxels.hh
+++ b/game/shared/game_voxels.hh
@@ -20,7 +20,6 @@ extern const world::Voxel* oak_planks;
extern const world::Voxel* oak_log;
extern const world::Voxel* glass;
extern const world::Voxel* slime;
-extern const world::Voxel* mud;
} // namespace game_voxels
namespace game_voxels
diff --git a/game/shared/world/CMakeLists.txt b/game/shared/world/CMakeLists.txt
index 15d5b59..cc7ed90 100644
--- a/game/shared/world/CMakeLists.txt
+++ b/game/shared/world/CMakeLists.txt
@@ -13,4 +13,6 @@ target_sources(shared PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/voxel_registry.cc"
"${CMAKE_CURRENT_LIST_DIR}/voxel_registry.hh"
"${CMAKE_CURRENT_LIST_DIR}/voxel_storage.cc"
- "${CMAKE_CURRENT_LIST_DIR}/voxel_storage.hh")
+ "${CMAKE_CURRENT_LIST_DIR}/voxel_storage.hh"
+ "${CMAKE_CURRENT_LIST_DIR}/voxel.cc"
+ "${CMAKE_CURRENT_LIST_DIR}/voxel.hh")
diff --git a/game/shared/world/feature.cc b/game/shared/world/feature.cc
index cbf660e..e704ced 100644
--- a/game/shared/world/feature.cc
+++ b/game/shared/world/feature.cc
@@ -4,6 +4,7 @@
#include "shared/world/chunk.hh"
#include "shared/world/dimension.hh"
+#include "shared/world/voxel.hh"
#include "shared/coord.hh"
@@ -50,3 +51,30 @@ void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, Chunk&
}
}
}
+
+void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const
+{
+ for(const auto [rpos, voxel, overwrite] : (*this)) {
+ auto it_vpos = vpos + rpos;
+ auto it_cpos = coord::to_chunk(it_vpos);
+
+ if(it_cpos == cpos) {
+ auto it_lpos = coord::to_local(it_vpos);
+ auto it_index = coord::to_index(it_lpos);
+
+ if(voxels[it_index] && !overwrite) {
+ // There is something in the way
+ // and the called intentionally requested
+ // we do not force feature to overwrite voxels
+ continue;
+ }
+
+ if(voxel == nullptr) {
+ voxels[it_index] = NULL_VOXEL_ID;
+ }
+ else {
+ voxels[it_index] = voxel->get_id();
+ }
+ }
+ }
+}
diff --git a/game/shared/world/feature.hh b/game/shared/world/feature.hh
index 09b913e..a543632 100644
--- a/game/shared/world/feature.hh
+++ b/game/shared/world/feature.hh
@@ -7,6 +7,7 @@ namespace world
class Chunk;
class Dimension;
class Voxel;
+class VoxelStorage;
} // namespace world
namespace world
@@ -19,5 +20,6 @@ public:
public:
void place(const voxel_pos& vpos, Dimension* dimension) const;
void place(const voxel_pos& vpos, const chunk_pos& cpos, Chunk& chunk) const;
+ void place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const;
};
} // namespace world
diff --git a/game/shared/world/item_registry.cc b/game/shared/world/item_registry.cc
index d1b9ff4..783c85e 100644
--- a/game/shared/world/item_registry.cc
+++ b/game/shared/world/item_registry.cc
@@ -14,7 +14,7 @@ world::ItemInfoBuilder::ItemInfoBuilder(std::string_view name)
{
prototype.name = name;
prototype.texture = std::string();
- prototype.place_voxel = NULL_VOXEL_ID;
+ prototype.place_voxel = nullptr;
prototype.cached_texture = nullptr;
}
@@ -25,7 +25,7 @@ world::ItemInfoBuilder& world::ItemInfoBuilder::set_texture(std::string_view tex
return *this;
}
-world::ItemInfoBuilder& world::ItemInfoBuilder::set_place_voxel(voxel_id place_voxel)
+world::ItemInfoBuilder& world::ItemInfoBuilder::set_place_voxel(const Voxel* place_voxel)
{
prototype.place_voxel = place_voxel;
return *this;
@@ -99,7 +99,10 @@ std::uint64_t world::item_registry::calculate_checksum(void)
for(const auto& info : world::item_registry::items) {
result = math::crc64(info->name, result);
- result += static_cast<std::uint64_t>(info->place_voxel);
+
+ if(info->place_voxel) {
+ result += static_cast<std::uint64_t>(info->place_voxel->get_id());
+ }
}
return result;
diff --git a/game/shared/world/item_registry.hh b/game/shared/world/item_registry.hh
index c3e6cf9..2274da2 100644
--- a/game/shared/world/item_registry.hh
+++ b/game/shared/world/item_registry.hh
@@ -11,10 +11,15 @@ struct TextureGUI;
namespace world
{
+class Voxel;
+} // namespace world
+
+namespace world
+{
struct ItemInfo final {
std::string name;
std::string texture;
- voxel_id place_voxel;
+ const Voxel* place_voxel;
resource_ptr<TextureGUI> cached_texture; // Client-side only
};
@@ -29,7 +34,7 @@ public:
public:
ItemInfoBuilder& set_texture(std::string_view texture);
- ItemInfoBuilder& set_place_voxel(voxel_id place_voxel);
+ ItemInfoBuilder& set_place_voxel(const Voxel* place_voxel);
public:
item_id build(void) const;
diff --git a/game/shared/world/voxel.cc b/game/shared/world/voxel.cc
index d51b5a1..1cd1504 100644
--- a/game/shared/world/voxel.cc
+++ b/game/shared/world/voxel.cc
@@ -6,24 +6,30 @@
#include "shared/world/dimension.hh"
-void world::Voxel::on_place(Dimension* dimension, const voxel_pos& vpos) const
+world::Voxel::Voxel(const Voxel& source, voxel_id id) noexcept : Voxel(source)
{
- // empty
+ m_id = id;
}
-void world::Voxel::on_remove(Dimension* dimension, const voxel_pos& vpos) const
+void world::Voxel::on_place(Dimension* dimension, const voxel_pos& vpos) const
{
- // empty
+ if(m_on_place) {
+ m_on_place(dimension, vpos);
+ }
}
-void world::Voxel::on_tick(Dimension* dimension, const voxel_pos& vpos) const
+void world::Voxel::on_remove(Dimension* dimension, const voxel_pos& vpos) const
{
- // empty
+ if(m_on_remove) {
+ m_on_remove(dimension, vpos);
+ }
}
-void world::Voxel::set_id(voxel_id id) noexcept
+void world::Voxel::on_tick(Dimension* dimension, const voxel_pos& vpos) const
{
- m_id = id;
+ if(m_on_tick) {
+ m_on_tick(dimension, vpos);
+ }
}
std::size_t world::Voxel::get_random_texture_index(VoxelFace face, const voxel_pos& vpos) const
@@ -57,64 +63,113 @@ std::uint64_t world::Voxel::calculate_checksum(std::uint64_t combine) const
return combine;
}
-std::shared_ptr<world::Voxel> world::Voxel::clone(void) const
+world::VoxelBuilder::VoxelBuilder(std::string_view name)
+{
+ set_name(name);
+}
+
+world::VoxelBuilder& world::VoxelBuilder::set_on_place(VoxelOnPlaceFunc func) noexcept
+{
+ m_on_place = std::move(func);
+
+ return *this;
+}
+
+world::VoxelBuilder& world::VoxelBuilder::set_on_remove(VoxelOnRemoveFunc func) noexcept
+{
+ m_on_remove = std::move(func);
+
+ return *this;
+}
+
+world::VoxelBuilder& world::VoxelBuilder::set_on_tick(VoxelOnTickFunc func) noexcept
{
- return std::make_shared<Voxel>(*this);
+ m_on_tick = std::move(func);
+
+ return *this;
}
-void world::Voxel::set_name(std::string_view name) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_name(std::string_view name) noexcept
{
assert(name.size());
m_name = name;
+
+ return *this;
}
-void world::Voxel::set_render_mode(VoxelRender mode) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_render_mode(VoxelRender mode) noexcept
{
m_render_mode = mode;
+
+ return *this;
}
-void world::Voxel::set_shape(VoxelShape shape) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_shape(VoxelShape shape) noexcept
{
m_shape = shape;
+
+ return *this;
}
-void world::Voxel::set_animated(bool animated) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_animated(bool animated) noexcept
{
m_animated = animated;
+
+ return *this;
}
-void world::Voxel::set_touch_type(VoxelTouch type) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_touch_type(VoxelTouch type) noexcept
{
m_touch_type = type;
+
+ return *this;
}
-void world::Voxel::set_touch_values(const glm::fvec3& values) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_touch_values(const glm::fvec3& values) noexcept
{
m_touch_values = values;
+
+ return *this;
}
-void world::Voxel::set_surface_material(VoxelMaterial material) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_surface_material(VoxelMaterial material) noexcept
{
m_surface_material = material;
+
+ return *this;
}
-void world::Voxel::set_collision(const math::AABBf& box) noexcept
+world::VoxelBuilder& world::VoxelBuilder::set_collision(const math::AABBf& box) noexcept
{
m_collision = box;
+
+ return *this;
}
-void world::Voxel::add_default_texture(std::string_view path)
+world::VoxelBuilder& world::VoxelBuilder::add_default_texture(std::string_view path)
{
assert(path.size());
m_default_textures.emplace_back(path);
+
+ return *this;
}
-void world::Voxel::add_face_texture(VoxelFace face, std::string_view path)
+world::VoxelBuilder& world::VoxelBuilder::add_face_texture(VoxelFace face, std::string_view path)
{
assert(face < m_face_textures.size());
assert(path.size());
m_face_textures[face].emplace_back(path);
+
+ return *this;
+}
+
+std::unique_ptr<world::Voxel> world::VoxelBuilder::build(voxel_id id) const
+{
+ assert(m_name.size());
+ assert(id);
+
+ return std::make_unique<Voxel>(*this, id);
}
diff --git a/game/shared/world/voxel.hh b/game/shared/world/voxel.hh
index 0bd83b7..9cc0e4c 100644
--- a/game/shared/world/voxel.hh
+++ b/game/shared/world/voxel.hh
@@ -11,19 +11,19 @@ class Dimension;
namespace world
{
-enum VoxelRender : unsigned short {
+enum VoxelRender : unsigned int {
VRENDER_NONE = 0U, ///< The voxel is not rendered at all
VRENDER_OPAQUE, ///< The voxel is fully opaque
VRENDER_BLEND, ///< The voxel is blended (e.g. water, glass)
};
-enum VoxelShape : unsigned short {
+enum VoxelShape : unsigned int {
VSHAPE_CUBE = 0U, ///< Full cube shape
VSHAPE_CROSS, ///< TODO: Cross shape
VSHAPE_MODEL, ///< TODO: Custom model shape
};
-enum VoxelFace : unsigned short {
+enum VoxelFace : unsigned int {
VFACE_NORTH = 0U, ///< Positive Z face
VFACE_SOUTH, ///< Negative Z face
VFACE_EAST, ///< Positive X face
@@ -35,14 +35,15 @@ enum VoxelFace : unsigned short {
VFACE_COUNT
};
-enum VoxelTouch : unsigned short {
+enum VoxelTouch : unsigned int {
VTOUCH_NONE = 0xFFFFU,
VTOUCH_SOLID = 0U, ///< The entity is stopped in its tracks
VTOUCH_BOUNCE, ///< The entity bounces back with some energy loss
VTOUCH_SINK, ///< The entity phases/sinks through the voxel
};
-enum VoxelMaterial : unsigned short {
+enum VoxelMaterial : unsigned int {
+ VMAT_UNKNOWN = 0xFFFFU,
VMAT_DEFAULT = 0U,
VMAT_STONE,
VMAT_DIRT,
@@ -56,7 +57,7 @@ enum VoxelMaterial : unsigned short {
VMAT_COUNT
};
-enum VoxelVisBits : unsigned short {
+enum VoxelVisBits : unsigned int {
VVIS_NORTH = 1U << VFACE_NORTH, ///< Positive Z
VVIS_SOUTH = 1U << VFACE_SOUTH, ///< Negative Z
VVIS_EAST = 1U << VFACE_EAST, ///< Positive X
@@ -68,26 +69,24 @@ enum VoxelVisBits : unsigned short {
namespace world
{
+using VoxelOnPlaceFunc = std::function<void(Dimension*, const voxel_pos&)>;
+using VoxelOnRemoveFunc = std::function<void(Dimension*, const voxel_pos&)>;
+using VoxelOnTickFunc = std::function<void(Dimension*, const voxel_pos&)>;
+} // namespace world
+
+namespace world
+{
class Voxel {
public:
- /// Called when the voxel is placed in the world
- /// @param dimension The dimension the voxel is placed in
- /// @param vpos The absolute voxel position where the voxel is placed
- virtual void on_place(Dimension* dimension, const voxel_pos& vpos) const;
-
- /// Called when the voxel is removed from the world
- /// @param dimension The dimension the voxel is removed from
- /// @param vpos The absolute voxel position where the voxel is removed
- virtual void on_remove(Dimension* dimension, const voxel_pos& vpos) const;
+ Voxel(void) = default;
+ explicit Voxel(const Voxel& source, voxel_id id) noexcept;
- /// Called when the voxel is ticked by something
- /// @param dimension The dimension the voxel is ticked in
- /// @param vpos The absolute voxel position where the voxel is ticked
- virtual void on_tick(Dimension* dimension, const voxel_pos& vpos) const;
+ void on_place(Dimension* dimension, const voxel_pos& vpos) const;
+ void on_remove(Dimension* dimension, const voxel_pos& vpos) const;
+ void on_tick(Dimension* dimension, const voxel_pos& vpos) const;
constexpr std::string_view get_name(void) const noexcept;
constexpr voxel_id get_id(void) const noexcept;
- void set_id(voxel_id id) noexcept;
constexpr VoxelRender get_render_mode(void) const noexcept;
constexpr VoxelShape get_shape(void) const noexcept;
@@ -104,6 +103,15 @@ public:
constexpr std::size_t get_cached_face_offset(VoxelFace face) const noexcept;
constexpr std::size_t get_cached_face_plane(VoxelFace face) const noexcept;
+ template<VoxelRender RenderMode>
+ constexpr bool is_render_mode(void) const noexcept;
+ template<VoxelShape Shape>
+ constexpr bool is_shape(void) const noexcept;
+ template<VoxelTouch TouchType>
+ constexpr bool is_touch_type(void) const noexcept;
+ template<VoxelMaterial Material>
+ constexpr bool is_surface_material(void) const noexcept;
+
/// Non-model voxel shapes support texture variation based on the
/// voxel position on the world; this method handles the math behind this
/// @param face The face of the voxel to get the texture index for
@@ -123,27 +131,7 @@ public:
/// @return The calculated checksum
std::uint64_t calculate_checksum(std::uint64_t combine = 0U) const;
- /// Produce a copy of itself as a shared pointer; the voxel registry
- /// does not change the owner of a voxel instance it registers
- virtual std::shared_ptr<Voxel> clone(void) const;
-
protected:
- void set_name(std::string_view name) noexcept;
-
- void set_render_mode(VoxelRender mode) noexcept;
- void set_shape(VoxelShape shape) noexcept;
- void set_animated(bool animated) noexcept;
-
- void set_touch_type(VoxelTouch type) noexcept;
- void set_touch_values(const glm::fvec3& values) noexcept;
- void set_surface_material(VoxelMaterial material) noexcept;
-
- void set_collision(const math::AABBf& box) noexcept;
-
- void add_default_texture(std::string_view path);
- void add_face_texture(VoxelFace face, std::string_view path);
-
-private:
std::string m_name;
voxel_id m_id { NULL_VOXEL_ID };
@@ -161,6 +149,40 @@ private:
std::array<std::vector<std::string>, VFACE_COUNT> m_face_textures;
std::array<std::size_t, VFACE_COUNT> m_cached_face_offsets;
std::array<std::size_t, VFACE_COUNT> m_cached_face_planes;
+
+ VoxelOnPlaceFunc m_on_place;
+ VoxelOnRemoveFunc m_on_remove;
+ VoxelOnTickFunc m_on_tick;
+};
+} // namespace world
+
+namespace world
+{
+class VoxelBuilder final : public Voxel {
+public:
+ VoxelBuilder(void) = default;
+ explicit VoxelBuilder(std::string_view name);
+
+ VoxelBuilder& set_on_place(VoxelOnPlaceFunc func) noexcept;
+ VoxelBuilder& set_on_remove(VoxelOnRemoveFunc func) noexcept;
+ VoxelBuilder& set_on_tick(VoxelOnTickFunc func) noexcept;
+
+ VoxelBuilder& set_name(std::string_view name) noexcept;
+
+ VoxelBuilder& set_render_mode(VoxelRender mode) noexcept;
+ VoxelBuilder& set_shape(VoxelShape shape) noexcept;
+ VoxelBuilder& set_animated(bool animated) noexcept;
+
+ VoxelBuilder& set_touch_type(VoxelTouch type) noexcept;
+ VoxelBuilder& set_touch_values(const glm::fvec3& values) noexcept;
+ VoxelBuilder& set_surface_material(VoxelMaterial material) noexcept;
+
+ VoxelBuilder& set_collision(const math::AABBf& box) noexcept;
+
+ VoxelBuilder& add_default_texture(std::string_view path);
+ VoxelBuilder& add_face_texture(VoxelFace face, std::string_view path);
+
+ std::unique_ptr<Voxel> build(voxel_id id) const;
};
} // namespace world
@@ -218,6 +240,10 @@ constexpr const std::vector<std::string>& world::Voxel::get_face_textures(VoxelF
{
assert(face <= m_face_textures.size());
+ if(m_face_textures[face].empty()) {
+ return m_default_textures;
+ }
+
return m_face_textures[face];
}
@@ -234,3 +260,27 @@ constexpr std::size_t world::Voxel::get_cached_face_plane(VoxelFace face) const
return m_cached_face_planes[face];
}
+
+template<world::VoxelRender RenderMode>
+constexpr bool world::Voxel::is_render_mode(void) const noexcept
+{
+ return m_render_mode == RenderMode;
+}
+
+template<world::VoxelShape Shape>
+constexpr bool world::Voxel::is_shape(void) const noexcept
+{
+ return m_shape == Shape;
+}
+
+template<world::VoxelTouch TouchType>
+constexpr bool world::Voxel::is_touch_type(void) const noexcept
+{
+ return m_touch_type == TouchType;
+}
+
+template<world::VoxelMaterial Material>
+constexpr bool world::Voxel::is_surface_material(void) const noexcept
+{
+ return m_surface_material == Material;
+}
diff --git a/game/shared/world/voxel_registry.cc b/game/shared/world/voxel_registry.cc
index 4c2f360..97485e6 100644
--- a/game/shared/world/voxel_registry.cc
+++ b/game/shared/world/voxel_registry.cc
@@ -4,7 +4,7 @@
static std::uint64_t registry_checksum = 0U;
emhash8::HashMap<std::string, voxel_id> world::voxel_registry::names;
-std::vector<std::shared_ptr<world::Voxel>> world::voxel_registry::voxels;
+std::vector<std::unique_ptr<world::Voxel>> world::voxel_registry::voxels;
static void recalculate_checksum(void)
{
@@ -15,17 +15,15 @@ static void recalculate_checksum(void)
}
}
-world::Voxel* world::voxel_registry::register_voxel(const Voxel& voxel_template)
+world::Voxel* world::voxel_registry::register_voxel(const VoxelBuilder& builder)
{
- assert(voxel_template.get_name().size());
- assert(nullptr == find(voxel_template.get_name()));
+ assert(builder.get_name().size());
+ assert(nullptr == find(builder.get_name()));
- const auto id = static_cast<voxel_id>(voxels.size());
+ const auto id = static_cast<voxel_id>(1 + voxels.size());
- auto voxel = voxel_template.clone();
- voxel->set_id(id);
-
- names.emplace(std::string(voxel_template.get_name()), id);
+ std::unique_ptr<Voxel> voxel(builder.build(id));
+ names.emplace(std::string(builder.get_name()), id);
voxels.push_back(std::move(voxel));
recalculate_checksum();
@@ -35,7 +33,7 @@ world::Voxel* world::voxel_registry::register_voxel(const Voxel& voxel_template)
world::Voxel* world::voxel_registry::find(std::string_view name)
{
- const auto it = names.find(name);
+ const auto it = names.find(std::string(name));
if(it == names.end()) {
return nullptr;
@@ -46,11 +44,11 @@ world::Voxel* world::voxel_registry::find(std::string_view name)
world::Voxel* world::voxel_registry::find(voxel_id id)
{
- if(id >= voxels.size()) {
+ if(id == NULL_VOXEL_ID || id > voxels.size()) {
return nullptr;
}
- return voxels[id].get();
+ return voxels[id - 1].get();
}
void world::voxel_registry::purge(void)
diff --git a/game/shared/world/voxel_registry.hh b/game/shared/world/voxel_registry.hh
index b9d9a34..5ba2aed 100644
--- a/game/shared/world/voxel_registry.hh
+++ b/game/shared/world/voxel_registry.hh
@@ -5,12 +5,12 @@
namespace world::voxel_registry
{
extern emhash8::HashMap<std::string, voxel_id> names;
-extern std::vector<std::shared_ptr<Voxel>> voxels;
+extern std::vector<std::unique_ptr<Voxel>> voxels;
} // namespace world::voxel_registry
namespace world::voxel_registry
{
-Voxel* register_voxel(const Voxel& voxel_template);
+Voxel* register_voxel(const VoxelBuilder& builder);
Voxel* find(std::string_view name);
Voxel* find(voxel_id id);
} // namespace world::voxel_registry
diff --git a/game/shared/world/voxels/generic.hh b/game/shared/world/voxels/generic.hh
deleted file mode 100644
index a661792..0000000
--- a/game/shared/world/voxels/generic.hh
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "shared/world/voxel.hh"
-
-namespace world::voxels
-{
-class GenericCube final : public Voxel {
-public:
- template<typename... TexturesT>
- requires(std::is_convertible_v<TexturesT, std::string_view> && ...)
- explicit GenericCube(std::string_view name, VoxelRender render_mode, bool animated, VoxelMaterial surface_material, VoxelTouch touch,
- const glm::fvec3& touch_values, TexturesT&&... textures) noexcept
- {
- set_name(name);
-
- set_shape(VoxelShape::CUBE);
- set_render_mode(render_mode);
- set_animated(animated);
-
- set_surface_material(surface_material);
- set_touch_values(touch_values);
- set_touch_type(touch);
-
- add_texture_default(std::forward<TexturesT>(textures)...);
- }
-};
-} // namespace world::voxels