2 #include "BlockGravity.hpp"
3 #include "BlockType.hpp"
4 #include "BlockTypeRegistry.hpp"
6 #include "../io/TokenStreamReader.hpp"
7 #include "../model/ShapeRegistry.hpp"
8 #include "../shared/ResourceIndex.hpp"
12 #include <glm/gtx/euler_angles.hpp>
13 #include <glm/gtx/norm.hpp>
14 #include <glm/gtx/transform.hpp>
19 std::ostream &operator <<(std::ostream &out, const Block &block) {
20 return out << "Block(" << block.type << ", " << block.GetFace() << ", " << block.GetTurn() << ')';
23 std::ostream &operator <<(std::ostream &out, const Block::Face &face) {
28 case Block::FACE_DOWN:
31 case Block::FACE_RIGHT:
34 case Block::FACE_LEFT:
37 case Block::FACE_FRONT:
40 case Block::FACE_BACK:
44 case Block::FACE_COUNT:
45 out << "invalid Block::Face";
51 std::ostream &operator <<(std::ostream &out, const Block::Turn &turn) {
53 case Block::TURN_NONE:
56 case Block::TURN_LEFT:
59 case Block::TURN_AROUND:
62 case Block::TURN_RIGHT:
66 case Block::TURN_COUNT:
67 out << "invalid Block::Turn";
74 BlockType::BlockType() noexcept
77 , hsl_mod(0, 255, 255)
78 , rgb_mod(255, 255, 255)
79 , outline_color(-1, -1, -1)
98 , min_temperature(-1.0f)
99 , mid_temperature(0.0f)
100 , max_temperature(1.0f)
101 , min_richness(-1.0f)
108 void BlockType::Copy(const BlockType &other) noexcept {
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;
137 void BlockType::Read(
138 TokenStreamReader &in,
139 ResourceIndex &snd_index,
140 ResourceIndex &tex_index,
141 const ShapeRegistry &shapes
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") {
154 textures.push_back(tex_index.GetID(name));
155 } else if (name == "textures") {
157 in.Skip(Token::BRACKET_OPEN);
158 while (in.Peek().type != Token::BRACKET_CLOSE) {
160 textures.push_back(tex_index.GetID(name));
161 if (in.Peek().type == Token::COMMA) {
162 in.Skip(Token::COMMA);
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") {
181 place_sound = snd_index.GetID(name);
182 } else if (name == "remove_sound") {
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);
225 std::cerr << "warning: unknown block type property " << name << std::endl;
226 while (in.Peek().type != Token::SEMICOLON) {
230 in.Skip(Token::SEMICOLON);
232 in.Skip(Token::ANGLE_BRACKET_CLOSE);
235 void BlockType::FillEntityMesh(
236 EntityMesh::Buffer &buf,
237 const glm::mat4 &transform
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);
245 void BlockType::FillBlockMesh(
246 BlockMesh::Buffer &buf,
247 const glm::mat4 &transform,
248 BlockMesh::Index idx_offset
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);
256 void BlockType::OutlinePrimitiveMesh(PrimitiveMesh::Buffer &buf) const noexcept {
259 buf.colors.insert(buf.colors.end(), shape->OutlineCount(), glm::tvec4<unsigned char>(outline_color, 255));
263 BlockTypeRegistry::BlockTypeRegistry() {
268 air.block_light = false;
269 air.collision = false;
270 air.collide_block = false;
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);
279 types.push_back(std::move(t));
280 types.back().id = id;
284 BlockType &BlockTypeRegistry::Get(const std::string &name) {
285 auto entry = names.find(name);
286 if (entry != names.end()) {
287 return Get(entry->second);
289 throw std::runtime_error("unknown block type " + name);
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);
298 throw std::runtime_error("unknown block type " + name);
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
310 : public BlockGravity {
312 explicit RadialGravity(float strength)
313 : strength(strength) { }
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);
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 {
333 explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
334 : strength(strength), extent(extent) { }
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);
345 /// otherwise it's out constant strength in block orientation
346 return glm::vec3(M * glm::vec4(strength, 0.0f));
358 BlockGravity::~BlockGravity() {
362 std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
364 in.ReadIdentifier(type);
365 if (type == "Radial") {
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") {
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);
382 return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
384 throw std::runtime_error("unknown gravity type: " + type);
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
416 const glm::ivec3 Block::face2normal[FACE_COUNT] = {
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