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