]> git.localhorst.tv Git - blank.git/blob - src/world/block.cpp
block type prototypability and new types
[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                         in.ReadString(name);
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) {
156                                 in.ReadString(name);
157                                 textures.push_back(tex_index.GetID(name));
158                                 if (in.Peek().type == Token::COMMA) {
159                                         in.Skip(Token::COMMA);
160                                 }
161                         }
162                         in.Skip(Token::BRACKET_CLOSE);
163                 } else if (name == "rgb_mod") {
164                         in.ReadVec(rgb_mod);
165                 } else if (name == "hsl_mod") {
166                         in.ReadVec(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") {
174                         in.ReadString(name);
175                         place_sound = snd_index.GetID(name);
176                 } else if (name == "remove_sound") {
177                         in.ReadString(name);
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);
218                 } else {
219                         std::cerr << "warning: unknown block type property " << name << std::endl;
220                         while (in.Peek().type != Token::SEMICOLON) {
221                                 in.Next();
222                         }
223                 }
224                 in.Skip(Token::SEMICOLON);
225         }
226         in.Skip(Token::ANGLE_BRACKET_CLOSE);
227 }
228
229 void BlockType::FillEntityMesh(
230         EntityMesh::Buffer &buf,
231         const glm::mat4 &transform
232 ) const noexcept {
233         if (!shape) return;
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);
237 }
238
239 void BlockType::FillBlockMesh(
240         BlockMesh::Buffer &buf,
241         const glm::mat4 &transform,
242         BlockMesh::Index idx_offset
243 ) const noexcept {
244         if (!shape) return;
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);
248 }
249
250 void BlockType::OutlinePrimitiveMesh(PrimitiveMesh::Buffer &buf) const noexcept {
251         if (!shape) return;
252         shape->Outline(buf);
253         buf.colors.insert(buf.colors.end(), shape->OutlineCount(), glm::vec4(outline_color, 1.0f));
254 }
255
256
257 BlockTypeRegistry::BlockTypeRegistry() {
258         BlockType air;
259         air.name = "air";
260         air.label = "Air";
261         air.visible = false;
262         air.block_light = false;
263         air.collision = false;
264         air.collide_block = false;
265         Add(std::move(air));
266 }
267
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);
272         }
273         types.push_back(std::move(t));
274         types.back().id = id;
275         return id;
276 }
277
278 BlockType &BlockTypeRegistry::Get(const std::string &name) {
279         auto entry = names.find(name);
280         if (entry != names.end()) {
281                 return Get(entry->second);
282         } else {
283                 throw std::runtime_error("unknown block type " + name);
284         }
285 }
286
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);
291         } else {
292                 throw std::runtime_error("unknown block type " + name);
293         }
294 }
295
296
297 namespace {
298
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
303 struct RadialGravity
304 : public BlockGravity {
305
306         explicit RadialGravity(float strength)
307         : strength(strength) { }
308
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);
313         }
314
315         float strength;
316
317 };
318
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 {
326
327         explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
328         : strength(strength), extent(extent) { }
329
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);
338                 } else {
339                         /// otherwise it's out constant strength in block orientation
340                         return glm::vec3(M * glm::vec4(strength, 0.0f));
341                 }
342         }
343
344         glm::vec3 strength;
345         AABB extent;
346
347 };
348
349
350 }
351
352 BlockGravity::~BlockGravity() {
353
354 }
355
356 std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
357         std::string type;
358         in.ReadIdentifier(type);
359         if (type == "Radial") {
360                 float strength;
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") {
366                 glm::vec3 strength;
367                 AABB extent;
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);
375                 extent.Adjust();
376                 return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
377         } else {
378                 throw std::runtime_error("unknown gravity type: " + type);
379         }
380 }
381
382
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
408 };
409
410 const glm::ivec3 Block::face2normal[FACE_COUNT] = {
411         {  0,  1,  0 },
412         {  0, -1,  0 },
413         {  1,  0,  0 },
414         { -1,  0,  0 },
415         {  0,  0,  1 },
416         {  0,  0, -1 },
417 };
418
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
444 };
445
446 }