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 os << serializeString(name);
211 animation.serialize(os, version);
212 bool has_scale = scale > 0;
214 if (backface_culling)
215 flags |= TILE_FLAG_BACKFACE_CULLING;
216 if (tileable_horizontal)
217 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
218 if (tileable_vertical)
219 flags |= TILE_FLAG_TILEABLE_VERTICAL;
221 flags |= TILE_FLAG_HAS_COLOR;
223 flags |= TILE_FLAG_HAS_SCALE;
224 if (align_style != ALIGN_STYLE_NODE)
225 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
228 writeU8(os, color.getRed());
229 writeU8(os, color.getGreen());
230 writeU8(os, color.getBlue());
234 if (align_style != ALIGN_STYLE_NODE)
235 writeU8(os, align_style);
238 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
239 NodeDrawType drawtype)
241 int version = readU8(is);
243 throw SerializationError("unsupported TileDef version");
244 name = deSerializeString(is);
245 animation.deSerialize(is, version);
246 u16 flags = readU16(is);
247 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
248 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
249 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
250 has_color = flags & TILE_FLAG_HAS_COLOR;
251 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
252 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
254 color.setRed(readU8(is));
255 color.setGreen(readU8(is));
256 color.setBlue(readU8(is));
258 scale = has_scale ? readU8(is) : 0;
260 align_style = static_cast<AlignStyle>(readU8(is));
262 align_style = ALIGN_STYLE_NODE;
265 void TextureSettings::readSettings()
267 connected_glass = g_settings->getBool("connected_glass");
268 opaque_water = g_settings->getBool("opaque_water");
269 bool enable_shaders = g_settings->getBool("enable_shaders");
270 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
271 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
272 bool smooth_lighting = g_settings->getBool("smooth_lighting");
273 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
274 enable_minimap = g_settings->getBool("enable_minimap");
275 node_texture_size = g_settings->getU16("texture_min_size");
276 std::string leaves_style_str = g_settings->get("leaves_style");
277 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
278 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
280 // Mesh cache is not supported in combination with smooth lighting
282 enable_mesh_cache = false;
284 use_normal_texture = enable_shaders &&
285 (enable_bumpmapping || enable_parallax_occlusion);
286 if (leaves_style_str == "fancy") {
287 leaves_style = LEAVES_FANCY;
288 } else if (leaves_style_str == "simple") {
289 leaves_style = LEAVES_SIMPLE;
291 leaves_style = LEAVES_OPAQUE;
294 if (world_aligned_mode_str == "enable")
295 world_aligned_mode = WORLDALIGN_ENABLE;
296 else if (world_aligned_mode_str == "force_solid")
297 world_aligned_mode = WORLDALIGN_FORCE;
298 else if (world_aligned_mode_str == "force_nodebox")
299 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
301 world_aligned_mode = WORLDALIGN_DISABLE;
303 if (autoscale_mode_str == "enable")
304 autoscale_mode = AUTOSCALE_ENABLE;
305 else if (autoscale_mode_str == "force")
306 autoscale_mode = AUTOSCALE_FORCE;
308 autoscale_mode = AUTOSCALE_DISABLE;
315 ContentFeatures::ContentFeatures()
320 void ContentFeatures::reset()
327 visual_solidness = 0;
328 backface_culling = true;
331 has_on_construct = false;
332 has_on_destruct = false;
333 has_after_destruct = false;
337 NOTE: Most of this is always overridden by the default values given
342 // Unknown nodes can be dug
343 groups["dig_immediate"] = 2;
344 drawtype = NDT_NORMAL;
347 for (auto &i : mesh_ptr)
349 minimap_color = video::SColor(0, 0, 0, 0);
352 for (auto &i : tiledef)
354 for (auto &j : tiledef_special)
357 post_effect_color = video::SColor(0, 0, 0, 0);
358 param_type = CPT_NONE;
359 param_type_2 = CPT2_NONE;
360 is_ground_content = false;
361 light_propagates = false;
362 sunlight_propagates = false;
367 buildable_to = false;
369 rightclickable = true;
371 liquid_type = LIQUID_NONE;
372 liquid_alternative_flowing = "";
373 liquid_alternative_source = "";
374 liquid_viscosity = 0;
375 liquid_renewable = true;
376 liquid_range = LIQUID_LEVEL_MAX+1;
379 damage_per_second = 0;
380 node_box = NodeBox();
381 selection_box = NodeBox();
382 collision_box = NodeBox();
384 legacy_facedir_simple = false;
385 legacy_wallmounted = false;
386 sound_footstep = SimpleSoundSpec();
387 sound_dig = SimpleSoundSpec("__group");
388 sound_dug = SimpleSoundSpec();
390 connects_to_ids.clear();
392 color = video::SColor(0xFFFFFFFF);
395 node_dig_prediction = "air";
398 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
400 const u8 version = CONTENTFEATURES_VERSION;
401 writeU8(os, version);
404 os << serializeString(name);
405 writeU16(os, groups.size());
406 for (const auto &group : groups) {
407 os << serializeString(group.first);
408 writeS16(os, group.second);
410 writeU8(os, param_type);
411 writeU8(os, param_type_2);
414 writeU8(os, drawtype);
415 os << serializeString(mesh);
416 writeF32(os, visual_scale);
418 for (const TileDef &td : tiledef)
419 td.serialize(os, protocol_version);
420 for (const TileDef &td : tiledef_overlay)
421 td.serialize(os, protocol_version);
422 writeU8(os, CF_SPECIAL_COUNT);
423 for (const TileDef &td : tiledef_special) {
424 td.serialize(os, protocol_version);
427 writeU8(os, color.getRed());
428 writeU8(os, color.getGreen());
429 writeU8(os, color.getBlue());
430 os << serializeString(palette_name);
432 writeU8(os, connect_sides);
433 writeU16(os, connects_to_ids.size());
434 for (u16 connects_to_id : connects_to_ids)
435 writeU16(os, connects_to_id);
436 writeARGB8(os, post_effect_color);
437 writeU8(os, leveled);
440 writeU8(os, light_propagates);
441 writeU8(os, sunlight_propagates);
442 writeU8(os, light_source);
445 writeU8(os, is_ground_content);
448 writeU8(os, walkable);
449 writeU8(os, pointable);
450 writeU8(os, diggable);
451 writeU8(os, climbable);
452 writeU8(os, buildable_to);
453 writeU8(os, rightclickable);
454 writeU32(os, damage_per_second);
457 writeU8(os, liquid_type);
458 os << serializeString(liquid_alternative_flowing);
459 os << serializeString(liquid_alternative_source);
460 writeU8(os, liquid_viscosity);
461 writeU8(os, liquid_renewable);
462 writeU8(os, liquid_range);
463 writeU8(os, drowning);
464 writeU8(os, floodable);
467 node_box.serialize(os, protocol_version);
468 selection_box.serialize(os, protocol_version);
469 collision_box.serialize(os, protocol_version);
472 sound_footstep.serialize(os, version);
473 sound_dig.serialize(os, version);
474 sound_dug.serialize(os, version);
477 writeU8(os, legacy_facedir_simple);
478 writeU8(os, legacy_wallmounted);
480 os << serializeString(node_dig_prediction);
483 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
485 // alpha == 0 means that the node is using texture alpha
486 if (alpha == 0 || alpha == 255)
489 for (int i = 0; i < length; i++) {
490 if (tiles[i].name.empty())
493 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
494 tiles[i].name = s.str();
498 void ContentFeatures::deSerialize(std::istream &is)
501 const u8 version = readU8(is);
502 if (version < CONTENTFEATURES_VERSION)
503 throw SerializationError("unsupported ContentFeatures version");
506 name = deSerializeString(is);
508 u32 groups_size = readU16(is);
509 for (u32 i = 0; i < groups_size; i++) {
510 std::string name = deSerializeString(is);
511 int value = readS16(is);
512 groups[name] = value;
514 param_type = (enum ContentParamType) readU8(is);
515 param_type_2 = (enum ContentParamType2) readU8(is);
518 drawtype = (enum NodeDrawType) readU8(is);
519 mesh = deSerializeString(is);
520 visual_scale = readF32(is);
522 throw SerializationError("unsupported tile count");
523 for (TileDef &td : tiledef)
524 td.deSerialize(is, version, drawtype);
525 for (TileDef &td : tiledef_overlay)
526 td.deSerialize(is, version, drawtype);
527 if (readU8(is) != CF_SPECIAL_COUNT)
528 throw SerializationError("unsupported CF_SPECIAL_COUNT");
529 for (TileDef &td : tiledef_special)
530 td.deSerialize(is, version, drawtype);
532 color.setRed(readU8(is));
533 color.setGreen(readU8(is));
534 color.setBlue(readU8(is));
535 palette_name = deSerializeString(is);
537 connect_sides = readU8(is);
538 u16 connects_to_size = readU16(is);
539 connects_to_ids.clear();
540 for (u16 i = 0; i < connects_to_size; i++)
541 connects_to_ids.push_back(readU16(is));
542 post_effect_color = readARGB8(is);
543 leveled = readU8(is);
546 light_propagates = readU8(is);
547 sunlight_propagates = readU8(is);
548 light_source = readU8(is);
549 light_source = MYMIN(light_source, LIGHT_MAX);
552 is_ground_content = readU8(is);
555 walkable = readU8(is);
556 pointable = readU8(is);
557 diggable = readU8(is);
558 climbable = readU8(is);
559 buildable_to = readU8(is);
560 rightclickable = readU8(is);
561 damage_per_second = readU32(is);
564 liquid_type = (enum LiquidType) readU8(is);
565 liquid_alternative_flowing = deSerializeString(is);
566 liquid_alternative_source = deSerializeString(is);
567 liquid_viscosity = readU8(is);
568 liquid_renewable = readU8(is);
569 liquid_range = readU8(is);
570 drowning = readU8(is);
571 floodable = readU8(is);
574 node_box.deSerialize(is);
575 selection_box.deSerialize(is);
576 collision_box.deSerialize(is);
579 sound_footstep.deSerialize(is, version);
580 sound_dig.deSerialize(is, version);
581 sound_dug.deSerialize(is, version);
583 // read legacy properties
584 legacy_facedir_simple = readU8(is);
585 legacy_wallmounted = readU8(is);
588 node_dig_prediction = deSerializeString(is);
589 } catch(SerializationError &e) {};
593 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
594 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
595 u8 material_type, u32 shader_id, bool backface_culling,
596 const TextureSettings &tsettings)
598 layer->shader_id = shader_id;
599 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
600 layer->material_type = material_type;
602 bool has_scale = tiledef.scale > 0;
603 if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
604 (tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
605 auto texture_size = layer->texture->getOriginalSize();
606 float base_size = tsettings.node_texture_size;
607 float size = std::fmin(texture_size.Width, texture_size.Height);
608 layer->scale = std::fmax(base_size, size) / base_size;
609 } else if (has_scale) {
610 layer->scale = tiledef.scale;
614 if (!tile.world_aligned)
617 // Normal texture and shader flags texture
618 if (tsettings.use_normal_texture) {
619 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
621 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
624 layer->material_flags = 0;
625 if (backface_culling)
626 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
627 if (tiledef.animation.type != TAT_NONE)
628 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
629 if (tiledef.tileable_horizontal)
630 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
631 if (tiledef.tileable_vertical)
632 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
635 layer->has_color = tiledef.has_color;
636 if (tiledef.has_color)
637 layer->color = tiledef.color;
639 layer->color = color;
641 // Animation parameters
643 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
645 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
646 &frame_count, &frame_length_ms, NULL);
647 layer->animation_frame_count = frame_count;
648 layer->animation_frame_length_ms = frame_length_ms;
651 if (frame_count == 1) {
652 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
654 std::ostringstream os(std::ios::binary);
655 if (!layer->frames) {
656 layer->frames = std::make_shared<std::vector<FrameSpec>>();
658 layer->frames->resize(frame_count);
660 for (int i = 0; i < frame_count; i++) {
666 tiledef.animation.getTextureModifer(os,
667 layer->texture->getOriginalSize(), i);
669 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
670 if (layer->normal_texture)
671 frame.normal_texture = tsrc->getNormalTexture(os.str());
672 frame.flags_texture = layer->flags_texture;
673 (*layer->frames)[i] = frame;
680 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
682 if (style == ALIGN_STYLE_WORLD)
684 if (mode == WORLDALIGN_DISABLE)
686 if (style == ALIGN_STYLE_USER_DEFINED)
688 if (drawtype == NDT_NORMAL)
689 return mode >= WORLDALIGN_FORCE;
690 if (drawtype == NDT_NODEBOX)
691 return mode >= WORLDALIGN_FORCE_NODEBOX;
695 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
696 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
698 // minimap pixel color - the average color of a texture
699 if (tsettings.enable_minimap && !tiledef[0].name.empty())
700 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
702 // Figure out the actual tiles to use
704 for (u32 j = 0; j < 6; j++) {
705 tdef[j] = tiledef[j];
706 if (tdef[j].name.empty())
707 tdef[j].name = "unknown_node.png";
709 // also the overlay tiles
710 TileDef tdef_overlay[6];
711 for (u32 j = 0; j < 6; j++)
712 tdef_overlay[j] = tiledef_overlay[j];
713 // also the special tiles
714 TileDef tdef_spec[6];
715 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
716 tdef_spec[j] = tiledef_special[j];
718 bool is_liquid = false;
720 u8 material_type = (alpha == 255) ?
721 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
726 material_type = (alpha == 255) ?
727 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
734 assert(liquid_type == LIQUID_SOURCE);
735 if (tsettings.opaque_water)
740 case NDT_FLOWINGLIQUID:
741 assert(liquid_type == LIQUID_FLOWING);
743 if (tsettings.opaque_water)
749 visual_solidness = 1;
751 case NDT_GLASSLIKE_FRAMED:
753 visual_solidness = 1;
755 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
757 visual_solidness = 1;
758 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
762 visual_solidness = 1;
764 case NDT_ALLFACES_OPTIONAL:
765 if (tsettings.leaves_style == LEAVES_FANCY) {
766 drawtype = NDT_ALLFACES;
768 visual_solidness = 1;
769 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
770 for (u32 j = 0; j < 6; j++) {
771 if (!tdef_spec[j].name.empty())
772 tdef[j].name = tdef_spec[j].name;
774 drawtype = NDT_GLASSLIKE;
776 visual_solidness = 1;
778 drawtype = NDT_NORMAL;
780 for (TileDef &td : tdef)
781 td.name += std::string("^[noalpha");
784 material_type = TILE_MATERIAL_WAVING_LEAVES;
789 material_type = TILE_MATERIAL_WAVING_PLANTS;
798 material_type = TILE_MATERIAL_WAVING_PLANTS;
799 else if (waving == 2)
800 material_type = TILE_MATERIAL_WAVING_LEAVES;
801 else if (waving == 3)
802 material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC;
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);
822 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
823 TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
825 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
826 TILE_MATERIAL_LIQUID_TRANSPARENT;
830 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
832 u8 overlay_material = material_type;
833 if (overlay_material == TILE_MATERIAL_OPAQUE)
834 overlay_material = TILE_MATERIAL_BASIC;
835 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
836 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
838 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
840 // Tiles (fill in f->tiles[])
841 for (u16 j = 0; j < 6; j++) {
842 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
843 tsettings.world_aligned_mode, drawtype);
844 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
845 color, material_type, tile_shader,
846 tdef[j].backface_culling, tsettings);
847 if (!tdef_overlay[j].name.empty())
848 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
849 color, overlay_material, overlay_shader,
850 tdef[j].backface_culling, tsettings);
853 u8 special_material = material_type;
854 if (drawtype == NDT_PLANTLIKE_ROOTED) {
856 special_material = TILE_MATERIAL_WAVING_PLANTS;
857 else if (waving == 2)
858 special_material = TILE_MATERIAL_WAVING_LEAVES;
860 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
862 // Special tiles (fill in f->special_tiles[])
863 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
864 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
865 color, special_material, special_shader,
866 tdef_spec[j].backface_culling, tsettings);
868 if (param_type_2 == CPT2_COLOR ||
869 param_type_2 == CPT2_COLORED_FACEDIR ||
870 param_type_2 == CPT2_COLORED_WALLMOUNTED)
871 palette = tsrc->getPalette(palette_name);
873 if (drawtype == NDT_MESH && !mesh.empty()) {
875 // Read the mesh and apply scale
876 mesh_ptr[0] = client->getMesh(mesh);
878 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
879 scaleMesh(mesh_ptr[0], scale);
880 recalculateBoundingBox(mesh_ptr[0]);
881 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
885 //Cache 6dfacedir and wallmounted rotated clones of meshes
886 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
887 (param_type_2 == CPT2_FACEDIR
888 || param_type_2 == CPT2_COLORED_FACEDIR)) {
889 for (u16 j = 1; j < 24; j++) {
890 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
891 rotateMeshBy6dFacedir(mesh_ptr[j], j);
892 recalculateBoundingBox(mesh_ptr[j]);
893 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
895 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
896 && (param_type_2 == CPT2_WALLMOUNTED ||
897 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
898 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
899 for (u16 j = 1; j < 6; j++) {
900 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
901 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
902 recalculateBoundingBox(mesh_ptr[j]);
903 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
905 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
906 recalculateBoundingBox(mesh_ptr[0]);
907 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
919 NodeDefManager::NodeDefManager()
925 NodeDefManager::~NodeDefManager()
928 for (ContentFeatures &f : m_content_features) {
929 for (auto &j : f.mesh_ptr) {
938 void NodeDefManager::clear()
940 m_content_features.clear();
941 m_name_id_mapping.clear();
942 m_name_id_mapping_with_aliases.clear();
943 m_group_to_items.clear();
945 m_selection_box_union.reset(0,0,0);
946 m_selection_box_int_union.reset(0,0,0);
948 resetNodeResolveState();
950 u32 initial_length = 0;
951 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
952 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
953 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
954 m_content_features.resize(initial_length);
956 // Set CONTENT_UNKNOWN
960 // Insert directly into containers
961 content_t c = CONTENT_UNKNOWN;
962 m_content_features[c] = f;
963 addNameIdMapping(c, f.name);
970 f.drawtype = NDT_AIRLIKE;
971 f.param_type = CPT_LIGHT;
972 f.light_propagates = true;
973 f.sunlight_propagates = true;
977 f.buildable_to = true;
979 f.is_ground_content = true;
980 // Insert directly into containers
981 content_t c = CONTENT_AIR;
982 m_content_features[c] = f;
983 addNameIdMapping(c, f.name);
986 // Set CONTENT_IGNORE
990 f.drawtype = NDT_AIRLIKE;
991 f.param_type = CPT_NONE;
992 f.light_propagates = false;
993 f.sunlight_propagates = false;
997 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
998 f.is_ground_content = true;
999 // Insert directly into containers
1000 content_t c = CONTENT_IGNORE;
1001 m_content_features[c] = f;
1002 addNameIdMapping(c, f.name);
1007 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1009 std::unordered_map<std::string, content_t>::const_iterator
1010 i = m_name_id_mapping_with_aliases.find(name);
1011 if(i == m_name_id_mapping_with_aliases.end())
1018 content_t NodeDefManager::getId(const std::string &name) const
1020 content_t id = CONTENT_IGNORE;
1026 bool NodeDefManager::getIds(const std::string &name,
1027 std::vector<content_t> &result) const
1029 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1030 if (name.substr(0,6) != "group:") {
1031 content_t id = CONTENT_IGNORE;
1032 bool exists = getId(name, id);
1034 result.push_back(id);
1037 std::string group = name.substr(6);
1039 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1040 i = m_group_to_items.find(group);
1041 if (i == m_group_to_items.end())
1044 const std::vector<content_t> &items = i->second;
1045 result.insert(result.end(), items.begin(), items.end());
1046 //printf("getIds: %dus\n", t.stop());
1051 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1053 content_t id = CONTENT_UNKNOWN;
1059 // returns CONTENT_IGNORE if no free ID found
1060 content_t NodeDefManager::allocateId()
1062 for (content_t id = m_next_id;
1063 id >= m_next_id; // overflow?
1065 while (id >= m_content_features.size()) {
1066 m_content_features.emplace_back();
1068 const ContentFeatures &f = m_content_features[id];
1069 if (f.name.empty()) {
1074 // If we arrive here, an overflow occurred in id.
1075 // That means no ID was found
1076 return CONTENT_IGNORE;
1081 * Returns the smallest box that contains all boxes
1082 * in the vector. Box_union is expanded.
1083 * @param[in] boxes the vector containing the boxes
1084 * @param[in, out] box_union the union of the arguments
1086 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1088 for (const aabb3f &box : boxes) {
1089 box_union->addInternalBox(box);
1095 * Returns a box that contains the nodebox in every case.
1096 * The argument node_union is expanded.
1097 * @param[in] nodebox the nodebox to be measured
1098 * @param[in] features used to decide whether the nodebox
1100 * @param[in, out] box_union the union of the arguments
1102 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1105 switch(nodebox.type) {
1107 case NODEBOX_LEVELED: {
1109 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1110 boxVectorUnion(nodebox.fixed, &half_processed);
1111 // Set leveled boxes to maximal
1112 if (nodebox.type == NODEBOX_LEVELED) {
1113 half_processed.MaxEdge.Y = +BS / 2;
1115 if (features.param_type_2 == CPT2_FACEDIR ||
1116 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1117 // Get maximal coordinate
1119 fabsf(half_processed.MinEdge.X),
1120 fabsf(half_processed.MinEdge.Y),
1121 fabsf(half_processed.MinEdge.Z),
1122 fabsf(half_processed.MaxEdge.X),
1123 fabsf(half_processed.MaxEdge.Y),
1124 fabsf(half_processed.MaxEdge.Z) };
1126 for (float coord : coords) {
1131 // Add the union of all possible rotated boxes
1132 box_union->addInternalPoint(-max, -max, -max);
1133 box_union->addInternalPoint(+max, +max, +max);
1135 box_union->addInternalBox(half_processed);
1139 case NODEBOX_WALLMOUNTED: {
1141 box_union->addInternalBox(nodebox.wall_top);
1142 box_union->addInternalBox(nodebox.wall_bottom);
1143 // Find maximal coordinate in the X-Z plane
1145 fabsf(nodebox.wall_side.MinEdge.X),
1146 fabsf(nodebox.wall_side.MinEdge.Z),
1147 fabsf(nodebox.wall_side.MaxEdge.X),
1148 fabsf(nodebox.wall_side.MaxEdge.Z) };
1150 for (float coord : coords) {
1155 // Add the union of all possible rotated boxes
1156 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1157 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1160 case NODEBOX_CONNECTED: {
1161 // Add all possible connected boxes
1162 boxVectorUnion(nodebox.fixed, box_union);
1163 boxVectorUnion(nodebox.connect_top, box_union);
1164 boxVectorUnion(nodebox.connect_bottom, box_union);
1165 boxVectorUnion(nodebox.connect_front, box_union);
1166 boxVectorUnion(nodebox.connect_left, box_union);
1167 boxVectorUnion(nodebox.connect_back, box_union);
1168 boxVectorUnion(nodebox.connect_right, box_union);
1169 boxVectorUnion(nodebox.disconnected_top, box_union);
1170 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1171 boxVectorUnion(nodebox.disconnected_front, box_union);
1172 boxVectorUnion(nodebox.disconnected_left, box_union);
1173 boxVectorUnion(nodebox.disconnected_back, box_union);
1174 boxVectorUnion(nodebox.disconnected_right, box_union);
1175 boxVectorUnion(nodebox.disconnected, box_union);
1176 boxVectorUnion(nodebox.disconnected_sides, box_union);
1181 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1182 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1188 inline void NodeDefManager::fixSelectionBoxIntUnion()
1190 m_selection_box_int_union.MinEdge.X = floorf(
1191 m_selection_box_union.MinEdge.X / BS + 0.5f);
1192 m_selection_box_int_union.MinEdge.Y = floorf(
1193 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1194 m_selection_box_int_union.MinEdge.Z = floorf(
1195 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1196 m_selection_box_int_union.MaxEdge.X = ceilf(
1197 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1198 m_selection_box_int_union.MaxEdge.Y = ceilf(
1199 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1200 m_selection_box_int_union.MaxEdge.Z = ceilf(
1201 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1205 void NodeDefManager::eraseIdFromGroups(content_t id)
1207 // For all groups in m_group_to_items...
1208 for (auto iter_groups = m_group_to_items.begin();
1209 iter_groups != m_group_to_items.end();) {
1210 // Get the group items vector.
1211 std::vector<content_t> &items = iter_groups->second;
1213 // Remove any occurence of the id in the group items vector.
1214 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1216 // If group is empty, erase its vector from the map.
1218 iter_groups = m_group_to_items.erase(iter_groups);
1225 // IWritableNodeDefManager
1226 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1230 assert(name != "ignore");
1231 assert(name == def.name);
1233 content_t id = CONTENT_IGNORE;
1234 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1237 if (id == CONTENT_IGNORE) {
1238 warningstream << "NodeDefManager: Absolute "
1239 "limit reached" << std::endl;
1240 return CONTENT_IGNORE;
1242 assert(id != CONTENT_IGNORE);
1243 addNameIdMapping(id, name);
1246 // If there is already ContentFeatures registered for this id, clear old groups
1247 if (id < m_content_features.size())
1248 eraseIdFromGroups(id);
1250 m_content_features[id] = def;
1251 verbosestream << "NodeDefManager: registering content id \"" << id
1252 << "\": name=\"" << def.name << "\""<<std::endl;
1254 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1255 fixSelectionBoxIntUnion();
1257 // Add this content to the list of all groups it belongs to
1258 for (const auto &group : def.groups) {
1259 const std::string &group_name = group.first;
1260 m_group_to_items[group_name].push_back(id);
1267 content_t NodeDefManager::allocateDummy(const std::string &name)
1269 assert(name != ""); // Pre-condition
1272 return set(name, f);
1276 void NodeDefManager::removeNode(const std::string &name)
1281 // Erase name from name ID mapping
1282 content_t id = CONTENT_IGNORE;
1283 if (m_name_id_mapping.getId(name, id)) {
1284 m_name_id_mapping.eraseName(name);
1285 m_name_id_mapping_with_aliases.erase(name);
1288 eraseIdFromGroups(id);
1292 void NodeDefManager::updateAliases(IItemDefManager *idef)
1294 std::set<std::string> all;
1296 m_name_id_mapping_with_aliases.clear();
1297 for (const std::string &name : all) {
1298 const std::string &convert_to = idef->getAlias(name);
1300 if (m_name_id_mapping.getId(convert_to, id)) {
1301 m_name_id_mapping_with_aliases.insert(
1302 std::make_pair(name, id));
1307 void NodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1309 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1310 "overrides to textures from " << override_filepath << std::endl;
1312 std::ifstream infile(override_filepath.c_str());
1315 while (std::getline(infile, line)) {
1317 // Also trim '\r' on DOS-style files
1322 std::vector<std::string> splitted = str_split(line, ' ');
1323 if (splitted.size() != 3) {
1324 errorstream << override_filepath
1325 << ":" << line_c << " Could not apply texture override \""
1326 << line << "\": Syntax error" << std::endl;
1331 if (!getId(splitted[0], id))
1332 continue; // Ignore unknown node
1334 ContentFeatures &nodedef = m_content_features[id];
1336 if (splitted[1] == "top")
1337 nodedef.tiledef[0].name = splitted[2];
1338 else if (splitted[1] == "bottom")
1339 nodedef.tiledef[1].name = splitted[2];
1340 else if (splitted[1] == "right")
1341 nodedef.tiledef[2].name = splitted[2];
1342 else if (splitted[1] == "left")
1343 nodedef.tiledef[3].name = splitted[2];
1344 else if (splitted[1] == "back")
1345 nodedef.tiledef[4].name = splitted[2];
1346 else if (splitted[1] == "front")
1347 nodedef.tiledef[5].name = splitted[2];
1348 else if (splitted[1] == "all" || splitted[1] == "*")
1349 for (TileDef &i : nodedef.tiledef)
1350 i.name = splitted[2];
1351 else if (splitted[1] == "sides")
1352 for (int i = 2; i < 6; i++)
1353 nodedef.tiledef[i].name = splitted[2];
1355 errorstream << override_filepath
1356 << ":" << line_c << " Could not apply texture override \""
1357 << line << "\": Unknown node side \""
1358 << splitted[1] << "\"" << std::endl;
1364 void NodeDefManager::updateTextures(IGameDef *gamedef,
1365 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1366 void *progress_callback_args)
1369 infostream << "NodeDefManager::updateTextures(): Updating "
1370 "textures in node definitions" << std::endl;
1372 Client *client = (Client *)gamedef;
1373 ITextureSource *tsrc = client->tsrc();
1374 IShaderSource *shdsrc = client->getShaderSource();
1375 scene::IMeshManipulator *meshmanip =
1376 RenderingEngine::get_scene_manager()->getMeshManipulator();
1377 TextureSettings tsettings;
1378 tsettings.readSettings();
1380 u32 size = m_content_features.size();
1382 for (u32 i = 0; i < size; i++) {
1383 ContentFeatures *f = &(m_content_features[i]);
1384 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1385 progress_callback(progress_callback_args, i, size);
1390 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1392 writeU8(os, 1); // version
1394 std::ostringstream os2(std::ios::binary);
1395 for (u32 i = 0; i < m_content_features.size(); i++) {
1396 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1397 || i == CONTENT_UNKNOWN)
1399 const ContentFeatures *f = &m_content_features[i];
1400 if (f->name.empty())
1403 // Wrap it in a string to allow different lengths without
1404 // strict version incompatibilities
1405 std::ostringstream wrapper_os(std::ios::binary);
1406 f->serialize(wrapper_os, protocol_version);
1407 os2<<serializeString(wrapper_os.str());
1409 // must not overflow
1410 u16 next = count + 1;
1411 FATAL_ERROR_IF(next < count, "Overflow");
1414 writeU16(os, count);
1415 os << serializeLongString(os2.str());
1419 void NodeDefManager::deSerialize(std::istream &is)
1422 int version = readU8(is);
1424 throw SerializationError("unsupported NodeDefinitionManager version");
1425 u16 count = readU16(is);
1426 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1428 for (u16 n = 0; n < count; n++) {
1429 u16 i = readU16(is2);
1431 // Read it from the string wrapper
1432 std::string wrapper = deSerializeString(is2);
1433 std::istringstream wrapper_is(wrapper, std::ios::binary);
1434 f.deSerialize(wrapper_is);
1436 // Check error conditions
1437 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1438 warningstream << "NodeDefManager::deSerialize(): "
1439 "not changing builtin node " << i << std::endl;
1442 if (f.name.empty()) {
1443 warningstream << "NodeDefManager::deSerialize(): "
1444 "received empty name" << std::endl;
1450 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1451 warningstream << "NodeDefManager::deSerialize(): "
1452 "already defined with different ID: " << f.name << std::endl;
1456 // All is ok, add node definition with the requested ID
1457 if (i >= m_content_features.size())
1458 m_content_features.resize((u32)(i) + 1);
1459 m_content_features[i] = f;
1460 addNameIdMapping(i, f.name);
1461 verbosestream << "deserialized " << f.name << std::endl;
1463 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1464 fixSelectionBoxIntUnion();
1469 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1471 m_name_id_mapping.set(i, name);
1472 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1476 NodeDefManager *createNodeDefManager()
1478 return new NodeDefManager();
1482 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1485 if (m_node_registration_complete)
1486 nr->nodeResolveInternal();
1488 m_pending_resolve_callbacks.push_back(nr);
1492 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1494 size_t len = m_pending_resolve_callbacks.size();
1495 for (size_t i = 0; i != len; i++) {
1496 if (nr != m_pending_resolve_callbacks[i])
1500 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1501 m_pending_resolve_callbacks.resize(len);
1509 void NodeDefManager::runNodeResolveCallbacks()
1511 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1512 NodeResolver *nr = m_pending_resolve_callbacks[i];
1513 nr->nodeResolveInternal();
1516 m_pending_resolve_callbacks.clear();
1520 void NodeDefManager::resetNodeResolveState()
1522 m_node_registration_complete = false;
1523 m_pending_resolve_callbacks.clear();
1526 void NodeDefManager::mapNodeboxConnections()
1528 for (ContentFeatures &f : m_content_features) {
1529 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1532 for (const std::string &name : f.connects_to) {
1533 getIds(name, f.connects_to_ids);
1538 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1539 u8 connect_face) const
1541 const ContentFeatures &f1 = get(from);
1543 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1546 // lookup target in connected set
1547 if (!CONTAINS(f1.connects_to_ids, to.param0))
1550 const ContentFeatures &f2 = get(to);
1552 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1553 // ignores actually looking if back connection exists
1554 return CONTAINS(f2.connects_to_ids, from.param0);
1556 // does to node declare usable faces?
1557 if (f2.connect_sides > 0) {
1558 if ((f2.param_type_2 == CPT2_FACEDIR ||
1559 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1560 && (connect_face >= 4)) {
1561 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1562 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1564 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1566 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1567 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1569 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1570 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1571 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1573 return (f2.connect_sides
1574 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1576 return (f2.connect_sides & connect_face);
1578 // the target is just a regular node, so connect no matter back connection
1586 NodeResolver::NodeResolver()
1588 m_nodenames.reserve(16);
1589 m_nnlistsizes.reserve(4);
1593 NodeResolver::~NodeResolver()
1595 if (!m_resolve_done && m_ndef)
1596 m_ndef->cancelNodeResolveCallback(this);
1600 void NodeResolver::nodeResolveInternal()
1602 m_nodenames_idx = 0;
1603 m_nnlistsizes_idx = 0;
1606 m_resolve_done = true;
1608 m_nodenames.clear();
1609 m_nnlistsizes.clear();
1613 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1614 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1616 if (m_nodenames_idx == m_nodenames.size()) {
1617 *result_out = c_fallback;
1618 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1623 std::string name = m_nodenames[m_nodenames_idx++];
1625 bool success = m_ndef->getId(name, c);
1626 if (!success && !node_alt.empty()) {
1628 success = m_ndef->getId(name, c);
1632 if (error_on_fallback)
1633 errorstream << "NodeResolver: failed to resolve node name '" << name
1634 << "'." << std::endl;
1643 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1644 bool all_required, content_t c_fallback)
1646 bool success = true;
1648 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1649 errorstream << "NodeResolver: no more node lists" << std::endl;
1653 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1656 if (m_nodenames_idx == m_nodenames.size()) {
1657 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1662 std::string &name = m_nodenames[m_nodenames_idx++];
1664 if (name.substr(0,6) != "group:") {
1665 if (m_ndef->getId(name, c)) {
1666 result_out->push_back(c);
1667 } else if (all_required) {
1668 errorstream << "NodeResolver: failed to resolve node name '"
1669 << name << "'." << std::endl;
1670 result_out->push_back(c_fallback);
1674 m_ndef->getIds(name, *result_out);