3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
50 type = NODEBOX_REGULAR;
53 // default is sign/ladder-like
54 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57 // no default for other parts
59 connect_bottom.clear();
60 connect_front.clear();
63 connect_right.clear();
66 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
77 writeU16(os, fixed.size());
78 for (const aabb3f &nodebox : fixed) {
79 writeV3F1000(os, nodebox.MinEdge);
80 writeV3F1000(os, nodebox.MaxEdge);
83 case NODEBOX_WALLMOUNTED:
86 writeV3F1000(os, wall_top.MinEdge);
87 writeV3F1000(os, wall_top.MaxEdge);
88 writeV3F1000(os, wall_bottom.MinEdge);
89 writeV3F1000(os, wall_bottom.MaxEdge);
90 writeV3F1000(os, wall_side.MinEdge);
91 writeV3F1000(os, wall_side.MaxEdge);
93 case NODEBOX_CONNECTED:
96 #define WRITEBOX(box) \
97 writeU16(os, (box).size()); \
98 for (const aabb3f &i: (box)) { \
99 writeV3F1000(os, i.MinEdge); \
100 writeV3F1000(os, i.MaxEdge); \
104 WRITEBOX(connect_top);
105 WRITEBOX(connect_bottom);
106 WRITEBOX(connect_front);
107 WRITEBOX(connect_left);
108 WRITEBOX(connect_back);
109 WRITEBOX(connect_right);
117 void NodeBox::deSerialize(std::istream &is)
119 int version = readU8(is);
121 throw SerializationError("unsupported NodeBox version");
125 type = (enum NodeBoxType)readU8(is);
127 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
129 u16 fixed_count = readU16(is);
133 box.MinEdge = readV3F1000(is);
134 box.MaxEdge = readV3F1000(is);
135 fixed.push_back(box);
138 else if(type == NODEBOX_WALLMOUNTED)
140 wall_top.MinEdge = readV3F1000(is);
141 wall_top.MaxEdge = readV3F1000(is);
142 wall_bottom.MinEdge = readV3F1000(is);
143 wall_bottom.MaxEdge = readV3F1000(is);
144 wall_side.MinEdge = readV3F1000(is);
145 wall_side.MaxEdge = readV3F1000(is);
147 else if (type == NODEBOX_CONNECTED)
149 #define READBOXES(box) { \
150 count = readU16(is); \
151 (box).reserve(count); \
153 v3f min = readV3F1000(is); \
154 v3f max = readV3F1000(is); \
155 (box).emplace_back(min, max); }; }
160 READBOXES(connect_top);
161 READBOXES(connect_bottom);
162 READBOXES(connect_front);
163 READBOXES(connect_left);
164 READBOXES(connect_back);
165 READBOXES(connect_right);
173 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
174 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
175 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
176 #define TILE_FLAG_HAS_COLOR (1 << 3)
177 #define TILE_FLAG_HAS_SCALE (1 << 4)
178 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
180 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
182 // protocol_version >= 36
184 writeU8(os, version);
186 os << serializeString(name);
187 animation.serialize(os, version);
188 bool has_scale = scale > 0;
190 if (backface_culling)
191 flags |= TILE_FLAG_BACKFACE_CULLING;
192 if (tileable_horizontal)
193 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
194 if (tileable_vertical)
195 flags |= TILE_FLAG_TILEABLE_VERTICAL;
197 flags |= TILE_FLAG_HAS_COLOR;
199 flags |= TILE_FLAG_HAS_SCALE;
200 if (align_style != ALIGN_STYLE_NODE)
201 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
204 writeU8(os, color.getRed());
205 writeU8(os, color.getGreen());
206 writeU8(os, color.getBlue());
210 if (align_style != ALIGN_STYLE_NODE)
211 writeU8(os, align_style);
214 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
215 NodeDrawType drawtype)
217 int version = readU8(is);
219 throw SerializationError("unsupported TileDef version");
220 name = deSerializeString(is);
221 animation.deSerialize(is, version);
222 u16 flags = readU16(is);
223 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
224 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
225 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
226 has_color = flags & TILE_FLAG_HAS_COLOR;
227 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
228 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
230 color.setRed(readU8(is));
231 color.setGreen(readU8(is));
232 color.setBlue(readU8(is));
234 scale = has_scale ? readU8(is) : 0;
236 align_style = static_cast<AlignStyle>(readU8(is));
238 align_style = ALIGN_STYLE_NODE;
243 SimpleSoundSpec serialization
246 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
247 std::ostream &os, u8 version)
249 os<<serializeString(ss.name);
250 writeF1000(os, ss.gain);
251 writeF1000(os, ss.pitch);
253 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss,
254 std::istream &is, u8 version)
256 ss.name = deSerializeString(is);
257 ss.gain = readF1000(is);
258 ss.pitch = readF1000(is);
261 void TextureSettings::readSettings()
263 connected_glass = g_settings->getBool("connected_glass");
264 opaque_water = g_settings->getBool("opaque_water");
265 bool enable_shaders = g_settings->getBool("enable_shaders");
266 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
267 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
268 bool smooth_lighting = g_settings->getBool("smooth_lighting");
269 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
270 enable_minimap = g_settings->getBool("enable_minimap");
271 node_texture_size = g_settings->getU16("texture_min_size");
272 std::string leaves_style_str = g_settings->get("leaves_style");
273 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
274 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
276 // Mesh cache is not supported in combination with smooth lighting
278 enable_mesh_cache = false;
280 use_normal_texture = enable_shaders &&
281 (enable_bumpmapping || enable_parallax_occlusion);
282 if (leaves_style_str == "fancy") {
283 leaves_style = LEAVES_FANCY;
284 } else if (leaves_style_str == "simple") {
285 leaves_style = LEAVES_SIMPLE;
287 leaves_style = LEAVES_OPAQUE;
290 if (world_aligned_mode_str == "enable")
291 world_aligned_mode = WORLDALIGN_ENABLE;
292 else if (world_aligned_mode_str == "force_solid")
293 world_aligned_mode = WORLDALIGN_FORCE;
294 else if (world_aligned_mode_str == "force_nodebox")
295 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
297 world_aligned_mode = WORLDALIGN_DISABLE;
299 if (autoscale_mode_str == "enable")
300 autoscale_mode = AUTOSCALE_ENABLE;
301 else if (autoscale_mode_str == "force")
302 autoscale_mode = AUTOSCALE_FORCE;
304 autoscale_mode = AUTOSCALE_DISABLE;
311 ContentFeatures::ContentFeatures()
316 void ContentFeatures::reset()
323 visual_solidness = 0;
324 backface_culling = true;
327 has_on_construct = false;
328 has_on_destruct = false;
329 has_after_destruct = false;
333 NOTE: Most of this is always overridden by the default values given
338 // Unknown nodes can be dug
339 groups["dig_immediate"] = 2;
340 drawtype = NDT_NORMAL;
343 for (auto &i : mesh_ptr)
345 minimap_color = video::SColor(0, 0, 0, 0);
348 for (auto &i : tiledef)
350 for (auto &j : tiledef_special)
353 post_effect_color = video::SColor(0, 0, 0, 0);
354 param_type = CPT_NONE;
355 param_type_2 = CPT2_NONE;
356 is_ground_content = false;
357 light_propagates = false;
358 sunlight_propagates = false;
363 buildable_to = false;
365 rightclickable = true;
367 liquid_type = LIQUID_NONE;
368 liquid_alternative_flowing = "";
369 liquid_alternative_source = "";
370 liquid_viscosity = 0;
371 liquid_renewable = true;
372 liquid_range = LIQUID_LEVEL_MAX+1;
375 damage_per_second = 0;
376 node_box = NodeBox();
377 selection_box = NodeBox();
378 collision_box = NodeBox();
380 legacy_facedir_simple = false;
381 legacy_wallmounted = false;
382 sound_footstep = SimpleSoundSpec();
383 sound_dig = SimpleSoundSpec("__group");
384 sound_dug = SimpleSoundSpec();
386 connects_to_ids.clear();
388 color = video::SColor(0xFFFFFFFF);
391 node_dig_prediction = "air";
394 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
396 // protocol_version >= 36
398 writeU8(os, version);
401 os << serializeString(name);
402 writeU16(os, groups.size());
403 for (const auto &group : groups) {
404 os << serializeString(group.first);
405 writeS16(os, group.second);
407 writeU8(os, param_type);
408 writeU8(os, param_type_2);
411 writeU8(os, drawtype);
412 os << serializeString(mesh);
413 writeF1000(os, visual_scale);
415 for (const TileDef &td : tiledef)
416 td.serialize(os, protocol_version);
417 for (const TileDef &td : tiledef_overlay)
418 td.serialize(os, protocol_version);
419 writeU8(os, CF_SPECIAL_COUNT);
420 for (const TileDef &td : tiledef_special) {
421 td.serialize(os, protocol_version);
424 writeU8(os, color.getRed());
425 writeU8(os, color.getGreen());
426 writeU8(os, color.getBlue());
427 os << serializeString(palette_name);
429 writeU8(os, connect_sides);
430 writeU16(os, connects_to_ids.size());
431 for (u16 connects_to_id : connects_to_ids)
432 writeU16(os, connects_to_id);
433 writeU8(os, post_effect_color.getAlpha());
434 writeU8(os, post_effect_color.getRed());
435 writeU8(os, post_effect_color.getGreen());
436 writeU8(os, post_effect_color.getBlue());
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 serializeSimpleSoundSpec(sound_footstep, os, version);
473 serializeSimpleSoundSpec(sound_dig, os, version);
474 serializeSimpleSoundSpec(sound_dug, 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 int version = readU8(is);
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 = readF1000(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.setAlpha(readU8(is));
543 post_effect_color.setRed(readU8(is));
544 post_effect_color.setGreen(readU8(is));
545 post_effect_color.setBlue(readU8(is));
546 leveled = readU8(is);
549 light_propagates = readU8(is);
550 sunlight_propagates = readU8(is);
551 light_source = readU8(is);
552 light_source = MYMIN(light_source, LIGHT_MAX);
555 is_ground_content = readU8(is);
558 walkable = readU8(is);
559 pointable = readU8(is);
560 diggable = readU8(is);
561 climbable = readU8(is);
562 buildable_to = readU8(is);
563 rightclickable = readU8(is);
564 damage_per_second = readU32(is);
567 liquid_type = (enum LiquidType) readU8(is);
568 liquid_alternative_flowing = deSerializeString(is);
569 liquid_alternative_source = deSerializeString(is);
570 liquid_viscosity = readU8(is);
571 liquid_renewable = readU8(is);
572 liquid_range = readU8(is);
573 drowning = readU8(is);
574 floodable = readU8(is);
577 node_box.deSerialize(is);
578 selection_box.deSerialize(is);
579 collision_box.deSerialize(is);
582 deSerializeSimpleSoundSpec(sound_footstep, is, version);
583 deSerializeSimpleSoundSpec(sound_dig, is, version);
584 deSerializeSimpleSoundSpec(sound_dug, is, version);
586 // read legacy properties
587 legacy_facedir_simple = readU8(is);
588 legacy_wallmounted = readU8(is);
591 node_dig_prediction = deSerializeString(is);
592 } catch(SerializationError &e) {};
596 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
597 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
598 u8 material_type, u32 shader_id, bool backface_culling,
599 const TextureSettings &tsettings)
601 layer->shader_id = shader_id;
602 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
603 layer->material_type = material_type;
605 bool has_scale = tiledef.scale > 0;
606 if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
607 (tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
608 auto texture_size = layer->texture->getOriginalSize();
609 float base_size = tsettings.node_texture_size;
610 float size = std::fmin(texture_size.Width, texture_size.Height);
611 layer->scale = std::fmax(base_size, size) / base_size;
612 } else if (has_scale) {
613 layer->scale = tiledef.scale;
617 if (!tile.world_aligned)
620 // Normal texture and shader flags texture
621 if (tsettings.use_normal_texture) {
622 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
624 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
627 layer->material_flags = 0;
628 if (backface_culling)
629 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
630 if (tiledef.animation.type != TAT_NONE)
631 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
632 if (tiledef.tileable_horizontal)
633 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
634 if (tiledef.tileable_vertical)
635 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
638 layer->has_color = tiledef.has_color;
639 if (tiledef.has_color)
640 layer->color = tiledef.color;
642 layer->color = color;
644 // Animation parameters
646 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
648 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
649 &frame_count, &frame_length_ms, NULL);
650 layer->animation_frame_count = frame_count;
651 layer->animation_frame_length_ms = frame_length_ms;
654 if (frame_count == 1) {
655 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
657 std::ostringstream os(std::ios::binary);
658 if (!layer->frames) {
659 layer->frames = std::make_shared<std::vector<FrameSpec>>();
661 layer->frames->resize(frame_count);
663 for (int i = 0; i < frame_count; i++) {
669 tiledef.animation.getTextureModifer(os,
670 layer->texture->getOriginalSize(), i);
672 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
673 if (layer->normal_texture)
674 frame.normal_texture = tsrc->getNormalTexture(os.str());
675 frame.flags_texture = layer->flags_texture;
676 (*layer->frames)[i] = frame;
683 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
685 if (style == ALIGN_STYLE_WORLD)
687 if (mode == WORLDALIGN_DISABLE)
689 if (style == ALIGN_STYLE_USER_DEFINED)
691 if (drawtype == NDT_NORMAL)
692 return mode >= WORLDALIGN_FORCE;
693 if (drawtype == NDT_NODEBOX)
694 return mode >= WORLDALIGN_FORCE_NODEBOX;
698 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
699 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
701 // minimap pixel color - the average color of a texture
702 if (tsettings.enable_minimap && !tiledef[0].name.empty())
703 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
705 // Figure out the actual tiles to use
707 for (u32 j = 0; j < 6; j++) {
708 tdef[j] = tiledef[j];
709 if (tdef[j].name.empty())
710 tdef[j].name = "unknown_node.png";
712 // also the overlay tiles
713 TileDef tdef_overlay[6];
714 for (u32 j = 0; j < 6; j++)
715 tdef_overlay[j] = tiledef_overlay[j];
716 // also the special tiles
717 TileDef tdef_spec[6];
718 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
719 tdef_spec[j] = tiledef_special[j];
721 bool is_liquid = false;
723 u8 material_type = (alpha == 255) ?
724 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
729 material_type = (alpha == 255) ?
730 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
737 assert(liquid_type == LIQUID_SOURCE);
738 if (tsettings.opaque_water)
743 case NDT_FLOWINGLIQUID:
744 assert(liquid_type == LIQUID_FLOWING);
746 if (tsettings.opaque_water)
752 visual_solidness = 1;
754 case NDT_GLASSLIKE_FRAMED:
756 visual_solidness = 1;
758 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
760 visual_solidness = 1;
761 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
765 visual_solidness = 1;
767 case NDT_ALLFACES_OPTIONAL:
768 if (tsettings.leaves_style == LEAVES_FANCY) {
769 drawtype = NDT_ALLFACES;
771 visual_solidness = 1;
772 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
773 for (u32 j = 0; j < 6; j++) {
774 if (!tdef_spec[j].name.empty())
775 tdef[j].name = tdef_spec[j].name;
777 drawtype = NDT_GLASSLIKE;
779 visual_solidness = 1;
781 drawtype = NDT_NORMAL;
783 for (TileDef &td : tdef)
784 td.name += std::string("^[noalpha");
787 material_type = TILE_MATERIAL_WAVING_LEAVES;
792 material_type = TILE_MATERIAL_WAVING_PLANTS;
801 material_type = TILE_MATERIAL_WAVING_PLANTS;
802 else if (waving == 2)
803 material_type = TILE_MATERIAL_WAVING_LEAVES;
811 case NDT_PLANTLIKE_ROOTED:
817 // Vertex alpha is no longer supported, correct if necessary.
818 correctAlpha(tdef, 6);
819 correctAlpha(tdef_overlay, 6);
820 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
821 material_type = (alpha == 255) ?
822 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
825 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
827 u8 overlay_material = material_type;
828 if (overlay_material == TILE_MATERIAL_OPAQUE)
829 overlay_material = TILE_MATERIAL_BASIC;
830 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
831 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
833 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
835 // Tiles (fill in f->tiles[])
836 for (u16 j = 0; j < 6; j++) {
837 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
838 tsettings.world_aligned_mode, drawtype);
839 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
840 color, material_type, tile_shader,
841 tdef[j].backface_culling, tsettings);
842 if (!tdef_overlay[j].name.empty())
843 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
844 color, overlay_material, overlay_shader,
845 tdef[j].backface_culling, tsettings);
848 u8 special_material = material_type;
849 if (drawtype == NDT_PLANTLIKE_ROOTED) {
851 special_material = TILE_MATERIAL_WAVING_PLANTS;
852 else if (waving == 2)
853 special_material = TILE_MATERIAL_WAVING_LEAVES;
855 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
857 // Special tiles (fill in f->special_tiles[])
858 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
859 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
860 color, special_material, special_shader,
861 tdef_spec[j].backface_culling, tsettings);
863 if (param_type_2 == CPT2_COLOR ||
864 param_type_2 == CPT2_COLORED_FACEDIR ||
865 param_type_2 == CPT2_COLORED_WALLMOUNTED)
866 palette = tsrc->getPalette(palette_name);
868 if (drawtype == NDT_MESH && !mesh.empty()) {
870 // Read the mesh and apply scale
871 mesh_ptr[0] = client->getMesh(mesh);
873 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
874 scaleMesh(mesh_ptr[0], scale);
875 recalculateBoundingBox(mesh_ptr[0]);
876 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
880 //Cache 6dfacedir and wallmounted rotated clones of meshes
881 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
882 (param_type_2 == CPT2_FACEDIR
883 || param_type_2 == CPT2_COLORED_FACEDIR)) {
884 for (u16 j = 1; j < 24; j++) {
885 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
886 rotateMeshBy6dFacedir(mesh_ptr[j], j);
887 recalculateBoundingBox(mesh_ptr[j]);
888 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
890 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
891 && (param_type_2 == CPT2_WALLMOUNTED ||
892 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
893 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
894 for (u16 j = 1; j < 6; j++) {
895 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
896 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
897 recalculateBoundingBox(mesh_ptr[j]);
898 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
900 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
901 recalculateBoundingBox(mesh_ptr[0]);
902 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
911 class CNodeDefManager: public IWritableNodeDefManager {
914 virtual ~CNodeDefManager();
917 inline virtual const ContentFeatures& get(content_t c) const;
918 inline virtual const ContentFeatures& get(const MapNode &n) const;
919 virtual bool getId(const std::string &name, content_t &result) const;
920 virtual content_t getId(const std::string &name) const;
921 virtual bool getIds(const std::string &name, std::vector<content_t> &result) const;
922 virtual const ContentFeatures& get(const std::string &name) const;
923 content_t allocateId();
924 virtual content_t set(const std::string &name, const ContentFeatures &def);
925 virtual content_t allocateDummy(const std::string &name);
926 virtual void removeNode(const std::string &name);
927 virtual void updateAliases(IItemDefManager *idef);
928 virtual void applyTextureOverrides(const std::string &override_filepath);
929 virtual void updateTextures(IGameDef *gamedef,
930 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
931 void *progress_cbk_args);
932 void serialize(std::ostream &os, u16 protocol_version) const;
933 void deSerialize(std::istream &is);
935 inline virtual void setNodeRegistrationStatus(bool completed);
937 virtual void pendNodeResolve(NodeResolver *nr);
938 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
939 virtual void runNodeResolveCallbacks();
940 virtual void resetNodeResolveState();
941 virtual void mapNodeboxConnections();
942 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
943 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
945 return m_selection_box_int_union;
949 void addNameIdMapping(content_t i, std::string name);
951 * Recalculates m_selection_box_int_union based on
952 * m_selection_box_union.
954 void fixSelectionBoxIntUnion();
956 // Features indexed by id
957 std::vector<ContentFeatures> m_content_features;
959 // A mapping for fast converting back and forth between names and ids
960 NameIdMapping m_name_id_mapping;
962 // Like m_name_id_mapping, but only from names to ids, and includes
963 // item aliases too. Updated by updateAliases()
964 // Note: Not serialized.
966 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
968 // A mapping from groups to a vector of content_ts that belong to it.
969 // Necessary for a direct lookup in getIds().
970 // Note: Not serialized.
971 std::unordered_map<std::string, std::vector<content_t>> m_group_to_items;
973 // Next possibly free id
976 // NodeResolvers to callback once node registration has ended
977 std::vector<NodeResolver *> m_pending_resolve_callbacks;
979 // True when all nodes have been registered
980 bool m_node_registration_complete;
982 //! The union of all nodes' selection boxes.
983 aabb3f m_selection_box_union;
985 * The smallest box in node coordinates that
986 * contains all nodes' selection boxes.
988 core::aabbox3d<s16> m_selection_box_int_union;
992 CNodeDefManager::CNodeDefManager()
998 CNodeDefManager::~CNodeDefManager()
1001 for (ContentFeatures &f : m_content_features) {
1002 for (auto &j : f.mesh_ptr) {
1011 void CNodeDefManager::clear()
1013 m_content_features.clear();
1014 m_name_id_mapping.clear();
1015 m_name_id_mapping_with_aliases.clear();
1016 m_group_to_items.clear();
1018 m_selection_box_union.reset(0,0,0);
1019 m_selection_box_int_union.reset(0,0,0);
1021 resetNodeResolveState();
1023 u32 initial_length = 0;
1024 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1025 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1026 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1027 m_content_features.resize(initial_length);
1029 // Set CONTENT_UNKNOWN
1033 // Insert directly into containers
1034 content_t c = CONTENT_UNKNOWN;
1035 m_content_features[c] = f;
1036 addNameIdMapping(c, f.name);
1043 f.drawtype = NDT_AIRLIKE;
1044 f.param_type = CPT_LIGHT;
1045 f.light_propagates = true;
1046 f.sunlight_propagates = true;
1048 f.pointable = false;
1050 f.buildable_to = true;
1052 f.is_ground_content = true;
1053 // Insert directly into containers
1054 content_t c = CONTENT_AIR;
1055 m_content_features[c] = f;
1056 addNameIdMapping(c, f.name);
1059 // Set CONTENT_IGNORE
1063 f.drawtype = NDT_AIRLIKE;
1064 f.param_type = CPT_NONE;
1065 f.light_propagates = false;
1066 f.sunlight_propagates = false;
1068 f.pointable = false;
1070 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1071 f.is_ground_content = true;
1072 // Insert directly into containers
1073 content_t c = CONTENT_IGNORE;
1074 m_content_features[c] = f;
1075 addNameIdMapping(c, f.name);
1080 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1082 return c < m_content_features.size()
1083 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1087 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1089 return get(n.getContent());
1093 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1095 std::unordered_map<std::string, content_t>::const_iterator
1096 i = m_name_id_mapping_with_aliases.find(name);
1097 if(i == m_name_id_mapping_with_aliases.end())
1104 content_t CNodeDefManager::getId(const std::string &name) const
1106 content_t id = CONTENT_IGNORE;
1112 bool CNodeDefManager::getIds(const std::string &name,
1113 std::vector<content_t> &result) const
1115 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1116 if (name.substr(0,6) != "group:") {
1117 content_t id = CONTENT_IGNORE;
1118 bool exists = getId(name, id);
1120 result.push_back(id);
1123 std::string group = name.substr(6);
1125 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1126 i = m_group_to_items.find(group);
1127 if (i == m_group_to_items.end())
1130 const std::vector<content_t> &items = i->second;
1131 result.insert(result.end(), items.begin(), items.end());
1132 //printf("getIds: %dus\n", t.stop());
1137 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1139 content_t id = CONTENT_UNKNOWN;
1145 // returns CONTENT_IGNORE if no free ID found
1146 content_t CNodeDefManager::allocateId()
1148 for (content_t id = m_next_id;
1149 id >= m_next_id; // overflow?
1151 while (id >= m_content_features.size()) {
1152 m_content_features.emplace_back();
1154 const ContentFeatures &f = m_content_features[id];
1155 if (f.name.empty()) {
1160 // If we arrive here, an overflow occurred in id.
1161 // That means no ID was found
1162 return CONTENT_IGNORE;
1167 * Returns the smallest box that contains all boxes
1168 * in the vector. Box_union is expanded.
1169 * @param[in] boxes the vector containing the boxes
1170 * @param[in, out] box_union the union of the arguments
1172 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1174 for (const aabb3f &box : boxes) {
1175 box_union->addInternalBox(box);
1181 * Returns a box that contains the nodebox in every case.
1182 * The argument node_union is expanded.
1183 * @param[in] nodebox the nodebox to be measured
1184 * @param[in] features used to decide whether the nodebox
1186 * @param[in, out] box_union the union of the arguments
1188 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1191 switch(nodebox.type) {
1193 case NODEBOX_LEVELED: {
1195 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1196 boxVectorUnion(nodebox.fixed, &half_processed);
1197 // Set leveled boxes to maximal
1198 if (nodebox.type == NODEBOX_LEVELED) {
1199 half_processed.MaxEdge.Y = +BS / 2;
1201 if (features.param_type_2 == CPT2_FACEDIR ||
1202 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1203 // Get maximal coordinate
1205 fabsf(half_processed.MinEdge.X),
1206 fabsf(half_processed.MinEdge.Y),
1207 fabsf(half_processed.MinEdge.Z),
1208 fabsf(half_processed.MaxEdge.X),
1209 fabsf(half_processed.MaxEdge.Y),
1210 fabsf(half_processed.MaxEdge.Z) };
1212 for (float coord : coords) {
1217 // Add the union of all possible rotated boxes
1218 box_union->addInternalPoint(-max, -max, -max);
1219 box_union->addInternalPoint(+max, +max, +max);
1221 box_union->addInternalBox(half_processed);
1225 case NODEBOX_WALLMOUNTED: {
1227 box_union->addInternalBox(nodebox.wall_top);
1228 box_union->addInternalBox(nodebox.wall_bottom);
1229 // Find maximal coordinate in the X-Z plane
1231 fabsf(nodebox.wall_side.MinEdge.X),
1232 fabsf(nodebox.wall_side.MinEdge.Z),
1233 fabsf(nodebox.wall_side.MaxEdge.X),
1234 fabsf(nodebox.wall_side.MaxEdge.Z) };
1236 for (float coord : coords) {
1241 // Add the union of all possible rotated boxes
1242 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1243 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1246 case NODEBOX_CONNECTED: {
1247 // Add all possible connected boxes
1248 boxVectorUnion(nodebox.fixed, box_union);
1249 boxVectorUnion(nodebox.connect_top, box_union);
1250 boxVectorUnion(nodebox.connect_bottom, box_union);
1251 boxVectorUnion(nodebox.connect_front, box_union);
1252 boxVectorUnion(nodebox.connect_left, box_union);
1253 boxVectorUnion(nodebox.connect_back, box_union);
1254 boxVectorUnion(nodebox.connect_right, box_union);
1259 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1260 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1266 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1268 m_selection_box_int_union.MinEdge.X = floorf(
1269 m_selection_box_union.MinEdge.X / BS + 0.5f);
1270 m_selection_box_int_union.MinEdge.Y = floorf(
1271 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1272 m_selection_box_int_union.MinEdge.Z = floorf(
1273 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1274 m_selection_box_int_union.MaxEdge.X = ceilf(
1275 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1276 m_selection_box_int_union.MaxEdge.Y = ceilf(
1277 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1278 m_selection_box_int_union.MaxEdge.Z = ceilf(
1279 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1283 // IWritableNodeDefManager
1284 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1288 assert(name == def.name);
1290 // Don't allow redefining ignore (but allow air and unknown)
1291 if (name == "ignore") {
1292 warningstream << "NodeDefManager: Ignoring "
1293 "CONTENT_IGNORE redefinition"<<std::endl;
1294 return CONTENT_IGNORE;
1297 content_t id = CONTENT_IGNORE;
1298 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1301 if (id == CONTENT_IGNORE) {
1302 warningstream << "NodeDefManager: Absolute "
1303 "limit reached" << std::endl;
1304 return CONTENT_IGNORE;
1306 assert(id != CONTENT_IGNORE);
1307 addNameIdMapping(id, name);
1309 m_content_features[id] = def;
1310 verbosestream << "NodeDefManager: registering content id \"" << id
1311 << "\": name=\"" << def.name << "\""<<std::endl;
1313 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1314 fixSelectionBoxIntUnion();
1315 // Add this content to the list of all groups it belongs to
1316 // FIXME: This should remove a node from groups it no longer
1317 // belongs to when a node is re-registered
1318 for (const auto &group : def.groups) {
1319 const std::string &group_name = group.first;
1320 m_group_to_items[group_name].push_back(id);
1326 content_t CNodeDefManager::allocateDummy(const std::string &name)
1328 assert(name != ""); // Pre-condition
1331 return set(name, f);
1335 void CNodeDefManager::removeNode(const std::string &name)
1340 // Erase name from name ID mapping
1341 content_t id = CONTENT_IGNORE;
1342 if (m_name_id_mapping.getId(name, id)) {
1343 m_name_id_mapping.eraseName(name);
1344 m_name_id_mapping_with_aliases.erase(name);
1347 // Erase node content from all groups it belongs to
1348 for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
1349 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1350 std::vector<content_t> &items = iter_groups->second;
1351 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1353 // Check if group is empty
1355 m_group_to_items.erase(iter_groups++);
1362 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1364 std::set<std::string> all;
1366 m_name_id_mapping_with_aliases.clear();
1367 for (const std::string &name : all) {
1368 const std::string &convert_to = idef->getAlias(name);
1370 if (m_name_id_mapping.getId(convert_to, id)) {
1371 m_name_id_mapping_with_aliases.insert(
1372 std::make_pair(name, id));
1377 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1379 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1380 "overrides to textures from " << override_filepath << std::endl;
1382 std::ifstream infile(override_filepath.c_str());
1385 while (std::getline(infile, line)) {
1387 if (trim(line).empty())
1389 std::vector<std::string> splitted = str_split(line, ' ');
1390 if (splitted.size() != 3) {
1391 errorstream << override_filepath
1392 << ":" << line_c << " Could not apply texture override \""
1393 << line << "\": Syntax error" << std::endl;
1398 if (!getId(splitted[0], id))
1399 continue; // Ignore unknown node
1401 ContentFeatures &nodedef = m_content_features[id];
1403 if (splitted[1] == "top")
1404 nodedef.tiledef[0].name = splitted[2];
1405 else if (splitted[1] == "bottom")
1406 nodedef.tiledef[1].name = splitted[2];
1407 else if (splitted[1] == "right")
1408 nodedef.tiledef[2].name = splitted[2];
1409 else if (splitted[1] == "left")
1410 nodedef.tiledef[3].name = splitted[2];
1411 else if (splitted[1] == "back")
1412 nodedef.tiledef[4].name = splitted[2];
1413 else if (splitted[1] == "front")
1414 nodedef.tiledef[5].name = splitted[2];
1415 else if (splitted[1] == "all" || splitted[1] == "*")
1416 for (TileDef &i : nodedef.tiledef)
1417 i.name = splitted[2];
1418 else if (splitted[1] == "sides")
1419 for (int i = 2; i < 6; i++)
1420 nodedef.tiledef[i].name = splitted[2];
1422 errorstream << override_filepath
1423 << ":" << line_c << " Could not apply texture override \""
1424 << line << "\": Unknown node side \""
1425 << splitted[1] << "\"" << std::endl;
1431 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1432 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1433 void *progress_callback_args)
1436 infostream << "CNodeDefManager::updateTextures(): Updating "
1437 "textures in node definitions" << std::endl;
1439 Client *client = (Client *)gamedef;
1440 ITextureSource *tsrc = client->tsrc();
1441 IShaderSource *shdsrc = client->getShaderSource();
1442 scene::IMeshManipulator *meshmanip =
1443 RenderingEngine::get_scene_manager()->getMeshManipulator();
1444 TextureSettings tsettings;
1445 tsettings.readSettings();
1447 u32 size = m_content_features.size();
1449 for (u32 i = 0; i < size; i++) {
1450 ContentFeatures *f = &(m_content_features[i]);
1451 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1452 progress_callback(progress_callback_args, i, size);
1457 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1459 writeU8(os, 1); // version
1461 std::ostringstream os2(std::ios::binary);
1462 for (u32 i = 0; i < m_content_features.size(); i++) {
1463 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1464 || i == CONTENT_UNKNOWN)
1466 const ContentFeatures *f = &m_content_features[i];
1467 if (f->name.empty())
1470 // Wrap it in a string to allow different lengths without
1471 // strict version incompatibilities
1472 std::ostringstream wrapper_os(std::ios::binary);
1473 f->serialize(wrapper_os, protocol_version);
1474 os2<<serializeString(wrapper_os.str());
1476 // must not overflow
1477 u16 next = count + 1;
1478 FATAL_ERROR_IF(next < count, "Overflow");
1481 writeU16(os, count);
1482 os << serializeLongString(os2.str());
1486 void CNodeDefManager::deSerialize(std::istream &is)
1489 int version = readU8(is);
1491 throw SerializationError("unsupported NodeDefinitionManager version");
1492 u16 count = readU16(is);
1493 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1495 for (u16 n = 0; n < count; n++) {
1496 u16 i = readU16(is2);
1498 // Read it from the string wrapper
1499 std::string wrapper = deSerializeString(is2);
1500 std::istringstream wrapper_is(wrapper, std::ios::binary);
1501 f.deSerialize(wrapper_is);
1503 // Check error conditions
1504 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1505 warningstream << "NodeDefManager::deSerialize(): "
1506 "not changing builtin node " << i << std::endl;
1509 if (f.name.empty()) {
1510 warningstream << "NodeDefManager::deSerialize(): "
1511 "received empty name" << std::endl;
1517 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1518 warningstream << "NodeDefManager::deSerialize(): "
1519 "already defined with different ID: " << f.name << std::endl;
1523 // All is ok, add node definition with the requested ID
1524 if (i >= m_content_features.size())
1525 m_content_features.resize((u32)(i) + 1);
1526 m_content_features[i] = f;
1527 addNameIdMapping(i, f.name);
1528 verbosestream << "deserialized " << f.name << std::endl;
1530 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1531 fixSelectionBoxIntUnion();
1536 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1538 m_name_id_mapping.set(i, name);
1539 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1543 IWritableNodeDefManager *createNodeDefManager()
1545 return new CNodeDefManager();
1548 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1550 m_node_registration_complete = completed;
1554 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1557 if (m_node_registration_complete)
1558 nr->nodeResolveInternal();
1560 m_pending_resolve_callbacks.push_back(nr);
1564 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1566 size_t len = m_pending_resolve_callbacks.size();
1567 for (size_t i = 0; i != len; i++) {
1568 if (nr != m_pending_resolve_callbacks[i])
1572 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1573 m_pending_resolve_callbacks.resize(len);
1581 void CNodeDefManager::runNodeResolveCallbacks()
1583 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1584 NodeResolver *nr = m_pending_resolve_callbacks[i];
1585 nr->nodeResolveInternal();
1588 m_pending_resolve_callbacks.clear();
1592 void CNodeDefManager::resetNodeResolveState()
1594 m_node_registration_complete = false;
1595 m_pending_resolve_callbacks.clear();
1598 void CNodeDefManager::mapNodeboxConnections()
1600 for (ContentFeatures &f : m_content_features) {
1601 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1604 for (const std::string &name : f.connects_to) {
1605 getIds(name, f.connects_to_ids);
1610 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1612 const ContentFeatures &f1 = get(from);
1614 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1617 // lookup target in connected set
1618 if (!CONTAINS(f1.connects_to_ids, to.param0))
1621 const ContentFeatures &f2 = get(to);
1623 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1624 // ignores actually looking if back connection exists
1625 return CONTAINS(f2.connects_to_ids, from.param0);
1627 // does to node declare usable faces?
1628 if (f2.connect_sides > 0) {
1629 if ((f2.param_type_2 == CPT2_FACEDIR ||
1630 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1631 && (connect_face >= 4)) {
1632 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1633 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1635 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1637 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1638 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1642 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1644 return (f2.connect_sides
1645 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1647 return (f2.connect_sides & connect_face);
1649 // the target is just a regular node, so connect no matter back connection
1657 NodeResolver::NodeResolver()
1659 m_nodenames.reserve(16);
1660 m_nnlistsizes.reserve(4);
1664 NodeResolver::~NodeResolver()
1666 if (!m_resolve_done && m_ndef)
1667 m_ndef->cancelNodeResolveCallback(this);
1671 void NodeResolver::nodeResolveInternal()
1673 m_nodenames_idx = 0;
1674 m_nnlistsizes_idx = 0;
1677 m_resolve_done = true;
1679 m_nodenames.clear();
1680 m_nnlistsizes.clear();
1684 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1685 const std::string &node_alt, content_t c_fallback)
1687 if (m_nodenames_idx == m_nodenames.size()) {
1688 *result_out = c_fallback;
1689 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1694 std::string name = m_nodenames[m_nodenames_idx++];
1696 bool success = m_ndef->getId(name, c);
1697 if (!success && !node_alt.empty()) {
1699 success = m_ndef->getId(name, c);
1703 errorstream << "NodeResolver: failed to resolve node name '" << name
1704 << "'." << std::endl;
1713 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1714 bool all_required, content_t c_fallback)
1716 bool success = true;
1718 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1719 errorstream << "NodeResolver: no more node lists" << std::endl;
1723 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1726 if (m_nodenames_idx == m_nodenames.size()) {
1727 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1732 std::string &name = m_nodenames[m_nodenames_idx++];
1734 if (name.substr(0,6) != "group:") {
1735 if (m_ndef->getId(name, c)) {
1736 result_out->push_back(c);
1737 } else if (all_required) {
1738 errorstream << "NodeResolver: failed to resolve node name '"
1739 << name << "'." << std::endl;
1740 result_out->push_back(c_fallback);
1744 m_ndef->getIds(name, *result_out);