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.
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 writeV3F1000(os, nodebox.MinEdge);
88 writeV3F1000(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F1000(os, wall_top.MinEdge);
95 writeV3F1000(os, wall_top.MaxEdge);
96 writeV3F1000(os, wall_bottom.MinEdge);
97 writeV3F1000(os, wall_bottom.MaxEdge);
98 writeV3F1000(os, wall_side.MinEdge);
99 writeV3F1000(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F1000(os, i.MinEdge); \
108 writeV3F1000(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 = readV3F1000(is);
150 box.MaxEdge = readV3F1000(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F1000(is);
157 wall_top.MaxEdge = readV3F1000(is);
158 wall_bottom.MinEdge = readV3F1000(is);
159 wall_bottom.MaxEdge = readV3F1000(is);
160 wall_side.MinEdge = readV3F1000(is);
161 wall_side.MaxEdge = readV3F1000(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F1000(is); \
170 v3f max = readV3F1000(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);
183 READBOXES(disconnected_top);
184 READBOXES(disconnected_bottom);
185 READBOXES(disconnected_front);
186 READBOXES(disconnected_left);
187 READBOXES(disconnected_back);
188 READBOXES(disconnected_right);
189 READBOXES(disconnected);
190 READBOXES(disconnected_sides);
199 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
200 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
201 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
202 #define TILE_FLAG_HAS_COLOR (1 << 3)
203 #define TILE_FLAG_HAS_SCALE (1 << 4)
204 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
206 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
208 // protocol_version >= 36
210 writeU8(os, version);
212 os << serializeString(name);
213 animation.serialize(os, version);
214 bool has_scale = scale > 0;
216 if (backface_culling)
217 flags |= TILE_FLAG_BACKFACE_CULLING;
218 if (tileable_horizontal)
219 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
220 if (tileable_vertical)
221 flags |= TILE_FLAG_TILEABLE_VERTICAL;
223 flags |= TILE_FLAG_HAS_COLOR;
225 flags |= TILE_FLAG_HAS_SCALE;
226 if (align_style != ALIGN_STYLE_NODE)
227 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
230 writeU8(os, color.getRed());
231 writeU8(os, color.getGreen());
232 writeU8(os, color.getBlue());
236 if (align_style != ALIGN_STYLE_NODE)
237 writeU8(os, align_style);
240 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
241 NodeDrawType drawtype)
243 int version = readU8(is);
245 throw SerializationError("unsupported TileDef version");
246 name = deSerializeString(is);
247 animation.deSerialize(is, version);
248 u16 flags = readU16(is);
249 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
250 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
251 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
252 has_color = flags & TILE_FLAG_HAS_COLOR;
253 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
254 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
256 color.setRed(readU8(is));
257 color.setGreen(readU8(is));
258 color.setBlue(readU8(is));
260 scale = has_scale ? readU8(is) : 0;
262 align_style = static_cast<AlignStyle>(readU8(is));
264 align_style = ALIGN_STYLE_NODE;
269 SimpleSoundSpec serialization
272 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
273 std::ostream &os, u8 version)
275 os<<serializeString(ss.name);
276 writeF1000(os, ss.gain);
277 writeF1000(os, ss.pitch);
279 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
280 std::istream &is, u8 version)
282 ss.name = deSerializeString(is);
283 ss.gain = readF1000(is);
284 ss.pitch = readF1000(is);
287 void TextureSettings::readSettings()
289 connected_glass = g_settings->getBool("connected_glass");
290 opaque_water = g_settings->getBool("opaque_water");
291 bool enable_shaders = g_settings->getBool("enable_shaders");
292 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
293 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
294 bool smooth_lighting = g_settings->getBool("smooth_lighting");
295 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
296 enable_minimap = g_settings->getBool("enable_minimap");
297 node_texture_size = g_settings->getU16("texture_min_size");
298 std::string leaves_style_str = g_settings->get("leaves_style");
299 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
300 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
302 // Mesh cache is not supported in combination with smooth lighting
304 enable_mesh_cache = false;
306 use_normal_texture = enable_shaders &&
307 (enable_bumpmapping || enable_parallax_occlusion);
308 if (leaves_style_str == "fancy") {
309 leaves_style = LEAVES_FANCY;
310 } else if (leaves_style_str == "simple") {
311 leaves_style = LEAVES_SIMPLE;
313 leaves_style = LEAVES_OPAQUE;
316 if (world_aligned_mode_str == "enable")
317 world_aligned_mode = WORLDALIGN_ENABLE;
318 else if (world_aligned_mode_str == "force_solid")
319 world_aligned_mode = WORLDALIGN_FORCE;
320 else if (world_aligned_mode_str == "force_nodebox")
321 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
323 world_aligned_mode = WORLDALIGN_DISABLE;
325 if (autoscale_mode_str == "enable")
326 autoscale_mode = AUTOSCALE_ENABLE;
327 else if (autoscale_mode_str == "force")
328 autoscale_mode = AUTOSCALE_FORCE;
330 autoscale_mode = AUTOSCALE_DISABLE;
337 ContentFeatures::ContentFeatures()
342 void ContentFeatures::reset()
349 visual_solidness = 0;
350 backface_culling = true;
353 has_on_construct = false;
354 has_on_destruct = false;
355 has_after_destruct = false;
359 NOTE: Most of this is always overridden by the default values given
364 // Unknown nodes can be dug
365 groups["dig_immediate"] = 2;
366 drawtype = NDT_NORMAL;
369 for (auto &i : mesh_ptr)
371 minimap_color = video::SColor(0, 0, 0, 0);
374 for (auto &i : tiledef)
376 for (auto &j : tiledef_special)
379 post_effect_color = video::SColor(0, 0, 0, 0);
380 param_type = CPT_NONE;
381 param_type_2 = CPT2_NONE;
382 is_ground_content = false;
383 light_propagates = false;
384 sunlight_propagates = false;
389 buildable_to = false;
391 rightclickable = true;
393 liquid_type = LIQUID_NONE;
394 liquid_alternative_flowing = "";
395 liquid_alternative_source = "";
396 liquid_viscosity = 0;
397 liquid_renewable = true;
398 liquid_range = LIQUID_LEVEL_MAX+1;
401 damage_per_second = 0;
402 node_box = NodeBox();
403 selection_box = NodeBox();
404 collision_box = NodeBox();
406 legacy_facedir_simple = false;
407 legacy_wallmounted = false;
408 sound_footstep = SimpleSoundSpec();
409 sound_dig = SimpleSoundSpec("__group");
410 sound_dug = SimpleSoundSpec();
412 connects_to_ids.clear();
414 color = video::SColor(0xFFFFFFFF);
417 node_dig_prediction = "air";
420 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
422 // protocol_version >= 36
424 writeU8(os, version);
427 os << serializeString(name);
428 writeU16(os, groups.size());
429 for (const auto &group : groups) {
430 os << serializeString(group.first);
431 writeS16(os, group.second);
433 writeU8(os, param_type);
434 writeU8(os, param_type_2);
437 writeU8(os, drawtype);
438 os << serializeString(mesh);
439 writeF1000(os, visual_scale);
441 for (const TileDef &td : tiledef)
442 td.serialize(os, protocol_version);
443 for (const TileDef &td : tiledef_overlay)
444 td.serialize(os, protocol_version);
445 writeU8(os, CF_SPECIAL_COUNT);
446 for (const TileDef &td : tiledef_special) {
447 td.serialize(os, protocol_version);
450 writeU8(os, color.getRed());
451 writeU8(os, color.getGreen());
452 writeU8(os, color.getBlue());
453 os << serializeString(palette_name);
455 writeU8(os, connect_sides);
456 writeU16(os, connects_to_ids.size());
457 for (u16 connects_to_id : connects_to_ids)
458 writeU16(os, connects_to_id);
459 writeU8(os, post_effect_color.getAlpha());
460 writeU8(os, post_effect_color.getRed());
461 writeU8(os, post_effect_color.getGreen());
462 writeU8(os, post_effect_color.getBlue());
463 writeU8(os, leveled);
466 writeU8(os, light_propagates);
467 writeU8(os, sunlight_propagates);
468 writeU8(os, light_source);
471 writeU8(os, is_ground_content);
474 writeU8(os, walkable);
475 writeU8(os, pointable);
476 writeU8(os, diggable);
477 writeU8(os, climbable);
478 writeU8(os, buildable_to);
479 writeU8(os, rightclickable);
480 writeU32(os, damage_per_second);
483 writeU8(os, liquid_type);
484 os << serializeString(liquid_alternative_flowing);
485 os << serializeString(liquid_alternative_source);
486 writeU8(os, liquid_viscosity);
487 writeU8(os, liquid_renewable);
488 writeU8(os, liquid_range);
489 writeU8(os, drowning);
490 writeU8(os, floodable);
493 node_box.serialize(os, protocol_version);
494 selection_box.serialize(os, protocol_version);
495 collision_box.serialize(os, protocol_version);
498 serializeSimpleSoundSpec(sound_footstep, os, version);
499 serializeSimpleSoundSpec(sound_dig, os, version);
500 serializeSimpleSoundSpec(sound_dug, os, version);
503 writeU8(os, legacy_facedir_simple);
504 writeU8(os, legacy_wallmounted);
506 os << serializeString(node_dig_prediction);
509 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
511 // alpha == 0 means that the node is using texture alpha
512 if (alpha == 0 || alpha == 255)
515 for (int i = 0; i < length; i++) {
516 if (tiles[i].name.empty())
519 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
520 tiles[i].name = s.str();
524 void ContentFeatures::deSerialize(std::istream &is)
527 int version = readU8(is);
529 throw SerializationError("unsupported ContentFeatures version");
532 name = deSerializeString(is);
534 u32 groups_size = readU16(is);
535 for (u32 i = 0; i < groups_size; i++) {
536 std::string name = deSerializeString(is);
537 int value = readS16(is);
538 groups[name] = value;
540 param_type = (enum ContentParamType) readU8(is);
541 param_type_2 = (enum ContentParamType2) readU8(is);
544 drawtype = (enum NodeDrawType) readU8(is);
545 mesh = deSerializeString(is);
546 visual_scale = readF1000(is);
548 throw SerializationError("unsupported tile count");
549 for (TileDef &td : tiledef)
550 td.deSerialize(is, version, drawtype);
551 for (TileDef &td : tiledef_overlay)
552 td.deSerialize(is, version, drawtype);
553 if (readU8(is) != CF_SPECIAL_COUNT)
554 throw SerializationError("unsupported CF_SPECIAL_COUNT");
555 for (TileDef &td : tiledef_special)
556 td.deSerialize(is, version, drawtype);
558 color.setRed(readU8(is));
559 color.setGreen(readU8(is));
560 color.setBlue(readU8(is));
561 palette_name = deSerializeString(is);
563 connect_sides = readU8(is);
564 u16 connects_to_size = readU16(is);
565 connects_to_ids.clear();
566 for (u16 i = 0; i < connects_to_size; i++)
567 connects_to_ids.push_back(readU16(is));
568 post_effect_color.setAlpha(readU8(is));
569 post_effect_color.setRed(readU8(is));
570 post_effect_color.setGreen(readU8(is));
571 post_effect_color.setBlue(readU8(is));
572 leveled = readU8(is);
575 light_propagates = readU8(is);
576 sunlight_propagates = readU8(is);
577 light_source = readU8(is);
578 light_source = MYMIN(light_source, LIGHT_MAX);
581 is_ground_content = readU8(is);
584 walkable = readU8(is);
585 pointable = readU8(is);
586 diggable = readU8(is);
587 climbable = readU8(is);
588 buildable_to = readU8(is);
589 rightclickable = readU8(is);
590 damage_per_second = readU32(is);
593 liquid_type = (enum LiquidType) readU8(is);
594 liquid_alternative_flowing = deSerializeString(is);
595 liquid_alternative_source = deSerializeString(is);
596 liquid_viscosity = readU8(is);
597 liquid_renewable = readU8(is);
598 liquid_range = readU8(is);
599 drowning = readU8(is);
600 floodable = readU8(is);
603 node_box.deSerialize(is);
604 selection_box.deSerialize(is);
605 collision_box.deSerialize(is);
608 deSerializeSimpleSoundSpec(sound_footstep, is, version);
609 deSerializeSimpleSoundSpec(sound_dig, is, version);
610 deSerializeSimpleSoundSpec(sound_dug, is, version);
612 // read legacy properties
613 legacy_facedir_simple = readU8(is);
614 legacy_wallmounted = readU8(is);
617 node_dig_prediction = deSerializeString(is);
618 } catch(SerializationError &e) {};
622 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
623 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
624 u8 material_type, u32 shader_id, bool backface_culling,
625 const TextureSettings &tsettings)
627 layer->shader_id = shader_id;
628 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
629 layer->material_type = material_type;
631 bool has_scale = tiledef.scale > 0;
632 if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
633 (tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
634 auto texture_size = layer->texture->getOriginalSize();
635 float base_size = tsettings.node_texture_size;
636 float size = std::fmin(texture_size.Width, texture_size.Height);
637 layer->scale = std::fmax(base_size, size) / base_size;
638 } else if (has_scale) {
639 layer->scale = tiledef.scale;
643 if (!tile.world_aligned)
646 // Normal texture and shader flags texture
647 if (tsettings.use_normal_texture) {
648 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
650 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
653 layer->material_flags = 0;
654 if (backface_culling)
655 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
656 if (tiledef.animation.type != TAT_NONE)
657 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
658 if (tiledef.tileable_horizontal)
659 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
660 if (tiledef.tileable_vertical)
661 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
664 layer->has_color = tiledef.has_color;
665 if (tiledef.has_color)
666 layer->color = tiledef.color;
668 layer->color = color;
670 // Animation parameters
672 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
674 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
675 &frame_count, &frame_length_ms, NULL);
676 layer->animation_frame_count = frame_count;
677 layer->animation_frame_length_ms = frame_length_ms;
680 if (frame_count == 1) {
681 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
683 std::ostringstream os(std::ios::binary);
684 if (!layer->frames) {
685 layer->frames = std::make_shared<std::vector<FrameSpec>>();
687 layer->frames->resize(frame_count);
689 for (int i = 0; i < frame_count; i++) {
695 tiledef.animation.getTextureModifer(os,
696 layer->texture->getOriginalSize(), i);
698 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
699 if (layer->normal_texture)
700 frame.normal_texture = tsrc->getNormalTexture(os.str());
701 frame.flags_texture = layer->flags_texture;
702 (*layer->frames)[i] = frame;
709 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
711 if (style == ALIGN_STYLE_WORLD)
713 if (mode == WORLDALIGN_DISABLE)
715 if (style == ALIGN_STYLE_USER_DEFINED)
717 if (drawtype == NDT_NORMAL)
718 return mode >= WORLDALIGN_FORCE;
719 if (drawtype == NDT_NODEBOX)
720 return mode >= WORLDALIGN_FORCE_NODEBOX;
724 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
725 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
727 // minimap pixel color - the average color of a texture
728 if (tsettings.enable_minimap && !tiledef[0].name.empty())
729 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
731 // Figure out the actual tiles to use
733 for (u32 j = 0; j < 6; j++) {
734 tdef[j] = tiledef[j];
735 if (tdef[j].name.empty())
736 tdef[j].name = "unknown_node.png";
738 // also the overlay tiles
739 TileDef tdef_overlay[6];
740 for (u32 j = 0; j < 6; j++)
741 tdef_overlay[j] = tiledef_overlay[j];
742 // also the special tiles
743 TileDef tdef_spec[6];
744 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
745 tdef_spec[j] = tiledef_special[j];
747 bool is_liquid = false;
749 u8 material_type = (alpha == 255) ?
750 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
755 material_type = (alpha == 255) ?
756 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
763 assert(liquid_type == LIQUID_SOURCE);
764 if (tsettings.opaque_water)
769 case NDT_FLOWINGLIQUID:
770 assert(liquid_type == LIQUID_FLOWING);
772 if (tsettings.opaque_water)
778 visual_solidness = 1;
780 case NDT_GLASSLIKE_FRAMED:
782 visual_solidness = 1;
784 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
786 visual_solidness = 1;
787 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
791 visual_solidness = 1;
793 case NDT_ALLFACES_OPTIONAL:
794 if (tsettings.leaves_style == LEAVES_FANCY) {
795 drawtype = NDT_ALLFACES;
797 visual_solidness = 1;
798 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
799 for (u32 j = 0; j < 6; j++) {
800 if (!tdef_spec[j].name.empty())
801 tdef[j].name = tdef_spec[j].name;
803 drawtype = NDT_GLASSLIKE;
805 visual_solidness = 1;
807 drawtype = NDT_NORMAL;
809 for (TileDef &td : tdef)
810 td.name += std::string("^[noalpha");
813 material_type = TILE_MATERIAL_WAVING_LEAVES;
818 material_type = TILE_MATERIAL_WAVING_PLANTS;
827 material_type = TILE_MATERIAL_WAVING_PLANTS;
828 else if (waving == 2)
829 material_type = TILE_MATERIAL_WAVING_LEAVES;
837 case NDT_PLANTLIKE_ROOTED:
843 // Vertex alpha is no longer supported, correct if necessary.
844 correctAlpha(tdef, 6);
845 correctAlpha(tdef_overlay, 6);
846 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
847 material_type = (alpha == 255) ?
848 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
851 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
853 u8 overlay_material = material_type;
854 if (overlay_material == TILE_MATERIAL_OPAQUE)
855 overlay_material = TILE_MATERIAL_BASIC;
856 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
857 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
859 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
861 // Tiles (fill in f->tiles[])
862 for (u16 j = 0; j < 6; j++) {
863 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
864 tsettings.world_aligned_mode, drawtype);
865 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
866 color, material_type, tile_shader,
867 tdef[j].backface_culling, tsettings);
868 if (!tdef_overlay[j].name.empty())
869 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
870 color, overlay_material, overlay_shader,
871 tdef[j].backface_culling, tsettings);
874 u8 special_material = material_type;
875 if (drawtype == NDT_PLANTLIKE_ROOTED) {
877 special_material = TILE_MATERIAL_WAVING_PLANTS;
878 else if (waving == 2)
879 special_material = TILE_MATERIAL_WAVING_LEAVES;
881 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
883 // Special tiles (fill in f->special_tiles[])
884 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
885 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
886 color, special_material, special_shader,
887 tdef_spec[j].backface_culling, tsettings);
889 if (param_type_2 == CPT2_COLOR ||
890 param_type_2 == CPT2_COLORED_FACEDIR ||
891 param_type_2 == CPT2_COLORED_WALLMOUNTED)
892 palette = tsrc->getPalette(palette_name);
894 if (drawtype == NDT_MESH && !mesh.empty()) {
896 // Read the mesh and apply scale
897 mesh_ptr[0] = client->getMesh(mesh);
899 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
900 scaleMesh(mesh_ptr[0], scale);
901 recalculateBoundingBox(mesh_ptr[0]);
902 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
906 //Cache 6dfacedir and wallmounted rotated clones of meshes
907 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
908 (param_type_2 == CPT2_FACEDIR
909 || param_type_2 == CPT2_COLORED_FACEDIR)) {
910 for (u16 j = 1; j < 24; j++) {
911 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
912 rotateMeshBy6dFacedir(mesh_ptr[j], j);
913 recalculateBoundingBox(mesh_ptr[j]);
914 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
916 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
917 && (param_type_2 == CPT2_WALLMOUNTED ||
918 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
919 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
920 for (u16 j = 1; j < 6; j++) {
921 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
922 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
923 recalculateBoundingBox(mesh_ptr[j]);
924 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
926 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
927 recalculateBoundingBox(mesh_ptr[0]);
928 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
937 class CNodeDefManager: public IWritableNodeDefManager {
940 virtual ~CNodeDefManager();
943 inline virtual const ContentFeatures& get(content_t c) const;
944 inline virtual const ContentFeatures& get(const MapNode &n) const;
945 virtual bool getId(const std::string &name, content_t &result) const;
946 virtual content_t getId(const std::string &name) const;
947 virtual bool getIds(const std::string &name, std::vector<content_t> &result) const;
948 virtual const ContentFeatures& get(const std::string &name) const;
949 content_t allocateId();
950 virtual content_t set(const std::string &name, const ContentFeatures &def);
951 virtual content_t allocateDummy(const std::string &name);
952 virtual void removeNode(const std::string &name);
953 virtual void updateAliases(IItemDefManager *idef);
954 virtual void applyTextureOverrides(const std::string &override_filepath);
955 virtual void updateTextures(IGameDef *gamedef,
956 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
957 void *progress_cbk_args);
958 void serialize(std::ostream &os, u16 protocol_version) const;
959 void deSerialize(std::istream &is);
961 inline virtual void setNodeRegistrationStatus(bool completed);
963 virtual void pendNodeResolve(NodeResolver *nr);
964 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
965 virtual void runNodeResolveCallbacks();
966 virtual void resetNodeResolveState();
967 virtual void mapNodeboxConnections();
968 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
969 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
971 return m_selection_box_int_union;
975 void addNameIdMapping(content_t i, std::string name);
977 * Recalculates m_selection_box_int_union based on
978 * m_selection_box_union.
980 void fixSelectionBoxIntUnion();
982 // Features indexed by id
983 std::vector<ContentFeatures> m_content_features;
985 // A mapping for fast converting back and forth between names and ids
986 NameIdMapping m_name_id_mapping;
988 // Like m_name_id_mapping, but only from names to ids, and includes
989 // item aliases too. Updated by updateAliases()
990 // Note: Not serialized.
992 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
994 // A mapping from groups to a vector of content_ts that belong to it.
995 // Necessary for a direct lookup in getIds().
996 // Note: Not serialized.
997 std::unordered_map<std::string, std::vector<content_t>> m_group_to_items;
999 // Next possibly free id
1000 content_t m_next_id;
1002 // NodeResolvers to callback once node registration has ended
1003 std::vector<NodeResolver *> m_pending_resolve_callbacks;
1005 // True when all nodes have been registered
1006 bool m_node_registration_complete;
1008 //! The union of all nodes' selection boxes.
1009 aabb3f m_selection_box_union;
1011 * The smallest box in node coordinates that
1012 * contains all nodes' selection boxes.
1014 core::aabbox3d<s16> m_selection_box_int_union;
1018 CNodeDefManager::CNodeDefManager()
1024 CNodeDefManager::~CNodeDefManager()
1027 for (ContentFeatures &f : m_content_features) {
1028 for (auto &j : f.mesh_ptr) {
1037 void CNodeDefManager::clear()
1039 m_content_features.clear();
1040 m_name_id_mapping.clear();
1041 m_name_id_mapping_with_aliases.clear();
1042 m_group_to_items.clear();
1044 m_selection_box_union.reset(0,0,0);
1045 m_selection_box_int_union.reset(0,0,0);
1047 resetNodeResolveState();
1049 u32 initial_length = 0;
1050 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1051 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1052 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1053 m_content_features.resize(initial_length);
1055 // Set CONTENT_UNKNOWN
1059 // Insert directly into containers
1060 content_t c = CONTENT_UNKNOWN;
1061 m_content_features[c] = f;
1062 addNameIdMapping(c, f.name);
1069 f.drawtype = NDT_AIRLIKE;
1070 f.param_type = CPT_LIGHT;
1071 f.light_propagates = true;
1072 f.sunlight_propagates = true;
1074 f.pointable = false;
1076 f.buildable_to = true;
1078 f.is_ground_content = true;
1079 // Insert directly into containers
1080 content_t c = CONTENT_AIR;
1081 m_content_features[c] = f;
1082 addNameIdMapping(c, f.name);
1085 // Set CONTENT_IGNORE
1089 f.drawtype = NDT_AIRLIKE;
1090 f.param_type = CPT_NONE;
1091 f.light_propagates = false;
1092 f.sunlight_propagates = false;
1094 f.pointable = false;
1096 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1097 f.is_ground_content = true;
1098 // Insert directly into containers
1099 content_t c = CONTENT_IGNORE;
1100 m_content_features[c] = f;
1101 addNameIdMapping(c, f.name);
1106 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1108 return c < m_content_features.size()
1109 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1113 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1115 return get(n.getContent());
1119 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1121 std::unordered_map<std::string, content_t>::const_iterator
1122 i = m_name_id_mapping_with_aliases.find(name);
1123 if(i == m_name_id_mapping_with_aliases.end())
1130 content_t CNodeDefManager::getId(const std::string &name) const
1132 content_t id = CONTENT_IGNORE;
1138 bool CNodeDefManager::getIds(const std::string &name,
1139 std::vector<content_t> &result) const
1141 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1142 if (name.substr(0,6) != "group:") {
1143 content_t id = CONTENT_IGNORE;
1144 bool exists = getId(name, id);
1146 result.push_back(id);
1149 std::string group = name.substr(6);
1151 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1152 i = m_group_to_items.find(group);
1153 if (i == m_group_to_items.end())
1156 const std::vector<content_t> &items = i->second;
1157 result.insert(result.end(), items.begin(), items.end());
1158 //printf("getIds: %dus\n", t.stop());
1163 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1165 content_t id = CONTENT_UNKNOWN;
1171 // returns CONTENT_IGNORE if no free ID found
1172 content_t CNodeDefManager::allocateId()
1174 for (content_t id = m_next_id;
1175 id >= m_next_id; // overflow?
1177 while (id >= m_content_features.size()) {
1178 m_content_features.emplace_back();
1180 const ContentFeatures &f = m_content_features[id];
1181 if (f.name.empty()) {
1186 // If we arrive here, an overflow occurred in id.
1187 // That means no ID was found
1188 return CONTENT_IGNORE;
1193 * Returns the smallest box that contains all boxes
1194 * in the vector. Box_union is expanded.
1195 * @param[in] boxes the vector containing the boxes
1196 * @param[in, out] box_union the union of the arguments
1198 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1200 for (const aabb3f &box : boxes) {
1201 box_union->addInternalBox(box);
1207 * Returns a box that contains the nodebox in every case.
1208 * The argument node_union is expanded.
1209 * @param[in] nodebox the nodebox to be measured
1210 * @param[in] features used to decide whether the nodebox
1212 * @param[in, out] box_union the union of the arguments
1214 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1217 switch(nodebox.type) {
1219 case NODEBOX_LEVELED: {
1221 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1222 boxVectorUnion(nodebox.fixed, &half_processed);
1223 // Set leveled boxes to maximal
1224 if (nodebox.type == NODEBOX_LEVELED) {
1225 half_processed.MaxEdge.Y = +BS / 2;
1227 if (features.param_type_2 == CPT2_FACEDIR ||
1228 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1229 // Get maximal coordinate
1231 fabsf(half_processed.MinEdge.X),
1232 fabsf(half_processed.MinEdge.Y),
1233 fabsf(half_processed.MinEdge.Z),
1234 fabsf(half_processed.MaxEdge.X),
1235 fabsf(half_processed.MaxEdge.Y),
1236 fabsf(half_processed.MaxEdge.Z) };
1238 for (float coord : coords) {
1243 // Add the union of all possible rotated boxes
1244 box_union->addInternalPoint(-max, -max, -max);
1245 box_union->addInternalPoint(+max, +max, +max);
1247 box_union->addInternalBox(half_processed);
1251 case NODEBOX_WALLMOUNTED: {
1253 box_union->addInternalBox(nodebox.wall_top);
1254 box_union->addInternalBox(nodebox.wall_bottom);
1255 // Find maximal coordinate in the X-Z plane
1257 fabsf(nodebox.wall_side.MinEdge.X),
1258 fabsf(nodebox.wall_side.MinEdge.Z),
1259 fabsf(nodebox.wall_side.MaxEdge.X),
1260 fabsf(nodebox.wall_side.MaxEdge.Z) };
1262 for (float coord : coords) {
1267 // Add the union of all possible rotated boxes
1268 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1269 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1272 case NODEBOX_CONNECTED: {
1273 // Add all possible connected boxes
1274 boxVectorUnion(nodebox.fixed, box_union);
1275 boxVectorUnion(nodebox.connect_top, box_union);
1276 boxVectorUnion(nodebox.connect_bottom, box_union);
1277 boxVectorUnion(nodebox.connect_front, box_union);
1278 boxVectorUnion(nodebox.connect_left, box_union);
1279 boxVectorUnion(nodebox.connect_back, box_union);
1280 boxVectorUnion(nodebox.connect_right, box_union);
1281 boxVectorUnion(nodebox.disconnected_top, box_union);
1282 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1283 boxVectorUnion(nodebox.disconnected_front, box_union);
1284 boxVectorUnion(nodebox.disconnected_left, box_union);
1285 boxVectorUnion(nodebox.disconnected_back, box_union);
1286 boxVectorUnion(nodebox.disconnected_right, box_union);
1287 boxVectorUnion(nodebox.disconnected, box_union);
1288 boxVectorUnion(nodebox.disconnected_sides, box_union);
1293 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1294 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1300 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1302 m_selection_box_int_union.MinEdge.X = floorf(
1303 m_selection_box_union.MinEdge.X / BS + 0.5f);
1304 m_selection_box_int_union.MinEdge.Y = floorf(
1305 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1306 m_selection_box_int_union.MinEdge.Z = floorf(
1307 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1308 m_selection_box_int_union.MaxEdge.X = ceilf(
1309 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1310 m_selection_box_int_union.MaxEdge.Y = ceilf(
1311 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1312 m_selection_box_int_union.MaxEdge.Z = ceilf(
1313 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1317 // IWritableNodeDefManager
1318 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1322 assert(name == def.name);
1324 // Don't allow redefining ignore (but allow air and unknown)
1325 if (name == "ignore") {
1326 warningstream << "NodeDefManager: Ignoring "
1327 "CONTENT_IGNORE redefinition"<<std::endl;
1328 return CONTENT_IGNORE;
1331 content_t id = CONTENT_IGNORE;
1332 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1335 if (id == CONTENT_IGNORE) {
1336 warningstream << "NodeDefManager: Absolute "
1337 "limit reached" << std::endl;
1338 return CONTENT_IGNORE;
1340 assert(id != CONTENT_IGNORE);
1341 addNameIdMapping(id, name);
1343 m_content_features[id] = def;
1344 verbosestream << "NodeDefManager: registering content id \"" << id
1345 << "\": name=\"" << def.name << "\""<<std::endl;
1347 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1348 fixSelectionBoxIntUnion();
1349 // Add this content to the list of all groups it belongs to
1350 // FIXME: This should remove a node from groups it no longer
1351 // belongs to when a node is re-registered
1352 for (const auto &group : def.groups) {
1353 const std::string &group_name = group.first;
1354 m_group_to_items[group_name].push_back(id);
1360 content_t CNodeDefManager::allocateDummy(const std::string &name)
1362 assert(name != ""); // Pre-condition
1365 return set(name, f);
1369 void CNodeDefManager::removeNode(const std::string &name)
1374 // Erase name from name ID mapping
1375 content_t id = CONTENT_IGNORE;
1376 if (m_name_id_mapping.getId(name, id)) {
1377 m_name_id_mapping.eraseName(name);
1378 m_name_id_mapping_with_aliases.erase(name);
1381 // Erase node content from all groups it belongs to
1382 for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
1383 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1384 std::vector<content_t> &items = iter_groups->second;
1385 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1387 // Check if group is empty
1389 m_group_to_items.erase(iter_groups++);
1396 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1398 std::set<std::string> all;
1400 m_name_id_mapping_with_aliases.clear();
1401 for (const std::string &name : all) {
1402 const std::string &convert_to = idef->getAlias(name);
1404 if (m_name_id_mapping.getId(convert_to, id)) {
1405 m_name_id_mapping_with_aliases.insert(
1406 std::make_pair(name, id));
1411 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1413 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1414 "overrides to textures from " << override_filepath << std::endl;
1416 std::ifstream infile(override_filepath.c_str());
1419 while (std::getline(infile, line)) {
1421 if (trim(line).empty())
1423 std::vector<std::string> splitted = str_split(line, ' ');
1424 if (splitted.size() != 3) {
1425 errorstream << override_filepath
1426 << ":" << line_c << " Could not apply texture override \""
1427 << line << "\": Syntax error" << std::endl;
1432 if (!getId(splitted[0], id))
1433 continue; // Ignore unknown node
1435 ContentFeatures &nodedef = m_content_features[id];
1437 if (splitted[1] == "top")
1438 nodedef.tiledef[0].name = splitted[2];
1439 else if (splitted[1] == "bottom")
1440 nodedef.tiledef[1].name = splitted[2];
1441 else if (splitted[1] == "right")
1442 nodedef.tiledef[2].name = splitted[2];
1443 else if (splitted[1] == "left")
1444 nodedef.tiledef[3].name = splitted[2];
1445 else if (splitted[1] == "back")
1446 nodedef.tiledef[4].name = splitted[2];
1447 else if (splitted[1] == "front")
1448 nodedef.tiledef[5].name = splitted[2];
1449 else if (splitted[1] == "all" || splitted[1] == "*")
1450 for (TileDef &i : nodedef.tiledef)
1451 i.name = splitted[2];
1452 else if (splitted[1] == "sides")
1453 for (int i = 2; i < 6; i++)
1454 nodedef.tiledef[i].name = splitted[2];
1456 errorstream << override_filepath
1457 << ":" << line_c << " Could not apply texture override \""
1458 << line << "\": Unknown node side \""
1459 << splitted[1] << "\"" << std::endl;
1465 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1466 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1467 void *progress_callback_args)
1470 infostream << "CNodeDefManager::updateTextures(): Updating "
1471 "textures in node definitions" << std::endl;
1473 Client *client = (Client *)gamedef;
1474 ITextureSource *tsrc = client->tsrc();
1475 IShaderSource *shdsrc = client->getShaderSource();
1476 scene::IMeshManipulator *meshmanip =
1477 RenderingEngine::get_scene_manager()->getMeshManipulator();
1478 TextureSettings tsettings;
1479 tsettings.readSettings();
1481 u32 size = m_content_features.size();
1483 for (u32 i = 0; i < size; i++) {
1484 ContentFeatures *f = &(m_content_features[i]);
1485 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1486 progress_callback(progress_callback_args, i, size);
1491 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1493 writeU8(os, 1); // version
1495 std::ostringstream os2(std::ios::binary);
1496 for (u32 i = 0; i < m_content_features.size(); i++) {
1497 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1498 || i == CONTENT_UNKNOWN)
1500 const ContentFeatures *f = &m_content_features[i];
1501 if (f->name.empty())
1504 // Wrap it in a string to allow different lengths without
1505 // strict version incompatibilities
1506 std::ostringstream wrapper_os(std::ios::binary);
1507 f->serialize(wrapper_os, protocol_version);
1508 os2<<serializeString(wrapper_os.str());
1510 // must not overflow
1511 u16 next = count + 1;
1512 FATAL_ERROR_IF(next < count, "Overflow");
1515 writeU16(os, count);
1516 os << serializeLongString(os2.str());
1520 void CNodeDefManager::deSerialize(std::istream &is)
1523 int version = readU8(is);
1525 throw SerializationError("unsupported NodeDefinitionManager version");
1526 u16 count = readU16(is);
1527 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1529 for (u16 n = 0; n < count; n++) {
1530 u16 i = readU16(is2);
1532 // Read it from the string wrapper
1533 std::string wrapper = deSerializeString(is2);
1534 std::istringstream wrapper_is(wrapper, std::ios::binary);
1535 f.deSerialize(wrapper_is);
1537 // Check error conditions
1538 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1539 warningstream << "NodeDefManager::deSerialize(): "
1540 "not changing builtin node " << i << std::endl;
1543 if (f.name.empty()) {
1544 warningstream << "NodeDefManager::deSerialize(): "
1545 "received empty name" << std::endl;
1551 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1552 warningstream << "NodeDefManager::deSerialize(): "
1553 "already defined with different ID: " << f.name << std::endl;
1557 // All is ok, add node definition with the requested ID
1558 if (i >= m_content_features.size())
1559 m_content_features.resize((u32)(i) + 1);
1560 m_content_features[i] = f;
1561 addNameIdMapping(i, f.name);
1562 verbosestream << "deserialized " << f.name << std::endl;
1564 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1565 fixSelectionBoxIntUnion();
1570 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1572 m_name_id_mapping.set(i, name);
1573 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1577 IWritableNodeDefManager *createNodeDefManager()
1579 return new CNodeDefManager();
1582 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1584 m_node_registration_complete = completed;
1588 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1591 if (m_node_registration_complete)
1592 nr->nodeResolveInternal();
1594 m_pending_resolve_callbacks.push_back(nr);
1598 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1600 size_t len = m_pending_resolve_callbacks.size();
1601 for (size_t i = 0; i != len; i++) {
1602 if (nr != m_pending_resolve_callbacks[i])
1606 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1607 m_pending_resolve_callbacks.resize(len);
1615 void CNodeDefManager::runNodeResolveCallbacks()
1617 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1618 NodeResolver *nr = m_pending_resolve_callbacks[i];
1619 nr->nodeResolveInternal();
1622 m_pending_resolve_callbacks.clear();
1626 void CNodeDefManager::resetNodeResolveState()
1628 m_node_registration_complete = false;
1629 m_pending_resolve_callbacks.clear();
1632 void CNodeDefManager::mapNodeboxConnections()
1634 for (ContentFeatures &f : m_content_features) {
1635 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1638 for (const std::string &name : f.connects_to) {
1639 getIds(name, f.connects_to_ids);
1644 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1646 const ContentFeatures &f1 = get(from);
1648 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1651 // lookup target in connected set
1652 if (!CONTAINS(f1.connects_to_ids, to.param0))
1655 const ContentFeatures &f2 = get(to);
1657 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1658 // ignores actually looking if back connection exists
1659 return CONTAINS(f2.connects_to_ids, from.param0);
1661 // does to node declare usable faces?
1662 if (f2.connect_sides > 0) {
1663 if ((f2.param_type_2 == CPT2_FACEDIR ||
1664 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1665 && (connect_face >= 4)) {
1666 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1667 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1669 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1671 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1672 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1674 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1675 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1676 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1678 return (f2.connect_sides
1679 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1681 return (f2.connect_sides & connect_face);
1683 // the target is just a regular node, so connect no matter back connection
1691 NodeResolver::NodeResolver()
1693 m_nodenames.reserve(16);
1694 m_nnlistsizes.reserve(4);
1698 NodeResolver::~NodeResolver()
1700 if (!m_resolve_done && m_ndef)
1701 m_ndef->cancelNodeResolveCallback(this);
1705 void NodeResolver::nodeResolveInternal()
1707 m_nodenames_idx = 0;
1708 m_nnlistsizes_idx = 0;
1711 m_resolve_done = true;
1713 m_nodenames.clear();
1714 m_nnlistsizes.clear();
1718 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1719 const std::string &node_alt, content_t c_fallback)
1721 if (m_nodenames_idx == m_nodenames.size()) {
1722 *result_out = c_fallback;
1723 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1728 std::string name = m_nodenames[m_nodenames_idx++];
1730 bool success = m_ndef->getId(name, c);
1731 if (!success && !node_alt.empty()) {
1733 success = m_ndef->getId(name, c);
1737 errorstream << "NodeResolver: failed to resolve node name '" << name
1738 << "'." << std::endl;
1747 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1748 bool all_required, content_t c_fallback)
1750 bool success = true;
1752 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1753 errorstream << "NodeResolver: no more node lists" << std::endl;
1757 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1760 if (m_nodenames_idx == m_nodenames.size()) {
1761 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1766 std::string &name = m_nodenames[m_nodenames_idx++];
1768 if (name.substr(0,6) != "group:") {
1769 if (m_ndef->getId(name, c)) {
1770 result_out->push_back(c);
1771 } else if (all_required) {
1772 errorstream << "NodeResolver: failed to resolve node name '"
1773 << name << "'." << std::endl;
1774 result_out->push_back(c_fallback);
1778 m_ndef->getIds(name, *result_out);