3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
50 type = NODEBOX_REGULAR;
53 // default is sign/ladder-like
54 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57 // no default for other parts
59 connect_bottom.clear();
60 connect_front.clear();
63 connect_right.clear();
64 disconnected_top.clear();
65 disconnected_bottom.clear();
66 disconnected_front.clear();
67 disconnected_left.clear();
68 disconnected_back.clear();
69 disconnected_right.clear();
71 disconnected_sides.clear();
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F32(os, wall_top.MinEdge);
95 writeV3F32(os, wall_top.MaxEdge);
96 writeV3F32(os, wall_bottom.MinEdge);
97 writeV3F32(os, wall_bottom.MaxEdge);
98 writeV3F32(os, wall_side.MinEdge);
99 writeV3F32(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F32(os, i.MinEdge); \
108 writeV3F32(os, i.MaxEdge); \
112 WRITEBOX(connect_top);
113 WRITEBOX(connect_bottom);
114 WRITEBOX(connect_front);
115 WRITEBOX(connect_left);
116 WRITEBOX(connect_back);
117 WRITEBOX(connect_right);
118 WRITEBOX(disconnected_top);
119 WRITEBOX(disconnected_bottom);
120 WRITEBOX(disconnected_front);
121 WRITEBOX(disconnected_left);
122 WRITEBOX(disconnected_back);
123 WRITEBOX(disconnected_right);
124 WRITEBOX(disconnected);
125 WRITEBOX(disconnected_sides);
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
137 throw SerializationError("unsupported NodeBox version");
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
149 box.MinEdge = readV3F32(is);
150 box.MaxEdge = readV3F32(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F32(is);
157 wall_top.MaxEdge = readV3F32(is);
158 wall_bottom.MinEdge = readV3F32(is);
159 wall_bottom.MaxEdge = readV3F32(is);
160 wall_side.MinEdge = readV3F32(is);
161 wall_side.MaxEdge = readV3F32(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F32(is); \
170 v3f max = readV3F32(is); \
171 (box).emplace_back(min, max); }; }
176 READBOXES(connect_top);
177 READBOXES(connect_bottom);
178 READBOXES(connect_front);
179 READBOXES(connect_left);
180 READBOXES(connect_back);
181 READBOXES(connect_right);
182 READBOXES(disconnected_top);
183 READBOXES(disconnected_bottom);
184 READBOXES(disconnected_front);
185 READBOXES(disconnected_left);
186 READBOXES(disconnected_back);
187 READBOXES(disconnected_right);
188 READBOXES(disconnected);
189 READBOXES(disconnected_sides);
197 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
198 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
199 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
200 #define TILE_FLAG_HAS_COLOR (1 << 3)
201 #define TILE_FLAG_HAS_SCALE (1 << 4)
202 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
206 // protocol_version >= 36
208 writeU8(os, version);
210 if (protocol_version > 39) {
211 os << serializeString16(name);
213 // Before f018737, TextureSource::getTextureAverageColor did not handle
214 // missing textures. "[png" can be used as base texture, but is not known
215 // on older clients. Hence use "blank.png" to avoid this problem.
216 if (!name.empty() && name[0] == '[')
217 os << serializeString16("blank.png^" + name);
219 os << serializeString16(name);
221 animation.serialize(os, version);
222 bool has_scale = scale > 0;
224 if (backface_culling)
225 flags |= TILE_FLAG_BACKFACE_CULLING;
226 if (tileable_horizontal)
227 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
228 if (tileable_vertical)
229 flags |= TILE_FLAG_TILEABLE_VERTICAL;
231 flags |= TILE_FLAG_HAS_COLOR;
233 flags |= TILE_FLAG_HAS_SCALE;
234 if (align_style != ALIGN_STYLE_NODE)
235 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
238 writeU8(os, color.getRed());
239 writeU8(os, color.getGreen());
240 writeU8(os, color.getBlue());
244 if (align_style != ALIGN_STYLE_NODE)
245 writeU8(os, align_style);
248 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
249 NodeDrawType drawtype)
251 int version = readU8(is);
253 throw SerializationError("unsupported TileDef version");
254 name = deSerializeString16(is);
255 animation.deSerialize(is, version);
256 u16 flags = readU16(is);
257 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
258 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
259 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
260 has_color = flags & TILE_FLAG_HAS_COLOR;
261 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
262 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
264 color.setRed(readU8(is));
265 color.setGreen(readU8(is));
266 color.setBlue(readU8(is));
268 scale = has_scale ? readU8(is) : 0;
270 align_style = static_cast<AlignStyle>(readU8(is));
272 align_style = ALIGN_STYLE_NODE;
275 void TextureSettings::readSettings()
277 connected_glass = g_settings->getBool("connected_glass");
278 opaque_water = g_settings->getBool("opaque_water");
279 bool smooth_lighting = g_settings->getBool("smooth_lighting");
280 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
281 enable_minimap = g_settings->getBool("enable_minimap");
282 node_texture_size = g_settings->getU16("texture_min_size");
283 std::string leaves_style_str = g_settings->get("leaves_style");
284 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
285 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
287 // Mesh cache is not supported in combination with smooth lighting
289 enable_mesh_cache = false;
291 if (leaves_style_str == "fancy") {
292 leaves_style = LEAVES_FANCY;
293 } else if (leaves_style_str == "simple") {
294 leaves_style = LEAVES_SIMPLE;
296 leaves_style = LEAVES_OPAQUE;
299 if (world_aligned_mode_str == "enable")
300 world_aligned_mode = WORLDALIGN_ENABLE;
301 else if (world_aligned_mode_str == "force_solid")
302 world_aligned_mode = WORLDALIGN_FORCE;
303 else if (world_aligned_mode_str == "force_nodebox")
304 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
306 world_aligned_mode = WORLDALIGN_DISABLE;
308 if (autoscale_mode_str == "enable")
309 autoscale_mode = AUTOSCALE_ENABLE;
310 else if (autoscale_mode_str == "force")
311 autoscale_mode = AUTOSCALE_FORCE;
313 autoscale_mode = AUTOSCALE_DISABLE;
320 ContentFeatures::ContentFeatures()
325 ContentFeatures::~ContentFeatures()
328 for (u16 j = 0; j < 6; j++) {
329 delete tiles[j].layers[0].frames;
330 delete tiles[j].layers[1].frames;
332 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
333 delete special_tiles[j].layers[0].frames;
337 void ContentFeatures::reset()
344 visual_solidness = 0;
345 backface_culling = true;
348 has_on_construct = false;
349 has_on_destruct = false;
350 has_after_destruct = false;
354 NOTE: Most of this is always overridden by the default values given
359 // Unknown nodes can be dug
360 groups["dig_immediate"] = 2;
361 drawtype = NDT_NORMAL;
364 for (auto &i : mesh_ptr)
366 minimap_color = video::SColor(0, 0, 0, 0);
369 for (auto &i : tiledef)
371 for (auto &j : tiledef_special)
373 alpha = ALPHAMODE_OPAQUE;
374 post_effect_color = video::SColor(0, 0, 0, 0);
375 param_type = CPT_NONE;
376 param_type_2 = CPT2_NONE;
377 is_ground_content = false;
378 light_propagates = false;
379 sunlight_propagates = false;
384 buildable_to = false;
386 rightclickable = true;
388 leveled_max = LEVELED_MAX;
389 liquid_type = LIQUID_NONE;
390 liquid_alternative_flowing = "";
391 liquid_alternative_flowing_id = CONTENT_IGNORE;
392 liquid_alternative_source = "";
393 liquid_alternative_source_id = CONTENT_IGNORE;
394 liquid_viscosity = 0;
395 liquid_renewable = true;
396 liquid_range = LIQUID_LEVEL_MAX+1;
399 damage_per_second = 0;
400 node_box = NodeBox();
401 selection_box = NodeBox();
402 collision_box = NodeBox();
404 legacy_facedir_simple = false;
405 legacy_wallmounted = false;
406 sound_footstep = SimpleSoundSpec();
407 sound_dig = SimpleSoundSpec("__group");
408 sound_dug = SimpleSoundSpec();
410 connects_to_ids.clear();
412 color = video::SColor(0xFFFFFFFF);
415 node_dig_prediction = "air";
417 liquid_move_physics = false;
420 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
422 // No special handling for nodebox/mesh here as it doesn't make sense to
423 // throw warnings when the server is too old to support the "correct" way
426 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
429 case NDT_FLOWINGLIQUID:
430 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
433 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
438 u8 ContentFeatures::getAlphaForLegacy() const
440 // This is so simple only because 255 and 0 mean wildly different things
441 // depending on drawtype...
442 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
445 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
447 const u8 version = CONTENTFEATURES_VERSION;
448 writeU8(os, version);
451 os << serializeString16(name);
452 writeU16(os, groups.size());
453 for (const auto &group : groups) {
454 os << serializeString16(group.first);
455 writeS16(os, group.second);
457 writeU8(os, param_type);
458 writeU8(os, param_type_2);
461 writeU8(os, drawtype);
462 os << serializeString16(mesh);
463 writeF32(os, visual_scale);
465 for (const TileDef &td : tiledef)
466 td.serialize(os, protocol_version);
467 for (const TileDef &td : tiledef_overlay)
468 td.serialize(os, protocol_version);
469 writeU8(os, CF_SPECIAL_COUNT);
470 for (const TileDef &td : tiledef_special) {
471 td.serialize(os, protocol_version);
473 writeU8(os, getAlphaForLegacy());
474 writeU8(os, color.getRed());
475 writeU8(os, color.getGreen());
476 writeU8(os, color.getBlue());
477 os << serializeString16(palette_name);
479 writeU8(os, connect_sides);
480 writeU16(os, connects_to_ids.size());
481 for (u16 connects_to_id : connects_to_ids)
482 writeU16(os, connects_to_id);
483 writeARGB8(os, post_effect_color);
484 writeU8(os, leveled);
487 writeU8(os, light_propagates);
488 writeU8(os, sunlight_propagates);
489 writeU8(os, light_source);
492 writeU8(os, is_ground_content);
495 writeU8(os, walkable);
496 writeU8(os, pointable);
497 writeU8(os, diggable);
498 writeU8(os, climbable);
499 writeU8(os, buildable_to);
500 writeU8(os, rightclickable);
501 writeU32(os, damage_per_second);
504 LiquidType liquid_type_bc = liquid_type;
505 if (protocol_version <= 39) {
506 // Since commit 7f25823, liquid drawtypes can be used even with LIQUID_NONE
507 // solution: force liquid type accordingly to accepted values
508 if (drawtype == NDT_LIQUID)
509 liquid_type_bc = LIQUID_SOURCE;
510 else if (drawtype == NDT_FLOWINGLIQUID)
511 liquid_type_bc = LIQUID_FLOWING;
513 writeU8(os, liquid_type_bc);
514 os << serializeString16(liquid_alternative_flowing);
515 os << serializeString16(liquid_alternative_source);
516 writeU8(os, liquid_viscosity);
517 writeU8(os, liquid_renewable);
518 writeU8(os, liquid_range);
519 writeU8(os, drowning);
520 writeU8(os, floodable);
523 node_box.serialize(os, protocol_version);
524 selection_box.serialize(os, protocol_version);
525 collision_box.serialize(os, protocol_version);
528 sound_footstep.serialize(os, version);
529 sound_dig.serialize(os, version);
530 sound_dug.serialize(os, version);
533 writeU8(os, legacy_facedir_simple);
534 writeU8(os, legacy_wallmounted);
537 os << serializeString16(node_dig_prediction);
538 writeU8(os, leveled_max);
540 writeU8(os, move_resistance);
541 writeU8(os, liquid_move_physics);
544 void ContentFeatures::deSerialize(std::istream &is)
547 const u8 version = readU8(is);
548 if (version < CONTENTFEATURES_VERSION)
549 throw SerializationError("unsupported ContentFeatures version");
552 name = deSerializeString16(is);
554 u32 groups_size = readU16(is);
555 for (u32 i = 0; i < groups_size; i++) {
556 std::string name = deSerializeString16(is);
557 int value = readS16(is);
558 groups[name] = value;
560 param_type = (enum ContentParamType) readU8(is);
561 param_type_2 = (enum ContentParamType2) readU8(is);
564 drawtype = (enum NodeDrawType) readU8(is);
565 mesh = deSerializeString16(is);
566 visual_scale = readF32(is);
568 throw SerializationError("unsupported tile count");
569 for (TileDef &td : tiledef)
570 td.deSerialize(is, version, drawtype);
571 for (TileDef &td : tiledef_overlay)
572 td.deSerialize(is, version, drawtype);
573 if (readU8(is) != CF_SPECIAL_COUNT)
574 throw SerializationError("unsupported CF_SPECIAL_COUNT");
575 for (TileDef &td : tiledef_special)
576 td.deSerialize(is, version, drawtype);
577 setAlphaFromLegacy(readU8(is));
578 color.setRed(readU8(is));
579 color.setGreen(readU8(is));
580 color.setBlue(readU8(is));
581 palette_name = deSerializeString16(is);
583 connect_sides = readU8(is);
584 u16 connects_to_size = readU16(is);
585 connects_to_ids.clear();
586 for (u16 i = 0; i < connects_to_size; i++)
587 connects_to_ids.push_back(readU16(is));
588 post_effect_color = readARGB8(is);
589 leveled = readU8(is);
592 light_propagates = readU8(is);
593 sunlight_propagates = readU8(is);
594 light_source = readU8(is);
595 light_source = MYMIN(light_source, LIGHT_MAX);
598 is_ground_content = readU8(is);
601 walkable = readU8(is);
602 pointable = readU8(is);
603 diggable = readU8(is);
604 climbable = readU8(is);
605 buildable_to = readU8(is);
606 rightclickable = readU8(is);
607 damage_per_second = readU32(is);
610 liquid_type = (enum LiquidType) readU8(is);
611 liquid_move_physics = liquid_type != LIQUID_NONE;
612 liquid_alternative_flowing = deSerializeString16(is);
613 liquid_alternative_source = deSerializeString16(is);
614 liquid_viscosity = readU8(is);
615 move_resistance = liquid_viscosity; // set default move_resistance
616 liquid_renewable = readU8(is);
617 liquid_range = readU8(is);
618 drowning = readU8(is);
619 floodable = readU8(is);
622 node_box.deSerialize(is);
623 selection_box.deSerialize(is);
624 collision_box.deSerialize(is);
627 sound_footstep.deSerialize(is, version);
628 sound_dig.deSerialize(is, version);
629 sound_dug.deSerialize(is, version);
631 // read legacy properties
632 legacy_facedir_simple = readU8(is);
633 legacy_wallmounted = readU8(is);
636 node_dig_prediction = deSerializeString16(is);
639 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
640 throw SerializationError("");
645 throw SerializationError("");
646 alpha = static_cast<enum AlphaMode>(tmp);
650 throw SerializationError("");
651 move_resistance = tmp;
655 throw SerializationError("");
656 liquid_move_physics = tmp;
657 } catch(SerializationError &e) {};
661 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
662 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
663 u8 material_type, u32 shader_id, bool backface_culling,
664 const TextureSettings &tsettings)
666 layer->shader_id = shader_id;
667 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
668 layer->material_type = material_type;
670 bool has_scale = tiledef.scale > 0;
671 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
672 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
674 auto texture_size = layer->texture->getOriginalSize();
675 float base_size = tsettings.node_texture_size;
676 float size = std::fmin(texture_size.Width, texture_size.Height);
677 layer->scale = std::fmax(base_size, size) / base_size;
678 } else if (has_scale) {
679 layer->scale = tiledef.scale;
683 if (!tile.world_aligned)
686 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
689 layer->material_flags = 0;
690 if (backface_culling)
691 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
692 if (tiledef.animation.type != TAT_NONE)
693 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
694 if (tiledef.tileable_horizontal)
695 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
696 if (tiledef.tileable_vertical)
697 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
700 layer->has_color = tiledef.has_color;
701 if (tiledef.has_color)
702 layer->color = tiledef.color;
704 layer->color = color;
706 // Animation parameters
708 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
710 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
711 &frame_count, &frame_length_ms, NULL);
712 layer->animation_frame_count = frame_count;
713 layer->animation_frame_length_ms = frame_length_ms;
716 if (frame_count == 1) {
717 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
719 std::ostringstream os(std::ios::binary);
720 if (!layer->frames) {
721 layer->frames = new std::vector<FrameSpec>();
723 layer->frames->resize(frame_count);
725 for (int i = 0; i < frame_count; i++) {
731 tiledef.animation.getTextureModifer(os,
732 layer->texture->getOriginalSize(), i);
734 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
735 if (layer->normal_texture)
736 frame.normal_texture = tsrc->getNormalTexture(os.str());
737 frame.flags_texture = layer->flags_texture;
738 (*layer->frames)[i] = frame;
743 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
745 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
746 static thread_local bool long_warning_printed = false;
747 std::set<std::string> seen;
749 for (int i = 0; i < length; i++) {
750 if (seen.find(tiles[i].name) != seen.end())
752 seen.insert(tiles[i].name);
754 // Load the texture and see if there's any transparent pixels
755 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
756 video::IImage *image = driver->createImage(texture,
757 core::position2d<s32>(0, 0), texture->getOriginalSize());
760 core::dimension2d<u32> dim = image->getDimension();
762 for (u16 x = 0; x < dim.Width; x++) {
763 for (u16 y = 0; y < dim.Height; y++) {
764 if (image->getPixel(x, y).getAlpha() < 255) {
775 warningstream << "Texture \"" << tiles[i].name << "\" of "
776 << name << " has transparency, assuming "
777 "use_texture_alpha = \"clip\"." << std::endl;
778 if (!long_warning_printed) {
779 warningstream << " This warning can be a false-positive if "
780 "unused pixels in the texture are transparent. However if "
781 "it is meant to be transparent, you *MUST* update the "
782 "nodedef and set use_texture_alpha = \"clip\"! This "
783 "compatibility code will be removed in a few releases."
785 long_warning_printed = true;
792 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
794 if (style == ALIGN_STYLE_WORLD)
796 if (mode == WORLDALIGN_DISABLE)
798 if (style == ALIGN_STYLE_USER_DEFINED)
800 if (drawtype == NDT_NORMAL)
801 return mode >= WORLDALIGN_FORCE;
802 if (drawtype == NDT_NODEBOX)
803 return mode >= WORLDALIGN_FORCE_NODEBOX;
807 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
808 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
810 // minimap pixel color - the average color of a texture
811 if (tsettings.enable_minimap && !tiledef[0].name.empty())
812 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
814 // Figure out the actual tiles to use
816 for (u32 j = 0; j < 6; j++) {
817 tdef[j] = tiledef[j];
818 if (tdef[j].name.empty()) {
819 tdef[j].name = "no_texture.png";
820 tdef[j].backface_culling = false;
823 // also the overlay tiles
824 TileDef tdef_overlay[6];
825 for (u32 j = 0; j < 6; j++)
826 tdef_overlay[j] = tiledef_overlay[j];
827 // also the special tiles
828 TileDef tdef_spec[6];
829 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
830 tdef_spec[j] = tiledef_special[j];
833 bool is_liquid = false;
835 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
836 // Before working with the alpha mode, resolve any legacy kludges
837 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
840 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
841 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
842 TILE_MATERIAL_ALPHA);
853 if (tsettings.opaque_water)
854 alpha = ALPHAMODE_OPAQUE;
858 case NDT_FLOWINGLIQUID:
860 if (tsettings.opaque_water)
861 alpha = ALPHAMODE_OPAQUE;
866 visual_solidness = 1;
868 case NDT_GLASSLIKE_FRAMED:
870 visual_solidness = 1;
872 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
874 visual_solidness = 1;
875 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
879 visual_solidness = 1;
881 case NDT_ALLFACES_OPTIONAL:
882 if (tsettings.leaves_style == LEAVES_FANCY) {
883 drawtype = NDT_ALLFACES;
885 visual_solidness = 1;
886 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
887 for (u32 j = 0; j < 6; j++) {
888 if (!tdef_spec[j].name.empty())
889 tdef[j].name = tdef_spec[j].name;
891 drawtype = NDT_GLASSLIKE;
893 visual_solidness = 1;
895 drawtype = NDT_NORMAL;
897 for (TileDef &td : tdef)
898 td.name += std::string("^[noalpha");
901 material_type = TILE_MATERIAL_WAVING_LEAVES;
906 material_type = TILE_MATERIAL_WAVING_PLANTS;
915 material_type = TILE_MATERIAL_WAVING_PLANTS;
916 } else if (waving == 2) {
917 material_type = TILE_MATERIAL_WAVING_LEAVES;
918 } else if (waving == 3) {
919 material_type = alpha == ALPHAMODE_OPAQUE ?
920 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
921 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
930 case NDT_PLANTLIKE_ROOTED:
937 material_type = alpha == ALPHAMODE_OPAQUE ?
938 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
939 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
941 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
942 TILE_MATERIAL_LIQUID_TRANSPARENT;
946 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
948 MaterialType overlay_material = material_type;
949 if (overlay_material == TILE_MATERIAL_OPAQUE)
950 overlay_material = TILE_MATERIAL_BASIC;
951 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
952 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
954 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
956 // Tiles (fill in f->tiles[])
957 for (u16 j = 0; j < 6; j++) {
958 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
959 tsettings.world_aligned_mode, drawtype);
960 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
961 color, material_type, tile_shader,
962 tdef[j].backface_culling, tsettings);
963 if (!tdef_overlay[j].name.empty())
964 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
965 color, overlay_material, overlay_shader,
966 tdef[j].backface_culling, tsettings);
969 MaterialType special_material = material_type;
970 if (drawtype == NDT_PLANTLIKE_ROOTED) {
972 special_material = TILE_MATERIAL_WAVING_PLANTS;
973 else if (waving == 2)
974 special_material = TILE_MATERIAL_WAVING_LEAVES;
976 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
978 // Special tiles (fill in f->special_tiles[])
979 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
980 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
981 color, special_material, special_shader,
982 tdef_spec[j].backface_culling, tsettings);
984 if (param_type_2 == CPT2_COLOR ||
985 param_type_2 == CPT2_COLORED_FACEDIR ||
986 param_type_2 == CPT2_COLORED_WALLMOUNTED ||
987 param_type_2 == CPT2_COLORED_DEGROTATE)
988 palette = tsrc->getPalette(palette_name);
990 if (drawtype == NDT_MESH && !mesh.empty()) {
992 // Read the mesh and apply scale
993 mesh_ptr[0] = client->getMesh(mesh);
995 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
996 scaleMesh(mesh_ptr[0], scale);
997 recalculateBoundingBox(mesh_ptr[0]);
998 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1002 //Cache 6dfacedir and wallmounted rotated clones of meshes
1003 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
1004 (param_type_2 == CPT2_FACEDIR
1005 || param_type_2 == CPT2_COLORED_FACEDIR)) {
1006 for (u16 j = 1; j < 24; j++) {
1007 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1008 rotateMeshBy6dFacedir(mesh_ptr[j], j);
1009 recalculateBoundingBox(mesh_ptr[j]);
1010 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1012 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
1013 && (param_type_2 == CPT2_WALLMOUNTED ||
1014 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
1015 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
1016 for (u16 j = 1; j < 6; j++) {
1017 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
1018 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
1019 recalculateBoundingBox(mesh_ptr[j]);
1020 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
1022 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
1023 recalculateBoundingBox(mesh_ptr[0]);
1024 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
1036 NodeDefManager::NodeDefManager()
1042 NodeDefManager::~NodeDefManager()
1045 for (ContentFeatures &f : m_content_features) {
1046 for (auto &j : f.mesh_ptr) {
1055 void NodeDefManager::clear()
1057 m_content_features.clear();
1058 m_name_id_mapping.clear();
1059 m_name_id_mapping_with_aliases.clear();
1060 m_group_to_items.clear();
1062 m_selection_box_union.reset(0,0,0);
1063 m_selection_box_int_union.reset(0,0,0);
1065 resetNodeResolveState();
1067 u32 initial_length = 0;
1068 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1069 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1070 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1071 m_content_features.resize(initial_length);
1073 // Set CONTENT_UNKNOWN
1077 TileDef unknownTile;
1078 unknownTile.name = "unknown_node.png";
1079 for (int t = 0; t < 6; t++)
1080 f.tiledef[t] = unknownTile;
1081 // Insert directly into containers
1082 content_t c = CONTENT_UNKNOWN;
1083 m_content_features[c] = f;
1084 addNameIdMapping(c, f.name);
1091 f.drawtype = NDT_AIRLIKE;
1092 f.param_type = CPT_LIGHT;
1093 f.light_propagates = true;
1094 f.sunlight_propagates = true;
1096 f.pointable = false;
1098 f.buildable_to = true;
1100 f.is_ground_content = true;
1101 // Insert directly into containers
1102 content_t c = CONTENT_AIR;
1103 m_content_features[c] = f;
1104 addNameIdMapping(c, f.name);
1107 // Set CONTENT_IGNORE
1111 f.drawtype = NDT_AIRLIKE;
1112 f.param_type = CPT_NONE;
1113 f.light_propagates = false;
1114 f.sunlight_propagates = false;
1116 f.pointable = false;
1118 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1119 f.is_ground_content = true;
1120 // Insert directly into containers
1121 content_t c = CONTENT_IGNORE;
1122 m_content_features[c] = f;
1123 addNameIdMapping(c, f.name);
1128 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1130 std::unordered_map<std::string, content_t>::const_iterator
1131 i = m_name_id_mapping_with_aliases.find(name);
1132 if(i == m_name_id_mapping_with_aliases.end())
1139 content_t NodeDefManager::getId(const std::string &name) const
1141 content_t id = CONTENT_IGNORE;
1147 bool NodeDefManager::getIds(const std::string &name,
1148 std::vector<content_t> &result) const
1150 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1151 if (name.substr(0,6) != "group:") {
1152 content_t id = CONTENT_IGNORE;
1153 bool exists = getId(name, id);
1155 result.push_back(id);
1158 std::string group = name.substr(6);
1160 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1161 i = m_group_to_items.find(group);
1162 if (i == m_group_to_items.end())
1165 const std::vector<content_t> &items = i->second;
1166 result.insert(result.end(), items.begin(), items.end());
1167 //printf("getIds: %dus\n", t.stop());
1172 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1174 content_t id = CONTENT_UNKNOWN;
1180 // returns CONTENT_IGNORE if no free ID found
1181 content_t NodeDefManager::allocateId()
1183 for (content_t id = m_next_id;
1184 id >= m_next_id; // overflow?
1186 while (id >= m_content_features.size()) {
1187 m_content_features.emplace_back();
1189 const ContentFeatures &f = m_content_features[id];
1190 if (f.name.empty()) {
1195 // If we arrive here, an overflow occurred in id.
1196 // That means no ID was found
1197 return CONTENT_IGNORE;
1202 * Returns the smallest box that contains all boxes
1203 * in the vector. Box_union is expanded.
1204 * @param[in] boxes the vector containing the boxes
1205 * @param[in, out] box_union the union of the arguments
1207 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1209 for (const aabb3f &box : boxes) {
1210 box_union->addInternalBox(box);
1216 * Returns a box that contains the nodebox in every case.
1217 * The argument node_union is expanded.
1218 * @param[in] nodebox the nodebox to be measured
1219 * @param[in] features used to decide whether the nodebox
1221 * @param[in, out] box_union the union of the arguments
1223 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1226 switch(nodebox.type) {
1228 case NODEBOX_LEVELED: {
1230 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1231 boxVectorUnion(nodebox.fixed, &half_processed);
1232 // Set leveled boxes to maximal
1233 if (nodebox.type == NODEBOX_LEVELED) {
1234 half_processed.MaxEdge.Y = +BS / 2;
1236 if (features.param_type_2 == CPT2_FACEDIR ||
1237 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1238 // Get maximal coordinate
1240 fabsf(half_processed.MinEdge.X),
1241 fabsf(half_processed.MinEdge.Y),
1242 fabsf(half_processed.MinEdge.Z),
1243 fabsf(half_processed.MaxEdge.X),
1244 fabsf(half_processed.MaxEdge.Y),
1245 fabsf(half_processed.MaxEdge.Z) };
1247 for (float coord : coords) {
1252 // Add the union of all possible rotated boxes
1253 box_union->addInternalPoint(-max, -max, -max);
1254 box_union->addInternalPoint(+max, +max, +max);
1256 box_union->addInternalBox(half_processed);
1260 case NODEBOX_WALLMOUNTED: {
1262 box_union->addInternalBox(nodebox.wall_top);
1263 box_union->addInternalBox(nodebox.wall_bottom);
1264 // Find maximal coordinate in the X-Z plane
1266 fabsf(nodebox.wall_side.MinEdge.X),
1267 fabsf(nodebox.wall_side.MinEdge.Z),
1268 fabsf(nodebox.wall_side.MaxEdge.X),
1269 fabsf(nodebox.wall_side.MaxEdge.Z) };
1271 for (float coord : coords) {
1276 // Add the union of all possible rotated boxes
1277 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1278 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1281 case NODEBOX_CONNECTED: {
1282 // Add all possible connected boxes
1283 boxVectorUnion(nodebox.fixed, box_union);
1284 boxVectorUnion(nodebox.connect_top, box_union);
1285 boxVectorUnion(nodebox.connect_bottom, box_union);
1286 boxVectorUnion(nodebox.connect_front, box_union);
1287 boxVectorUnion(nodebox.connect_left, box_union);
1288 boxVectorUnion(nodebox.connect_back, box_union);
1289 boxVectorUnion(nodebox.connect_right, box_union);
1290 boxVectorUnion(nodebox.disconnected_top, box_union);
1291 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1292 boxVectorUnion(nodebox.disconnected_front, box_union);
1293 boxVectorUnion(nodebox.disconnected_left, box_union);
1294 boxVectorUnion(nodebox.disconnected_back, box_union);
1295 boxVectorUnion(nodebox.disconnected_right, box_union);
1296 boxVectorUnion(nodebox.disconnected, box_union);
1297 boxVectorUnion(nodebox.disconnected_sides, box_union);
1302 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1303 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1309 inline void NodeDefManager::fixSelectionBoxIntUnion()
1311 m_selection_box_int_union.MinEdge.X = floorf(
1312 m_selection_box_union.MinEdge.X / BS + 0.5f);
1313 m_selection_box_int_union.MinEdge.Y = floorf(
1314 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1315 m_selection_box_int_union.MinEdge.Z = floorf(
1316 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1317 m_selection_box_int_union.MaxEdge.X = ceilf(
1318 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1319 m_selection_box_int_union.MaxEdge.Y = ceilf(
1320 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1321 m_selection_box_int_union.MaxEdge.Z = ceilf(
1322 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1326 void NodeDefManager::eraseIdFromGroups(content_t id)
1328 // For all groups in m_group_to_items...
1329 for (auto iter_groups = m_group_to_items.begin();
1330 iter_groups != m_group_to_items.end();) {
1331 // Get the group items vector.
1332 std::vector<content_t> &items = iter_groups->second;
1334 // Remove any occurence of the id in the group items vector.
1335 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1337 // If group is empty, erase its vector from the map.
1339 iter_groups = m_group_to_items.erase(iter_groups);
1346 // IWritableNodeDefManager
1347 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1351 assert(name != "ignore");
1352 assert(name == def.name);
1354 content_t id = CONTENT_IGNORE;
1355 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1358 if (id == CONTENT_IGNORE) {
1359 warningstream << "NodeDefManager: Absolute "
1360 "limit reached" << std::endl;
1361 return CONTENT_IGNORE;
1363 assert(id != CONTENT_IGNORE);
1364 addNameIdMapping(id, name);
1367 // If there is already ContentFeatures registered for this id, clear old groups
1368 if (id < m_content_features.size())
1369 eraseIdFromGroups(id);
1371 m_content_features[id] = def;
1372 verbosestream << "NodeDefManager: registering content id \"" << id
1373 << "\": name=\"" << def.name << "\""<<std::endl;
1375 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1376 fixSelectionBoxIntUnion();
1378 // Add this content to the list of all groups it belongs to
1379 for (const auto &group : def.groups) {
1380 const std::string &group_name = group.first;
1381 m_group_to_items[group_name].push_back(id);
1388 content_t NodeDefManager::allocateDummy(const std::string &name)
1390 assert(name != ""); // Pre-condition
1393 return set(name, f);
1397 void NodeDefManager::removeNode(const std::string &name)
1402 // Erase name from name ID mapping
1403 content_t id = CONTENT_IGNORE;
1404 if (m_name_id_mapping.getId(name, id)) {
1405 m_name_id_mapping.eraseName(name);
1406 m_name_id_mapping_with_aliases.erase(name);
1409 eraseIdFromGroups(id);
1413 void NodeDefManager::updateAliases(IItemDefManager *idef)
1415 std::set<std::string> all;
1417 m_name_id_mapping_with_aliases.clear();
1418 for (const std::string &name : all) {
1419 const std::string &convert_to = idef->getAlias(name);
1421 if (m_name_id_mapping.getId(convert_to, id)) {
1422 m_name_id_mapping_with_aliases.insert(
1423 std::make_pair(name, id));
1428 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1430 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1431 "overrides to textures" << std::endl;
1433 for (const TextureOverride& texture_override : overrides) {
1435 if (!getId(texture_override.id, id))
1436 continue; // Ignore unknown node
1438 ContentFeatures &nodedef = m_content_features[id];
1441 if (texture_override.hasTarget(OverrideTarget::TOP))
1442 nodedef.tiledef[0].name = texture_override.texture;
1444 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1445 nodedef.tiledef[1].name = texture_override.texture;
1447 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1448 nodedef.tiledef[2].name = texture_override.texture;
1450 if (texture_override.hasTarget(OverrideTarget::LEFT))
1451 nodedef.tiledef[3].name = texture_override.texture;
1453 if (texture_override.hasTarget(OverrideTarget::BACK))
1454 nodedef.tiledef[4].name = texture_override.texture;
1456 if (texture_override.hasTarget(OverrideTarget::FRONT))
1457 nodedef.tiledef[5].name = texture_override.texture;
1460 // Override special tiles, if applicable
1461 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1462 nodedef.tiledef_special[0].name = texture_override.texture;
1464 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1465 nodedef.tiledef_special[1].name = texture_override.texture;
1467 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1468 nodedef.tiledef_special[2].name = texture_override.texture;
1470 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1471 nodedef.tiledef_special[3].name = texture_override.texture;
1473 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1474 nodedef.tiledef_special[4].name = texture_override.texture;
1476 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1477 nodedef.tiledef_special[5].name = texture_override.texture;
1481 void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
1484 infostream << "NodeDefManager::updateTextures(): Updating "
1485 "textures in node definitions" << std::endl;
1487 Client *client = (Client *)gamedef;
1488 ITextureSource *tsrc = client->tsrc();
1489 IShaderSource *shdsrc = client->getShaderSource();
1490 auto smgr = client->getSceneManager();
1491 scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
1492 TextureSettings tsettings;
1493 tsettings.readSettings();
1495 u32 size = m_content_features.size();
1497 for (u32 i = 0; i < size; i++) {
1498 ContentFeatures *f = &(m_content_features[i]);
1499 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1500 client->showUpdateProgressTexture(progress_callback_args, i, size);
1505 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1507 writeU8(os, 1); // version
1509 std::ostringstream os2(std::ios::binary);
1510 for (u32 i = 0; i < m_content_features.size(); i++) {
1511 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1512 || i == CONTENT_UNKNOWN)
1514 const ContentFeatures *f = &m_content_features[i];
1515 if (f->name.empty())
1518 // Wrap it in a string to allow different lengths without
1519 // strict version incompatibilities
1520 std::ostringstream wrapper_os(std::ios::binary);
1521 f->serialize(wrapper_os, protocol_version);
1522 os2<<serializeString16(wrapper_os.str());
1524 // must not overflow
1525 u16 next = count + 1;
1526 FATAL_ERROR_IF(next < count, "Overflow");
1529 writeU16(os, count);
1530 os << serializeString32(os2.str());
1534 void NodeDefManager::deSerialize(std::istream &is)
1537 int version = readU8(is);
1539 throw SerializationError("unsupported NodeDefinitionManager version");
1540 u16 count = readU16(is);
1541 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1543 for (u16 n = 0; n < count; n++) {
1544 u16 i = readU16(is2);
1546 // Read it from the string wrapper
1547 std::string wrapper = deSerializeString16(is2);
1548 std::istringstream wrapper_is(wrapper, std::ios::binary);
1549 f.deSerialize(wrapper_is);
1551 // Check error conditions
1552 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1553 warningstream << "NodeDefManager::deSerialize(): "
1554 "not changing builtin node " << i << std::endl;
1557 if (f.name.empty()) {
1558 warningstream << "NodeDefManager::deSerialize(): "
1559 "received empty name" << std::endl;
1565 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1566 warningstream << "NodeDefManager::deSerialize(): "
1567 "already defined with different ID: " << f.name << std::endl;
1571 // All is ok, add node definition with the requested ID
1572 if (i >= m_content_features.size())
1573 m_content_features.resize((u32)(i) + 1);
1574 m_content_features[i] = f;
1575 addNameIdMapping(i, f.name);
1576 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1578 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1579 fixSelectionBoxIntUnion();
1582 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1583 // are not sent, resolve them client-side too.
1588 void NodeDefManager::addNameIdMapping(content_t i, const std::string &name)
1590 m_name_id_mapping.set(i, name);
1591 m_name_id_mapping_with_aliases.emplace(name, i);
1595 NodeDefManager *createNodeDefManager()
1597 return new NodeDefManager();
1601 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1604 if (m_node_registration_complete)
1605 nr->nodeResolveInternal();
1607 m_pending_resolve_callbacks.push_back(nr);
1611 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1613 size_t len = m_pending_resolve_callbacks.size();
1614 for (size_t i = 0; i != len; i++) {
1615 if (nr != m_pending_resolve_callbacks[i])
1619 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1620 m_pending_resolve_callbacks.resize(len);
1628 void NodeDefManager::runNodeResolveCallbacks()
1630 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1631 NodeResolver *nr = m_pending_resolve_callbacks[i];
1632 nr->nodeResolveInternal();
1635 m_pending_resolve_callbacks.clear();
1639 void NodeDefManager::resetNodeResolveState()
1641 m_node_registration_complete = false;
1642 m_pending_resolve_callbacks.clear();
1645 static void removeDupes(std::vector<content_t> &list)
1647 std::sort(list.begin(), list.end());
1648 auto new_end = std::unique(list.begin(), list.end());
1649 list.erase(new_end, list.end());
1652 void NodeDefManager::resolveCrossrefs()
1654 for (ContentFeatures &f : m_content_features) {
1655 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1656 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1657 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1660 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1663 for (const std::string &name : f.connects_to) {
1664 getIds(name, f.connects_to_ids);
1666 removeDupes(f.connects_to_ids);
1670 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1671 u8 connect_face) const
1673 const ContentFeatures &f1 = get(from);
1675 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1678 // lookup target in connected set
1679 if (!CONTAINS(f1.connects_to_ids, to.param0))
1682 const ContentFeatures &f2 = get(to);
1684 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1685 // ignores actually looking if back connection exists
1686 return CONTAINS(f2.connects_to_ids, from.param0);
1688 // does to node declare usable faces?
1689 if (f2.connect_sides > 0) {
1690 if ((f2.param_type_2 == CPT2_FACEDIR ||
1691 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1692 && (connect_face >= 4)) {
1693 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1694 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1696 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1698 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1699 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1701 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1702 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1703 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1705 return (f2.connect_sides
1706 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1708 return (f2.connect_sides & connect_face);
1710 // the target is just a regular node, so connect no matter back connection
1718 NodeResolver::NodeResolver()
1724 NodeResolver::~NodeResolver()
1726 if (!m_resolve_done && m_ndef)
1727 m_ndef->cancelNodeResolveCallback(this);
1731 void NodeResolver::cloneTo(NodeResolver *res) const
1733 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1734 " after resolving has completed");
1735 /* We don't actually do anything significant. Since the node resolving has
1736 * already completed, the class that called us will already have the
1737 * resolved IDs in its data structures (which it copies on its own) */
1738 res->m_ndef = m_ndef;
1739 res->m_resolve_done = true;
1743 void NodeResolver::nodeResolveInternal()
1745 m_nodenames_idx = 0;
1746 m_nnlistsizes_idx = 0;
1749 m_resolve_done = true;
1751 m_nodenames.clear();
1752 m_nnlistsizes.clear();
1756 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1757 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1759 if (m_nodenames_idx == m_nodenames.size()) {
1760 *result_out = c_fallback;
1761 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1766 std::string name = m_nodenames[m_nodenames_idx++];
1768 bool success = m_ndef->getId(name, c);
1769 if (!success && !node_alt.empty()) {
1771 success = m_ndef->getId(name, c);
1775 if (error_on_fallback)
1776 errorstream << "NodeResolver: failed to resolve node name '" << name
1777 << "'." << std::endl;
1786 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1787 bool all_required, content_t c_fallback)
1789 bool success = true;
1791 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1792 errorstream << "NodeResolver: no more node lists" << std::endl;
1796 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1799 if (m_nodenames_idx == m_nodenames.size()) {
1800 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1805 std::string &name = m_nodenames[m_nodenames_idx++];
1807 if (name.substr(0,6) != "group:") {
1808 if (m_ndef->getId(name, c)) {
1809 result_out->push_back(c);
1810 } else if (all_required) {
1811 errorstream << "NodeResolver: failed to resolve node name '"
1812 << name << "'." << std::endl;
1813 result_out->push_back(c_fallback);
1817 m_ndef->getIds(name, *result_out);
1824 void NodeResolver::reset(bool resolve_done)
1826 m_nodenames.clear();
1827 m_nodenames_idx = 0;
1828 m_nnlistsizes.clear();
1829 m_nnlistsizes_idx = 0;
1831 m_resolve_done = resolve_done;
1833 m_nodenames.reserve(16);
1834 m_nnlistsizes.reserve(4);