diff options
| author | untodesu <kirill@untode.su> | 2025-09-12 16:16:06 +0500 |
|---|---|---|
| committer | untodesu <kirill@untode.su> | 2025-09-12 16:16:06 +0500 |
| commit | fc80fa024fc93dac6ea89461ef36f455c5e468a2 (patch) | |
| tree | 7c4ea8f03b6778572d59784dc28b600e3f8f2268 /game/shared | |
| parent | 12947aafcc6a6eb362cc454e2149796ec9265743 (diff) | |
| parent | 522a7514012da86f7b9643179f0763746f3b232e (diff) | |
| download | voxelius-fc80fa024fc93dac6ea89461ef36f455c5e468a2.tar.bz2 voxelius-fc80fa024fc93dac6ea89461ef36f455c5e468a2.zip | |
Merge pull request #15 from untodesu/metavoxels
Metavoxels
Diffstat (limited to 'game/shared')
27 files changed, 983 insertions, 671 deletions
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..34a0f9e 100644 --- a/game/shared/entity/grounded.hh +++ b/game/shared/entity/grounded.hh @@ -1,16 +1,12 @@ -#ifndef SHARED_ENTITY_GROUNDED
-#define SHARED_ENTITY_GROUNDED 1
#pragma once
-#include "shared/world/voxel_registry.hh"
+#include "shared/world/voxel.hh"
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
-
-#endif // SHARED_ENTITY_GROUNDED
diff --git a/game/shared/game.hh b/game/shared/game.hh index 21286e8..0dfbadb 100644 --- a/game/shared/game.hh +++ b/game/shared/game.hh @@ -1,5 +1,3 @@ -#ifndef SHARED_GAME
-#define SHARED_GAME 1
#pragma once
namespace shared_game
@@ -7,5 +5,3 @@ namespace shared_game void init(int argc, char** argv);
void shutdown(void);
} // namespace shared_game
-
-#endif // SHARED_GAME
diff --git a/game/shared/game_items.cc b/game/shared/game_items.cc index 6073117..cad73da 100644 --- a/game/shared/game_items.cc +++ b/game/shared/game_items.cc @@ -6,61 +6,60 @@ #include "shared/game_voxels.hh"
-item_id game_items::stone = NULL_ITEM_ID;
-item_id game_items::cobblestone = NULL_ITEM_ID;
-item_id game_items::dirt = NULL_ITEM_ID;
-item_id game_items::grass = NULL_ITEM_ID;
-item_id game_items::oak_leaves = NULL_ITEM_ID;
-item_id game_items::oak_planks = NULL_ITEM_ID;
-item_id game_items::oak_log = NULL_ITEM_ID;
-item_id game_items::glass = NULL_ITEM_ID;
-item_id game_items::slime = NULL_ITEM_ID;
-item_id game_items::mud = NULL_ITEM_ID;
+const world::Item* game_items::stone = nullptr;
+const world::Item* game_items::cobblestone = nullptr;
+const world::Item* game_items::dirt = nullptr;
+const world::Item* game_items::grass = nullptr;
+const world::Item* game_items::oak_leaves = nullptr;
+const world::Item* game_items::oak_planks = nullptr;
+const world::Item* game_items::oak_log = nullptr;
+const world::Item* game_items::glass = nullptr;
+const world::Item* game_items::slime = nullptr;
void game_items::populate(void)
{
- // Stone; a hardened slate rock
- game_items::stone =
- world::item_registry::construct("stone").set_texture("textures/item/stone.png").set_place_voxel(game_voxels::stone).build();
+ auto stone_builder = world::ItemBuilder("stone");
+ stone_builder.set_texture("textures/item/stone.png");
+ stone_builder.set_place_voxel(game_voxels::stone);
+ stone = world::item_registry::register_item(stone_builder);
- // Cobblestone; a bunch of small stones
- game_items::cobblestone = world::item_registry::construct("cobblestone")
- .set_texture("textures/item/cobblestone.png")
- .set_place_voxel(game_voxels::cobblestone)
- .build();
+ auto cobblestone_builder = world::ItemBuilder("cobblestone");
+ cobblestone_builder.set_texture("textures/item/cobblestone.png");
+ cobblestone_builder.set_place_voxel(game_voxels::cobblestone);
+ cobblestone = world::item_registry::register_item(cobblestone_builder);
- // Dirt; it's very dirty
- game_items::dirt =
- world::item_registry::construct("dirt").set_texture("textures/item/dirt.png").set_place_voxel(game_voxels::dirt).build();
+ auto dirt_builder = world::ItemBuilder("dirt");
+ dirt_builder.set_texture("textures/item/dirt.png");
+ dirt_builder.set_place_voxel(game_voxels::dirt);
+ dirt = world::item_registry::register_item(dirt_builder);
- // Grass; literally just grassy dirt
- game_items::grass =
- world::item_registry::construct("grass").set_texture("textures/item/grass.png").set_place_voxel(game_voxels::grass).build();
+ auto grass_builder = world::ItemBuilder("grass");
+ grass_builder.set_texture("textures/item/grass.png");
+ grass_builder.set_place_voxel(game_voxels::grass);
+ grass = world::item_registry::register_item(grass_builder);
- // Oak leaves; they're bushy!
- game_items::oak_leaves = world::item_registry::construct("oak_leaves")
- .set_texture("textures/item/oak_leaves.png")
- .set_place_voxel(game_voxels::oak_leaves)
- .build();
+ auto oak_leaves_builder = world::ItemBuilder("oak_leaves");
+ oak_leaves_builder.set_texture("textures/item/oak_leaves.png");
+ oak_leaves_builder.set_place_voxel(game_voxels::oak_leaves);
+ oak_leaves = world::item_registry::register_item(oak_leaves_builder);
- // Oak planks; watch for splinters!
- game_items::oak_planks = world::item_registry::construct("oak_planks")
- .set_texture("textures/item/oak_planks.png")
- .set_place_voxel(game_voxels::oak_planks)
- .build();
+ auto oak_planks_builder = world::ItemBuilder("oak_planks");
+ oak_planks_builder.set_texture("textures/item/oak_planks.png");
+ oak_planks_builder.set_place_voxel(game_voxels::oak_planks);
+ oak_planks = world::item_registry::register_item(oak_planks_builder);
- // Oak log; a big wad of wood
- game_items::oak_log =
- world::item_registry::construct("oak_log").set_texture("textures/item/oak_log.png").set_place_voxel(game_voxels::oak_log).build();
+ auto oak_log_builder = world::ItemBuilder("oak_log");
+ oak_log_builder.set_texture("textures/item/oak_log.png");
+ oak_log_builder.set_place_voxel(game_voxels::oak_log);
+ oak_log = world::item_registry::register_item(oak_log_builder);
- // Glass; used for windowing
- game_items::glass =
- world::item_registry::construct("glass").set_texture("textures/item/glass.png").set_place_voxel(game_voxels::glass).build();
+ auto glass_builder = world::ItemBuilder("glass");
+ glass_builder.set_texture("textures/item/glass.png");
+ glass_builder.set_place_voxel(game_voxels::glass);
+ glass = world::item_registry::register_item(glass_builder);
- // Slime; it's bouncy!
- game_items::slime =
- world::item_registry::construct("slime").set_texture("textures/item/slime.png").set_place_voxel(game_voxels::slime).build();
-
- // Mud; you sink in it!
- game_items::mud = world::item_registry::construct("mud").set_texture("textures/item/mud.png").build();
+ auto slime_builder = world::ItemBuilder("slime");
+ slime_builder.set_texture("textures/item/slime.png");
+ slime_builder.set_place_voxel(game_voxels::slime);
+ slime = world::item_registry::register_item(slime_builder);
}
diff --git a/game/shared/game_items.hh b/game/shared/game_items.hh index 099923f..6f8eac9 100644 --- a/game/shared/game_items.hh +++ b/game/shared/game_items.hh @@ -1,26 +1,24 @@ -#ifndef SHARED_GAME_ITEMS
-#define SHARED_GAME_ITEMS 1
#pragma once
-#include "shared/types.hh"
+namespace world
+{
+class Item;
+} // namespace world
namespace game_items
{
-extern item_id stone;
-extern item_id cobblestone;
-extern item_id dirt;
-extern item_id grass;
-extern item_id oak_leaves;
-extern item_id oak_planks;
-extern item_id oak_log;
-extern item_id glass;
-extern item_id slime;
-extern item_id mud;
+extern const world::Item* stone;
+extern const world::Item* cobblestone;
+extern const world::Item* dirt;
+extern const world::Item* grass;
+extern const world::Item* oak_leaves;
+extern const world::Item* oak_planks;
+extern const world::Item* oak_log;
+extern const world::Item* glass;
+extern const world::Item* slime;
} // namespace game_items
namespace game_items
{
void populate(void);
} // namespace game_items
-
-#endif // SHARED_GAME_ITEMS
diff --git a/game/shared/game_voxels.cc b/game/shared/game_voxels.cc index 51de1a1..51cf873 100644 --- a/game/shared/game_voxels.cc +++ b/game/shared/game_voxels.cc @@ -2,118 +2,151 @@ #include "shared/game_voxels.hh"
+#include "shared/world/dimension.hh"
#include "shared/world/voxel_registry.hh"
-voxel_id game_voxels::cobblestone = NULL_VOXEL_ID;
-voxel_id game_voxels::dirt = NULL_VOXEL_ID;
-voxel_id game_voxels::grass = NULL_VOXEL_ID;
-voxel_id game_voxels::stone = NULL_VOXEL_ID;
-voxel_id game_voxels::vtest = NULL_VOXEL_ID;
-voxel_id game_voxels::vtest_ck = NULL_VOXEL_ID;
-voxel_id game_voxels::oak_leaves = NULL_VOXEL_ID;
-voxel_id game_voxels::oak_planks = NULL_VOXEL_ID;
-voxel_id game_voxels::oak_log = NULL_VOXEL_ID;
-voxel_id game_voxels::glass = NULL_VOXEL_ID;
-voxel_id game_voxels::slime = NULL_VOXEL_ID;
-voxel_id game_voxels::mud = NULL_VOXEL_ID;
+#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;
+const world::Voxel* game_voxels::stone = nullptr;
+const world::Voxel* game_voxels::vtest = nullptr;
+const world::Voxel* game_voxels::vtest_ck = nullptr;
+const world::Voxel* game_voxels::oak_leaves = nullptr;
+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;
+
+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) {
+ dimension->set_voxel(game_voxels::grass, vpos);
+ }
+}
+
+static void grass_tick(world::Dimension* dimension, const voxel_pos& vpos)
+{
+ auto above_vpos = vpos + voxel_pos(0, 1, 0);
+ auto above_voxel = dimension->get_voxel(above_vpos);
+
+ if(above_voxel && !above_voxel->is_surface_material<world::VMAT_GLASS>()) {
+ // Decay into dirt if something is blocking airflow
+ dimension->set_voxel(game_voxels::dirt, vpos);
+ }
+}
void game_voxels::populate(void)
{
- // Stone; the backbone of the generated world
- game_voxels::stone =
- world::voxel_registry::construct("stone", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/stone_01.png")
- .add_texture_default("textures/voxel/stone_02.png")
- .add_texture_default("textures/voxel/stone_03.png")
- .add_texture_default("textures/voxel/stone_04.png")
- .set_surface(world::voxel_surface::STONE)
- .build();
-
- // Cobblestone; should drop when a stone is broken, might also be present in surface features
- game_voxels::cobblestone =
- world::voxel_registry::construct("cobblestone", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/cobblestone_01.png")
- .add_texture_default("textures/voxel/cobblestone_02.png")
- .set_surface(world::voxel_surface::STONE)
- .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();
-
- // VTest; a test voxel to ensure animations work
- game_voxels::vtest = world::voxel_registry::construct("vtest", world::voxel_type::CUBE, true, false)
- .add_texture_default("textures/voxel/vtest_F1.png")
- .add_texture_default("textures/voxel/vtest_F2.png")
- .add_texture_default("textures/voxel/vtest_F3.png")
- .add_texture_default("textures/voxel/vtest_F4.png")
- .build();
-
- // VTest-CK; a pure blue chromakey I used to make the game's logo
- game_voxels::vtest_ck = world::voxel_registry::construct("vtest_ck", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/chromakey.png")
- .build();
-
- // Oak leaves; greenery. TODO: add trees as surface features
- game_voxels::oak_leaves = world::voxel_registry::construct("oak_leaves", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/oak_leaves.png")
- .set_surface(world::voxel_surface::GRASS)
- .build();
-
- // Oak planks; the thing that comes out of oak logs
- game_voxels::oak_planks = world::voxel_registry::construct("oak_planks", world::voxel_type::CUBE, false, false)
- .add_texture_default("textures/voxel/oak_planks_01.png")
- .add_texture_default("textures/voxel/oak_planks_02.png")
- .set_surface(world::voxel_surface::WOOD)
- .build();
-
- // 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();
-
- // Slime; it's bouncy!
- game_voxels::slime = world::voxel_registry::construct("slime", world::voxel_type::CUBE, false, true)
- .set_touch(world::voxel_touch::BOUNCE, glm::fvec3(0.00f, 0.60f, 0.00f))
- .add_texture_default("textures/voxel/slime_01.png")
- .build();
-
- // Mud; you sink in it
- game_voxels::mud = world::voxel_registry::construct("mud", world::voxel_type::CUBE, false, false)
- .set_touch(world::voxel_touch::SINK, glm::fvec3(0.50f, 0.75f, 0.50f))
- .add_texture_default("textures/voxel/mud_01.png")
- .add_texture_default("textures/voxel/mud_02.png")
- .set_surface(world::voxel_surface::DIRT)
- .build();
+ auto stone_builder = world::VoxelBuilder("stone");
+ stone_builder.add_default_texture("textures/voxel/stone_01.png");
+ stone_builder.add_default_texture("textures/voxel/stone_02.png");
+ stone_builder.add_default_texture("textures/voxel/stone_03.png");
+ stone_builder.add_default_texture("textures/voxel/stone_04.png");
+ stone = world::voxel_registry::register_voxel(stone_builder);
+
+ auto cobblestone_builder = world::VoxelBuilder("cobblestone");
+ cobblestone_builder.add_default_texture("textures/voxel/cobblestone_01.png");
+ cobblestone_builder.add_default_texture("textures/voxel/cobblestone_02.png");
+ cobblestone = world::voxel_registry::register_voxel(cobblestone_builder);
+
+ auto dirt_builder = world::VoxelBuilder("dirt");
+ dirt_builder.add_default_texture("textures/voxel/dirt_01.png");
+ dirt_builder.add_default_texture("textures/voxel/dirt_02.png");
+ dirt_builder.add_default_texture("textures/voxel/dirt_03.png");
+ dirt_builder.add_default_texture("textures/voxel/dirt_04.png");
+ dirt_builder.set_surface_material(world::VMAT_DIRT);
+ dirt_builder.set_on_tick(&dirt_tick);
+ dirt = world::voxel_registry::register_voxel(dirt_builder);
+
+ auto grass_builder = world::VoxelBuilder("grass");
+ grass_builder.add_default_texture("textures/voxel/grass_side_01.png");
+ grass_builder.add_default_texture("textures/voxel/grass_side_02.png");
+ grass_builder.add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_01.png");
+ grass_builder.add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_02.png");
+ grass_builder.add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_03.png");
+ grass_builder.add_face_texture(world::VFACE_BOTTOM, "textures/voxel/dirt_04.png");
+ grass_builder.add_face_texture(world::VFACE_TOP, "textures/voxel/grass_01.png");
+ grass_builder.add_face_texture(world::VFACE_TOP, "textures/voxel/grass_02.png");
+ grass_builder.set_surface_material(world::VMAT_GRASS);
+ grass_builder.set_on_tick(&grass_tick);
+ grass = world::voxel_registry::register_voxel(grass_builder);
+
+ auto vtest_builder = world::VoxelBuilder("vtest");
+ vtest_builder.add_default_texture("textures/voxel/vtest_F1.png");
+ vtest_builder.add_default_texture("textures/voxel/vtest_F2.png");
+ vtest_builder.add_default_texture("textures/voxel/vtest_F3.png");
+ vtest_builder.add_default_texture("textures/voxel/vtest_F4.png");
+ vtest_builder.set_animated(true);
+ vtest = world::voxel_registry::register_voxel(vtest_builder);
+
+ auto vtest_ck_builder = world::VoxelBuilder("vtest_ck");
+ vtest_ck_builder.add_default_texture("textures/voxel/chromakey.png");
+ vtest_ck = world::voxel_registry::register_voxel(vtest_ck_builder);
+
+ auto oak_leaves_builder = world::VoxelBuilder("oak_leaves");
+ oak_leaves_builder.add_default_texture("textures/voxel/oak_leaves.png");
+ oak_leaves_builder.set_surface_material(world::VMAT_GRASS);
+ oak_leaves = world::voxel_registry::register_voxel(oak_leaves_builder);
+
+ auto oak_planks_builder = world::VoxelBuilder("oak_planks");
+ oak_planks_builder.add_default_texture("textures/voxel/oak_planks_01.png");
+ oak_planks_builder.add_default_texture("textures/voxel/oak_planks_02.png");
+ oak_planks_builder.set_surface_material(world::VMAT_WOOD);
+ oak_planks = world::voxel_registry::register_voxel(oak_planks_builder);
+
+ auto oak_log_builder = world::VoxelBuilder("oak_log");
+ oak_log_builder.add_default_texture("textures/voxel/oak_wood_01.png");
+ oak_log_builder.add_default_texture("textures/voxel/oak_wood_02.png");
+ oak_log_builder.add_face_texture(world::VFACE_BOTTOM, "textures/voxel/oak_wood_top.png");
+ oak_log_builder.add_face_texture(world::VFACE_TOP, "textures/voxel/oak_wood_top.png");
+ oak_log_builder.set_surface_material(world::VMAT_WOOD);
+ oak_log = world::voxel_registry::register_voxel(oak_log_builder);
+
+ auto glass_builder = world::VoxelBuilder("glass");
+ glass_builder.add_default_texture("textures/voxel/glass_01.png");
+ glass_builder.set_render_mode(world::VRENDER_BLEND);
+ glass_builder.set_surface_material(world::VMAT_GLASS);
+ glass = world::voxel_registry::register_voxel(glass_builder);
+
+ auto slime_builder = world::VoxelBuilder("slime");
+ slime_builder.add_default_texture("textures/voxel/slime_01.png");
+ slime_builder.set_render_mode(world::VRENDER_BLEND);
+ slime_builder.set_surface_material(world::VMAT_SLOSH);
+ slime_builder.set_touch_type(world::VTOUCH_BOUNCE);
+ slime_builder.set_touch_values({ 0.00f, 0.60f, 0.00f });
+ slime = world::voxel_registry::register_voxel(slime_builder);
}
diff --git a/game/shared/game_voxels.hh b/game/shared/game_voxels.hh index 21102d0..2211619 100644 --- a/game/shared/game_voxels.hh +++ b/game/shared/game_voxels.hh @@ -1,28 +1,26 @@ -#ifndef SHARED_GAME_VOXELS
-#define SHARED_GAME_VOXELS 1
#pragma once
-#include "shared/types.hh"
+namespace world
+{
+class Voxel;
+} // namespace world
namespace game_voxels
{
-extern voxel_id cobblestone;
-extern voxel_id dirt;
-extern voxel_id grass;
-extern voxel_id stone;
-extern voxel_id vtest;
-extern voxel_id vtest_ck;
-extern voxel_id oak_leaves;
-extern voxel_id oak_planks;
-extern voxel_id oak_log;
-extern voxel_id glass;
-extern voxel_id slime;
-extern voxel_id mud;
+extern const world::Voxel* cobblestone;
+extern const world::Voxel* dirt;
+extern const world::Voxel* grass;
+extern const world::Voxel* stone;
+extern const world::Voxel* vtest;
+extern const world::Voxel* vtest_ck;
+extern const world::Voxel* oak_leaves;
+extern const world::Voxel* oak_planks;
+extern const world::Voxel* oak_log;
+extern const world::Voxel* glass;
+extern const world::Voxel* slime;
} // namespace game_voxels
namespace game_voxels
{
void populate(void);
} // namespace game_voxels
-
-#endif // SHARED_GAME_VOXELS
diff --git a/game/shared/protocol.cc b/game/shared/protocol.cc index 576502e..7115807 100644 --- a/game/shared/protocol.cc +++ b/game/shared/protocol.cc @@ -21,7 +21,7 @@ ENetPacket* protocol::encode(const protocol::StatusRequest& packet, enet_uint32 {
write_buffer.reset();
write_buffer.write<std::uint16_t>(protocol::StatusRequest::ID);
- write_buffer.write<std::uint32_t>(packet.version);
+ write_buffer.write<std::uint32_t>(packet.game_version_major);
return write_buffer.to_packet(flags);
}
@@ -29,10 +29,12 @@ ENetPacket* protocol::encode(const protocol::StatusResponse& packet, enet_uint32 {
write_buffer.reset();
write_buffer.write<std::uint16_t>(protocol::StatusResponse::ID);
- write_buffer.write<std::uint32_t>(packet.version);
+ write_buffer.write<std::uint32_t>(packet.game_version_major);
write_buffer.write<std::uint16_t>(packet.max_players);
write_buffer.write<std::uint16_t>(packet.num_players);
write_buffer.write<std::string_view>(packet.motd);
+ write_buffer.write<std::uint32_t>(packet.game_version_minor);
+ write_buffer.write<std::uint32_t>(packet.game_version_patch);
return write_buffer.to_packet(flags);
}
@@ -40,11 +42,13 @@ ENetPacket* protocol::encode(const protocol::LoginRequest& packet, enet_uint32 f {
write_buffer.reset();
write_buffer.write<std::uint16_t>(protocol::LoginRequest::ID);
- write_buffer.write<std::uint32_t>(packet.version);
+ write_buffer.write<std::uint32_t>(packet.game_version_major);
write_buffer.write<std::uint64_t>(packet.voxel_registry_checksum);
write_buffer.write<std::uint64_t>(packet.item_registry_checksum);
write_buffer.write<std::uint64_t>(packet.password_hash);
write_buffer.write<std::string_view>(packet.username.substr(0, protocol::MAX_USERNAME));
+ write_buffer.write<std::uint32_t>(packet.game_version_minor);
+ write_buffer.write<std::uint32_t>(packet.game_version_patch);
return write_buffer.to_packet(flags);
}
@@ -268,26 +272,30 @@ void protocol::decode(entt::dispatcher& dispatcher, const ENetPacket* packet, EN switch(id) {
case protocol::StatusRequest::ID:
status_request.peer = peer;
- status_request.version = read_buffer.read<std::uint32_t>();
+ status_request.game_version_major = read_buffer.read<std::uint32_t>();
dispatcher.trigger(status_request);
break;
case protocol::StatusResponse::ID:
status_response.peer = peer;
- status_response.version = read_buffer.read<std::uint32_t>();
+ status_response.game_version_major = read_buffer.read<std::uint32_t>();
status_response.max_players = read_buffer.read<std::uint16_t>();
status_response.num_players = read_buffer.read<std::uint16_t>();
status_response.motd = read_buffer.read<std::string>();
+ status_response.game_version_minor = read_buffer.read<std::uint32_t>();
+ status_response.game_version_patch = read_buffer.read<std::uint32_t>();
dispatcher.trigger(status_response);
break;
case protocol::LoginRequest::ID:
login_request.peer = peer;
- login_request.version = read_buffer.read<std::uint32_t>();
+ login_request.game_version_major = read_buffer.read<std::uint32_t>();
login_request.voxel_registry_checksum = read_buffer.read<std::uint64_t>();
login_request.item_registry_checksum = read_buffer.read<std::uint64_t>();
login_request.password_hash = read_buffer.read<std::uint64_t>();
login_request.username = read_buffer.read<std::string>();
+ login_request.game_version_minor = read_buffer.read<std::uint32_t>();
+ login_request.game_version_patch = read_buffer.read<std::uint32_t>();
dispatcher.trigger(login_request);
break;
diff --git a/game/shared/protocol.hh b/game/shared/protocol.hh index 3133275..f0bdff6 100644 --- a/game/shared/protocol.hh +++ b/game/shared/protocol.hh @@ -14,7 +14,6 @@ constexpr static std::size_t MAX_USERNAME = 64; constexpr static std::size_t MAX_SOUNDNAME = 1024;
constexpr static std::uint16_t TICKRATE = 60;
constexpr static std::uint16_t PORT = 43103;
-constexpr static std::uint32_t VERSION = 15;
constexpr static std::uint8_t CHANNEL = 0;
} // namespace protocol
@@ -107,22 +106,26 @@ ENetPacket* make_dimension_info(const world::Dimension* dimension); } // namespace protocol::utils
struct protocol::StatusRequest final : public protocol::Base<0x0000> {
- std::uint32_t version;
+ std::uint32_t game_version_major; // renamed from 'version' in v16.x.x
};
struct protocol::StatusResponse final : public protocol::Base<0x0001> {
- std::uint32_t version;
+ std::uint32_t game_version_major; // renamed from 'version' in v16.x.x
std::uint16_t max_players;
std::uint16_t num_players;
std::string motd;
+ std::uint32_t game_version_minor { UINT32_MAX }; // added in v16.x.x
+ std::uint32_t game_version_patch { UINT32_MAX };
};
struct protocol::LoginRequest final : public protocol::Base<0x0002> {
- std::uint32_t version;
+ std::uint32_t game_version_major; // renamed from 'version' in v16.x.x
std::uint64_t voxel_registry_checksum;
std::uint64_t item_registry_checksum;
std::uint64_t password_hash;
std::string username;
+ std::uint32_t game_version_minor; // added in v16.x.x
+ std::uint32_t game_version_patch;
};
struct protocol::LoginResponse final : public protocol::Base<0x0003> {
diff --git a/game/shared/world/CMakeLists.txt b/game/shared/world/CMakeLists.txt index 15d5b59..db3f370 100644 --- a/game/shared/world/CMakeLists.txt +++ b/game/shared/world/CMakeLists.txt @@ -8,9 +8,13 @@ target_sources(shared PRIVATE "${CMAKE_CURRENT_LIST_DIR}/feature.hh" "${CMAKE_CURRENT_LIST_DIR}/item_registry.cc" "${CMAKE_CURRENT_LIST_DIR}/item_registry.hh" + "${CMAKE_CURRENT_LIST_DIR}/item.cc" + "${CMAKE_CURRENT_LIST_DIR}/item.hh" "${CMAKE_CURRENT_LIST_DIR}/ray_dda.cc" "${CMAKE_CURRENT_LIST_DIR}/ray_dda.hh" "${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/chunk.cc b/game/shared/world/chunk.cc index e59b68d..f8f7b93 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,30 @@ 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;
+ m_voxels[index] = voxel ? voxel->get_id() : NULL_VOXEL_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/chunk_aabb.hh b/game/shared/world/chunk_aabb.hh index 3a2d26f..d926b55 100644 --- a/game/shared/world/chunk_aabb.hh +++ b/game/shared/world/chunk_aabb.hh @@ -1,5 +1,3 @@ -#ifndef SHARED_CHUNK_AABB
-#define SHARED_CHUNK_AABB 1
#pragma once
#include "core/math/aabb.hh"
@@ -10,5 +8,3 @@ namespace world {
using ChunkAABB = math::AABB<chunk_pos::value_type>;
} // namespace world
-
-#endif // SHARED_CHUNK_AABB
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..8fe95f1 100644 --- a/game/shared/world/feature.cc +++ b/game/shared/world/feature.cc @@ -4,7 +4,7 @@ #include "shared/world/chunk.hh"
#include "shared/world/dimension.hh"
-#include "shared/world/voxel_storage.hh"
+#include "shared/world/voxel.hh"
#include "shared/coord.hh"
@@ -30,6 +30,28 @@ void world::Feature::place(const voxel_pos& vpos, Dimension* dimension) 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;
+ 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(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;
+ }
+
+ chunk.set_voxel(voxel, it_index);
+ }
+ }
+}
+
void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const
{
for(const auto [rpos, voxel, overwrite] : (*this)) {
@@ -47,7 +69,7 @@ void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelSt continue;
}
- voxels[it_index] = voxel;
+ voxels[it_index] = voxel ? voxel->get_id() : NULL_VOXEL_ID;
}
}
}
diff --git a/game/shared/world/feature.hh b/game/shared/world/feature.hh index 20a809b..a543632 100644 --- a/game/shared/world/feature.hh +++ b/game/shared/world/feature.hh @@ -4,19 +4,22 @@ namespace world
{
+class Chunk;
class Dimension;
+class Voxel;
class VoxelStorage;
} // 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, 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.cc b/game/shared/world/item.cc new file mode 100644 index 0000000..5e60609 --- /dev/null +++ b/game/shared/world/item.cc @@ -0,0 +1,55 @@ +#include "shared/pch.hh" + +#include "shared/world/item.hh" + +#include "core/math/crc64.hh" + +#include "shared/world/voxel.hh" + +world::Item::Item(const Item& source, item_id id) noexcept : Item(source) +{ + m_id = id; +} + +void world::Item::set_cached_texture(resource_ptr<TextureGUI> texture) const noexcept +{ + m_cached_texture = std::move(texture); +} + +std::uint64_t world::Item::get_checksum(std::uint64_t combine) const +{ + combine = math::crc64(m_name.data(), m_name.size(), combine); + combine = math::crc64(m_texture.data(), m_texture.size(), combine); + + std::uint32_t id = m_place_voxel ? m_place_voxel->get_id() : NULL_VOXEL_ID; + combine = math::crc64(&id, sizeof(id), combine); + + return combine; +} + +world::ItemBuilder::ItemBuilder(std::string_view name) +{ + set_name(name); +} + +void world::ItemBuilder::set_name(std::string_view name) +{ + assert(name.size()); + + m_name = name; +} + +void world::ItemBuilder::set_texture(std::string_view texture) +{ + m_texture = texture; +} + +void world::ItemBuilder::set_place_voxel(const Voxel* place_voxel) +{ + m_place_voxel = place_voxel; +} + +std::unique_ptr<world::Item> world::ItemBuilder::build(item_id id) const +{ + return std::make_unique<Item>(*this, id); +} diff --git a/game/shared/world/item.hh b/game/shared/world/item.hh new file mode 100644 index 0000000..ffa7f5c --- /dev/null +++ b/game/shared/world/item.hh @@ -0,0 +1,84 @@ +#pragma once + +#include "core/resource/resource.hh" + +#include "shared/types.hh" + +// This resource is only defined client-side and +// resource_ptr<TextureGUI> should remain set to null +// anywhere else in the shared and server code +struct TextureGUI; + +namespace world +{ +class Voxel; +} // namespace world + +namespace world +{ +class Item { +public: + Item(void) = default; + explicit Item(const Item& source, item_id id) noexcept; + + constexpr std::string_view get_name(void) const noexcept; + constexpr item_id get_id(void) const noexcept; + + constexpr std::string_view get_texture(void) const noexcept; + constexpr const Voxel* get_place_voxel(void) const noexcept; + + constexpr resource_ptr<TextureGUI>& get_cached_texture(void) const noexcept; + void set_cached_texture(resource_ptr<TextureGUI> texture) const noexcept; + + std::uint64_t get_checksum(std::uint64_t combine = 0U) const; + +protected: + std::string m_name; + item_id m_id { NULL_ITEM_ID }; + + std::string m_texture; + const Voxel* m_place_voxel { nullptr }; + + mutable resource_ptr<TextureGUI> m_cached_texture; // Client-side only +}; +} // namespace world + +namespace world +{ +class ItemBuilder final : public Item { +public: + explicit ItemBuilder(std::string_view name); + + void set_name(std::string_view name); + + void set_texture(std::string_view texture); + void set_place_voxel(const Voxel* place_voxel); + + std::unique_ptr<Item> build(item_id id) const; +}; +} // namespace world + +constexpr std::string_view world::Item::get_name(void) const noexcept +{ + return m_name; +} + +constexpr item_id world::Item::get_id(void) const noexcept +{ + return m_id; +} + +constexpr std::string_view world::Item::get_texture(void) const noexcept +{ + return m_texture; +} + +constexpr const world::Voxel* world::Item::get_place_voxel(void) const noexcept +{ + return m_place_voxel; +} + +constexpr resource_ptr<TextureGUI>& world::Item::get_cached_texture(void) const noexcept +{ + return m_cached_texture; +} diff --git a/game/shared/world/item_registry.cc b/game/shared/world/item_registry.cc index d1b9ff4..4e0932c 100644 --- a/game/shared/world/item_registry.cc +++ b/game/shared/world/item_registry.cc @@ -6,101 +6,63 @@ #include "shared/world/voxel_registry.hh"
-std::unordered_map<std::string, world::ItemInfoBuilder> world::item_registry::builders = {};
+static std::uint64_t registry_checksum = 0U;
std::unordered_map<std::string, item_id> world::item_registry::names = {};
-std::vector<std::shared_ptr<world::ItemInfo>> world::item_registry::items = {};
+std::vector<std::unique_ptr<world::Item>> world::item_registry::items = {};
-world::ItemInfoBuilder::ItemInfoBuilder(std::string_view name)
+static void recalculate_checksum(void)
{
- prototype.name = name;
- prototype.texture = std::string();
- prototype.place_voxel = NULL_VOXEL_ID;
- prototype.cached_texture = nullptr;
-}
+ registry_checksum = 0U;
-world::ItemInfoBuilder& world::ItemInfoBuilder::set_texture(std::string_view texture)
-{
- prototype.texture = texture;
- prototype.cached_texture = nullptr;
- return *this;
-}
-
-world::ItemInfoBuilder& world::ItemInfoBuilder::set_place_voxel(voxel_id place_voxel)
-{
- prototype.place_voxel = place_voxel;
- return *this;
+ for(const auto& item : world::item_registry::items) {
+ registry_checksum = item->get_checksum(registry_checksum);
+ }
}
-item_id world::ItemInfoBuilder::build(void) const
+world::Item* world::item_registry::register_item(const ItemBuilder& builder)
{
- const auto it = world::item_registry::names.find(prototype.name);
-
- if(it != world::item_registry::names.cend()) {
- spdlog::warn("item_registry: cannot build {}: name already present", prototype.name);
- return it->second;
- }
+ assert(builder.get_name().size());
+ assert(nullptr == find(builder.get_name()));
- auto new_info = std::make_shared<ItemInfo>();
- new_info->name = prototype.name;
- new_info->texture = prototype.texture;
- new_info->place_voxel = prototype.place_voxel;
- new_info->cached_texture = nullptr;
+ const auto id = static_cast<item_id>(1 + items.size());
- world::item_registry::items.push_back(new_info);
- world::item_registry::names.insert_or_assign(prototype.name, static_cast<item_id>(world::item_registry::items.size()));
-
- return static_cast<item_id>(world::item_registry::items.size());
-}
+ std::unique_ptr<Item> item(builder.build(id));
+ names.emplace(std::string(builder.get_name()), id);
+ items.push_back(std::move(item));
-world::ItemInfoBuilder& world::item_registry::construct(std::string_view name)
-{
- const auto it = world::item_registry::builders.find(std::string(name));
+ recalculate_checksum();
- if(it != world::item_registry::builders.cend()) {
- return it->second;
- }
- else {
- return world::item_registry::builders.emplace(std::string(name), ItemInfoBuilder(name)).first->second;
- }
+ return items.back().get();
}
-world::ItemInfo* world::item_registry::find(std::string_view name)
+world::Item* world::item_registry::find(std::string_view name)
{
- const auto it = world::item_registry::names.find(std::string(name));
+ const auto it = names.find(std::string(name));
- if(it != world::item_registry::names.cend()) {
- return world::item_registry::find(it->second);
- }
- else {
+ if(it == names.end()) {
return nullptr;
}
+
+ return items[it->second - 1].get();
}
-world::ItemInfo* world::item_registry::find(const item_id item)
+world::Item* world::item_registry::find(const item_id item)
{
- if((item != NULL_ITEM_ID) && (item <= world::item_registry::items.size())) {
- return world::item_registry::items[item - 1].get();
- }
- else {
+ if(item == NULL_ITEM_ID || item > items.size()) {
return nullptr;
}
+
+ return items[item - 1].get();
}
void world::item_registry::purge(void)
{
- world::item_registry::builders.clear();
- world::item_registry::names.clear();
- world::item_registry::items.clear();
+ registry_checksum = 0U;
+ items.clear();
+ names.clear();
}
-std::uint64_t world::item_registry::calculate_checksum(void)
+std::uint64_t world::item_registry::get_checksum(void)
{
- std::uint64_t result = 0;
-
- for(const auto& info : world::item_registry::items) {
- result = math::crc64(info->name, result);
- result += static_cast<std::uint64_t>(info->place_voxel);
- }
-
- return result;
+ return registry_checksum;
}
diff --git a/game/shared/world/item_registry.hh b/game/shared/world/item_registry.hh index c3e6cf9..b4c9fda 100644 --- a/game/shared/world/item_registry.hh +++ b/game/shared/world/item_registry.hh @@ -1,56 +1,18 @@ #pragma once
-#include "core/resource/resource.hh"
-
-#include "shared/types.hh"
-
-// This resource is only defined client-side and
-// resource_ptr<TextureGUI> should remain set to null
-// anywhere else in the shared and server code
-struct TextureGUI;
-
-namespace world
-{
-struct ItemInfo final {
- std::string name;
- std::string texture;
- voxel_id place_voxel;
-
- resource_ptr<TextureGUI> cached_texture; // Client-side only
-};
-} // namespace world
-
-namespace world
-{
-class ItemInfoBuilder final {
-public:
- explicit ItemInfoBuilder(std::string_view name);
- virtual ~ItemInfoBuilder(void) = default;
-
-public:
- ItemInfoBuilder& set_texture(std::string_view texture);
- ItemInfoBuilder& set_place_voxel(voxel_id place_voxel);
-
-public:
- item_id build(void) const;
-
-private:
- ItemInfo prototype;
-};
-} // namespace world
+#include "shared/world/item.hh"
namespace world::item_registry
{
-extern std::unordered_map<std::string, ItemInfoBuilder> builders;
extern std::unordered_map<std::string, item_id> names;
-extern std::vector<std::shared_ptr<ItemInfo>> items;
+extern std::vector<std::unique_ptr<Item>> items;
} // namespace world::item_registry
namespace world::item_registry
{
-ItemInfoBuilder& construct(std::string_view name);
-ItemInfo* find(std::string_view name);
-ItemInfo* find(const item_id item);
+Item* register_item(const ItemBuilder& builder);
+Item* find(std::string_view name);
+Item* find(const item_id item);
} // namespace world::item_registry
namespace world::item_registry
@@ -60,5 +22,5 @@ void purge(void); namespace world::item_registry
{
-std::uint64_t calculate_checksum(void);
+std::uint64_t get_checksum(void);
} // namespace world::item_registry
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..110e3d4 100644 --- a/game/shared/world/ray_dda.hh +++ b/game/shared/world/ray_dda.hh @@ -1,5 +1,3 @@ -#ifndef SHARED_RAY_DDA
-#define SHARED_RAY_DDA 1
#pragma once
#include "shared/types.hh"
@@ -7,6 +5,7 @@ namespace world
{
class Dimension;
+class Voxel;
} // namespace world
namespace world
@@ -20,7 +19,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;
@@ -37,5 +36,3 @@ public: voxel_pos vpos;
};
} // namespace world
-
-#endif // SHARED_RAY_DDA
diff --git a/game/shared/world/voxel.cc b/game/shared/world/voxel.cc new file mode 100644 index 0000000..21fe62c --- /dev/null +++ b/game/shared/world/voxel.cc @@ -0,0 +1,149 @@ +#include "shared/pch.hh" + +#include "shared/world/voxel.hh" + +#include "core/math/crc64.hh" + +#include "shared/world/dimension.hh" + +world::Voxel::Voxel(const Voxel& source, voxel_id id) noexcept : Voxel(source) +{ + m_id = id; +} + +void world::Voxel::on_place(Dimension* dimension, const voxel_pos& vpos) const +{ + if(m_on_place) { + m_on_place(dimension, vpos); + } +} + +void world::Voxel::on_remove(Dimension* dimension, const voxel_pos& vpos) const +{ + if(m_on_remove) { + m_on_remove(dimension, vpos); + } +} + +void world::Voxel::on_tick(Dimension* dimension, const voxel_pos& vpos) const +{ + 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 +{ + 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::get_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; +} + +world::VoxelBuilder::VoxelBuilder(std::string_view name) +{ + set_name(name); +} + +void world::VoxelBuilder::set_on_place(VoxelOnPlaceFunc func) noexcept +{ + m_on_place = std::move(func); +} + +void world::VoxelBuilder::set_on_remove(VoxelOnRemoveFunc func) noexcept +{ + m_on_remove = std::move(func); +} + +void world::VoxelBuilder::set_on_tick(VoxelOnTickFunc func) noexcept +{ + m_on_tick = std::move(func); +} + +void world::VoxelBuilder::set_name(std::string_view name) noexcept +{ + assert(name.size()); + + m_name = name; +} + +void world::VoxelBuilder::set_render_mode(VoxelRender mode) noexcept +{ + m_render_mode = mode; +} + +void world::VoxelBuilder::set_shape(VoxelShape shape) noexcept +{ + m_shape = shape; +} + +void world::VoxelBuilder::set_animated(bool animated) noexcept +{ + m_animated = animated; +} + +void world::VoxelBuilder::set_touch_type(VoxelTouch type) noexcept +{ + m_touch_type = type; +} + +void world::VoxelBuilder::set_touch_values(const glm::fvec3& values) noexcept +{ + m_touch_values = values; +} + +void world::VoxelBuilder::set_surface_material(VoxelMaterial material) noexcept +{ + m_surface_material = material; +} + +void world::VoxelBuilder::set_collision(const math::AABBf& box) noexcept +{ + m_collision = box; +} + +void world::VoxelBuilder::add_default_texture(std::string_view path) +{ + assert(path.size()); + + m_default_textures.emplace_back(path); +} + +void 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); +} + +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 new file mode 100644 index 0000000..6013962 --- /dev/null +++ b/game/shared/world/voxel.hh @@ -0,0 +1,286 @@ +#pragma once + +#include "core/math/aabb.hh" + +#include "shared/types.hh" + +namespace world +{ +class Dimension; +} // namespace world + +namespace world +{ +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 int { + VSHAPE_CUBE = 0U, ///< Full cube shape + VSHAPE_CROSS, ///< TODO: Cross shape + VSHAPE_MODEL, ///< TODO: Custom model shape +}; + +enum VoxelFace : unsigned int { + 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 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 int { + VMAT_UNKNOWN = 0xFFFFU, + 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 int { + 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 +{ +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: + Voxel(void) = default; + explicit Voxel(const Voxel& source, voxel_id id) noexcept; + + 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; + + 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; + + 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 + /// @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 get_checksum(std::uint64_t combine = 0U) const; + +protected: + 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; + + 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); + + void set_on_place(VoxelOnPlaceFunc func) noexcept; + void set_on_remove(VoxelOnRemoveFunc func) noexcept; + void set_on_tick(VoxelOnTickFunc func) noexcept; + + 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); + + std::unique_ptr<Voxel> build(voxel_id id) const; +}; +} // 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()); + + if(m_face_textures[face].empty()) { + return m_default_textures; + } + + 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]; +} + +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 b44845e..573a606 100644 --- a/game/shared/world/voxel_registry.cc +++ b/game/shared/world/voxel_registry.cc @@ -2,195 +2,63 @@ #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::unique_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;
-}
-
-world::VoxelInfoBuilder& world::VoxelInfoBuilder::set_surface(voxel_surface surface)
-{
- prototype.surface = surface;
- return *this;
+ for(const auto& voxel : world::voxel_registry::voxels) {
+ registry_checksum = voxel->get_checksum(registry_checksum);
+ }
}
-voxel_id world::VoxelInfoBuilder::build(void) const
+world::Voxel* world::voxel_registry::register_voxel(const VoxelBuilder& builder)
{
- const auto it = world::voxel_registry::names.find(prototype.name);
-
- if(it != world::voxel_registry::names.cend()) {
- spdlog::warn("voxel_registry: cannot build {}: name already present", prototype.name);
- return it->second;
- }
-
- 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();
- }
-
- if((world::voxel_registry::voxels.size() + state_count) >= MAX_VOXEL_ID) {
- spdlog::critical("voxel_registry: voxel registry overflow");
- std::terminate();
- }
-
- 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;
- }
- }
+ assert(builder.get_name().size());
+ assert(nullptr == find(builder.get_name()));
- // Physics properties
- new_info->touch_type = prototype.touch_type;
- new_info->touch_values = prototype.touch_values;
- new_info->surface = prototype.surface;
+ const auto id = static_cast<voxel_id>(1 + voxels.size());
- // Things set in future by item_def
- new_info->item_pick = prototype.item_pick;
+ std::unique_ptr<Voxel> voxel(builder.build(id));
+ names.emplace(std::string(builder.get_name()), id);
+ voxels.push_back(std::move(voxel));
- // Base voxel identifier offset
- new_info->base_voxel = world::voxel_registry::voxels.size() + 1;
+ recalculate_checksum();
- 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;
+ return voxels.back().get();
}
-world::VoxelInfoBuilder& world::voxel_registry::construct(std::string_view name, voxel_type type, bool animated, bool blending)
+world::Voxel* world::voxel_registry::find(std::string_view name)
{
- 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;
- }
-}
+ const auto it = names.find(std::string(name));
-world::VoxelInfo* world::voxel_registry::find(std::string_view name)
-{
- const auto it = world::voxel_registry::names.find(std::string(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 - 1].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 == NULL_VOXEL_ID || id > voxels.size()) {
return nullptr;
}
+
+ return voxels[id - 1].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..5ba2aed 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::unique_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 VoxelBuilder& builder);
+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
|
