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(0, 0, 0)
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)
100 void BlockType::Copy(const BlockType &other) noexcept {
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;
121 void BlockType::Read(
122 TokenStreamReader &in,
123 ResourceIndex &snd_index,
124 ResourceIndex &tex_index,
125 const ShapeRegistry &shapes
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") {
138 textures.push_back(tex_index.GetID(name));
139 } else if (name == "textures") {
141 in.Skip(Token::BRACKET_OPEN);
142 while (in.Peek().type != Token::BRACKET_CLOSE) {
144 textures.push_back(tex_index.GetID(name));
145 if (in.Peek().type == Token::COMMA) {
146 in.Skip(Token::COMMA);
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") {
165 place_sound = snd_index.GetID(name);
166 } else if (name == "remove_sound") {
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);
209 std::cerr << "warning: unknown block type property " << name << std::endl;
210 while (in.Peek().type != Token::SEMICOLON) {
214 in.Skip(Token::SEMICOLON);
216 in.Skip(Token::ANGLE_BRACKET_CLOSE);
219 void BlockType::FillEntityMesh(
220 EntityMesh::Buffer &buf,
221 const glm::mat4 &transform
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);
229 void BlockType::FillBlockMesh(
230 BlockMesh::Buffer &buf,
231 const glm::mat4 &transform,
232 BlockMesh::Index idx_offset
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);
240 void BlockType::OutlinePrimitiveMesh(PrimitiveMesh::Buffer &buf) const noexcept {
243 buf.colors.insert(buf.colors.end(), shape->OutlineCount(), glm::tvec4<unsigned char>(outline_color, 255));
247 BlockTypeRegistry::BlockTypeRegistry() {
252 air.block_light = false;
253 air.collision = false;
254 air.collide_block = false;
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);
263 types.push_back(std::move(t));
264 types.back().id = id;
268 BlockType &BlockTypeRegistry::Get(const std::string &name) {
269 auto entry = names.find(name);
270 if (entry != names.end()) {
271 return Get(entry->second);
273 throw std::runtime_error("unknown block type " + name);
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);
282 throw std::runtime_error("unknown block type " + name);
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
294 : public BlockGravity {
296 explicit RadialGravity(float strength)
297 : strength(strength) { }
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);
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 {
317 explicit CuboidFieldGravity(const glm::vec3 &strength, const AABB &extent)
318 : strength(strength), extent(extent) { }
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);
329 /// otherwise it's out constant strength in block orientation
330 return glm::vec3(M * glm::vec4(strength, 0.0f));
342 BlockGravity::~BlockGravity() {
346 std::unique_ptr<BlockGravity> BlockGravity::Read(TokenStreamReader &in) {
348 in.ReadIdentifier(type);
349 if (type == "Radial") {
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") {
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);
366 return std::unique_ptr<BlockGravity>(new CuboidFieldGravity(strength, extent));
368 throw std::runtime_error("unknown gravity type: " + type);
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
400 const glm::ivec3 Block::face2normal[FACE_COUNT] = {
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