1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
#ifndef SHARED_WORLD_HH_
#define SHARED_WORLD_HH_
#include <algorithm>
#include <array>
#include <exception>
#include <functional>
#include <istream>
#include <memory>
#include <random>
#include <type_traits>
#include <vector>
#include <boost/functional/hash.hpp>
#include <glm/glm.hpp>
#include "shared/math.hh"
#include "shared/net/proto.hh"
#include "shared/shared.hh"
namespace shared {
namespace world {
class block {
public:
enum class type : std::uint8_t {
air, // zero means air which means don't render
dirt, // atlas should start from here
grass,
sand,
sandstone,
stone,
cobblestone,
wood,
leaves,
water,
shrub,
snow,
cactus,
dead_shrub,
snowy_wood,
snowy_leaves,
snowy_shrub,
};
enum class visibility { solid, partial, translucent, invisible };
public:
type type;
public:
static constexpr enum block::visibility
get_visibility(const enum block::type type) noexcept {
switch (type) {
case type::air:
return visibility::invisible;
case type::shrub:
case type::dead_shrub:
case type::leaves:
case type::snowy_leaves:
case type::snowy_shrub:
return visibility::partial;
case type::water:
return visibility::translucent;
default:
break;
}
return visibility::solid;
}
static constexpr bool is_tangible(const enum block::type type) noexcept {
switch (type) {
case type::air:
case type::shrub:
case type::dead_shrub:
case type::snowy_shrub:
return false;
default:
break;
}
return true;
}
static constexpr bool is_collidable(const enum block::type type) noexcept {
switch (type) {
case type::air:
case type::shrub:
case type::dead_shrub:
case type::snowy_shrub:
case type::water:
return false;
default:
break;
}
return true;
}
static constexpr bool is_liquid(const enum block::type type) noexcept {
switch (type) {
case type::water:
return true;
default:
break;
}
return false;
}
static constexpr bool is_replaceable(const enum block::type type) noexcept {
switch (type) {
case type::water:
case type::air:
return true;
default:
break;
}
return false;
}
static constexpr bool is_removable(const enum block::type type) noexcept {
switch (type) {
case type::water:
case type::air:
return false;
default:
break;
}
return true;
}
static constexpr float get_friction(const enum block::type type) noexcept {
switch (type) {
case type::water:
return 20.0f;
case type::air:
case type::shrub:
case type::dead_shrub:
case type::snowy_shrub:
return 0.1f;
default:
break;
}
return 10.0f;
}
public:
block(const enum block::type t = type::air) noexcept : type(t) {}
operator enum block::type() const noexcept { return this->type; }
};
class chunk {
public:
static constexpr int WIDTH = 16;
static constexpr int HEIGHT = 256;
static constexpr int VOLUME = WIDTH * WIDTH * HEIGHT;
// Stuff for unordered_map.
static std::size_t hash(const shared::math::coords& c) noexcept {
std::size_t seed = 0;
boost::hash_combine(seed, boost::hash_value(c.x));
boost::hash_combine(seed, boost::hash_value(c.z));
return seed;
}
static std::size_t equal(const shared::math::coords& a,
const shared::math::coords& b) noexcept {
return a == b;
}
using map = std::unordered_map<shared::math::coords, shared::world::chunk,
decltype(&shared::world::chunk::hash),
decltype(&shared::world::chunk::equal)>;
protected:
shared::math::coords pos;
std::array<block, VOLUME> blocks; // Use get_block for 3d index access.
public:
using block_array = decltype(chunk::blocks);
enum class biome { desert, islands, ocean, plains, forest, tundra, alpine };
static bool is_outside_chunk(const glm::ivec3& v) noexcept {
return (std::min(v.x, v.z) < 0) || (std::max(v.x, v.z) >= WIDTH) ||
(std::clamp(v.y, 0, HEIGHT - 1) != v.y);
}
static shared::math::coords
get_normalised_chunk(const shared::math::coords& coords, const int x,
const int z) noexcept {
return coords + shared::math::coords{(x >= WIDTH) - (x < 0),
(z >= WIDTH) - (z < 0)};
}
static std::pair<unsigned short, unsigned short>
get_normalised_coords(const int x, const int z) noexcept {
return {x + WIDTH * ((x < 0) - (x >= WIDTH)),
z + WIDTH * ((z < 0) - (z >= WIDTH))};
}
static glm::ivec3 get_normalised_coords(const glm::ivec3& c) noexcept {
const auto [x, z] = get_normalised_coords(c.x, c.z);
return {x, c.y, z};
}
static block_array
make_blocks_from_chunk(const proto::chunk& chunk) noexcept;
public:
static unsigned long get_3d_index(const glm::ivec3& v) noexcept {
#ifndef NDEBUG
if (is_outside_chunk(v)) {
throw std::range_error("bad chunk block coordinate access");
}
#endif
return static_cast<unsigned long>(v.x +
(WIDTH * (v.y + (HEIGHT * v.z))));
}
public:
// If a third non-std::nullopt argument is provided, we use those blocks
// instead of what we might have otherwise generated.
chunk(const std::uint64_t& seed, const shared::math::coords& coords,
const std::optional<block_array> blocks = std::nullopt) noexcept;
shared::math::coords get_pos() const noexcept { return this->pos; }
block get_block(const glm::ivec3& v) const noexcept {
return this->blocks[get_3d_index(v)];
}
block& get_block(const glm::ivec3& v) noexcept {
return this->blocks[get_3d_index(v)];
}
};
} // namespace world
} // namespace shared
#endif
|