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