]> git.localhorst.tv Git - blank.git/blob - src/world/block.cpp
e99c5be254b2fbb16309ca31b946bbf0be215071
[blank.git] / src / world / block.cpp
1 #include "Block.hpp"
2 #include "BlockGravity.hpp"
3 #include "BlockType.hpp"
4 #include "BlockTypeRegistry.hpp"
5
6 #include "../io/TokenStreamReader.hpp"
7 #include "../model/ShapeRegistry.hpp"
8 #include "../shared/ResourceIndex.hpp"
9
10 #include <iostream>
11 #include <stdexcept>
12 #include <glm/gtx/euler_angles.hpp>
13 #include <glm/gtx/norm.hpp>
14 #include <glm/gtx/transform.hpp>
15
16
17 namespace blank {
18
19 std::ostream &operator <<(std::ostream &out, const Block &block) {
20         return out << "Block(" << block.type << ", " << block.GetFace() << ", " << block.GetTurn() << ')';
21 }
22
23 std::ostream &operator <<(std::ostream &out, const Block::Face &face) {
24         switch (face) {
25                 case Block::FACE_UP:
26                         out << "FACE_UP";
27                         break;
28                 case Block::FACE_DOWN:
29                         out << "FACE_DOWN";
30                         break;
31                 case Block::FACE_RIGHT:
32                         out << "FACE_RIGHT";
33                         break;
34                 case Block::FACE_LEFT:
35                         out << "FACE_LEFT";
36                         break;
37                 case Block::FACE_FRONT:
38                         out << "FACE_FRONT";
39                         break;
40                 case Block::FACE_BACK:
41                         out << "FACE_BACK";
42                         break;
43                 default:
44                 case Block::FACE_COUNT:
45                         out << "invalid Block::Face";
46                         break;
47         }
48         return out;
49 }
50
51 std::ostream &operator <<(std::ostream &out, const Block::Turn &turn) {
52         switch (turn) {
53                 case Block::TURN_NONE:
54                         out << "TURN_NONE";
55                         break;
56                 case Block::TURN_LEFT:
57                         out << "TURN_LEFT";
58                         break;
59                 case Block::TURN_AROUND:
60                         out << "TURN_AROUND";
61                         break;
62                 case Block::TURN_RIGHT:
63                         out << "TURN_RIGHT";
64                         break;
65                 default:
66                 case Block::TURN_COUNT:
67                         out << "invalid Block::Turn";
68                         break;
69         }
70         return out;
71 }
72
73
74 BlockType::BlockType() noexcept
75 : shape(nullptr)
76 , textures()
77 , hsl_mod(0.0f, 1.0f, 1.0f)
78 , rgb_mod(1.0f, 1.0f, 1.0f)
79 , outline_color(-1, -1, -1)
80 , gravity()
81 , name("anonymous")
82 , label("some block")
83 , place_sound(-1)
84 , remove_sound(-1)
85 , id(0)
86 , luminosity(0)
87 , visible(true)
88 , block_light(true)
89 , collision(true)
90 , collide_block(true)
91 , generate(false)
92 , min_solidity(0.5f)
93 , mid_solidity(0.75f)
94 , max_solidity(1.0f)
95 , min_humidity(-1.0f)
96 , mid_humidity(0.0f)
97 , max_humidity(1.0f)
98 , min_temperature(-1.0f)
99 , mid_temperature(0.0f)
100 , max_temperature(1.0f)
101 , min_richness(-1.0f)
102 , mid_richness(0.0f)
103 , max_richness(1.0f)
104 , commonness(1.0f) {
105
106 }
107
108 void BlockType::Copy(const BlockType &other) noexcept {
109         shape = other.shape;
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;
135 }
136
137 void BlockType::Read(
138         TokenStreamReader &in,
139         ResourceIndex &snd_index,
140         ResourceIndex &tex_index,
141         const ShapeRegistry &shapes
142 ) {
143         std::string name;
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") {
151                         textures.clear();
152                         in.ReadString(name);
153                         textures.push_back(tex_index.GetID(name));
154                 } else if (name == "textures") {
155                         textures.clear();
156                         in.Skip(Token::BRACKET_OPEN);
157                         while (in.Peek().type != Token::BRACKET_CLOSE) {
158                                 in.ReadString(name);
159                                 textures.push_back(tex_index.GetID(name));
160                                 if (in.Peek().type == Token::COMMA) {
161                                         in.Skip(Token::COMMA);
162                                 }
163                         }
164                         in.Skip(Token::BRACKET_CLOSE);
165                 } else if (name == "rgb_mod") {
166                         in.ReadVec(rgb_mod);
167                 } else if (name == "hsl_mod") {
168                         in.ReadVec(hsl_mod);
169                 } else if (name == "outline") {
170                         in.ReadVec(outline_color);
171                 } else if (name == "gravity") {
172                         gravity = BlockGravity::Read(in);
173                 } else if (name == "label") {
174                         in.ReadString(label);
175                 } else if (name == "place_sound") {
176                         in.ReadString(name);
177                         place_sound = snd_index.GetID(name);
178                 } else if (name == "remove_sound") {
179                         in.ReadString(name);
180                         remove_sound = snd_index.GetID(name);
181                 } else if (name == "luminosity") {
182                         luminosity = in.GetInt();
183                 } else if (name == "block_light") {
184                         block_light = in.GetBool();
185                 } else if (name == "collision") {
186                         collision = in.GetBool();
187                 } else if (name == "collide_block") {
188                         collide_block = in.GetBool();
189                 } else if (name == "generate") {
190                         generate = in.GetBool();
191                 } else if (name == "min_solidity") {
192                         min_solidity = in.GetFloat();
193                 } else if (name == "mid_solidity") {
194                         mid_solidity = in.GetFloat();
195                 } else if (name == "max_solidity") {
196                         max_solidity = in.GetFloat();
197                 } else if (name == "min_humidity") {
198                         min_humidity = in.GetFloat();
199                 } else if (name == "mid_humidity") {
200                         mid_humidity = in.GetFloat();
201                 } else if (name == "max_humidity") {
202                         max_humidity = in.GetFloat();
203                 } else if (name == "min_temperature") {
204                         min_temperature = in.GetFloat();
205                 } else if (name == "mid_temperature") {
206                         mid_temperature = in.GetFloat();
207                 } else if (name == "max_temperature") {
208                         max_temperature = in.GetFloat();
209                 } else if (name == "min_richness") {
210                         min_richness = in.GetFloat();
211                 } else if (name == "mid_richness") {
212                         mid_richness = in.GetFloat();
213                 } else if (name == "max_richness") {
214                         max_richness = in.GetFloat();
215                 } else if (name == "commonness") {
216                         commonness = in.GetFloat();
217                 } else if (name == "shape") {
218                         in.ReadIdentifier(name);
219                         shape = &shapes.Get(name);
220                 } else {
221                         std::cerr << "warning: unknown block type property " << name << std::endl;
222                         while (in.Peek().type != Token::SEMICOLON) {
223                                 in.Next();
224                         }
225                 }
226                 in.Skip(Token::SEMICOLON);
227         }
228         in.Skip(Token::ANGLE_BRACKET_CLOSE);
229 }
230
231 void BlockType::FillEntityMesh(
232         EntityMesh::Buffer &buf,
233         const glm::mat4 &transform
234 ) const noexcept {
235         if (!shape) return;
236         shape->Fill(buf, transform, textures);
237         buf.hsl_mods.insert(buf.hsl_mods.end(), shape->VertexCount(), hsl_mod);
238         buf.rgb_mods.insert(buf.rgb_mods.end(), shape->VertexCount(), rgb_mod);
239 }
240
241 void BlockType::FillBlockMesh(
242         BlockMesh::Buffer &buf,
243         const glm::mat4 &transform,
244         BlockMesh::Index idx_offset
245 ) const noexcept {
246         if (!shape) return;
247         shape->Fill(buf, transform, textures, idx_offset);
248         buf.hsl_mods.insert(buf.hsl_mods.end(), shape->VertexCount(), hsl_mod);
249         buf.rgb_mods.insert(buf.rgb_mods.end(), shape->VertexCount(), rgb_mod);
250 }
251
252 void BlockType::OutlinePrimitiveMesh(PrimitiveMesh::Buffer &buf) const noexcept {
253         if (!shape) return;
254         shape->Outline(buf);
255         buf.colors.insert(buf.colors.end(), shape->OutlineCount(), glm::vec4(outline_color, 1.0f));
256 }
257
258
259 BlockTypeRegistry::BlockTypeRegistry() {
260         BlockType air;
261         air.name = "air";
262         air.label = "Air";
263         air.visible = false;
264         air.block_light = false;
265         air.collision = false;
266         air.collide_block = false;
267         Add(std::move(air));
268 }
269
270 Block::Type BlockTypeRegistry::Add(BlockType &&t) {
271         int id = types.size();
272         if (!names.emplace(t.name, id).second) {
273                 throw std::runtime_error("duplicate block type name " + t.name);
274         }
275         types.push_back(std::move(t));
276         types.back().id = id;
277         return id;
278 }
279
280 BlockType &BlockTypeRegistry::Get(const std::string &name) {
281         auto entry = names.find(name);
282         if (entry != names.end()) {
283                 return Get(entry->second);
284         } else {
285                 throw std::runtime_error("unknown block type " + name);
286         }
287 }
288
289 const BlockType &BlockTypeRegistry::Get(const std::string &name) const {
290         auto entry = names.find(name);
291         if (entry != names.end()) {
292                 return Get(entry->second);
293         } else {
294                 throw std::runtime_error("unknown block type " + name);
295         }
296 }
297
298
299 namespace {
300
301 /// the "usual" type of gravity
302 /// direction is towards the block's center, strength is inverse
303 /// proportional to distance squared
304 /// note that the effect can get clipped at distances > 16 units
305 struct RadialGravity
306 : public BlockGravity {
307
308         explicit RadialGravity(float strength)
309         : strength(strength) { }
310
311         glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &) const noexcept override {
312                 float dist2 = length2(diff);
313                 glm::vec3 dir = -normalize(diff);
314                 return dir * (strength / dist2);
315         }
316
317         float strength;
318
319 };
320
321 /// a "force field" variant of artificial gravity
322 /// strength and direction is constant throughout the cuboid
323 /// extent shouldn't exceed 16 units as gravity is only calculated for
324 /// chunks surrounding and entity (and sometimes not even those if they're
325 /// unavailable, but they will be for players)
326 struct CuboidFieldGravity
327 : public BlockGravity {
328
329         explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
330         : strength(strength), extent(extent) { }
331
332         glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &M) const noexcept override {
333                 /// rotate AABB endpoints accordingly, ignore translation
334                 glm::vec3 min(M * glm::vec4(extent.min, 0.0f));
335                 glm::vec3 max(M * glm::vec4(extent.max, 0.0f));
336                 if (diff.x < min.x || diff.y < min.y || diff.z < min.z ||
337                                 diff.x > max.x || diff.y > max.y || diff.z > max.z) {
338                         /// if point is outside, force is zero
339                         return glm::vec3(0.0f);
340                 } else {
341                         /// otherwise it's out constant strength in block orientation
342                         return glm::vec3(M * glm::vec4(strength, 0.0f));
343                 }
344         }
345
346         glm::vec3 strength;
347         AABB extent;
348
349 };
350
351
352 }
353
354 BlockGravity::~BlockGravity() {
355
356 }
357
358 std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
359         std::string type;
360         in.ReadIdentifier(type);
361         if (type == "Radial") {
362                 float strength;
363                 in.Skip(Token::PARENTHESIS_OPEN);
364                 in.ReadNumber(strength);
365                 in.Skip(Token::PARENTHESIS_CLOSE);
366                 return std::unique_ptr<BlockGravity>(new RadialGravity(strength));
367         } else if (type == "CuboidField") {
368                 glm::vec3 strength;
369                 AABB extent;
370                 in.Skip(Token::PARENTHESIS_OPEN);
371                 in.ReadVec(strength);
372                 in.Skip(Token::COMMA);
373                 in.ReadVec(extent.min);
374                 in.Skip(Token::COMMA);
375                 in.ReadVec(extent.max);
376                 in.Skip(Token::PARENTHESIS_CLOSE);
377                 extent.Adjust();
378                 return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
379         } else {
380                 throw std::runtime_error("unknown gravity type: " + type);
381         }
382 }
383
384
385 const glm::mat4 Block::orient2transform[ORIENT_COUNT] = {
386         {  1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: up,    turn: none
387         {  0,  0, -1,  0,  0,  1,  0,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: up,    turn: left
388         { -1,  0,  0,  0,  0,  1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: up,    turn: around
389         {  0,  0,  1,  0,  0,  1,  0,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: up,    turn: right
390         {  1,  0,  0,  0,  0, -1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: down,  turn: none
391         {  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: down,  turn: left
392         { -1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: down,  turn: around
393         {  0,  0,  1,  0,  0, -1,  0,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: down,  turn: right
394         {  0, -1,  0,  0,  1,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: right, turn: none
395         {  0, -1,  0,  0,  0,  0, -1,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: right, turn: left
396         {  0, -1,  0,  0, -1,  0,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: right, turn: around
397         {  0, -1,  0,  0,  0,  0,  1,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: right, turn: right
398         {  0,  1,  0,  0, -1,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: left,  turn: none
399         {  0,  1,  0,  0,  0,  0,  1,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: left,  turn: left
400         {  0,  1,  0,  0,  1,  0,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: left,  turn: around
401         {  0,  1,  0,  0,  0,  0, -1,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: left,  turn: right
402         {  1,  0,  0,  0,  0,  0,  1,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: none
403         {  0,  0, -1,  0,  1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: left
404         { -1,  0,  0,  0,  0,  0, -1,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: around
405         {  0,  0,  1,  0, -1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: right
406         {  1,  0,  0,  0,  0,  0, -1,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: none
407         {  0,  0, -1,  0, -1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: left
408         { -1,  0,  0,  0,  0,  0,  1,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: around
409         {  0,  0,  1,  0,  1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: right
410 };
411
412 const glm::ivec3 Block::face2normal[FACE_COUNT] = {
413         {  0,  1,  0 },
414         {  0, -1,  0 },
415         {  1,  0,  0 },
416         { -1,  0,  0 },
417         {  0,  0,  1 },
418         {  0,  0, -1 },
419 };
420
421 const Block::Face Block::orient2face[ORIENT_COUNT][FACE_COUNT] = {
422         { FACE_UP,    FACE_DOWN,  FACE_RIGHT, FACE_LEFT,  FACE_FRONT, FACE_BACK,  }, // face: up,    turn: none
423         { FACE_UP,    FACE_DOWN,  FACE_FRONT, FACE_BACK,  FACE_LEFT,  FACE_RIGHT, }, // face: up,    turn: left
424         { FACE_UP,    FACE_DOWN,  FACE_LEFT,  FACE_RIGHT, FACE_BACK,  FACE_FRONT, }, // face: up,    turn: around
425         { FACE_UP,    FACE_DOWN,  FACE_BACK,  FACE_FRONT, FACE_RIGHT, FACE_LEFT,  }, // face: up,    turn: right
426         { FACE_DOWN,  FACE_UP,    FACE_RIGHT, FACE_LEFT,  FACE_BACK,  FACE_FRONT, }, // face: down,  turn: none
427         { FACE_DOWN,  FACE_UP,    FACE_BACK,  FACE_FRONT, FACE_LEFT,  FACE_RIGHT, }, // face: down,  turn: left
428         { FACE_DOWN,  FACE_UP,    FACE_LEFT,  FACE_RIGHT, FACE_FRONT, FACE_BACK,  }, // face: down,  turn: around
429         { FACE_DOWN,  FACE_UP,    FACE_FRONT, FACE_BACK,  FACE_RIGHT, FACE_LEFT,  }, // face: down,  turn: right
430         { FACE_LEFT,  FACE_RIGHT, FACE_UP,    FACE_DOWN,  FACE_FRONT, FACE_BACK,  }, // face: right, turn: none
431         { FACE_LEFT,  FACE_RIGHT, FACE_FRONT, FACE_BACK,  FACE_DOWN,  FACE_UP,    }, // face: right, turn: left
432         { FACE_LEFT,  FACE_RIGHT, FACE_DOWN,  FACE_UP,    FACE_BACK,  FACE_FRONT, }, // face: right, turn: around
433         { FACE_LEFT,  FACE_RIGHT, FACE_BACK,  FACE_FRONT, FACE_UP,    FACE_DOWN,  }, // face: right, turn: right
434         { FACE_RIGHT, FACE_LEFT,  FACE_DOWN,  FACE_UP,    FACE_FRONT, FACE_BACK,  }, // face: left,  turn: none
435         { FACE_RIGHT, FACE_LEFT,  FACE_FRONT, FACE_BACK,  FACE_UP,    FACE_DOWN,  }, // face: left,  turn: left
436         { FACE_RIGHT, FACE_LEFT,  FACE_UP,    FACE_DOWN,  FACE_BACK,  FACE_FRONT, }, // face: left,  turn: around
437         { FACE_RIGHT, FACE_LEFT,  FACE_BACK,  FACE_FRONT, FACE_DOWN,  FACE_UP,    }, // face: left,  turn: right
438         { FACE_BACK,  FACE_FRONT, FACE_RIGHT, FACE_LEFT,  FACE_UP,    FACE_DOWN,  }, // face: front, turn: none
439         { FACE_BACK,  FACE_FRONT, FACE_UP,    FACE_DOWN,  FACE_LEFT,  FACE_RIGHT, }, // face: front, turn: left
440         { FACE_BACK,  FACE_FRONT, FACE_LEFT,  FACE_RIGHT, FACE_DOWN,  FACE_UP,    }, // face: front, turn: around
441         { FACE_BACK,  FACE_FRONT, FACE_DOWN,  FACE_UP,    FACE_RIGHT, FACE_LEFT,  }, // face: front, turn: right
442         { FACE_FRONT, FACE_BACK,  FACE_RIGHT, FACE_LEFT,  FACE_DOWN,  FACE_UP,    }, // face: back,  turn: none
443         { FACE_FRONT, FACE_BACK,  FACE_DOWN,  FACE_UP,    FACE_LEFT,  FACE_RIGHT, }, // face: back,  turn: left
444         { FACE_FRONT, FACE_BACK,  FACE_LEFT,  FACE_RIGHT, FACE_UP,    FACE_DOWN,  }, // face: back,  turn: around
445         { FACE_FRONT, FACE_BACK,  FACE_UP,    FACE_DOWN,  FACE_RIGHT, FACE_LEFT,  }, // face: back,  turn: right
446 };
447
448 }