2 #include "BlockGravity.hpp"
3 #include "BlockType.hpp"
4 #include "BlockTypeRegistry.hpp"
6 #include "../io/TokenStreamReader.hpp"
7 #include "../model/ShapeRegistry.hpp"
8 #include "../shared/ResourceIndex.hpp"
12 #include <glm/gtx/euler_angles.hpp>
13 #include <glm/gtx/norm.hpp>
14 #include <glm/gtx/transform.hpp>
19 std::ostream &operator <<(std::ostream &out, const Block &block) {
20 return out << "Block(" << block.type << ", " << block.GetFace() << ", " << block.GetTurn() << ')';
23 std::ostream &operator <<(std::ostream &out, const Block::Face &face) {
28 case Block::FACE_DOWN:
31 case Block::FACE_RIGHT:
34 case Block::FACE_LEFT:
37 case Block::FACE_FRONT:
40 case Block::FACE_BACK:
44 case Block::FACE_COUNT:
45 out << "invalid Block::Face";
51 std::ostream &operator <<(std::ostream &out, const Block::Turn &turn) {
53 case Block::TURN_NONE:
56 case Block::TURN_LEFT:
59 case Block::TURN_AROUND:
62 case Block::TURN_RIGHT:
66 case Block::TURN_COUNT:
67 out << "invalid Block::Turn";
74 BlockType::BlockType() noexcept
77 , hsl_mod(0.0f, 1.0f, 1.0f)
78 , rgb_mod(1.0f, 1.0f, 1.0f)
79 , outline_color(-1, -1, -1)
98 , min_temperature(-1.0f)
99 , mid_temperature(0.0f)
100 , max_temperature(1.0f)
101 , min_richness(-1.0f)
108 void BlockType::Copy(const BlockType &other) noexcept {
110 textures = other.textures;
111 hsl_mod = other.hsl_mod;
112 rgb_mod = other.rgb_mod;
113 outline_color = other.outline_color;
114 place_sound = other.place_sound;
115 remove_sound = other.remove_sound;
116 luminosity = other.luminosity;
117 visible = other.visible;
118 block_light = other.block_light;
119 collision = other.collision;
120 collide_block = collide_block;
121 generate = other.generate;
122 min_solidity = other.min_solidity;
123 mid_solidity = other.mid_solidity;
124 max_solidity = other.max_solidity;
125 min_humidity = other.min_humidity;
126 mid_humidity = other.mid_humidity;
127 max_humidity = other.max_humidity;
128 min_temperature = other.min_temperature;
129 mid_temperature = other.mid_temperature;
130 max_temperature = other.max_temperature;
131 min_richness = other.min_richness;
132 mid_richness = other.mid_richness;
133 max_richness = other.max_richness;
134 commonness = other.commonness;
137 void BlockType::Read(
138 TokenStreamReader &in,
139 ResourceIndex &snd_index,
140 ResourceIndex &tex_index,
141 const ShapeRegistry &shapes
144 in.Skip(Token::ANGLE_BRACKET_OPEN);
145 while (in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
146 in.ReadIdentifier(name);
147 in.Skip(Token::EQUALS);
148 if (name == "visible") {
149 visible = in.GetBool();
150 } else if (name == "texture") {
152 textures.push_back(tex_index.GetID(name));
153 } else if (name == "textures") {
154 in.Skip(Token::BRACKET_OPEN);
155 while (in.Peek().type != Token::BRACKET_CLOSE) {
157 textures.push_back(tex_index.GetID(name));
158 if (in.Peek().type == Token::COMMA) {
159 in.Skip(Token::COMMA);
162 in.Skip(Token::BRACKET_CLOSE);
163 } else if (name == "rgb_mod") {
165 } else if (name == "hsl_mod") {
167 } else if (name == "outline") {
168 in.ReadVec(outline_color);
169 } else if (name == "gravity") {
170 gravity = BlockGravity::Read(in);
171 } else if (name == "label") {
172 in.ReadString(label);
173 } else if (name == "place_sound") {
175 place_sound = snd_index.GetID(name);
176 } else if (name == "remove_sound") {
178 remove_sound = snd_index.GetID(name);
179 } else if (name == "luminosity") {
180 luminosity = in.GetInt();
181 } else if (name == "block_light") {
182 block_light = in.GetBool();
183 } else if (name == "collision") {
184 collision = in.GetBool();
185 } else if (name == "collide_block") {
186 collide_block = in.GetBool();
187 } else if (name == "generate") {
188 generate = in.GetBool();
189 } else if (name == "min_solidity") {
190 min_solidity = in.GetFloat();
191 } else if (name == "mid_solidity") {
192 mid_solidity = in.GetFloat();
193 } else if (name == "max_solidity") {
194 max_solidity = in.GetFloat();
195 } else if (name == "min_humidity") {
196 min_humidity = in.GetFloat();
197 } else if (name == "mid_humidity") {
198 mid_humidity = in.GetFloat();
199 } else if (name == "max_humidity") {
200 max_humidity = in.GetFloat();
201 } else if (name == "min_temperature") {
202 min_temperature = in.GetFloat();
203 } else if (name == "mid_temperature") {
204 mid_temperature = in.GetFloat();
205 } else if (name == "max_temperature") {
206 max_temperature = in.GetFloat();
207 } else if (name == "min_richness") {
208 min_richness = in.GetFloat();
209 } else if (name == "mid_richness") {
210 mid_richness = in.GetFloat();
211 } else if (name == "max_richness") {
212 max_richness = in.GetFloat();
213 } else if (name == "commonness") {
214 commonness = in.GetFloat();
215 } else if (name == "shape") {
216 in.ReadIdentifier(name);
217 shape = &shapes.Get(name);
219 std::cerr << "warning: unknown block type property " << name << std::endl;
220 while (in.Peek().type != Token::SEMICOLON) {
224 in.Skip(Token::SEMICOLON);
226 in.Skip(Token::ANGLE_BRACKET_CLOSE);
229 void BlockType::FillEntityMesh(
230 EntityMesh::Buffer &buf,
231 const glm::mat4 &transform
234 shape->Fill(buf, transform, textures);
235 buf.hsl_mods.insert(buf.hsl_mods.end(), shape->VertexCount(), hsl_mod);
236 buf.rgb_mods.insert(buf.rgb_mods.end(), shape->VertexCount(), rgb_mod);
239 void BlockType::FillBlockMesh(
240 BlockMesh::Buffer &buf,
241 const glm::mat4 &transform,
242 BlockMesh::Index idx_offset
245 shape->Fill(buf, transform, textures, idx_offset);
246 buf.hsl_mods.insert(buf.hsl_mods.end(), shape->VertexCount(), hsl_mod);
247 buf.rgb_mods.insert(buf.rgb_mods.end(), shape->VertexCount(), rgb_mod);
250 void BlockType::OutlinePrimitiveMesh(PrimitiveMesh::Buffer &buf) const noexcept {
253 buf.colors.insert(buf.colors.end(), shape->OutlineCount(), glm::vec4(outline_color, 1.0f));
257 BlockTypeRegistry::BlockTypeRegistry() {
262 air.block_light = false;
263 air.collision = false;
264 air.collide_block = false;
268 Block::Type BlockTypeRegistry::Add(BlockType &&t) {
269 int id = types.size();
270 if (!names.emplace(t.name, id).second) {
271 throw std::runtime_error("duplicate block type name " + t.name);
273 types.push_back(std::move(t));
274 types.back().id = id;
278 BlockType &BlockTypeRegistry::Get(const std::string &name) {
279 auto entry = names.find(name);
280 if (entry != names.end()) {
281 return Get(entry->second);
283 throw std::runtime_error("unknown block type " + name);
287 const BlockType &BlockTypeRegistry::Get(const std::string &name) const {
288 auto entry = names.find(name);
289 if (entry != names.end()) {
290 return Get(entry->second);
292 throw std::runtime_error("unknown block type " + name);
299 /// the "usual" type of gravity
300 /// direction is towards the block's center, strength is inverse
301 /// proportional to distance squared
302 /// note that the effect can get clipped at distances > 16 units
304 : public BlockGravity {
306 explicit RadialGravity(float strength)
307 : strength(strength) { }
309 glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &) const noexcept override {
310 float dist2 = length2(diff);
311 glm::vec3 dir = -normalize(diff);
312 return dir * (strength / dist2);
319 /// a "force field" variant of artificial gravity
320 /// strength and direction is constant throughout the cuboid
321 /// extent shouldn't exceed 16 units as gravity is only calculated for
322 /// chunks surrounding and entity (and sometimes not even those if they're
323 /// unavailable, but they will be for players)
324 struct CuboidFieldGravity
325 : public BlockGravity {
327 explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
328 : strength(strength), extent(extent) { }
330 glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &M) const noexcept override {
331 /// rotate AABB endpoints accordingly, ignore translation
332 glm::vec3 min(M * glm::vec4(extent.min, 0.0f));
333 glm::vec3 max(M * glm::vec4(extent.max, 0.0f));
334 if (diff.x < min.x || diff.y < min.y || diff.z < min.z ||
335 diff.x > max.x || diff.y > max.y || diff.z > max.z) {
336 /// if point is outside, force is zero
337 return glm::vec3(0.0f);
339 /// otherwise it's out constant strength in block orientation
340 return glm::vec3(M * glm::vec4(strength, 0.0f));
352 BlockGravity::~BlockGravity() {
356 std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
358 in.ReadIdentifier(type);
359 if (type == "Radial") {
361 in.Skip(Token::PARENTHESIS_OPEN);
362 in.ReadNumber(strength);
363 in.Skip(Token::PARENTHESIS_CLOSE);
364 return std::unique_ptr<BlockGravity>(new RadialGravity(strength));
365 } else if (type == "CuboidField") {
368 in.Skip(Token::PARENTHESIS_OPEN);
369 in.ReadVec(strength);
370 in.Skip(Token::COMMA);
371 in.ReadVec(extent.min);
372 in.Skip(Token::COMMA);
373 in.ReadVec(extent.max);
374 in.Skip(Token::PARENTHESIS_CLOSE);
376 return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
378 throw std::runtime_error("unknown gravity type: " + type);
383 const glm::mat4 Block::orient2transform[ORIENT_COUNT] = {
384 { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: up, turn: none
385 { 0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: left
386 { -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: up, turn: around
387 { 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: right
388 { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: down, turn: none
389 { 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: down, turn: left
390 { -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: down, turn: around
391 { 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: down, turn: right
392 { 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: right, turn: none
393 { 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: right, turn: left
394 { 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: right, turn: around
395 { 0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: right, turn: right
396 { 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: left, turn: none
397 { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: left, turn: left
398 { 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: left, turn: around
399 { 0, 1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: left, turn: right
400 { 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: none
401 { 0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: left
402 { -1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: around
403 { 0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: right
404 { 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: none
405 { 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: left
406 { -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: around
407 { 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: right
410 const glm::ivec3 Block::face2normal[FACE_COUNT] = {
419 const Block::Face Block::orient2face[ORIENT_COUNT][FACE_COUNT] = {
420 { FACE_UP, FACE_DOWN, FACE_RIGHT, FACE_LEFT, FACE_FRONT, FACE_BACK, }, // face: up, turn: none
421 { FACE_UP, FACE_DOWN, FACE_FRONT, FACE_BACK, FACE_LEFT, FACE_RIGHT, }, // face: up, turn: left
422 { FACE_UP, FACE_DOWN, FACE_LEFT, FACE_RIGHT, FACE_BACK, FACE_FRONT, }, // face: up, turn: around
423 { FACE_UP, FACE_DOWN, FACE_BACK, FACE_FRONT, FACE_RIGHT, FACE_LEFT, }, // face: up, turn: right
424 { FACE_DOWN, FACE_UP, FACE_RIGHT, FACE_LEFT, FACE_BACK, FACE_FRONT, }, // face: down, turn: none
425 { FACE_DOWN, FACE_UP, FACE_BACK, FACE_FRONT, FACE_LEFT, FACE_RIGHT, }, // face: down, turn: left
426 { FACE_DOWN, FACE_UP, FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, }, // face: down, turn: around
427 { FACE_DOWN, FACE_UP, FACE_FRONT, FACE_BACK, FACE_RIGHT, FACE_LEFT, }, // face: down, turn: right
428 { FACE_LEFT, FACE_RIGHT, FACE_UP, FACE_DOWN, FACE_FRONT, FACE_BACK, }, // face: right, turn: none
429 { FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, FACE_DOWN, FACE_UP, }, // face: right, turn: left
430 { FACE_LEFT, FACE_RIGHT, FACE_DOWN, FACE_UP, FACE_BACK, FACE_FRONT, }, // face: right, turn: around
431 { FACE_LEFT, FACE_RIGHT, FACE_BACK, FACE_FRONT, FACE_UP, FACE_DOWN, }, // face: right, turn: right
432 { FACE_RIGHT, FACE_LEFT, FACE_DOWN, FACE_UP, FACE_FRONT, FACE_BACK, }, // face: left, turn: none
433 { FACE_RIGHT, FACE_LEFT, FACE_FRONT, FACE_BACK, FACE_UP, FACE_DOWN, }, // face: left, turn: left
434 { FACE_RIGHT, FACE_LEFT, FACE_UP, FACE_DOWN, FACE_BACK, FACE_FRONT, }, // face: left, turn: around
435 { FACE_RIGHT, FACE_LEFT, FACE_BACK, FACE_FRONT, FACE_DOWN, FACE_UP, }, // face: left, turn: right
436 { FACE_BACK, FACE_FRONT, FACE_RIGHT, FACE_LEFT, FACE_UP, FACE_DOWN, }, // face: front, turn: none
437 { FACE_BACK, FACE_FRONT, FACE_UP, FACE_DOWN, FACE_LEFT, FACE_RIGHT, }, // face: front, turn: left
438 { FACE_BACK, FACE_FRONT, FACE_LEFT, FACE_RIGHT, FACE_DOWN, FACE_UP, }, // face: front, turn: around
439 { FACE_BACK, FACE_FRONT, FACE_DOWN, FACE_UP, FACE_RIGHT, FACE_LEFT, }, // face: front, turn: right
440 { FACE_FRONT, FACE_BACK, FACE_RIGHT, FACE_LEFT, FACE_DOWN, FACE_UP, }, // face: back, turn: none
441 { FACE_FRONT, FACE_BACK, FACE_DOWN, FACE_UP, FACE_LEFT, FACE_RIGHT, }, // face: back, turn: left
442 { FACE_FRONT, FACE_BACK, FACE_LEFT, FACE_RIGHT, FACE_UP, FACE_DOWN, }, // face: back, turn: around
443 { FACE_FRONT, FACE_BACK, FACE_UP, FACE_DOWN, FACE_RIGHT, FACE_LEFT, }, // face: back, turn: right