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 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
174 // protocol_version >= 36
176 writeU8(os, version);
178 os << serializeString(name);
179 animation.serialize(os, version);
180 writeU8(os, backface_culling);
181 writeU8(os, tileable_horizontal);
182 writeU8(os, tileable_vertical);
183 writeU8(os, has_color);
185 writeU8(os, color.getRed());
186 writeU8(os, color.getGreen());
187 writeU8(os, color.getBlue());
191 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
192 NodeDrawType drawtype)
194 int version = readU8(is);
195 name = deSerializeString(is);
196 animation.deSerialize(is, version);
197 backface_culling = readU8(is);
198 tileable_horizontal = readU8(is);
199 tileable_vertical = readU8(is);
200 has_color = readU8(is);
202 color.setRed(readU8(is));
203 color.setGreen(readU8(is));
204 color.setBlue(readU8(is));
210 SimpleSoundSpec serialization
213 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
214 std::ostream &os, u8 version)
216 os<<serializeString(ss.name);
217 writeF1000(os, ss.gain);
218 writeF1000(os, ss.pitch);
220 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
221 std::istream &is, u8 version)
223 ss.name = deSerializeString(is);
224 ss.gain = readF1000(is);
225 ss.pitch = readF1000(is);
228 void TextureSettings::readSettings()
230 connected_glass = g_settings->getBool("connected_glass");
231 opaque_water = g_settings->getBool("opaque_water");
232 bool enable_shaders = g_settings->getBool("enable_shaders");
233 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
234 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
235 bool smooth_lighting = g_settings->getBool("smooth_lighting");
236 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
237 enable_minimap = g_settings->getBool("enable_minimap");
238 std::string leaves_style_str = g_settings->get("leaves_style");
240 // Mesh cache is not supported in combination with smooth lighting
242 enable_mesh_cache = false;
244 use_normal_texture = enable_shaders &&
245 (enable_bumpmapping || enable_parallax_occlusion);
246 if (leaves_style_str == "fancy") {
247 leaves_style = LEAVES_FANCY;
248 } else if (leaves_style_str == "simple") {
249 leaves_style = LEAVES_SIMPLE;
251 leaves_style = LEAVES_OPAQUE;
259 ContentFeatures::ContentFeatures()
264 void ContentFeatures::reset()
271 visual_solidness = 0;
272 backface_culling = true;
275 has_on_construct = false;
276 has_on_destruct = false;
277 has_after_destruct = false;
281 NOTE: Most of this is always overridden by the default values given
286 // Unknown nodes can be dug
287 groups["dig_immediate"] = 2;
288 drawtype = NDT_NORMAL;
291 for (auto &i : mesh_ptr)
293 minimap_color = video::SColor(0, 0, 0, 0);
296 for (auto &i : tiledef)
298 for (auto &j : tiledef_special)
301 post_effect_color = video::SColor(0, 0, 0, 0);
302 param_type = CPT_NONE;
303 param_type_2 = CPT2_NONE;
304 is_ground_content = false;
305 light_propagates = false;
306 sunlight_propagates = false;
311 buildable_to = false;
313 rightclickable = true;
315 liquid_type = LIQUID_NONE;
316 liquid_alternative_flowing = "";
317 liquid_alternative_source = "";
318 liquid_viscosity = 0;
319 liquid_renewable = true;
320 liquid_range = LIQUID_LEVEL_MAX+1;
323 damage_per_second = 0;
324 node_box = NodeBox();
325 selection_box = NodeBox();
326 collision_box = NodeBox();
328 legacy_facedir_simple = false;
329 legacy_wallmounted = false;
330 sound_footstep = SimpleSoundSpec();
331 sound_dig = SimpleSoundSpec("__group");
332 sound_dug = SimpleSoundSpec();
334 connects_to_ids.clear();
336 color = video::SColor(0xFFFFFFFF);
339 node_dig_prediction = "air";
342 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
344 // protocol_version >= 36
346 writeU8(os, version);
349 os << serializeString(name);
350 writeU16(os, groups.size());
351 for (const auto &group : groups) {
352 os << serializeString(group.first);
353 writeS16(os, group.second);
355 writeU8(os, param_type);
356 writeU8(os, param_type_2);
359 writeU8(os, drawtype);
360 os << serializeString(mesh);
361 writeF1000(os, visual_scale);
363 for (const TileDef &td : tiledef)
364 td.serialize(os, protocol_version);
365 for (const TileDef &td : tiledef_overlay)
366 td.serialize(os, protocol_version);
367 writeU8(os, CF_SPECIAL_COUNT);
368 for (const TileDef &td : tiledef_special) {
369 td.serialize(os, protocol_version);
372 writeU8(os, color.getRed());
373 writeU8(os, color.getGreen());
374 writeU8(os, color.getBlue());
375 os << serializeString(palette_name);
377 writeU8(os, connect_sides);
378 writeU16(os, connects_to_ids.size());
379 for (u16 connects_to_id : connects_to_ids)
380 writeU16(os, connects_to_id);
381 writeU8(os, post_effect_color.getAlpha());
382 writeU8(os, post_effect_color.getRed());
383 writeU8(os, post_effect_color.getGreen());
384 writeU8(os, post_effect_color.getBlue());
385 writeU8(os, leveled);
388 writeU8(os, light_propagates);
389 writeU8(os, sunlight_propagates);
390 writeU8(os, light_source);
393 writeU8(os, is_ground_content);
396 writeU8(os, walkable);
397 writeU8(os, pointable);
398 writeU8(os, diggable);
399 writeU8(os, climbable);
400 writeU8(os, buildable_to);
401 writeU8(os, rightclickable);
402 writeU32(os, damage_per_second);
405 writeU8(os, liquid_type);
406 os << serializeString(liquid_alternative_flowing);
407 os << serializeString(liquid_alternative_source);
408 writeU8(os, liquid_viscosity);
409 writeU8(os, liquid_renewable);
410 writeU8(os, liquid_range);
411 writeU8(os, drowning);
412 writeU8(os, floodable);
415 node_box.serialize(os, protocol_version);
416 selection_box.serialize(os, protocol_version);
417 collision_box.serialize(os, protocol_version);
420 serializeSimpleSoundSpec(sound_footstep, os, version);
421 serializeSimpleSoundSpec(sound_dig, os, version);
422 serializeSimpleSoundSpec(sound_dug, os, version);
425 writeU8(os, legacy_facedir_simple);
426 writeU8(os, legacy_wallmounted);
428 os << serializeString(node_dig_prediction);
431 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
433 // alpha == 0 means that the node is using texture alpha
434 if (alpha == 0 || alpha == 255)
437 for (int i = 0; i < length; i++) {
438 if (tiles[i].name.empty())
441 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
442 tiles[i].name = s.str();
446 void ContentFeatures::deSerialize(std::istream &is)
449 int version = readU8(is);
451 throw SerializationError("unsupported ContentFeatures version");
454 name = deSerializeString(is);
456 u32 groups_size = readU16(is);
457 for (u32 i = 0; i < groups_size; i++) {
458 std::string name = deSerializeString(is);
459 int value = readS16(is);
460 groups[name] = value;
462 param_type = (enum ContentParamType) readU8(is);
463 param_type_2 = (enum ContentParamType2) readU8(is);
466 drawtype = (enum NodeDrawType) readU8(is);
467 mesh = deSerializeString(is);
468 visual_scale = readF1000(is);
470 throw SerializationError("unsupported tile count");
471 for (TileDef &td : tiledef)
472 td.deSerialize(is, version, drawtype);
473 for (TileDef &td : tiledef_overlay)
474 td.deSerialize(is, version, drawtype);
475 if (readU8(is) != CF_SPECIAL_COUNT)
476 throw SerializationError("unsupported CF_SPECIAL_COUNT");
477 for (TileDef &td : tiledef_special)
478 td.deSerialize(is, version, drawtype);
480 color.setRed(readU8(is));
481 color.setGreen(readU8(is));
482 color.setBlue(readU8(is));
483 palette_name = deSerializeString(is);
485 connect_sides = readU8(is);
486 u16 connects_to_size = readU16(is);
487 connects_to_ids.clear();
488 for (u16 i = 0; i < connects_to_size; i++)
489 connects_to_ids.push_back(readU16(is));
490 post_effect_color.setAlpha(readU8(is));
491 post_effect_color.setRed(readU8(is));
492 post_effect_color.setGreen(readU8(is));
493 post_effect_color.setBlue(readU8(is));
494 leveled = readU8(is);
497 light_propagates = readU8(is);
498 sunlight_propagates = readU8(is);
499 light_source = readU8(is);
500 light_source = MYMIN(light_source, LIGHT_MAX);
503 is_ground_content = readU8(is);
506 walkable = readU8(is);
507 pointable = readU8(is);
508 diggable = readU8(is);
509 climbable = readU8(is);
510 buildable_to = readU8(is);
511 rightclickable = readU8(is);
512 damage_per_second = readU32(is);
515 liquid_type = (enum LiquidType) readU8(is);
516 liquid_alternative_flowing = deSerializeString(is);
517 liquid_alternative_source = deSerializeString(is);
518 liquid_viscosity = readU8(is);
519 liquid_renewable = readU8(is);
520 liquid_range = readU8(is);
521 drowning = readU8(is);
522 floodable = readU8(is);
525 node_box.deSerialize(is);
526 selection_box.deSerialize(is);
527 collision_box.deSerialize(is);
530 deSerializeSimpleSoundSpec(sound_footstep, is, version);
531 deSerializeSimpleSoundSpec(sound_dig, is, version);
532 deSerializeSimpleSoundSpec(sound_dug, is, version);
534 // read legacy properties
535 legacy_facedir_simple = readU8(is);
536 legacy_wallmounted = readU8(is);
539 node_dig_prediction = deSerializeString(is);
540 } catch(SerializationError &e) {};
544 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
545 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
546 bool backface_culling, u8 material_type)
548 tile->shader_id = shader_id;
549 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
550 tile->material_type = material_type;
552 // Normal texture and shader flags texture
553 if (use_normal_texture) {
554 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
556 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
559 tile->material_flags = 0;
560 if (backface_culling)
561 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
562 if (tiledef->animation.type != TAT_NONE)
563 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
564 if (tiledef->tileable_horizontal)
565 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
566 if (tiledef->tileable_vertical)
567 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
570 tile->has_color = tiledef->has_color;
571 if (tiledef->has_color)
572 tile->color = tiledef->color;
576 // Animation parameters
578 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
580 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
581 &frame_count, &frame_length_ms, NULL);
582 tile->animation_frame_count = frame_count;
583 tile->animation_frame_length_ms = frame_length_ms;
586 if (frame_count == 1) {
587 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
589 std::ostringstream os(std::ios::binary);
591 tile->frames = std::make_shared<std::vector<FrameSpec>>();
593 tile->frames->resize(frame_count);
595 for (int i = 0; i < frame_count; i++) {
601 tiledef->animation.getTextureModifer(os,
602 tile->texture->getOriginalSize(), i);
604 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
605 if (tile->normal_texture)
606 frame.normal_texture = tsrc->getNormalTexture(os.str());
607 frame.flags_texture = tile->flags_texture;
608 (*tile->frames)[i] = frame;
615 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
616 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
618 // minimap pixel color - the average color of a texture
619 if (tsettings.enable_minimap && !tiledef[0].name.empty())
620 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
622 // Figure out the actual tiles to use
624 for (u32 j = 0; j < 6; j++) {
625 tdef[j] = tiledef[j];
626 if (tdef[j].name.empty())
627 tdef[j].name = "unknown_node.png";
629 // also the overlay tiles
630 TileDef tdef_overlay[6];
631 for (u32 j = 0; j < 6; j++)
632 tdef_overlay[j] = tiledef_overlay[j];
633 // also the special tiles
634 TileDef tdef_spec[6];
635 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
636 tdef_spec[j] = tiledef_special[j];
638 bool is_liquid = false;
640 u8 material_type = (alpha == 255) ?
641 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
646 material_type = (alpha == 255) ?
647 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
654 assert(liquid_type == LIQUID_SOURCE);
655 if (tsettings.opaque_water)
660 case NDT_FLOWINGLIQUID:
661 assert(liquid_type == LIQUID_FLOWING);
663 if (tsettings.opaque_water)
669 visual_solidness = 1;
671 case NDT_GLASSLIKE_FRAMED:
673 visual_solidness = 1;
675 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
677 visual_solidness = 1;
678 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
682 visual_solidness = 1;
684 case NDT_ALLFACES_OPTIONAL:
685 if (tsettings.leaves_style == LEAVES_FANCY) {
686 drawtype = NDT_ALLFACES;
688 visual_solidness = 1;
689 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
690 for (u32 j = 0; j < 6; j++) {
691 if (!tdef_spec[j].name.empty())
692 tdef[j].name = tdef_spec[j].name;
694 drawtype = NDT_GLASSLIKE;
696 visual_solidness = 1;
698 drawtype = NDT_NORMAL;
700 for (TileDef &td : tdef)
701 td.name += std::string("^[noalpha");
704 material_type = TILE_MATERIAL_WAVING_LEAVES;
709 material_type = TILE_MATERIAL_WAVING_PLANTS;
718 material_type = TILE_MATERIAL_WAVING_PLANTS;
719 else if (waving == 2)
720 material_type = TILE_MATERIAL_WAVING_LEAVES;
728 case NDT_PLANTLIKE_ROOTED:
734 // Vertex alpha is no longer supported, correct if necessary.
735 correctAlpha(tdef, 6);
736 correctAlpha(tdef_overlay, 6);
737 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
738 material_type = (alpha == 255) ?
739 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
742 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
744 u8 overlay_material = material_type;
745 if (overlay_material == TILE_MATERIAL_OPAQUE)
746 overlay_material = TILE_MATERIAL_BASIC;
747 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
748 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
750 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
752 // Tiles (fill in f->tiles[])
753 for (u16 j = 0; j < 6; j++) {
754 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
755 tsettings.use_normal_texture,
756 tdef[j].backface_culling, material_type);
757 if (!tdef_overlay[j].name.empty())
758 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
759 overlay_shader, tsettings.use_normal_texture,
760 tdef[j].backface_culling, overlay_material);
763 u8 special_material = material_type;
764 if (drawtype == NDT_PLANTLIKE_ROOTED) {
766 special_material = TILE_MATERIAL_WAVING_PLANTS;
767 else if (waving == 2)
768 special_material = TILE_MATERIAL_WAVING_LEAVES;
770 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
772 // Special tiles (fill in f->special_tiles[])
773 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
774 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
775 special_shader, tsettings.use_normal_texture,
776 tdef_spec[j].backface_culling, special_material);
779 if (param_type_2 == CPT2_COLOR ||
780 param_type_2 == CPT2_COLORED_FACEDIR ||
781 param_type_2 == CPT2_COLORED_WALLMOUNTED)
782 palette = tsrc->getPalette(palette_name);
784 if (drawtype == NDT_MESH && !mesh.empty()) {
786 // Read the mesh and apply scale
787 mesh_ptr[0] = client->getMesh(mesh);
789 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
790 scaleMesh(mesh_ptr[0], scale);
791 recalculateBoundingBox(mesh_ptr[0]);
792 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
794 } else if ((drawtype == NDT_NODEBOX) &&
795 ((node_box.type == NODEBOX_REGULAR) ||
796 (node_box.type == NODEBOX_FIXED)) &&
797 (!node_box.fixed.empty())) {
798 //Convert regular nodebox nodes to meshnodes
799 //Change the drawtype and apply scale
801 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
802 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
803 scaleMesh(mesh_ptr[0], scale);
804 recalculateBoundingBox(mesh_ptr[0]);
805 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
808 //Cache 6dfacedir and wallmounted rotated clones of meshes
809 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
810 (param_type_2 == CPT2_FACEDIR
811 || param_type_2 == CPT2_COLORED_FACEDIR)) {
812 for (u16 j = 1; j < 24; j++) {
813 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
814 rotateMeshBy6dFacedir(mesh_ptr[j], j);
815 recalculateBoundingBox(mesh_ptr[j]);
816 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
818 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
819 && (param_type_2 == CPT2_WALLMOUNTED ||
820 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
821 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
822 for (u16 j = 1; j < 6; j++) {
823 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
824 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
825 recalculateBoundingBox(mesh_ptr[j]);
826 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
828 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
829 recalculateBoundingBox(mesh_ptr[0]);
830 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
839 class CNodeDefManager: public IWritableNodeDefManager {
842 virtual ~CNodeDefManager();
845 inline virtual const ContentFeatures& get(content_t c) const;
846 inline virtual const ContentFeatures& get(const MapNode &n) const;
847 virtual bool getId(const std::string &name, content_t &result) const;
848 virtual content_t getId(const std::string &name) const;
849 virtual bool getIds(const std::string &name, std::vector<content_t> &result) const;
850 virtual const ContentFeatures& get(const std::string &name) const;
851 content_t allocateId();
852 virtual content_t set(const std::string &name, const ContentFeatures &def);
853 virtual content_t allocateDummy(const std::string &name);
854 virtual void removeNode(const std::string &name);
855 virtual void updateAliases(IItemDefManager *idef);
856 virtual void applyTextureOverrides(const std::string &override_filepath);
857 virtual void updateTextures(IGameDef *gamedef,
858 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
859 void *progress_cbk_args);
860 void serialize(std::ostream &os, u16 protocol_version) const;
861 void deSerialize(std::istream &is);
863 inline virtual void setNodeRegistrationStatus(bool completed);
865 virtual void pendNodeResolve(NodeResolver *nr);
866 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
867 virtual void runNodeResolveCallbacks();
868 virtual void resetNodeResolveState();
869 virtual void mapNodeboxConnections();
870 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
871 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
873 return m_selection_box_int_union;
877 void addNameIdMapping(content_t i, std::string name);
879 * Recalculates m_selection_box_int_union based on
880 * m_selection_box_union.
882 void fixSelectionBoxIntUnion();
884 // Features indexed by id
885 std::vector<ContentFeatures> m_content_features;
887 // A mapping for fast converting back and forth between names and ids
888 NameIdMapping m_name_id_mapping;
890 // Like m_name_id_mapping, but only from names to ids, and includes
891 // item aliases too. Updated by updateAliases()
892 // Note: Not serialized.
894 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
896 // A mapping from groups to a vector of content_ts that belong to it.
897 // Necessary for a direct lookup in getIds().
898 // Note: Not serialized.
899 std::unordered_map<std::string, std::vector<content_t>> m_group_to_items;
901 // Next possibly free id
904 // NodeResolvers to callback once node registration has ended
905 std::vector<NodeResolver *> m_pending_resolve_callbacks;
907 // True when all nodes have been registered
908 bool m_node_registration_complete;
910 //! The union of all nodes' selection boxes.
911 aabb3f m_selection_box_union;
913 * The smallest box in node coordinates that
914 * contains all nodes' selection boxes.
916 core::aabbox3d<s16> m_selection_box_int_union;
920 CNodeDefManager::CNodeDefManager()
926 CNodeDefManager::~CNodeDefManager()
929 for (ContentFeatures &f : m_content_features) {
930 for (auto &j : f.mesh_ptr) {
939 void CNodeDefManager::clear()
941 m_content_features.clear();
942 m_name_id_mapping.clear();
943 m_name_id_mapping_with_aliases.clear();
944 m_group_to_items.clear();
946 m_selection_box_union.reset(0,0,0);
947 m_selection_box_int_union.reset(0,0,0);
949 resetNodeResolveState();
951 u32 initial_length = 0;
952 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
953 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
954 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
955 m_content_features.resize(initial_length);
957 // Set CONTENT_UNKNOWN
961 // Insert directly into containers
962 content_t c = CONTENT_UNKNOWN;
963 m_content_features[c] = f;
964 addNameIdMapping(c, f.name);
971 f.drawtype = NDT_AIRLIKE;
972 f.param_type = CPT_LIGHT;
973 f.light_propagates = true;
974 f.sunlight_propagates = true;
978 f.buildable_to = true;
980 f.is_ground_content = true;
981 // Insert directly into containers
982 content_t c = CONTENT_AIR;
983 m_content_features[c] = f;
984 addNameIdMapping(c, f.name);
987 // Set CONTENT_IGNORE
991 f.drawtype = NDT_AIRLIKE;
992 f.param_type = CPT_NONE;
993 f.light_propagates = false;
994 f.sunlight_propagates = false;
998 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
999 f.is_ground_content = true;
1000 // Insert directly into containers
1001 content_t c = CONTENT_IGNORE;
1002 m_content_features[c] = f;
1003 addNameIdMapping(c, f.name);
1008 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1010 return c < m_content_features.size()
1011 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1015 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1017 return get(n.getContent());
1021 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1023 std::unordered_map<std::string, content_t>::const_iterator
1024 i = m_name_id_mapping_with_aliases.find(name);
1025 if(i == m_name_id_mapping_with_aliases.end())
1032 content_t CNodeDefManager::getId(const std::string &name) const
1034 content_t id = CONTENT_IGNORE;
1040 bool CNodeDefManager::getIds(const std::string &name,
1041 std::vector<content_t> &result) const
1043 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1044 if (name.substr(0,6) != "group:") {
1045 content_t id = CONTENT_IGNORE;
1046 bool exists = getId(name, id);
1048 result.push_back(id);
1051 std::string group = name.substr(6);
1053 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1054 i = m_group_to_items.find(group);
1055 if (i == m_group_to_items.end())
1058 const std::vector<content_t> &items = i->second;
1059 result.insert(result.end(), items.begin(), items.end());
1060 //printf("getIds: %dus\n", t.stop());
1065 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1067 content_t id = CONTENT_UNKNOWN;
1073 // returns CONTENT_IGNORE if no free ID found
1074 content_t CNodeDefManager::allocateId()
1076 for (content_t id = m_next_id;
1077 id >= m_next_id; // overflow?
1079 while (id >= m_content_features.size()) {
1080 m_content_features.emplace_back();
1082 const ContentFeatures &f = m_content_features[id];
1083 if (f.name.empty()) {
1088 // If we arrive here, an overflow occurred in id.
1089 // That means no ID was found
1090 return CONTENT_IGNORE;
1095 * Returns the smallest box that contains all boxes
1096 * in the vector. Box_union is expanded.
1097 * @param[in] boxes the vector containing the boxes
1098 * @param[in, out] box_union the union of the arguments
1100 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1102 for (const aabb3f &box : boxes) {
1103 box_union->addInternalBox(box);
1109 * Returns a box that contains the nodebox in every case.
1110 * The argument node_union is expanded.
1111 * @param[in] nodebox the nodebox to be measured
1112 * @param[in] features used to decide whether the nodebox
1114 * @param[in, out] box_union the union of the arguments
1116 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1119 switch(nodebox.type) {
1121 case NODEBOX_LEVELED: {
1123 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1124 boxVectorUnion(nodebox.fixed, &half_processed);
1125 // Set leveled boxes to maximal
1126 if (nodebox.type == NODEBOX_LEVELED) {
1127 half_processed.MaxEdge.Y = +BS / 2;
1129 if (features.param_type_2 == CPT2_FACEDIR ||
1130 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1131 // Get maximal coordinate
1133 fabsf(half_processed.MinEdge.X),
1134 fabsf(half_processed.MinEdge.Y),
1135 fabsf(half_processed.MinEdge.Z),
1136 fabsf(half_processed.MaxEdge.X),
1137 fabsf(half_processed.MaxEdge.Y),
1138 fabsf(half_processed.MaxEdge.Z) };
1140 for (float coord : coords) {
1145 // Add the union of all possible rotated boxes
1146 box_union->addInternalPoint(-max, -max, -max);
1147 box_union->addInternalPoint(+max, +max, +max);
1149 box_union->addInternalBox(half_processed);
1153 case NODEBOX_WALLMOUNTED: {
1155 box_union->addInternalBox(nodebox.wall_top);
1156 box_union->addInternalBox(nodebox.wall_bottom);
1157 // Find maximal coordinate in the X-Z plane
1159 fabsf(nodebox.wall_side.MinEdge.X),
1160 fabsf(nodebox.wall_side.MinEdge.Z),
1161 fabsf(nodebox.wall_side.MaxEdge.X),
1162 fabsf(nodebox.wall_side.MaxEdge.Z) };
1164 for (float coord : coords) {
1169 // Add the union of all possible rotated boxes
1170 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1171 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1174 case NODEBOX_CONNECTED: {
1175 // Add all possible connected boxes
1176 boxVectorUnion(nodebox.fixed, box_union);
1177 boxVectorUnion(nodebox.connect_top, box_union);
1178 boxVectorUnion(nodebox.connect_bottom, box_union);
1179 boxVectorUnion(nodebox.connect_front, box_union);
1180 boxVectorUnion(nodebox.connect_left, box_union);
1181 boxVectorUnion(nodebox.connect_back, box_union);
1182 boxVectorUnion(nodebox.connect_right, box_union);
1187 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1188 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1194 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1196 m_selection_box_int_union.MinEdge.X = floorf(
1197 m_selection_box_union.MinEdge.X / BS + 0.5f);
1198 m_selection_box_int_union.MinEdge.Y = floorf(
1199 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1200 m_selection_box_int_union.MinEdge.Z = floorf(
1201 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1202 m_selection_box_int_union.MaxEdge.X = ceilf(
1203 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1204 m_selection_box_int_union.MaxEdge.Y = ceilf(
1205 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1206 m_selection_box_int_union.MaxEdge.Z = ceilf(
1207 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1211 // IWritableNodeDefManager
1212 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1216 assert(name == def.name);
1218 // Don't allow redefining ignore (but allow air and unknown)
1219 if (name == "ignore") {
1220 warningstream << "NodeDefManager: Ignoring "
1221 "CONTENT_IGNORE redefinition"<<std::endl;
1222 return CONTENT_IGNORE;
1225 content_t id = CONTENT_IGNORE;
1226 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1229 if (id == CONTENT_IGNORE) {
1230 warningstream << "NodeDefManager: Absolute "
1231 "limit reached" << std::endl;
1232 return CONTENT_IGNORE;
1234 assert(id != CONTENT_IGNORE);
1235 addNameIdMapping(id, name);
1237 m_content_features[id] = def;
1238 verbosestream << "NodeDefManager: registering content id \"" << id
1239 << "\": name=\"" << def.name << "\""<<std::endl;
1241 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1242 fixSelectionBoxIntUnion();
1243 // Add this content to the list of all groups it belongs to
1244 // FIXME: This should remove a node from groups it no longer
1245 // belongs to when a node is re-registered
1246 for (const auto &group : def.groups) {
1247 const std::string &group_name = group.first;
1248 m_group_to_items[group_name].push_back(id);
1254 content_t CNodeDefManager::allocateDummy(const std::string &name)
1256 assert(name != ""); // Pre-condition
1259 return set(name, f);
1263 void CNodeDefManager::removeNode(const std::string &name)
1268 // Erase name from name ID mapping
1269 content_t id = CONTENT_IGNORE;
1270 if (m_name_id_mapping.getId(name, id)) {
1271 m_name_id_mapping.eraseName(name);
1272 m_name_id_mapping_with_aliases.erase(name);
1275 // Erase node content from all groups it belongs to
1276 for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
1277 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1278 std::vector<content_t> &items = iter_groups->second;
1279 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1281 // Check if group is empty
1283 m_group_to_items.erase(iter_groups++);
1290 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1292 std::set<std::string> all;
1294 m_name_id_mapping_with_aliases.clear();
1295 for (const std::string &name : all) {
1296 const std::string &convert_to = idef->getAlias(name);
1298 if (m_name_id_mapping.getId(convert_to, id)) {
1299 m_name_id_mapping_with_aliases.insert(
1300 std::make_pair(name, id));
1305 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1307 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1308 "overrides to textures from " << override_filepath << std::endl;
1310 std::ifstream infile(override_filepath.c_str());
1313 while (std::getline(infile, line)) {
1315 if (trim(line).empty())
1317 std::vector<std::string> splitted = str_split(line, ' ');
1318 if (splitted.size() != 3) {
1319 errorstream << override_filepath
1320 << ":" << line_c << " Could not apply texture override \""
1321 << line << "\": Syntax error" << std::endl;
1326 if (!getId(splitted[0], id))
1327 continue; // Ignore unknown node
1329 ContentFeatures &nodedef = m_content_features[id];
1331 if (splitted[1] == "top")
1332 nodedef.tiledef[0].name = splitted[2];
1333 else if (splitted[1] == "bottom")
1334 nodedef.tiledef[1].name = splitted[2];
1335 else if (splitted[1] == "right")
1336 nodedef.tiledef[2].name = splitted[2];
1337 else if (splitted[1] == "left")
1338 nodedef.tiledef[3].name = splitted[2];
1339 else if (splitted[1] == "back")
1340 nodedef.tiledef[4].name = splitted[2];
1341 else if (splitted[1] == "front")
1342 nodedef.tiledef[5].name = splitted[2];
1343 else if (splitted[1] == "all" || splitted[1] == "*")
1344 for (TileDef &i : nodedef.tiledef)
1345 i.name = splitted[2];
1346 else if (splitted[1] == "sides")
1347 for (int i = 2; i < 6; i++)
1348 nodedef.tiledef[i].name = splitted[2];
1350 errorstream << override_filepath
1351 << ":" << line_c << " Could not apply texture override \""
1352 << line << "\": Unknown node side \""
1353 << splitted[1] << "\"" << std::endl;
1359 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1360 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1361 void *progress_callback_args)
1364 infostream << "CNodeDefManager::updateTextures(): Updating "
1365 "textures in node definitions" << std::endl;
1367 Client *client = (Client *)gamedef;
1368 ITextureSource *tsrc = client->tsrc();
1369 IShaderSource *shdsrc = client->getShaderSource();
1370 scene::IMeshManipulator *meshmanip =
1371 RenderingEngine::get_scene_manager()->getMeshManipulator();
1372 TextureSettings tsettings;
1373 tsettings.readSettings();
1375 u32 size = m_content_features.size();
1377 for (u32 i = 0; i < size; i++) {
1378 ContentFeatures *f = &(m_content_features[i]);
1379 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1380 progress_callback(progress_callback_args, i, size);
1385 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1387 writeU8(os, 1); // version
1389 std::ostringstream os2(std::ios::binary);
1390 for (u32 i = 0; i < m_content_features.size(); i++) {
1391 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1392 || i == CONTENT_UNKNOWN)
1394 const ContentFeatures *f = &m_content_features[i];
1395 if (f->name.empty())
1398 // Wrap it in a string to allow different lengths without
1399 // strict version incompatibilities
1400 std::ostringstream wrapper_os(std::ios::binary);
1401 f->serialize(wrapper_os, protocol_version);
1402 os2<<serializeString(wrapper_os.str());
1404 // must not overflow
1405 u16 next = count + 1;
1406 FATAL_ERROR_IF(next < count, "Overflow");
1409 writeU16(os, count);
1410 os << serializeLongString(os2.str());
1414 void CNodeDefManager::deSerialize(std::istream &is)
1417 int version = readU8(is);
1419 throw SerializationError("unsupported NodeDefinitionManager version");
1420 u16 count = readU16(is);
1421 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1423 for (u16 n = 0; n < count; n++) {
1424 u16 i = readU16(is2);
1426 // Read it from the string wrapper
1427 std::string wrapper = deSerializeString(is2);
1428 std::istringstream wrapper_is(wrapper, std::ios::binary);
1429 f.deSerialize(wrapper_is);
1431 // Check error conditions
1432 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1433 warningstream << "NodeDefManager::deSerialize(): "
1434 "not changing builtin node " << i << std::endl;
1437 if (f.name.empty()) {
1438 warningstream << "NodeDefManager::deSerialize(): "
1439 "received empty name" << std::endl;
1445 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1446 warningstream << "NodeDefManager::deSerialize(): "
1447 "already defined with different ID: " << f.name << std::endl;
1451 // All is ok, add node definition with the requested ID
1452 if (i >= m_content_features.size())
1453 m_content_features.resize((u32)(i) + 1);
1454 m_content_features[i] = f;
1455 addNameIdMapping(i, f.name);
1456 verbosestream << "deserialized " << f.name << std::endl;
1458 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1459 fixSelectionBoxIntUnion();
1464 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1466 m_name_id_mapping.set(i, name);
1467 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1471 IWritableNodeDefManager *createNodeDefManager()
1473 return new CNodeDefManager();
1476 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1478 m_node_registration_complete = completed;
1482 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1485 if (m_node_registration_complete)
1486 nr->nodeResolveInternal();
1488 m_pending_resolve_callbacks.push_back(nr);
1492 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
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 CNodeDefManager::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 CNodeDefManager::resetNodeResolveState()
1522 m_node_registration_complete = false;
1523 m_pending_resolve_callbacks.clear();
1526 void CNodeDefManager::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 CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1540 const ContentFeatures &f1 = get(from);
1542 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1545 // lookup target in connected set
1546 if (!CONTAINS(f1.connects_to_ids, to.param0))
1549 const ContentFeatures &f2 = get(to);
1551 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1552 // ignores actually looking if back connection exists
1553 return CONTAINS(f2.connects_to_ids, from.param0);
1555 // does to node declare usable faces?
1556 if (f2.connect_sides > 0) {
1557 if ((f2.param_type_2 == CPT2_FACEDIR ||
1558 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1559 && (connect_face >= 4)) {
1560 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1561 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1563 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1565 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1566 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1568 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 32, 16, 8, 4 // 32 - left
1572 return (f2.connect_sides
1573 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1575 return (f2.connect_sides & connect_face);
1577 // the target is just a regular node, so connect no matter back connection
1585 NodeResolver::NodeResolver()
1587 m_nodenames.reserve(16);
1588 m_nnlistsizes.reserve(4);
1592 NodeResolver::~NodeResolver()
1594 if (!m_resolve_done && m_ndef)
1595 m_ndef->cancelNodeResolveCallback(this);
1599 void NodeResolver::nodeResolveInternal()
1601 m_nodenames_idx = 0;
1602 m_nnlistsizes_idx = 0;
1605 m_resolve_done = true;
1607 m_nodenames.clear();
1608 m_nnlistsizes.clear();
1612 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1613 const std::string &node_alt, content_t c_fallback)
1615 if (m_nodenames_idx == m_nodenames.size()) {
1616 *result_out = c_fallback;
1617 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1622 std::string name = m_nodenames[m_nodenames_idx++];
1624 bool success = m_ndef->getId(name, c);
1625 if (!success && !node_alt.empty()) {
1627 success = m_ndef->getId(name, c);
1631 errorstream << "NodeResolver: failed to resolve node name '" << name
1632 << "'." << std::endl;
1641 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1642 bool all_required, content_t c_fallback)
1644 bool success = true;
1646 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1647 errorstream << "NodeResolver: no more node lists" << std::endl;
1651 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1654 if (m_nodenames_idx == m_nodenames.size()) {
1655 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1660 std::string &name = m_nodenames[m_nodenames_idx++];
1662 if (name.substr(0,6) != "group:") {
1663 if (m_ndef->getId(name, c)) {
1664 result_out->push_back(c);
1665 } else if (all_required) {
1666 errorstream << "NodeResolver: failed to resolve node name '"
1667 << name << "'." << std::endl;
1668 result_out->push_back(c_fallback);
1672 m_ndef->getIds(name, *result_out);