summaryrefslogtreecommitdiffstats
path: root/game/shared/world
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/world')
-rw-r--r--game/shared/world/chunk.cc25
-rw-r--r--game/shared/world/chunk.hh9
-rw-r--r--game/shared/world/dimension.cc30
-rw-r--r--game/shared/world/dimension.hh11
-rw-r--r--game/shared/world/feature.cc7
-rw-r--r--game/shared/world/feature.hh7
-rw-r--r--game/shared/world/ray_dda.cc2
-rw-r--r--game/shared/world/ray_dda.hh3
-rw-r--r--game/shared/world/voxel.cc120
-rw-r--r--game/shared/world/voxel.hh236
-rw-r--r--game/shared/world/voxel_registry.cc194
-rw-r--r--game/shared/world/voxel_registry.hh137
-rw-r--r--game/shared/world/voxels/generic.hh27
13 files changed, 481 insertions, 327 deletions
diff --git a/game/shared/world/chunk.cc b/game/shared/world/chunk.cc
index e59b68d..b1b19a6 100644
--- a/game/shared/world/chunk.cc
+++ b/game/shared/world/chunk.cc
@@ -2,6 +2,8 @@
#include "shared/world/chunk.hh"
+#include "shared/world/voxel_registry.hh"
+
#include "shared/coord.hh"
world::Chunk::Chunk(entt::entity entity, Dimension* dimension)
@@ -12,30 +14,35 @@ world::Chunk::Chunk(entt::entity entity, Dimension* dimension)
m_biome = BIOME_VOID;
}
-voxel_id world::Chunk::get_voxel(const local_pos& lpos) const
+const world::Voxel* world::Chunk::get_voxel(const local_pos& lpos) const
{
return get_voxel(coord::to_index(lpos));
}
-voxel_id world::Chunk::get_voxel(const std::size_t index) const
+const world::Voxel* world::Chunk::get_voxel(const std::size_t index) const
{
if(index >= CHUNK_VOLUME) {
- return NULL_VOXEL_ID;
- }
- else {
- return m_voxels[index];
+ return nullptr;
}
+
+ return voxel_registry::find(m_voxels[index]);
}
-void world::Chunk::set_voxel(voxel_id voxel, const local_pos& lpos)
+void world::Chunk::set_voxel(const Voxel* voxel, const local_pos& lpos)
{
set_voxel(voxel, coord::to_index(lpos));
}
-void world::Chunk::set_voxel(voxel_id voxel, const std::size_t index)
+void world::Chunk::set_voxel(const Voxel* voxel, const std::size_t index)
{
if(index < CHUNK_VOLUME) {
- m_voxels[index] = voxel;
+ if(voxel == nullptr) {
+ m_voxels[index] = NULL_VOXEL_ID;
+ return;
+ }
+
+ m_voxels[index] = voxel->get_id();
+ return;
}
}
diff --git a/game/shared/world/chunk.hh b/game/shared/world/chunk.hh
index c5bba12..4a1e557 100644
--- a/game/shared/world/chunk.hh
+++ b/game/shared/world/chunk.hh
@@ -9,6 +9,7 @@ constexpr static unsigned int BIOME_VOID = 0U;
namespace world
{
class Dimension;
+class Voxel;
} // namespace world
namespace world
@@ -18,11 +19,11 @@ public:
explicit Chunk(entt::entity entity, Dimension* dimension);
virtual ~Chunk(void) = default;
- voxel_id get_voxel(const local_pos& lpos) const;
- voxel_id get_voxel(const std::size_t index) const;
+ const Voxel* get_voxel(const local_pos& lpos) const;
+ const Voxel* get_voxel(const std::size_t index) const;
- void set_voxel(voxel_id voxel, const local_pos& lpos);
- void set_voxel(voxel_id voxel, const std::size_t index);
+ void set_voxel(const Voxel* voxel, const local_pos& lpos);
+ void set_voxel(const Voxel* voxel, const std::size_t index);
const VoxelStorage& get_voxels(void) const;
void set_voxels(const VoxelStorage& voxels);
diff --git a/game/shared/world/dimension.cc b/game/shared/world/dimension.cc
index dd28449..31a19af 100644
--- a/game/shared/world/dimension.cc
+++ b/game/shared/world/dimension.cc
@@ -3,6 +3,7 @@
#include "shared/world/dimension.hh"
#include "shared/world/chunk.hh"
+#include "shared/world/voxel_registry.hh"
#include "shared/coord.hh"
#include "shared/globals.hh"
@@ -107,7 +108,7 @@ void world::Dimension::remove_chunk(Chunk* chunk)
}
}
-voxel_id world::Dimension::get_voxel(const voxel_pos& vpos) const
+const world::Voxel* world::Dimension::get_voxel(const voxel_pos& vpos) const
{
auto cpos = coord::to_chunk(vpos);
auto lpos = coord::to_local(vpos);
@@ -115,12 +116,11 @@ voxel_id world::Dimension::get_voxel(const voxel_pos& vpos) const
if(auto chunk = find_chunk(cpos)) {
return chunk->get_voxel(lpos);
}
- else {
- return NULL_VOXEL_ID;
- }
+
+ return nullptr;
}
-voxel_id world::Dimension::get_voxel(const chunk_pos& cpos, const local_pos& lpos) const
+const world::Voxel* world::Dimension::get_voxel(const chunk_pos& cpos, const local_pos& lpos) const
{
// This allows accessing get_voxel with negative
// local coordinates that usually would result in an
@@ -128,19 +128,33 @@ voxel_id world::Dimension::get_voxel(const chunk_pos& cpos, const local_pos& lpo
return get_voxel(coord::to_voxel(cpos, lpos));
}
-bool world::Dimension::set_voxel(voxel_id voxel, const voxel_pos& vpos)
+bool world::Dimension::set_voxel(const Voxel* voxel, const voxel_pos& vpos)
{
auto cpos = coord::to_chunk(vpos);
auto lpos = coord::to_local(vpos);
if(auto chunk = find_chunk(cpos)) {
+ if(auto old_voxel = chunk->get_voxel(lpos)) {
+ if(old_voxel != voxel) {
+ // Notify the old voxel that it is
+ // being replaced with a different voxel
+ old_voxel->on_remove(this, vpos);
+ }
+ }
+
chunk->set_voxel(voxel, lpos);
+ if(voxel) {
+ // If we're not placing air, notify the
+ // new voxel that it has been placed
+ voxel->on_place(this, vpos);
+ }
+
VoxelSetEvent event;
event.dimension = this;
+ event.voxel = voxel;
event.cpos = cpos;
event.lpos = lpos;
- event.voxel = voxel;
event.chunk = chunk;
globals::dispatcher.trigger(event);
@@ -151,7 +165,7 @@ bool world::Dimension::set_voxel(voxel_id voxel, const voxel_pos& vpos)
return false;
}
-bool world::Dimension::set_voxel(voxel_id voxel, const chunk_pos& cpos, const local_pos& lpos)
+bool world::Dimension::set_voxel(const Voxel* voxel, const chunk_pos& cpos, const local_pos& lpos)
{
// This allows accessing set_voxel with negative
// local coordinates that usually would result in an
diff --git a/game/shared/world/dimension.hh b/game/shared/world/dimension.hh
index bf9bfe1..58e0765 100644
--- a/game/shared/world/dimension.hh
+++ b/game/shared/world/dimension.hh
@@ -11,6 +11,7 @@ class ConfigMap;
namespace world
{
class Chunk;
+class Voxel;
class VoxelStorage;
} // namespace world
@@ -40,11 +41,11 @@ public:
void remove_chunk(Chunk* chunk);
public:
- voxel_id get_voxel(const voxel_pos& vpos) const;
- voxel_id get_voxel(const chunk_pos& cpos, const local_pos& lpos) const;
+ const Voxel* get_voxel(const voxel_pos& vpos) const;
+ const Voxel* get_voxel(const chunk_pos& cpos, const local_pos& lpos) const;
- bool set_voxel(voxel_id voxel, const voxel_pos& vpos);
- bool set_voxel(voxel_id voxel, const chunk_pos& cpos, const local_pos& lpos);
+ bool set_voxel(const Voxel* voxel, const voxel_pos& vpos);
+ bool set_voxel(const Voxel* voxel, const chunk_pos& cpos, const local_pos& lpos);
public:
virtual void init(io::ConfigMap& config);
@@ -92,9 +93,9 @@ struct ChunkUpdateEvent final {
struct VoxelSetEvent final {
Dimension* dimension;
+ const Voxel* voxel;
chunk_pos cpos;
local_pos lpos;
- voxel_id voxel;
Chunk* chunk;
};
} // namespace world
diff --git a/game/shared/world/feature.cc b/game/shared/world/feature.cc
index 4212043..cbf660e 100644
--- a/game/shared/world/feature.cc
+++ b/game/shared/world/feature.cc
@@ -4,7 +4,6 @@
#include "shared/world/chunk.hh"
#include "shared/world/dimension.hh"
-#include "shared/world/voxel_storage.hh"
#include "shared/coord.hh"
@@ -30,7 +29,7 @@ void world::Feature::place(const voxel_pos& vpos, Dimension* dimension) const
}
}
-void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const
+void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, Chunk& chunk) const
{
for(const auto [rpos, voxel, overwrite] : (*this)) {
auto it_vpos = vpos + rpos;
@@ -40,14 +39,14 @@ void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelSt
auto it_lpos = coord::to_local(it_vpos);
auto it_index = coord::to_index(it_lpos);
- if(voxels[it_index] && !overwrite) {
+ if(chunk.get_voxel(it_index) && !overwrite) {
// There is something in the way
// and the called intentionally requested
// we do not force feature to overwrite voxels
continue;
}
- voxels[it_index] = voxel;
+ chunk.set_voxel(voxel, it_index);
}
}
}
diff --git a/game/shared/world/feature.hh b/game/shared/world/feature.hh
index 20a809b..09b913e 100644
--- a/game/shared/world/feature.hh
+++ b/game/shared/world/feature.hh
@@ -4,19 +4,20 @@
namespace world
{
+class Chunk;
class Dimension;
-class VoxelStorage;
+class Voxel;
} // namespace world
namespace world
{
-class Feature final : public std::vector<std::tuple<voxel_pos, voxel_id, bool>> {
+class Feature final : public std::vector<std::tuple<voxel_pos, const Voxel*, bool>> {
public:
Feature(void) = default;
virtual ~Feature(void) = default;
public:
void place(const voxel_pos& vpos, Dimension* dimension) const;
- void place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const;
+ void place(const voxel_pos& vpos, const chunk_pos& cpos, Chunk& chunk) const;
};
} // namespace world
diff --git a/game/shared/world/ray_dda.cc b/game/shared/world/ray_dda.cc
index b337e9d..7bb1068 100644
--- a/game/shared/world/ray_dda.cc
+++ b/game/shared/world/ray_dda.cc
@@ -71,7 +71,7 @@ void world::RayDDA::reset(const world::Dimension& dimension, const chunk_pos& st
reset(&dimension, start_chunk, start_fpos, direction);
}
-voxel_id world::RayDDA::step(void)
+const world::Voxel* world::RayDDA::step(void)
{
if(side_dist.x < side_dist.z) {
if(side_dist.x < side_dist.y) {
diff --git a/game/shared/world/ray_dda.hh b/game/shared/world/ray_dda.hh
index 54bd48a..3071be2 100644
--- a/game/shared/world/ray_dda.hh
+++ b/game/shared/world/ray_dda.hh
@@ -7,6 +7,7 @@
namespace world
{
class Dimension;
+class Voxel;
} // namespace world
namespace world
@@ -20,7 +21,7 @@ public:
void reset(const Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction);
void reset(const Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction);
- voxel_id step(void);
+ const Voxel* step(void);
public:
const Dimension* dimension;
diff --git a/game/shared/world/voxel.cc b/game/shared/world/voxel.cc
new file mode 100644
index 0000000..d51b5a1
--- /dev/null
+++ b/game/shared/world/voxel.cc
@@ -0,0 +1,120 @@
+#include "shared/pch.hh"
+
+#include "shared/world/voxel.hh"
+
+#include "core/math/crc64.hh"
+
+#include "shared/world/dimension.hh"
+
+void world::Voxel::on_place(Dimension* dimension, const voxel_pos& vpos) const
+{
+ // empty
+}
+
+void world::Voxel::on_remove(Dimension* dimension, const voxel_pos& vpos) const
+{
+ // empty
+}
+
+void world::Voxel::on_tick(Dimension* dimension, const voxel_pos& vpos) const
+{
+ // empty
+}
+
+void world::Voxel::set_id(voxel_id id) noexcept
+{
+ m_id = id;
+}
+
+std::size_t world::Voxel::get_random_texture_index(VoxelFace face, const voxel_pos& vpos) const
+{
+ const auto& textures = get_face_textures(face);
+
+ assert(textures.size());
+
+ std::uint64_t hash = 0U;
+ hash = math::crc64(&vpos.x, sizeof(vpos.x), hash);
+ hash = math::crc64(&vpos.y, sizeof(vpos.y), hash);
+ hash = math::crc64(&vpos.z, sizeof(vpos.z), hash);
+
+ return static_cast<std::size_t>(hash % textures.size());
+}
+
+void world::Voxel::set_face_cache(VoxelFace face, std::size_t offset, std::size_t plane)
+{
+ assert(face < m_cached_face_offsets.size());
+ assert(face < m_cached_face_planes.size());
+
+ m_cached_face_offsets[face] = offset;
+ m_cached_face_planes[face] = plane;
+}
+
+std::uint64_t world::Voxel::calculate_checksum(std::uint64_t combine) const
+{
+ combine = math::crc64(m_name.data(), m_name.size(), combine);
+ combine += static_cast<std::uint64_t>(m_shape);
+ combine += static_cast<std::uint64_t>(m_render_mode);
+ return combine;
+}
+
+std::shared_ptr<world::Voxel> world::Voxel::clone(void) const
+{
+ return std::make_shared<Voxel>(*this);
+}
+
+void world::Voxel::set_name(std::string_view name) noexcept
+{
+ assert(name.size());
+
+ m_name = name;
+}
+
+void world::Voxel::set_render_mode(VoxelRender mode) noexcept
+{
+ m_render_mode = mode;
+}
+
+void world::Voxel::set_shape(VoxelShape shape) noexcept
+{
+ m_shape = shape;
+}
+
+void world::Voxel::set_animated(bool animated) noexcept
+{
+ m_animated = animated;
+}
+
+void world::Voxel::set_touch_type(VoxelTouch type) noexcept
+{
+ m_touch_type = type;
+}
+
+void world::Voxel::set_touch_values(const glm::fvec3& values) noexcept
+{
+ m_touch_values = values;
+}
+
+void world::Voxel::set_surface_material(VoxelMaterial material) noexcept
+{
+ m_surface_material = material;
+}
+
+void world::Voxel::set_collision(const math::AABBf& box) noexcept
+{
+ m_collision = box;
+}
+
+void world::Voxel::add_default_texture(std::string_view path)
+{
+ assert(path.size());
+
+ m_default_textures.emplace_back(path);
+}
+
+void world::Voxel::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);
+}
diff --git a/game/shared/world/voxel.hh b/game/shared/world/voxel.hh
new file mode 100644
index 0000000..0bd83b7
--- /dev/null
+++ b/game/shared/world/voxel.hh
@@ -0,0 +1,236 @@
+#pragma once
+
+#include "core/math/aabb.hh"
+
+#include "shared/types.hh"
+
+namespace world
+{
+class Dimension;
+} // namespace world
+
+namespace world
+{
+enum VoxelRender : unsigned short {
+ 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 {
+ VSHAPE_CUBE = 0U, ///< Full cube shape
+ VSHAPE_CROSS, ///< TODO: Cross shape
+ VSHAPE_MODEL, ///< TODO: Custom model shape
+};
+
+enum VoxelFace : unsigned short {
+ VFACE_NORTH = 0U, ///< Positive Z face
+ VFACE_SOUTH, ///< Negative Z face
+ VFACE_EAST, ///< Positive X face
+ VFACE_WEST, ///< Negative X face
+ VFACE_TOP, ///< Positive Y face
+ VFACE_BOTTOM, ///< Negative Y face
+ VFACE_CROSS_NWSE, ///< Diagonal cross face northwest-southeast
+ VFACE_CROSS_NESW, ///< Diagonal cross face northeast-southwest
+ VFACE_COUNT
+};
+
+enum VoxelTouch : unsigned short {
+ 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 {
+ VMAT_DEFAULT = 0U,
+ VMAT_STONE,
+ VMAT_DIRT,
+ VMAT_GLASS,
+ VMAT_GRASS,
+ VMAT_GRAVEL,
+ VMAT_METAL,
+ VMAT_SAND,
+ VMAT_WOOD,
+ VMAT_SLOSH,
+ VMAT_COUNT
+};
+
+enum VoxelVisBits : unsigned short {
+ VVIS_NORTH = 1U << VFACE_NORTH, ///< Positive Z
+ VVIS_SOUTH = 1U << VFACE_SOUTH, ///< Negative Z
+ VVIS_EAST = 1U << VFACE_EAST, ///< Positive X
+ VVIS_WEST = 1U << VFACE_WEST, ///< Negative X
+ VVIS_UP = 1U << VFACE_TOP, ///< Positive Y
+ VVIS_DOWN = 1U << VFACE_BOTTOM, ///< Negative Y
+};
+} // 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;
+
+ /// 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;
+
+ 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;
+ constexpr bool is_animated(void) const noexcept;
+
+ constexpr VoxelTouch get_touch_type(void) const noexcept;
+ constexpr const glm::fvec3& get_touch_values(void) const noexcept;
+ constexpr VoxelMaterial get_surface_material(void) const noexcept;
+
+ constexpr const math::AABBf& get_collision(void) const noexcept;
+
+ constexpr const std::vector<std::string>& get_default_textures(void) const noexcept;
+ constexpr const std::vector<std::string>& get_face_textures(VoxelFace face) const noexcept;
+ constexpr std::size_t get_cached_face_offset(VoxelFace face) const noexcept;
+ constexpr std::size_t get_cached_face_plane(VoxelFace face) 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
+ /// @param vpos The absolute voxel position to get the texture index for
+ /// @return The index of the texture to use for the given face at the given position
+ /// @remarks On client-side: plane[get_cached_face_plane][get_cached_face_offset + thisFunctionResult]
+ std::size_t get_random_texture_index(VoxelFace face, const voxel_pos& vpos) const;
+
+ /// Assign cached plane index and plane offset for a given face
+ /// @param face The face to assign the cache for
+ /// @param offset The offset to assign to the face
+ /// @param plane The plane index to assign to the face
+ void set_face_cache(VoxelFace face, std::size_t offset, std::size_t plane);
+
+ /// Calculate a checksum for the voxel's properties
+ /// @param combine An optional initial checksum to combine with
+ /// @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 };
+
+ VoxelRender m_render_mode { VRENDER_OPAQUE };
+ VoxelShape m_shape { VSHAPE_CUBE };
+ bool m_animated { false };
+
+ VoxelTouch m_touch_type { VTOUCH_SOLID };
+ glm::fvec3 m_touch_values { 0.0f };
+ VoxelMaterial m_surface_material { VMAT_DEFAULT };
+
+ math::AABBf m_collision { { 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f } };
+
+ std::vector<std::string> m_default_textures;
+ 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;
+};
+} // namespace world
+
+constexpr std::string_view world::Voxel::get_name(void) const noexcept
+{
+ return m_name;
+}
+
+constexpr voxel_id world::Voxel::get_id(void) const noexcept
+{
+ return m_id;
+}
+
+constexpr world::VoxelRender world::Voxel::get_render_mode(void) const noexcept
+{
+ return m_render_mode;
+}
+
+constexpr world::VoxelShape world::Voxel::get_shape(void) const noexcept
+{
+ return m_shape;
+}
+
+constexpr bool world::Voxel::is_animated(void) const noexcept
+{
+ return m_animated;
+}
+
+constexpr world::VoxelTouch world::Voxel::get_touch_type(void) const noexcept
+{
+ return m_touch_type;
+}
+
+constexpr const glm::fvec3& world::Voxel::get_touch_values(void) const noexcept
+{
+ return m_touch_values;
+}
+
+constexpr world::VoxelMaterial world::Voxel::get_surface_material(void) const noexcept
+{
+ return m_surface_material;
+}
+
+constexpr const math::AABBf& world::Voxel::get_collision(void) const noexcept
+{
+ return m_collision;
+}
+
+constexpr const std::vector<std::string>& world::Voxel::get_default_textures(void) const noexcept
+{
+ return m_default_textures;
+}
+
+constexpr const std::vector<std::string>& world::Voxel::get_face_textures(VoxelFace face) const noexcept
+{
+ assert(face <= m_face_textures.size());
+
+ return m_face_textures[face];
+}
+
+constexpr std::size_t world::Voxel::get_cached_face_offset(VoxelFace face) const noexcept
+{
+ assert(face <= m_cached_face_offsets.size());
+
+ return m_cached_face_offsets[face];
+}
+
+constexpr std::size_t world::Voxel::get_cached_face_plane(VoxelFace face) const noexcept
+{
+ assert(face <= m_cached_face_planes.size());
+
+ return m_cached_face_planes[face];
+}
diff --git a/game/shared/world/voxel_registry.cc b/game/shared/world/voxel_registry.cc
index b44845e..4c2f360 100644
--- a/game/shared/world/voxel_registry.cc
+++ b/game/shared/world/voxel_registry.cc
@@ -2,195 +2,65 @@
#include "shared/world/voxel_registry.hh"
-#include "core/math/crc64.hh"
+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::unordered_map<std::string, world::VoxelInfoBuilder> world::voxel_registry::builders = {};
-std::unordered_map<std::string, voxel_id> world::voxel_registry::names = {};
-std::vector<std::shared_ptr<world::VoxelInfo>> world::voxel_registry::voxels = {};
-
-world::VoxelInfoBuilder::VoxelInfoBuilder(std::string_view name, voxel_type type, bool animated, bool blending)
-{
- prototype.name = name;
- prototype.type = type;
- prototype.animated = animated;
- prototype.blending = blending;
-
- switch(type) {
- case voxel_type::CUBE:
- prototype.textures.resize(static_cast<std::size_t>(voxel_face::CUBE__NR));
- break;
- case voxel_type::CROSS:
- prototype.textures.resize(static_cast<std::size_t>(voxel_face::CROSS__NR));
- break;
- case voxel_type::MODEL:
- // Custom models should use a different texture
- // resource management that is not a voxel atlas
- // TODO: actually implement custom models lol
- prototype.textures.resize(0);
- break;
- default:
- // Something really bad should happen if we end up here.
- // The outside code would static_cast an int to VoxelType
- // and possibly fuck a lot of things up to cause this
- spdlog::critical("voxel_registry: {}: unknown voxel type {}", name, static_cast<int>(type));
- std::terminate();
- }
-
- // Physics properties
- prototype.touch_type = voxel_touch::SOLID;
- prototype.touch_values = glm::fvec3(0.0f, 0.0f, 0.0f);
- prototype.surface = voxel_surface::DEFAULT;
-
- // Things set in future by item_def
- prototype.item_pick = NULL_ITEM_ID;
-}
-
-world::VoxelInfoBuilder& world::VoxelInfoBuilder::add_texture_default(std::string_view texture)
+static void recalculate_checksum(void)
{
- default_texture.paths.push_back(std::string(texture));
- return *this;
-}
+ registry_checksum = 0U;
-world::VoxelInfoBuilder& world::VoxelInfoBuilder::add_texture(voxel_face face, std::string_view texture)
-{
- const auto index = static_cast<std::size_t>(face);
- prototype.textures[index].paths.push_back(std::string(texture));
- return *this;
-}
-
-world::VoxelInfoBuilder& world::VoxelInfoBuilder::set_touch(voxel_touch type, const glm::fvec3& values)
-{
- prototype.touch_type = type;
- prototype.touch_values = values;
- return *this;
+ for(const auto& voxel : world::voxel_registry::voxels) {
+ registry_checksum = voxel->calculate_checksum(registry_checksum);
+ }
}
-world::VoxelInfoBuilder& world::VoxelInfoBuilder::set_surface(voxel_surface surface)
+world::Voxel* world::voxel_registry::register_voxel(const Voxel& voxel_template)
{
- prototype.surface = surface;
- return *this;
-}
+ assert(voxel_template.get_name().size());
+ assert(nullptr == find(voxel_template.get_name()));
-voxel_id world::VoxelInfoBuilder::build(void) const
-{
- const auto it = world::voxel_registry::names.find(prototype.name);
+ const auto id = static_cast<voxel_id>(voxels.size());
- if(it != world::voxel_registry::names.cend()) {
- spdlog::warn("voxel_registry: cannot build {}: name already present", prototype.name);
- return it->second;
- }
+ auto voxel = voxel_template.clone();
+ voxel->set_id(id);
- std::size_t state_count;
-
- switch(prototype.type) {
- case voxel_type::CUBE:
- case voxel_type::CROSS:
- case voxel_type::MODEL:
- state_count = 1;
- break;
- default:
- // Something really bad should happen if we end up here.
- // The outside code would static_cast an int to VoxelType
- // and possibly fuck a lot of things up to cause this
- spdlog::critical("voxel_registry: {}: unknown voxel type {}", prototype.name, static_cast<int>(prototype.type));
- std::terminate();
- }
+ names.emplace(std::string(voxel_template.get_name()), id);
+ voxels.push_back(std::move(voxel));
- if((world::voxel_registry::voxels.size() + state_count) >= MAX_VOXEL_ID) {
- spdlog::critical("voxel_registry: voxel registry overflow");
- std::terminate();
- }
+ recalculate_checksum();
- auto new_info = std::make_shared<VoxelInfo>();
- new_info->name = prototype.name;
- new_info->type = prototype.type;
- new_info->animated = prototype.animated;
- new_info->blending = prototype.blending;
-
- new_info->textures.resize(prototype.textures.size());
-
- for(std::size_t i = 0; i < prototype.textures.size(); ++i) {
- if(prototype.textures[i].paths.empty()) {
- new_info->textures[i].paths = default_texture.paths;
- new_info->textures[i].cached_offset = SIZE_MAX;
- new_info->textures[i].cached_plane = SIZE_MAX;
- }
- else {
- new_info->textures[i].paths = prototype.textures[i].paths;
- new_info->textures[i].cached_offset = SIZE_MAX;
- new_info->textures[i].cached_plane = SIZE_MAX;
- }
- }
-
- // Physics properties
- new_info->touch_type = prototype.touch_type;
- new_info->touch_values = prototype.touch_values;
- new_info->surface = prototype.surface;
-
- // Things set in future by item_def
- new_info->item_pick = prototype.item_pick;
-
- // Base voxel identifier offset
- new_info->base_voxel = world::voxel_registry::voxels.size() + 1;
-
- for(std::size_t i = 0; i < state_count; ++i)
- world::voxel_registry::voxels.push_back(new_info);
- world::voxel_registry::names.insert_or_assign(new_info->name, new_info->base_voxel);
-
- return new_info->base_voxel;
-}
-
-world::VoxelInfoBuilder& world::voxel_registry::construct(std::string_view name, voxel_type type, bool animated, bool blending)
-{
- const auto it = world::voxel_registry::builders.find(std::string(name));
-
- if(it != world::voxel_registry::builders.cend()) {
- return it->second;
- }
- else {
- return world::voxel_registry::builders.emplace(std::string(name), VoxelInfoBuilder(name, type, animated, blending)).first->second;
- }
+ return voxels.back().get();
}
-world::VoxelInfo* world::voxel_registry::find(std::string_view name)
+world::Voxel* world::voxel_registry::find(std::string_view name)
{
- const auto it = world::voxel_registry::names.find(std::string(name));
+ const auto it = names.find(name);
- if(it != world::voxel_registry::names.cend()) {
- return world::voxel_registry::find(it->second);
- }
- else {
+ if(it == names.end()) {
return nullptr;
}
+
+ return voxels[it->second].get();
}
-world::VoxelInfo* world::voxel_registry::find(const voxel_id voxel)
+world::Voxel* world::voxel_registry::find(voxel_id id)
{
- if((voxel != NULL_VOXEL_ID) && (voxel <= world::voxel_registry::voxels.size())) {
- return world::voxel_registry::voxels[voxel - 1].get();
- }
- else {
+ if(id >= voxels.size()) {
return nullptr;
}
+
+ return voxels[id].get();
}
void world::voxel_registry::purge(void)
{
- world::voxel_registry::builders.clear();
- world::voxel_registry::names.clear();
- world::voxel_registry::voxels.clear();
+ registry_checksum = 0U;
+ voxels.clear();
+ names.clear();
}
-std::uint64_t world::voxel_registry::calculate_checksum(void)
+std::uint64_t world::voxel_registry::get_checksum(void)
{
- std::uint64_t result = 0;
-
- for(const std::shared_ptr<VoxelInfo>& info : world::voxel_registry::voxels) {
- result = math::crc64(info->name, result);
- result += static_cast<std::uint64_t>(info->type);
- result += static_cast<std::uint64_t>(info->base_voxel);
- result += info->blending ? 256 : 1;
- }
-
- return result;
+ return registry_checksum;
}
diff --git a/game/shared/world/voxel_registry.hh b/game/shared/world/voxel_registry.hh
index a75f59f..b9d9a34 100644
--- a/game/shared/world/voxel_registry.hh
+++ b/game/shared/world/voxel_registry.hh
@@ -1,141 +1,18 @@
#pragma once
-#include "shared/types.hh"
-
-namespace world
-{
-enum class voxel_face : unsigned short {
- CUBE_NORTH = 0x0000,
- CUBE_SOUTH = 0x0001,
- CUBE_EAST = 0x0002,
- CUBE_WEST = 0x0003,
- CUBE_TOP = 0x0004,
- CUBE_BOTTOM = 0x0005,
- CUBE__NR = 0x0006,
-
- CROSS_NESW = 0x0000,
- CROSS_NWSE = 0x0001,
- CROSS__NR = 0x0002,
-};
-
-enum class voxel_type : unsigned short {
- CUBE = 0x0000,
- CROSS = 0x0001, // TODO
- MODEL = 0x0002, // TODO
-};
-
-enum class voxel_facing : unsigned short {
- NORTH = 0x0000,
- SOUTH = 0x0001,
- EAST = 0x0002,
- WEST = 0x0003,
- UP = 0x0004,
- DOWN = 0x0005,
- NESW = 0x0006,
- NWSE = 0x0007,
-};
-
-enum class voxel_touch : unsigned short {
- SOLID = 0x0000, // The entity is stopped in its tracks
- BOUNCE = 0x0001, // The entity bounces back with some energy loss
- SINK = 0x0002, // The entity phases/sinks through the voxel
- NOTHING = 0xFFFF,
-};
-
-enum class voxel_surface : unsigned short {
- DEFAULT = 0x0000,
- STONE = 0x0001,
- DIRT = 0x0002,
- GLASS = 0x0003,
- GRASS = 0x0004,
- GRAVEL = 0x0005,
- METAL = 0x0006,
- SAND = 0x0007,
- WOOD = 0x0008,
- SLOSH = 0x0009,
- COUNT = 0x000A,
- UNKNOWN = 0xFFFF,
-};
-
-using voxel_vis = unsigned short;
-constexpr static voxel_vis VIS_NORTH = 1 << static_cast<unsigned int>(voxel_facing::NORTH);
-constexpr static voxel_vis VIS_SOUTH = 1 << static_cast<unsigned int>(voxel_facing::SOUTH);
-constexpr static voxel_vis VIS_EAST = 1 << static_cast<unsigned int>(voxel_facing::EAST);
-constexpr static voxel_vis VIS_WEST = 1 << static_cast<unsigned int>(voxel_facing::WEST);
-constexpr static voxel_vis VIS_UP = 1 << static_cast<unsigned int>(voxel_facing::UP);
-constexpr static voxel_vis VIS_DOWN = 1 << static_cast<unsigned int>(voxel_facing::DOWN);
-} // namespace world
-
-namespace world
-{
-struct VoxelTexture final {
- std::vector<std::string> paths;
- std::size_t cached_offset; // client-side only
- std::size_t cached_plane; // client-side only
-};
-
-struct VoxelInfo final {
- std::string name;
- voxel_type type;
- bool animated;
- bool blending;
-
- std::vector<VoxelTexture> textures;
-
- // Physics properties go here
- // TODO: player_move friction modifiers
- // that would make the voxel very sticky or
- // very slippery to walk on
- voxel_touch touch_type;
- glm::fvec3 touch_values;
- voxel_surface surface;
-
- // Some voxel types might occupy multiple voxel_id
- // values that reference to the exact same VoxelInfo
- // structure; the actual numeric state is figured out by
- // subtracting base_voxel from the checking voxel_id
- voxel_id base_voxel;
-
- // These will be set by item_registry
- // and by default set to NULL_ITEM_ID
- item_id item_pick;
-};
-} // namespace world
-
-namespace world
-{
-class VoxelInfoBuilder final {
-public:
- explicit VoxelInfoBuilder(std::string_view name, voxel_type type, bool animated, bool blending);
- virtual ~VoxelInfoBuilder(void) = default;
-
-public:
- VoxelInfoBuilder& add_texture_default(std::string_view texture);
- VoxelInfoBuilder& add_texture(voxel_face face, std::string_view texture);
- VoxelInfoBuilder& set_touch(voxel_touch type, const glm::fvec3& values);
- VoxelInfoBuilder& set_surface(voxel_surface surface);
-
-public:
- voxel_id build(void) const;
-
-private:
- VoxelTexture default_texture;
- VoxelInfo prototype;
-};
-} // namespace world
+#include "shared/world/voxel.hh"
namespace world::voxel_registry
{
-extern std::unordered_map<std::string, VoxelInfoBuilder> builders;
-extern std::unordered_map<std::string, voxel_id> names;
-extern std::vector<std::shared_ptr<VoxelInfo>> voxels;
+extern emhash8::HashMap<std::string, voxel_id> names;
+extern std::vector<std::shared_ptr<Voxel>> voxels;
} // namespace world::voxel_registry
namespace world::voxel_registry
{
-VoxelInfoBuilder& construct(std::string_view name, voxel_type type, bool animated, bool blending);
-VoxelInfo* find(std::string_view name);
-VoxelInfo* find(const voxel_id voxel);
+Voxel* register_voxel(const Voxel& voxel_template);
+Voxel* find(std::string_view name);
+Voxel* find(voxel_id id);
} // namespace world::voxel_registry
namespace world::voxel_registry
@@ -145,5 +22,5 @@ void purge(void);
namespace world::voxel_registry
{
-std::uint64_t calculate_checksum(void);
+std::uint64_t get_checksum(void);
} // namespace world::voxel_registry
diff --git a/game/shared/world/voxels/generic.hh b/game/shared/world/voxels/generic.hh
new file mode 100644
index 0000000..a661792
--- /dev/null
+++ b/game/shared/world/voxels/generic.hh
@@ -0,0 +1,27 @@
+#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