]> git.localhorst.tv Git - blank.git/blob - src/world/block.cpp
f518b64aeabc96ad4a7cc8d8313582cb64348f8f
[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, 255, 255)
78 , rgb_mod(255, 255, 255)
79 , outline_color(0, 0, 0)
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         glm::vec3 color_conv;
146         while (in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
147                 in.ReadIdentifier(name);
148                 in.Skip(Token::EQUALS);
149                 if (name == "visible") {
150                         visible = in.GetBool();
151                 } else if (name == "texture") {
152                         textures.clear();
153                         in.ReadString(name);
154                         textures.push_back(tex_index.GetID(name));
155                 } else if (name == "textures") {
156                         textures.clear();
157                         in.Skip(Token::BRACKET_OPEN);
158                         while (in.Peek().type != Token::BRACKET_CLOSE) {
159                                 in.ReadString(name);
160                                 textures.push_back(tex_index.GetID(name));
161                                 if (in.Peek().type == Token::COMMA) {
162                                         in.Skip(Token::COMMA);
163                                 }
164                         }
165                         in.Skip(Token::BRACKET_CLOSE);
166                 } else if (name == "rgb_mod") {
167                         in.ReadVec(color_conv);
168                         rgb_mod = glm::tvec3<unsigned char>(color_conv * 255.0f);
169                 } else if (name == "hsl_mod") {
170                         in.ReadVec(color_conv);
171                         hsl_mod = glm::tvec3<unsigned char>(color_conv * 255.0f);
172                 } else if (name == "outline") {
173                         in.ReadVec(color_conv);
174                         outline_color = glm::tvec3<unsigned char>(color_conv * 255.0f);
175                 } else if (name == "gravity") {
176                         gravity = BlockGravity::Read(in);
177                 } else if (name == "label") {
178                         in.ReadString(label);
179                 } else if (name == "place_sound") {
180                         in.ReadString(name);
181                         place_sound = snd_index.GetID(name);
182                 } else if (name == "remove_sound") {
183                         in.ReadString(name);
184                         remove_sound = snd_index.GetID(name);
185                 } else if (name == "luminosity") {
186                         luminosity = in.GetInt();
187                 } else if (name == "block_light") {
188                         block_light = in.GetBool();
189                 } else if (name == "collision") {
190                         collision = in.GetBool();
191                 } else if (name == "collide_block") {
192                         collide_block = in.GetBool();
193                 } else if (name == "generate") {
194                         generate = in.GetBool();
195                 } else if (name == "min_solidity") {
196                         min_solidity = in.GetFloat();
197                 } else if (name == "mid_solidity") {
198                         mid_solidity = in.GetFloat();
199                 } else if (name == "max_solidity") {
200                         max_solidity = in.GetFloat();
201                 } else if (name == "min_humidity") {
202                         min_humidity = in.GetFloat();
203                 } else if (name == "mid_humidity") {
204                         mid_humidity = in.GetFloat();
205                 } else if (name == "max_humidity") {
206                         max_humidity = in.GetFloat();
207                 } else if (name == "min_temperature") {
208                         min_temperature = in.GetFloat();
209                 } else if (name == "mid_temperature") {
210                         mid_temperature = in.GetFloat();
211                 } else if (name == "max_temperature") {
212                         max_temperature = in.GetFloat();
213                 } else if (name == "min_richness") {
214                         min_richness = in.GetFloat();
215                 } else if (name == "mid_richness") {
216                         mid_richness = in.GetFloat();
217                 } else if (name == "max_richness") {
218                         max_richness = in.GetFloat();
219                 } else if (name == "commonness") {
220                         commonness = in.GetFloat();
221                 } else if (name == "shape") {
222                         in.ReadIdentifier(name);
223                         shape = &shapes.Get(name);
224                 } else {
225                         std::cerr << "warning: unknown block type property " << name << std::endl;
226                         while (in.Peek().type != Token::SEMICOLON) {
227                                 in.Next();
228                         }
229                 }
230                 in.Skip(Token::SEMICOLON);
231         }
232         in.Skip(Token::ANGLE_BRACKET_CLOSE);
233 }
234
235 void BlockType::FillEntityMesh(
236         EntityMesh::Buffer &buf,
237         const glm::mat4 &transform
238 ) const noexcept {
239         if (!shape) return;
240         shape->Fill(buf, transform, textures);
241         buf.hsl_mods.insert(buf.hsl_mods.end(), shape->VertexCount(), hsl_mod);
242         buf.rgb_mods.insert(buf.rgb_mods.end(), shape->VertexCount(), rgb_mod);
243 }
244
245 void BlockType::FillBlockMesh(
246         BlockMesh::Buffer &buf,
247         const glm::mat4 &transform,
248         BlockMesh::Index idx_offset
249 ) const noexcept {
250         if (!shape) return;
251         shape->Fill(buf, transform, textures, idx_offset);
252         buf.hsl_mods.insert(buf.hsl_mods.end(), shape->VertexCount(), hsl_mod);
253         buf.rgb_mods.insert(buf.rgb_mods.end(), shape->VertexCount(), rgb_mod);
254 }
255
256 void BlockType::OutlinePrimitiveMesh(PrimitiveMesh::Buffer &buf) const noexcept {
257         if (!shape) return;
258         shape->Outline(buf);
259         buf.colors.insert(buf.colors.end(), shape->OutlineCount(), glm::tvec4<unsigned char>(outline_color, 255));
260 }
261
262
263 BlockTypeRegistry::BlockTypeRegistry() {
264         BlockType air;
265         air.name = "air";
266         air.label = "Air";
267         air.visible = false;
268         air.block_light = false;
269         air.collision = false;
270         air.collide_block = false;
271         Add(std::move(air));
272 }
273
274 Block::Type BlockTypeRegistry::Add(BlockType &&t) {
275         int id = types.size();
276         if (!names.emplace(t.name, id).second) {
277                 throw std::runtime_error("duplicate block type name " + t.name);
278         }
279         types.push_back(std::move(t));
280         types.back().id = id;
281         return id;
282 }
283
284 BlockType &BlockTypeRegistry::Get(const std::string &name) {
285         auto entry = names.find(name);
286         if (entry != names.end()) {
287                 return Get(entry->second);
288         } else {
289                 throw std::runtime_error("unknown block type " + name);
290         }
291 }
292
293 const BlockType &BlockTypeRegistry::Get(const std::string &name) const {
294         auto entry = names.find(name);
295         if (entry != names.end()) {
296                 return Get(entry->second);
297         } else {
298                 throw std::runtime_error("unknown block type " + name);
299         }
300 }
301
302
303 namespace {
304
305 /// the "usual" type of gravity
306 /// direction is towards the block's center, strength is inverse
307 /// proportional to distance squared
308 /// note that the effect can get clipped at distances > 16 units
309 struct RadialGravity
310 : public BlockGravity {
311
312         explicit RadialGravity(float strength)
313         : strength(strength) { }
314
315         glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &) const noexcept override {
316                 float dist2 = length2(diff);
317                 glm::vec3 dir = -normalize(diff);
318                 return dir * (strength / dist2);
319         }
320
321         float strength;
322
323 };
324
325 /// a "force field" variant of artificial gravity
326 /// strength and direction is constant throughout the cuboid
327 /// extent shouldn't exceed 16 units as gravity is only calculated for
328 /// chunks surrounding and entity (and sometimes not even those if they're
329 /// unavailable, but they will be for players)
330 struct CuboidFieldGravity
331 : public BlockGravity {
332
333         explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
334         : strength(strength), extent(extent) { }
335
336         glm::vec3 GetGravity(const glm::vec3 &diff, const glm::mat4 &M) const noexcept override {
337                 /// rotate AABB endpoints accordingly, ignore translation
338                 glm::vec3 min(M * glm::vec4(extent.min, 0.0f));
339                 glm::vec3 max(M * glm::vec4(extent.max, 0.0f));
340                 if (diff.x < min.x || diff.y < min.y || diff.z < min.z ||
341                                 diff.x > max.x || diff.y > max.y || diff.z > max.z) {
342                         /// if point is outside, force is zero
343                         return glm::vec3(0.0f);
344                 } else {
345                         /// otherwise it's out constant strength in block orientation
346                         return glm::vec3(M * glm::vec4(strength, 0.0f));
347                 }
348         }
349
350         glm::vec3 strength;
351         AABB extent;
352
353 };
354
355
356 }
357
358 BlockGravity::~BlockGravity() {
359
360 }
361
362 std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
363         std::string type;
364         in.ReadIdentifier(type);
365         if (type == "Radial") {
366                 float strength;
367                 in.Skip(Token::PARENTHESIS_OPEN);
368                 in.ReadNumber(strength);
369                 in.Skip(Token::PARENTHESIS_CLOSE);
370                 return std::unique_ptr<BlockGravity>(new RadialGravity(strength));
371         } else if (type == "CuboidField") {
372                 glm::vec3 strength;
373                 AABB extent;
374                 in.Skip(Token::PARENTHESIS_OPEN);
375                 in.ReadVec(strength);
376                 in.Skip(Token::COMMA);
377                 in.ReadVec(extent.min);
378                 in.Skip(Token::COMMA);
379                 in.ReadVec(extent.max);
380                 in.Skip(Token::PARENTHESIS_CLOSE);
381                 extent.Adjust();
382                 return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
383         } else {
384                 throw std::runtime_error("unknown gravity type: " + type);
385         }
386 }
387
388
389 const glm::mat4 Block::orient2transform[ORIENT_COUNT] = {
390         {  1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: up,    turn: none
391         {  0,  0, -1,  0,  0,  1,  0,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: up,    turn: left
392         { -1,  0,  0,  0,  0,  1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: up,    turn: around
393         {  0,  0,  1,  0,  0,  1,  0,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: up,    turn: right
394         {  1,  0,  0,  0,  0, -1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: down,  turn: none
395         {  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: down,  turn: left
396         { -1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: down,  turn: around
397         {  0,  0,  1,  0,  0, -1,  0,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: down,  turn: right
398         {  0, -1,  0,  0,  1,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: right, turn: none
399         {  0, -1,  0,  0,  0,  0, -1,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: right, turn: left
400         {  0, -1,  0,  0, -1,  0,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: right, turn: around
401         {  0, -1,  0,  0,  0,  0,  1,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: right, turn: right
402         {  0,  1,  0,  0, -1,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1, }, // face: left,  turn: none
403         {  0,  1,  0,  0,  0,  0,  1,  0,  1,  0,  0,  0,  0,  0,  0,  1, }, // face: left,  turn: left
404         {  0,  1,  0,  0,  1,  0,  0,  0,  0,  0, -1,  0,  0,  0,  0,  1, }, // face: left,  turn: around
405         {  0,  1,  0,  0,  0,  0, -1,  0, -1,  0,  0,  0,  0,  0,  0,  1, }, // face: left,  turn: right
406         {  1,  0,  0,  0,  0,  0,  1,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: none
407         {  0,  0, -1,  0,  1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: left
408         { -1,  0,  0,  0,  0,  0, -1,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: around
409         {  0,  0,  1,  0, -1,  0,  0,  0,  0, -1,  0,  0,  0,  0,  0,  1, }, // face: front, turn: right
410         {  1,  0,  0,  0,  0,  0, -1,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: none
411         {  0,  0, -1,  0, -1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: left
412         { -1,  0,  0,  0,  0,  0,  1,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: around
413         {  0,  0,  1,  0,  1,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  1, }, // face: back,  turn: right
414 };
415
416 const glm::ivec3 Block::face2normal[FACE_COUNT] = {
417         {  0,  1,  0 },
418         {  0, -1,  0 },
419         {  1,  0,  0 },
420         { -1,  0,  0 },
421         {  0,  0,  1 },
422         {  0,  0, -1 },
423 };
424
425 const Block::Face Block::orient2face[ORIENT_COUNT][FACE_COUNT] = {
426         { FACE_UP,    FACE_DOWN,  FACE_RIGHT, FACE_LEFT,  FACE_FRONT, FACE_BACK,  }, // face: up,    turn: none
427         { FACE_UP,    FACE_DOWN,  FACE_FRONT, FACE_BACK,  FACE_LEFT,  FACE_RIGHT, }, // face: up,    turn: left
428         { FACE_UP,    FACE_DOWN,  FACE_LEFT,  FACE_RIGHT, FACE_BACK,  FACE_FRONT, }, // face: up,    turn: around
429         { FACE_UP,    FACE_DOWN,  FACE_BACK,  FACE_FRONT, FACE_RIGHT, FACE_LEFT,  }, // face: up,    turn: right
430         { FACE_DOWN,  FACE_UP,    FACE_RIGHT, FACE_LEFT,  FACE_BACK,  FACE_FRONT, }, // face: down,  turn: none
431         { FACE_DOWN,  FACE_UP,    FACE_BACK,  FACE_FRONT, FACE_LEFT,  FACE_RIGHT, }, // face: down,  turn: left
432         { FACE_DOWN,  FACE_UP,    FACE_LEFT,  FACE_RIGHT, FACE_FRONT, FACE_BACK,  }, // face: down,  turn: around
433         { FACE_DOWN,  FACE_UP,    FACE_FRONT, FACE_BACK,  FACE_RIGHT, FACE_LEFT,  }, // face: down,  turn: right
434         { FACE_LEFT,  FACE_RIGHT, FACE_UP,    FACE_DOWN,  FACE_FRONT, FACE_BACK,  }, // face: right, turn: none
435         { FACE_LEFT,  FACE_RIGHT, FACE_FRONT, FACE_BACK,  FACE_DOWN,  FACE_UP,    }, // face: right, turn: left
436         { FACE_LEFT,  FACE_RIGHT, FACE_DOWN,  FACE_UP,    FACE_BACK,  FACE_FRONT, }, // face: right, turn: around
437         { FACE_LEFT,  FACE_RIGHT, FACE_BACK,  FACE_FRONT, FACE_UP,    FACE_DOWN,  }, // face: right, turn: right
438         { FACE_RIGHT, FACE_LEFT,  FACE_DOWN,  FACE_UP,    FACE_FRONT, FACE_BACK,  }, // face: left,  turn: none
439         { FACE_RIGHT, FACE_LEFT,  FACE_FRONT, FACE_BACK,  FACE_UP,    FACE_DOWN,  }, // face: left,  turn: left
440         { FACE_RIGHT, FACE_LEFT,  FACE_UP,    FACE_DOWN,  FACE_BACK,  FACE_FRONT, }, // face: left,  turn: around
441         { FACE_RIGHT, FACE_LEFT,  FACE_BACK,  FACE_FRONT, FACE_DOWN,  FACE_UP,    }, // face: left,  turn: right
442         { FACE_BACK,  FACE_FRONT, FACE_RIGHT, FACE_LEFT,  FACE_UP,    FACE_DOWN,  }, // face: front, turn: none
443         { FACE_BACK,  FACE_FRONT, FACE_UP,    FACE_DOWN,  FACE_LEFT,  FACE_RIGHT, }, // face: front, turn: left
444         { FACE_BACK,  FACE_FRONT, FACE_LEFT,  FACE_RIGHT, FACE_DOWN,  FACE_UP,    }, // face: front, turn: around
445         { FACE_BACK,  FACE_FRONT, FACE_DOWN,  FACE_UP,    FACE_RIGHT, FACE_LEFT,  }, // face: front, turn: right
446         { FACE_FRONT, FACE_BACK,  FACE_RIGHT, FACE_LEFT,  FACE_DOWN,  FACE_UP,    }, // face: back,  turn: none
447         { FACE_FRONT, FACE_BACK,  FACE_DOWN,  FACE_UP,    FACE_LEFT,  FACE_RIGHT, }, // face: back,  turn: left
448         { FACE_FRONT, FACE_BACK,  FACE_LEFT,  FACE_RIGHT, FACE_UP,    FACE_DOWN,  }, // face: back,  turn: around
449         { FACE_FRONT, FACE_BACK,  FACE_UP,    FACE_DOWN,  FACE_RIGHT, FACE_LEFT,  }, // face: back,  turn: right
450 };
451
452 }