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()
49 type = NODEBOX_REGULAR;
52 // default is sign/ladder-like
53 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
54 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
55 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
56 // no default for other parts
58 connect_bottom.clear();
59 connect_front.clear();
62 connect_right.clear();
65 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
76 writeU16(os, fixed.size());
77 for (const aabb3f &nodebox : fixed) {
78 writeV3F1000(os, nodebox.MinEdge);
79 writeV3F1000(os, nodebox.MaxEdge);
82 case NODEBOX_WALLMOUNTED:
85 writeV3F1000(os, wall_top.MinEdge);
86 writeV3F1000(os, wall_top.MaxEdge);
87 writeV3F1000(os, wall_bottom.MinEdge);
88 writeV3F1000(os, wall_bottom.MaxEdge);
89 writeV3F1000(os, wall_side.MinEdge);
90 writeV3F1000(os, wall_side.MaxEdge);
92 case NODEBOX_CONNECTED:
95 #define WRITEBOX(box) \
96 writeU16(os, (box).size()); \
97 for (const aabb3f &i: (box)) { \
98 writeV3F1000(os, i.MinEdge); \
99 writeV3F1000(os, i.MaxEdge); \
103 WRITEBOX(connect_top);
104 WRITEBOX(connect_bottom);
105 WRITEBOX(connect_front);
106 WRITEBOX(connect_left);
107 WRITEBOX(connect_back);
108 WRITEBOX(connect_right);
116 void NodeBox::deSerialize(std::istream &is)
118 int version = readU8(is);
120 throw SerializationError("unsupported NodeBox version");
124 type = (enum NodeBoxType)readU8(is);
126 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
128 u16 fixed_count = readU16(is);
132 box.MinEdge = readV3F1000(is);
133 box.MaxEdge = readV3F1000(is);
134 fixed.push_back(box);
137 else if(type == NODEBOX_WALLMOUNTED)
139 wall_top.MinEdge = readV3F1000(is);
140 wall_top.MaxEdge = readV3F1000(is);
141 wall_bottom.MinEdge = readV3F1000(is);
142 wall_bottom.MaxEdge = readV3F1000(is);
143 wall_side.MinEdge = readV3F1000(is);
144 wall_side.MaxEdge = readV3F1000(is);
146 else if (type == NODEBOX_CONNECTED)
148 #define READBOXES(box) { \
149 count = readU16(is); \
150 (box).reserve(count); \
152 v3f min = readV3F1000(is); \
153 v3f max = readV3F1000(is); \
154 (box).emplace_back(min, max); }; }
159 READBOXES(connect_top);
160 READBOXES(connect_bottom);
161 READBOXES(connect_front);
162 READBOXES(connect_left);
163 READBOXES(connect_back);
164 READBOXES(connect_right);
172 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
173 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
174 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
175 #define TILE_FLAG_HAS_COLOR (1 << 3)
176 #define TILE_FLAG_HAS_SCALE (1 << 4)
177 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
179 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
181 // protocol_version >= 36
183 writeU8(os, version);
185 os << serializeString(name);
186 animation.serialize(os, version);
187 bool has_scale = scale > 0;
189 if (backface_culling)
190 flags |= TILE_FLAG_BACKFACE_CULLING;
191 if (tileable_horizontal)
192 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
193 if (tileable_vertical)
194 flags |= TILE_FLAG_TILEABLE_VERTICAL;
196 flags |= TILE_FLAG_HAS_COLOR;
198 flags |= TILE_FLAG_HAS_SCALE;
199 if (align_style != ALIGN_STYLE_NODE)
200 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
203 writeU8(os, color.getRed());
204 writeU8(os, color.getGreen());
205 writeU8(os, color.getBlue());
209 if (align_style != ALIGN_STYLE_NODE)
210 writeU8(os, align_style);
213 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
214 NodeDrawType drawtype)
216 int version = readU8(is);
218 throw SerializationError("unsupported TileDef version");
219 name = deSerializeString(is);
220 animation.deSerialize(is, version);
221 u16 flags = readU16(is);
222 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
223 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
224 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
225 has_color = flags & TILE_FLAG_HAS_COLOR;
226 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
227 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
229 color.setRed(readU8(is));
230 color.setGreen(readU8(is));
231 color.setBlue(readU8(is));
233 scale = has_scale ? readU8(is) : 0;
235 align_style = static_cast<AlignStyle>(readU8(is));
237 align_style = ALIGN_STYLE_NODE;
242 SimpleSoundSpec serialization
245 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
246 std::ostream &os, u8 version)
248 os<<serializeString(ss.name);
249 writeF1000(os, ss.gain);
250 writeF1000(os, ss.pitch);
252 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
253 std::istream &is, u8 version)
255 ss.name = deSerializeString(is);
256 ss.gain = readF1000(is);
257 ss.pitch = readF1000(is);
260 void TextureSettings::readSettings()
262 connected_glass = g_settings->getBool("connected_glass");
263 opaque_water = g_settings->getBool("opaque_water");
264 bool enable_shaders = g_settings->getBool("enable_shaders");
265 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
266 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
267 bool smooth_lighting = g_settings->getBool("smooth_lighting");
268 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
269 enable_minimap = g_settings->getBool("enable_minimap");
270 node_texture_size = g_settings->getU16("texture_min_size");
271 std::string leaves_style_str = g_settings->get("leaves_style");
272 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
273 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
275 // Mesh cache is not supported in combination with smooth lighting
277 enable_mesh_cache = false;
279 use_normal_texture = enable_shaders &&
280 (enable_bumpmapping || enable_parallax_occlusion);
281 if (leaves_style_str == "fancy") {
282 leaves_style = LEAVES_FANCY;
283 } else if (leaves_style_str == "simple") {
284 leaves_style = LEAVES_SIMPLE;
286 leaves_style = LEAVES_OPAQUE;
289 if (world_aligned_mode_str == "enable")
290 world_aligned_mode = WORLDALIGN_ENABLE;
291 else if (world_aligned_mode_str == "force_solid")
292 world_aligned_mode = WORLDALIGN_FORCE;
293 else if (world_aligned_mode_str == "force_nodebox")
294 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
296 world_aligned_mode = WORLDALIGN_DISABLE;
298 if (autoscale_mode_str == "enable")
299 autoscale_mode = AUTOSCALE_ENABLE;
300 else if (autoscale_mode_str == "force")
301 autoscale_mode = AUTOSCALE_FORCE;
303 autoscale_mode = AUTOSCALE_DISABLE;
310 ContentFeatures::ContentFeatures()
315 void ContentFeatures::reset()
322 visual_solidness = 0;
323 backface_culling = true;
326 has_on_construct = false;
327 has_on_destruct = false;
328 has_after_destruct = false;
332 NOTE: Most of this is always overridden by the default values given
337 // Unknown nodes can be dug
338 groups["dig_immediate"] = 2;
339 drawtype = NDT_NORMAL;
342 for (auto &i : mesh_ptr)
344 minimap_color = video::SColor(0, 0, 0, 0);
347 for (auto &i : tiledef)
349 for (auto &j : tiledef_special)
352 post_effect_color = video::SColor(0, 0, 0, 0);
353 param_type = CPT_NONE;
354 param_type_2 = CPT2_NONE;
355 is_ground_content = false;
356 light_propagates = false;
357 sunlight_propagates = false;
362 buildable_to = false;
364 rightclickable = true;
366 liquid_type = LIQUID_NONE;
367 liquid_alternative_flowing = "";
368 liquid_alternative_source = "";
369 liquid_viscosity = 0;
370 liquid_renewable = true;
371 liquid_range = LIQUID_LEVEL_MAX+1;
374 damage_per_second = 0;
375 node_box = NodeBox();
376 selection_box = NodeBox();
377 collision_box = NodeBox();
379 legacy_facedir_simple = false;
380 legacy_wallmounted = false;
381 sound_footstep = SimpleSoundSpec();
382 sound_dig = SimpleSoundSpec("__group");
383 sound_dug = SimpleSoundSpec();
385 connects_to_ids.clear();
387 color = video::SColor(0xFFFFFFFF);
390 node_dig_prediction = "air";
393 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
395 // protocol_version >= 36
397 writeU8(os, version);
400 os << serializeString(name);
401 writeU16(os, groups.size());
402 for (const auto &group : groups) {
403 os << serializeString(group.first);
404 writeS16(os, group.second);
406 writeU8(os, param_type);
407 writeU8(os, param_type_2);
410 writeU8(os, drawtype);
411 os << serializeString(mesh);
412 writeF1000(os, visual_scale);
414 for (const TileDef &td : tiledef)
415 td.serialize(os, protocol_version);
416 for (const TileDef &td : tiledef_overlay)
417 td.serialize(os, protocol_version);
418 writeU8(os, CF_SPECIAL_COUNT);
419 for (const TileDef &td : tiledef_special) {
420 td.serialize(os, protocol_version);
423 writeU8(os, color.getRed());
424 writeU8(os, color.getGreen());
425 writeU8(os, color.getBlue());
426 os << serializeString(palette_name);
428 writeU8(os, connect_sides);
429 writeU16(os, connects_to_ids.size());
430 for (u16 connects_to_id : connects_to_ids)
431 writeU16(os, connects_to_id);
432 writeU8(os, post_effect_color.getAlpha());
433 writeU8(os, post_effect_color.getRed());
434 writeU8(os, post_effect_color.getGreen());
435 writeU8(os, post_effect_color.getBlue());
436 writeU8(os, leveled);
439 writeU8(os, light_propagates);
440 writeU8(os, sunlight_propagates);
441 writeU8(os, light_source);
444 writeU8(os, is_ground_content);
447 writeU8(os, walkable);
448 writeU8(os, pointable);
449 writeU8(os, diggable);
450 writeU8(os, climbable);
451 writeU8(os, buildable_to);
452 writeU8(os, rightclickable);
453 writeU32(os, damage_per_second);
456 writeU8(os, liquid_type);
457 os << serializeString(liquid_alternative_flowing);
458 os << serializeString(liquid_alternative_source);
459 writeU8(os, liquid_viscosity);
460 writeU8(os, liquid_renewable);
461 writeU8(os, liquid_range);
462 writeU8(os, drowning);
463 writeU8(os, floodable);
466 node_box.serialize(os, protocol_version);
467 selection_box.serialize(os, protocol_version);
468 collision_box.serialize(os, protocol_version);
471 serializeSimpleSoundSpec(sound_footstep, os, version);
472 serializeSimpleSoundSpec(sound_dig, os, version);
473 serializeSimpleSoundSpec(sound_dug, os, version);
476 writeU8(os, legacy_facedir_simple);
477 writeU8(os, legacy_wallmounted);
479 os << serializeString(node_dig_prediction);
482 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
484 // alpha == 0 means that the node is using texture alpha
485 if (alpha == 0 || alpha == 255)
488 for (int i = 0; i < length; i++) {
489 if (tiles[i].name.empty())
492 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
493 tiles[i].name = s.str();
497 void ContentFeatures::deSerialize(std::istream &is)
500 int version = readU8(is);
502 throw SerializationError("unsupported ContentFeatures version");
505 name = deSerializeString(is);
507 u32 groups_size = readU16(is);
508 for (u32 i = 0; i < groups_size; i++) {
509 std::string name = deSerializeString(is);
510 int value = readS16(is);
511 groups[name] = value;
513 param_type = (enum ContentParamType) readU8(is);
514 param_type_2 = (enum ContentParamType2) readU8(is);
517 drawtype = (enum NodeDrawType) readU8(is);
518 mesh = deSerializeString(is);
519 visual_scale = readF1000(is);
521 throw SerializationError("unsupported tile count");
522 for (TileDef &td : tiledef)
523 td.deSerialize(is, version, drawtype);
524 for (TileDef &td : tiledef_overlay)
525 td.deSerialize(is, version, drawtype);
526 if (readU8(is) != CF_SPECIAL_COUNT)
527 throw SerializationError("unsupported CF_SPECIAL_COUNT");
528 for (TileDef &td : tiledef_special)
529 td.deSerialize(is, version, drawtype);
531 color.setRed(readU8(is));
532 color.setGreen(readU8(is));
533 color.setBlue(readU8(is));
534 palette_name = deSerializeString(is);
536 connect_sides = readU8(is);
537 u16 connects_to_size = readU16(is);
538 connects_to_ids.clear();
539 for (u16 i = 0; i < connects_to_size; i++)
540 connects_to_ids.push_back(readU16(is));
541 post_effect_color.setAlpha(readU8(is));
542 post_effect_color.setRed(readU8(is));
543 post_effect_color.setGreen(readU8(is));
544 post_effect_color.setBlue(readU8(is));
545 leveled = readU8(is);
548 light_propagates = readU8(is);
549 sunlight_propagates = readU8(is);
550 light_source = readU8(is);
551 light_source = MYMIN(light_source, LIGHT_MAX);
554 is_ground_content = readU8(is);
557 walkable = readU8(is);
558 pointable = readU8(is);
559 diggable = readU8(is);
560 climbable = readU8(is);
561 buildable_to = readU8(is);
562 rightclickable = readU8(is);
563 damage_per_second = readU32(is);
566 liquid_type = (enum LiquidType) readU8(is);
567 liquid_alternative_flowing = deSerializeString(is);
568 liquid_alternative_source = deSerializeString(is);
569 liquid_viscosity = readU8(is);
570 liquid_renewable = readU8(is);
571 liquid_range = readU8(is);
572 drowning = readU8(is);
573 floodable = readU8(is);
576 node_box.deSerialize(is);
577 selection_box.deSerialize(is);
578 collision_box.deSerialize(is);
581 deSerializeSimpleSoundSpec(sound_footstep, is, version);
582 deSerializeSimpleSoundSpec(sound_dig, is, version);
583 deSerializeSimpleSoundSpec(sound_dug, is, version);
585 // read legacy properties
586 legacy_facedir_simple = readU8(is);
587 legacy_wallmounted = readU8(is);
590 node_dig_prediction = deSerializeString(is);
591 } catch(SerializationError &e) {};
595 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
596 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
597 u8 material_type, u32 shader_id, bool backface_culling,
598 const TextureSettings &tsettings)
600 layer->shader_id = shader_id;
601 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
602 layer->material_type = material_type;
604 bool has_scale = tiledef.scale > 0;
605 if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
606 (tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
607 auto texture_size = layer->texture->getOriginalSize();
608 float base_size = tsettings.node_texture_size;
609 float size = std::fmin(texture_size.Width, texture_size.Height);
610 layer->scale = std::fmax(base_size, size) / base_size;
611 } else if (has_scale) {
612 layer->scale = tiledef.scale;
616 if (!tile.world_aligned)
619 // Normal texture and shader flags texture
620 if (tsettings.use_normal_texture) {
621 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
623 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
626 layer->material_flags = 0;
627 if (backface_culling)
628 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
629 if (tiledef.animation.type != TAT_NONE)
630 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
631 if (tiledef.tileable_horizontal)
632 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
633 if (tiledef.tileable_vertical)
634 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
637 layer->has_color = tiledef.has_color;
638 if (tiledef.has_color)
639 layer->color = tiledef.color;
641 layer->color = color;
643 // Animation parameters
645 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
647 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
648 &frame_count, &frame_length_ms, NULL);
649 layer->animation_frame_count = frame_count;
650 layer->animation_frame_length_ms = frame_length_ms;
653 if (frame_count == 1) {
654 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
656 std::ostringstream os(std::ios::binary);
657 if (!layer->frames) {
658 layer->frames = std::make_shared<std::vector<FrameSpec>>();
660 layer->frames->resize(frame_count);
662 for (int i = 0; i < frame_count; i++) {
668 tiledef.animation.getTextureModifer(os,
669 layer->texture->getOriginalSize(), i);
671 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
672 if (layer->normal_texture)
673 frame.normal_texture = tsrc->getNormalTexture(os.str());
674 frame.flags_texture = layer->flags_texture;
675 (*layer->frames)[i] = frame;
682 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
684 if (style == ALIGN_STYLE_WORLD)
686 if (mode == WORLDALIGN_DISABLE)
688 if (style == ALIGN_STYLE_USER_DEFINED)
690 if (drawtype == NDT_NORMAL)
691 return mode >= WORLDALIGN_FORCE;
692 if (drawtype == NDT_NODEBOX)
693 return mode >= WORLDALIGN_FORCE_NODEBOX;
697 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
698 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
700 // minimap pixel color - the average color of a texture
701 if (tsettings.enable_minimap && !tiledef[0].name.empty())
702 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
704 // Figure out the actual tiles to use
706 for (u32 j = 0; j < 6; j++) {
707 tdef[j] = tiledef[j];
708 if (tdef[j].name.empty())
709 tdef[j].name = "unknown_node.png";
711 // also the overlay tiles
712 TileDef tdef_overlay[6];
713 for (u32 j = 0; j < 6; j++)
714 tdef_overlay[j] = tiledef_overlay[j];
715 // also the special tiles
716 TileDef tdef_spec[6];
717 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
718 tdef_spec[j] = tiledef_special[j];
720 bool is_liquid = false;
722 u8 material_type = (alpha == 255) ?
723 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
728 material_type = (alpha == 255) ?
729 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
736 assert(liquid_type == LIQUID_SOURCE);
737 if (tsettings.opaque_water)
742 case NDT_FLOWINGLIQUID:
743 assert(liquid_type == LIQUID_FLOWING);
745 if (tsettings.opaque_water)
751 visual_solidness = 1;
753 case NDT_GLASSLIKE_FRAMED:
755 visual_solidness = 1;
757 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
759 visual_solidness = 1;
760 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
764 visual_solidness = 1;
766 case NDT_ALLFACES_OPTIONAL:
767 if (tsettings.leaves_style == LEAVES_FANCY) {
768 drawtype = NDT_ALLFACES;
770 visual_solidness = 1;
771 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
772 for (u32 j = 0; j < 6; j++) {
773 if (!tdef_spec[j].name.empty())
774 tdef[j].name = tdef_spec[j].name;
776 drawtype = NDT_GLASSLIKE;
778 visual_solidness = 1;
780 drawtype = NDT_NORMAL;
782 for (TileDef &td : tdef)
783 td.name += std::string("^[noalpha");
786 material_type = TILE_MATERIAL_WAVING_LEAVES;
791 material_type = TILE_MATERIAL_WAVING_PLANTS;
800 material_type = TILE_MATERIAL_WAVING_PLANTS;
801 else if (waving == 2)
802 material_type = TILE_MATERIAL_WAVING_LEAVES;
810 case NDT_PLANTLIKE_ROOTED:
816 // Vertex alpha is no longer supported, correct if necessary.
817 correctAlpha(tdef, 6);
818 correctAlpha(tdef_overlay, 6);
819 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
820 material_type = (alpha == 255) ?
821 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
824 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
826 u8 overlay_material = material_type;
827 if (overlay_material == TILE_MATERIAL_OPAQUE)
828 overlay_material = TILE_MATERIAL_BASIC;
829 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
830 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
832 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
834 // Tiles (fill in f->tiles[])
835 for (u16 j = 0; j < 6; j++) {
836 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
837 tsettings.world_aligned_mode, drawtype);
838 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
839 color, material_type, tile_shader,
840 tdef[j].backface_culling, tsettings);
841 if (!tdef_overlay[j].name.empty())
842 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
843 color, overlay_material, overlay_shader,
844 tdef[j].backface_culling, tsettings);
847 u8 special_material = material_type;
848 if (drawtype == NDT_PLANTLIKE_ROOTED) {
850 special_material = TILE_MATERIAL_WAVING_PLANTS;
851 else if (waving == 2)
852 special_material = TILE_MATERIAL_WAVING_LEAVES;
854 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
856 // Special tiles (fill in f->special_tiles[])
857 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
858 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
859 color, special_material, special_shader,
860 tdef_spec[j].backface_culling, tsettings);
862 if (param_type_2 == CPT2_COLOR ||
863 param_type_2 == CPT2_COLORED_FACEDIR ||
864 param_type_2 == CPT2_COLORED_WALLMOUNTED)
865 palette = tsrc->getPalette(palette_name);
867 if (drawtype == NDT_MESH && !mesh.empty()) {
869 // Read the mesh and apply scale
870 mesh_ptr[0] = client->getMesh(mesh);
872 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
873 scaleMesh(mesh_ptr[0], scale);
874 recalculateBoundingBox(mesh_ptr[0]);
875 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
879 //Cache 6dfacedir and wallmounted rotated clones of meshes
880 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
881 (param_type_2 == CPT2_FACEDIR
882 || param_type_2 == CPT2_COLORED_FACEDIR)) {
883 for (u16 j = 1; j < 24; j++) {
884 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
885 rotateMeshBy6dFacedir(mesh_ptr[j], j);
886 recalculateBoundingBox(mesh_ptr[j]);
887 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
889 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
890 && (param_type_2 == CPT2_WALLMOUNTED ||
891 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
892 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
893 for (u16 j = 1; j < 6; j++) {
894 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
895 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
896 recalculateBoundingBox(mesh_ptr[j]);
897 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
899 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
900 recalculateBoundingBox(mesh_ptr[0]);
901 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
910 class CNodeDefManager: public IWritableNodeDefManager {
913 virtual ~CNodeDefManager();
916 inline virtual const ContentFeatures& get(content_t c) const;
917 inline virtual const ContentFeatures& get(const MapNode &n) const;
918 virtual bool getId(const std::string &name, content_t &result) const;
919 virtual content_t getId(const std::string &name) const;
920 virtual bool getIds(const std::string &name, std::vector<content_t> &result) const;
921 virtual const ContentFeatures& get(const std::string &name) const;
922 content_t allocateId();
923 virtual content_t set(const std::string &name, const ContentFeatures &def);
924 virtual content_t allocateDummy(const std::string &name);
925 virtual void removeNode(const std::string &name);
926 virtual void updateAliases(IItemDefManager *idef);
927 virtual void applyTextureOverrides(const std::string &override_filepath);
928 virtual void updateTextures(IGameDef *gamedef,
929 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
930 void *progress_cbk_args);
931 void serialize(std::ostream &os, u16 protocol_version) const;
932 void deSerialize(std::istream &is);
934 inline virtual void setNodeRegistrationStatus(bool completed);
936 virtual void pendNodeResolve(NodeResolver *nr);
937 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
938 virtual void runNodeResolveCallbacks();
939 virtual void resetNodeResolveState();
940 virtual void mapNodeboxConnections();
941 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
942 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
944 return m_selection_box_int_union;
948 void addNameIdMapping(content_t i, std::string name);
950 * Recalculates m_selection_box_int_union based on
951 * m_selection_box_union.
953 void fixSelectionBoxIntUnion();
955 // Features indexed by id
956 std::vector<ContentFeatures> m_content_features;
958 // A mapping for fast converting back and forth between names and ids
959 NameIdMapping m_name_id_mapping;
961 // Like m_name_id_mapping, but only from names to ids, and includes
962 // item aliases too. Updated by updateAliases()
963 // Note: Not serialized.
965 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
967 // A mapping from groups to a vector of content_ts that belong to it.
968 // Necessary for a direct lookup in getIds().
969 // Note: Not serialized.
970 std::unordered_map<std::string, std::vector<content_t>> m_group_to_items;
972 // Next possibly free id
975 // NodeResolvers to callback once node registration has ended
976 std::vector<NodeResolver *> m_pending_resolve_callbacks;
978 // True when all nodes have been registered
979 bool m_node_registration_complete;
981 //! The union of all nodes' selection boxes.
982 aabb3f m_selection_box_union;
984 * The smallest box in node coordinates that
985 * contains all nodes' selection boxes.
987 core::aabbox3d<s16> m_selection_box_int_union;
991 CNodeDefManager::CNodeDefManager()
997 CNodeDefManager::~CNodeDefManager()
1000 for (ContentFeatures &f : m_content_features) {
1001 for (auto &j : f.mesh_ptr) {
1010 void CNodeDefManager::clear()
1012 m_content_features.clear();
1013 m_name_id_mapping.clear();
1014 m_name_id_mapping_with_aliases.clear();
1015 m_group_to_items.clear();
1017 m_selection_box_union.reset(0,0,0);
1018 m_selection_box_int_union.reset(0,0,0);
1020 resetNodeResolveState();
1022 u32 initial_length = 0;
1023 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1024 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1025 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1026 m_content_features.resize(initial_length);
1028 // Set CONTENT_UNKNOWN
1032 // Insert directly into containers
1033 content_t c = CONTENT_UNKNOWN;
1034 m_content_features[c] = f;
1035 addNameIdMapping(c, f.name);
1042 f.drawtype = NDT_AIRLIKE;
1043 f.param_type = CPT_LIGHT;
1044 f.light_propagates = true;
1045 f.sunlight_propagates = true;
1047 f.pointable = false;
1049 f.buildable_to = true;
1051 f.is_ground_content = true;
1052 // Insert directly into containers
1053 content_t c = CONTENT_AIR;
1054 m_content_features[c] = f;
1055 addNameIdMapping(c, f.name);
1058 // Set CONTENT_IGNORE
1062 f.drawtype = NDT_AIRLIKE;
1063 f.param_type = CPT_NONE;
1064 f.light_propagates = false;
1065 f.sunlight_propagates = false;
1067 f.pointable = false;
1069 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1070 f.is_ground_content = true;
1071 // Insert directly into containers
1072 content_t c = CONTENT_IGNORE;
1073 m_content_features[c] = f;
1074 addNameIdMapping(c, f.name);
1079 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1081 return c < m_content_features.size()
1082 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1086 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1088 return get(n.getContent());
1092 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1094 std::unordered_map<std::string, content_t>::const_iterator
1095 i = m_name_id_mapping_with_aliases.find(name);
1096 if(i == m_name_id_mapping_with_aliases.end())
1103 content_t CNodeDefManager::getId(const std::string &name) const
1105 content_t id = CONTENT_IGNORE;
1111 bool CNodeDefManager::getIds(const std::string &name,
1112 std::vector<content_t> &result) const
1114 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1115 if (name.substr(0,6) != "group:") {
1116 content_t id = CONTENT_IGNORE;
1117 bool exists = getId(name, id);
1119 result.push_back(id);
1122 std::string group = name.substr(6);
1124 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1125 i = m_group_to_items.find(group);
1126 if (i == m_group_to_items.end())
1129 const std::vector<content_t> &items = i->second;
1130 result.insert(result.end(), items.begin(), items.end());
1131 //printf("getIds: %dus\n", t.stop());
1136 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1138 content_t id = CONTENT_UNKNOWN;
1144 // returns CONTENT_IGNORE if no free ID found
1145 content_t CNodeDefManager::allocateId()
1147 for (content_t id = m_next_id;
1148 id >= m_next_id; // overflow?
1150 while (id >= m_content_features.size()) {
1151 m_content_features.emplace_back();
1153 const ContentFeatures &f = m_content_features[id];
1154 if (f.name.empty()) {
1159 // If we arrive here, an overflow occurred in id.
1160 // That means no ID was found
1161 return CONTENT_IGNORE;
1166 * Returns the smallest box that contains all boxes
1167 * in the vector. Box_union is expanded.
1168 * @param[in] boxes the vector containing the boxes
1169 * @param[in, out] box_union the union of the arguments
1171 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1173 for (const aabb3f &box : boxes) {
1174 box_union->addInternalBox(box);
1180 * Returns a box that contains the nodebox in every case.
1181 * The argument node_union is expanded.
1182 * @param[in] nodebox the nodebox to be measured
1183 * @param[in] features used to decide whether the nodebox
1185 * @param[in, out] box_union the union of the arguments
1187 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1190 switch(nodebox.type) {
1192 case NODEBOX_LEVELED: {
1194 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1195 boxVectorUnion(nodebox.fixed, &half_processed);
1196 // Set leveled boxes to maximal
1197 if (nodebox.type == NODEBOX_LEVELED) {
1198 half_processed.MaxEdge.Y = +BS / 2;
1200 if (features.param_type_2 == CPT2_FACEDIR ||
1201 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1202 // Get maximal coordinate
1204 fabsf(half_processed.MinEdge.X),
1205 fabsf(half_processed.MinEdge.Y),
1206 fabsf(half_processed.MinEdge.Z),
1207 fabsf(half_processed.MaxEdge.X),
1208 fabsf(half_processed.MaxEdge.Y),
1209 fabsf(half_processed.MaxEdge.Z) };
1211 for (float coord : coords) {
1216 // Add the union of all possible rotated boxes
1217 box_union->addInternalPoint(-max, -max, -max);
1218 box_union->addInternalPoint(+max, +max, +max);
1220 box_union->addInternalBox(half_processed);
1224 case NODEBOX_WALLMOUNTED: {
1226 box_union->addInternalBox(nodebox.wall_top);
1227 box_union->addInternalBox(nodebox.wall_bottom);
1228 // Find maximal coordinate in the X-Z plane
1230 fabsf(nodebox.wall_side.MinEdge.X),
1231 fabsf(nodebox.wall_side.MinEdge.Z),
1232 fabsf(nodebox.wall_side.MaxEdge.X),
1233 fabsf(nodebox.wall_side.MaxEdge.Z) };
1235 for (float coord : coords) {
1240 // Add the union of all possible rotated boxes
1241 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1242 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1245 case NODEBOX_CONNECTED: {
1246 // Add all possible connected boxes
1247 boxVectorUnion(nodebox.fixed, box_union);
1248 boxVectorUnion(nodebox.connect_top, box_union);
1249 boxVectorUnion(nodebox.connect_bottom, box_union);
1250 boxVectorUnion(nodebox.connect_front, box_union);
1251 boxVectorUnion(nodebox.connect_left, box_union);
1252 boxVectorUnion(nodebox.connect_back, box_union);
1253 boxVectorUnion(nodebox.connect_right, box_union);
1258 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1259 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1265 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1267 m_selection_box_int_union.MinEdge.X = floorf(
1268 m_selection_box_union.MinEdge.X / BS + 0.5f);
1269 m_selection_box_int_union.MinEdge.Y = floorf(
1270 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1271 m_selection_box_int_union.MinEdge.Z = floorf(
1272 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1273 m_selection_box_int_union.MaxEdge.X = ceilf(
1274 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1275 m_selection_box_int_union.MaxEdge.Y = ceilf(
1276 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1277 m_selection_box_int_union.MaxEdge.Z = ceilf(
1278 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1282 // IWritableNodeDefManager
1283 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1287 assert(name == def.name);
1289 // Don't allow redefining ignore (but allow air and unknown)
1290 if (name == "ignore") {
1291 warningstream << "NodeDefManager: Ignoring "
1292 "CONTENT_IGNORE redefinition"<<std::endl;
1293 return CONTENT_IGNORE;
1296 content_t id = CONTENT_IGNORE;
1297 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1300 if (id == CONTENT_IGNORE) {
1301 warningstream << "NodeDefManager: Absolute "
1302 "limit reached" << std::endl;
1303 return CONTENT_IGNORE;
1305 assert(id != CONTENT_IGNORE);
1306 addNameIdMapping(id, name);
1308 m_content_features[id] = def;
1309 verbosestream << "NodeDefManager: registering content id \"" << id
1310 << "\": name=\"" << def.name << "\""<<std::endl;
1312 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1313 fixSelectionBoxIntUnion();
1314 // Add this content to the list of all groups it belongs to
1315 // FIXME: This should remove a node from groups it no longer
1316 // belongs to when a node is re-registered
1317 for (const auto &group : def.groups) {
1318 const std::string &group_name = group.first;
1319 m_group_to_items[group_name].push_back(id);
1325 content_t CNodeDefManager::allocateDummy(const std::string &name)
1327 assert(name != ""); // Pre-condition
1330 return set(name, f);
1334 void CNodeDefManager::removeNode(const std::string &name)
1339 // Erase name from name ID mapping
1340 content_t id = CONTENT_IGNORE;
1341 if (m_name_id_mapping.getId(name, id)) {
1342 m_name_id_mapping.eraseName(name);
1343 m_name_id_mapping_with_aliases.erase(name);
1346 // Erase node content from all groups it belongs to
1347 for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
1348 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1349 std::vector<content_t> &items = iter_groups->second;
1350 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1352 // Check if group is empty
1354 m_group_to_items.erase(iter_groups++);
1361 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1363 std::set<std::string> all;
1365 m_name_id_mapping_with_aliases.clear();
1366 for (const std::string &name : all) {
1367 const std::string &convert_to = idef->getAlias(name);
1369 if (m_name_id_mapping.getId(convert_to, id)) {
1370 m_name_id_mapping_with_aliases.insert(
1371 std::make_pair(name, id));
1376 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1378 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1379 "overrides to textures from " << override_filepath << std::endl;
1381 std::ifstream infile(override_filepath.c_str());
1384 while (std::getline(infile, line)) {
1386 if (trim(line).empty())
1388 std::vector<std::string> splitted = str_split(line, ' ');
1389 if (splitted.size() != 3) {
1390 errorstream << override_filepath
1391 << ":" << line_c << " Could not apply texture override \""
1392 << line << "\": Syntax error" << std::endl;
1397 if (!getId(splitted[0], id))
1398 continue; // Ignore unknown node
1400 ContentFeatures &nodedef = m_content_features[id];
1402 if (splitted[1] == "top")
1403 nodedef.tiledef[0].name = splitted[2];
1404 else if (splitted[1] == "bottom")
1405 nodedef.tiledef[1].name = splitted[2];
1406 else if (splitted[1] == "right")
1407 nodedef.tiledef[2].name = splitted[2];
1408 else if (splitted[1] == "left")
1409 nodedef.tiledef[3].name = splitted[2];
1410 else if (splitted[1] == "back")
1411 nodedef.tiledef[4].name = splitted[2];
1412 else if (splitted[1] == "front")
1413 nodedef.tiledef[5].name = splitted[2];
1414 else if (splitted[1] == "all" || splitted[1] == "*")
1415 for (TileDef &i : nodedef.tiledef)
1416 i.name = splitted[2];
1417 else if (splitted[1] == "sides")
1418 for (int i = 2; i < 6; i++)
1419 nodedef.tiledef[i].name = splitted[2];
1421 errorstream << override_filepath
1422 << ":" << line_c << " Could not apply texture override \""
1423 << line << "\": Unknown node side \""
1424 << splitted[1] << "\"" << std::endl;
1430 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1431 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1432 void *progress_callback_args)
1435 infostream << "CNodeDefManager::updateTextures(): Updating "
1436 "textures in node definitions" << std::endl;
1438 Client *client = (Client *)gamedef;
1439 ITextureSource *tsrc = client->tsrc();
1440 IShaderSource *shdsrc = client->getShaderSource();
1441 scene::IMeshManipulator *meshmanip =
1442 RenderingEngine::get_scene_manager()->getMeshManipulator();
1443 TextureSettings tsettings;
1444 tsettings.readSettings();
1446 u32 size = m_content_features.size();
1448 for (u32 i = 0; i < size; i++) {
1449 ContentFeatures *f = &(m_content_features[i]);
1450 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1451 progress_callback(progress_callback_args, i, size);
1456 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1458 writeU8(os, 1); // version
1460 std::ostringstream os2(std::ios::binary);
1461 for (u32 i = 0; i < m_content_features.size(); i++) {
1462 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1463 || i == CONTENT_UNKNOWN)
1465 const ContentFeatures *f = &m_content_features[i];
1466 if (f->name.empty())
1469 // Wrap it in a string to allow different lengths without
1470 // strict version incompatibilities
1471 std::ostringstream wrapper_os(std::ios::binary);
1472 f->serialize(wrapper_os, protocol_version);
1473 os2<<serializeString(wrapper_os.str());
1475 // must not overflow
1476 u16 next = count + 1;
1477 FATAL_ERROR_IF(next < count, "Overflow");
1480 writeU16(os, count);
1481 os << serializeLongString(os2.str());
1485 void CNodeDefManager::deSerialize(std::istream &is)
1488 int version = readU8(is);
1490 throw SerializationError("unsupported NodeDefinitionManager version");
1491 u16 count = readU16(is);
1492 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1494 for (u16 n = 0; n < count; n++) {
1495 u16 i = readU16(is2);
1497 // Read it from the string wrapper
1498 std::string wrapper = deSerializeString(is2);
1499 std::istringstream wrapper_is(wrapper, std::ios::binary);
1500 f.deSerialize(wrapper_is);
1502 // Check error conditions
1503 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1504 warningstream << "NodeDefManager::deSerialize(): "
1505 "not changing builtin node " << i << std::endl;
1508 if (f.name.empty()) {
1509 warningstream << "NodeDefManager::deSerialize(): "
1510 "received empty name" << std::endl;
1516 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1517 warningstream << "NodeDefManager::deSerialize(): "
1518 "already defined with different ID: " << f.name << std::endl;
1522 // All is ok, add node definition with the requested ID
1523 if (i >= m_content_features.size())
1524 m_content_features.resize((u32)(i) + 1);
1525 m_content_features[i] = f;
1526 addNameIdMapping(i, f.name);
1527 verbosestream << "deserialized " << f.name << std::endl;
1529 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1530 fixSelectionBoxIntUnion();
1535 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1537 m_name_id_mapping.set(i, name);
1538 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1542 IWritableNodeDefManager *createNodeDefManager()
1544 return new CNodeDefManager();
1547 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1549 m_node_registration_complete = completed;
1553 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1556 if (m_node_registration_complete)
1557 nr->nodeResolveInternal();
1559 m_pending_resolve_callbacks.push_back(nr);
1563 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1565 size_t len = m_pending_resolve_callbacks.size();
1566 for (size_t i = 0; i != len; i++) {
1567 if (nr != m_pending_resolve_callbacks[i])
1571 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1572 m_pending_resolve_callbacks.resize(len);
1580 void CNodeDefManager::runNodeResolveCallbacks()
1582 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1583 NodeResolver *nr = m_pending_resolve_callbacks[i];
1584 nr->nodeResolveInternal();
1587 m_pending_resolve_callbacks.clear();
1591 void CNodeDefManager::resetNodeResolveState()
1593 m_node_registration_complete = false;
1594 m_pending_resolve_callbacks.clear();
1597 void CNodeDefManager::mapNodeboxConnections()
1599 for (ContentFeatures &f : m_content_features) {
1600 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1603 for (const std::string &name : f.connects_to) {
1604 getIds(name, f.connects_to_ids);
1609 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1611 const ContentFeatures &f1 = get(from);
1613 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1616 // lookup target in connected set
1617 if (!CONTAINS(f1.connects_to_ids, to.param0))
1620 const ContentFeatures &f2 = get(to);
1622 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1623 // ignores actually looking if back connection exists
1624 return CONTAINS(f2.connects_to_ids, from.param0);
1626 // does to node declare usable faces?
1627 if (f2.connect_sides > 0) {
1628 if ((f2.param_type_2 == CPT2_FACEDIR ||
1629 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1630 && (connect_face >= 4)) {
1631 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1632 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1634 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1636 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1637 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1639 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1640 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1641 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1643 return (f2.connect_sides
1644 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1646 return (f2.connect_sides & connect_face);
1648 // the target is just a regular node, so connect no matter back connection
1656 NodeResolver::NodeResolver()
1658 m_nodenames.reserve(16);
1659 m_nnlistsizes.reserve(4);
1663 NodeResolver::~NodeResolver()
1665 if (!m_resolve_done && m_ndef)
1666 m_ndef->cancelNodeResolveCallback(this);
1670 void NodeResolver::nodeResolveInternal()
1672 m_nodenames_idx = 0;
1673 m_nnlistsizes_idx = 0;
1676 m_resolve_done = true;
1678 m_nodenames.clear();
1679 m_nnlistsizes.clear();
1683 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1684 const std::string &node_alt, content_t c_fallback)
1686 if (m_nodenames_idx == m_nodenames.size()) {
1687 *result_out = c_fallback;
1688 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1693 std::string name = m_nodenames[m_nodenames_idx++];
1695 bool success = m_ndef->getId(name, c);
1696 if (!success && !node_alt.empty()) {
1698 success = m_ndef->getId(name, c);
1702 errorstream << "NodeResolver: failed to resolve node name '" << name
1703 << "'." << std::endl;
1712 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1713 bool all_required, content_t c_fallback)
1715 bool success = true;
1717 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1718 errorstream << "NodeResolver: no more node lists" << std::endl;
1722 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1725 if (m_nodenames_idx == m_nodenames.size()) {
1726 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1731 std::string &name = m_nodenames[m_nodenames_idx++];
1733 if (name.substr(0,6) != "group:") {
1734 if (m_ndef->getId(name, c)) {
1735 result_out->push_back(c);
1736 } else if (all_required) {
1737 errorstream << "NodeResolver: failed to resolve node name '"
1738 << name << "'." << std::endl;
1739 result_out->push_back(c_fallback);
1743 m_ndef->getIds(name, *result_out);