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);
56 -BS / 2, -BS / 2, -BS / 2, BS / 2, -BS / 2 + BS / 16., BS / 2);
57 wall_side = aabb3f(-BS / 2, -BS / 2, -BS / 2, -BS / 2 + BS / 16., BS / 2, BS / 2);
58 // no default for other parts
60 connect_bottom.clear();
61 connect_front.clear();
64 connect_right.clear();
65 disconnected_top.clear();
66 disconnected_bottom.clear();
67 disconnected_front.clear();
68 disconnected_left.clear();
69 disconnected_back.clear();
70 disconnected_right.clear();
72 disconnected_sides.clear();
75 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
86 writeU16(os, fixed.size());
87 for (const aabb3f &nodebox : fixed) {
88 writeV3F32(os, nodebox.MinEdge);
89 writeV3F32(os, nodebox.MaxEdge);
92 case NODEBOX_WALLMOUNTED:
95 writeV3F32(os, wall_top.MinEdge);
96 writeV3F32(os, wall_top.MaxEdge);
97 writeV3F32(os, wall_bottom.MinEdge);
98 writeV3F32(os, wall_bottom.MaxEdge);
99 writeV3F32(os, wall_side.MinEdge);
100 writeV3F32(os, wall_side.MaxEdge);
102 case NODEBOX_CONNECTED:
105 #define WRITEBOX(box) \
106 writeU16(os, (box).size()); \
107 for (const aabb3f &i : (box)) { \
108 writeV3F32(os, i.MinEdge); \
109 writeV3F32(os, i.MaxEdge); \
113 WRITEBOX(connect_top);
114 WRITEBOX(connect_bottom);
115 WRITEBOX(connect_front);
116 WRITEBOX(connect_left);
117 WRITEBOX(connect_back);
118 WRITEBOX(connect_right);
119 WRITEBOX(disconnected_top);
120 WRITEBOX(disconnected_bottom);
121 WRITEBOX(disconnected_front);
122 WRITEBOX(disconnected_left);
123 WRITEBOX(disconnected_back);
124 WRITEBOX(disconnected_right);
125 WRITEBOX(disconnected);
126 WRITEBOX(disconnected_sides);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if (type == NODEBOX_FIXED || type == NODEBOX_LEVELED) {
145 u16 fixed_count = readU16(is);
146 while (fixed_count--) {
148 box.MinEdge = readV3F32(is);
149 box.MaxEdge = readV3F32(is);
150 fixed.push_back(box);
152 } else if (type == NODEBOX_WALLMOUNTED) {
153 wall_top.MinEdge = readV3F32(is);
154 wall_top.MaxEdge = readV3F32(is);
155 wall_bottom.MinEdge = readV3F32(is);
156 wall_bottom.MaxEdge = readV3F32(is);
157 wall_side.MinEdge = readV3F32(is);
158 wall_side.MaxEdge = readV3F32(is);
159 } else if (type == NODEBOX_CONNECTED) {
160 #define READBOXES(box) \
162 count = readU16(is); \
163 (box).reserve(count); \
165 v3f min = readV3F32(is); \
166 v3f max = readV3F32(is); \
167 (box).emplace_back(min, max); \
174 READBOXES(connect_top);
175 READBOXES(connect_bottom);
176 READBOXES(connect_front);
177 READBOXES(connect_left);
178 READBOXES(connect_back);
179 READBOXES(connect_right);
180 READBOXES(disconnected_top);
181 READBOXES(disconnected_bottom);
182 READBOXES(disconnected_front);
183 READBOXES(disconnected_left);
184 READBOXES(disconnected_back);
185 READBOXES(disconnected_right);
186 READBOXES(disconnected);
187 READBOXES(disconnected_sides);
195 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
196 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
197 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
198 #define TILE_FLAG_HAS_COLOR (1 << 3)
199 #define TILE_FLAG_HAS_SCALE (1 << 4)
200 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
202 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
204 // protocol_version >= 36
206 writeU8(os, version);
208 os << serializeString(name);
209 animation.serialize(os, version);
210 bool has_scale = scale > 0;
212 if (backface_culling)
213 flags |= TILE_FLAG_BACKFACE_CULLING;
214 if (tileable_horizontal)
215 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
216 if (tileable_vertical)
217 flags |= TILE_FLAG_TILEABLE_VERTICAL;
219 flags |= TILE_FLAG_HAS_COLOR;
221 flags |= TILE_FLAG_HAS_SCALE;
222 if (align_style != ALIGN_STYLE_NODE)
223 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
226 writeU8(os, color.getRed());
227 writeU8(os, color.getGreen());
228 writeU8(os, color.getBlue());
232 if (align_style != ALIGN_STYLE_NODE)
233 writeU8(os, align_style);
236 void TileDef::deSerialize(
237 std::istream &is, u8 contentfeatures_version, NodeDrawType drawtype)
239 int version = readU8(is);
241 throw SerializationError("unsupported TileDef version");
242 name = deSerializeString(is);
243 animation.deSerialize(is, version);
244 u16 flags = readU16(is);
245 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
246 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
247 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
248 has_color = flags & TILE_FLAG_HAS_COLOR;
249 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
250 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
252 color.setRed(readU8(is));
253 color.setGreen(readU8(is));
254 color.setBlue(readU8(is));
256 scale = has_scale ? readU8(is) : 0;
258 align_style = static_cast<AlignStyle>(readU8(is));
260 align_style = ALIGN_STYLE_NODE;
263 void TextureSettings::readSettings()
265 connected_glass = g_settings->getBool("connected_glass");
266 opaque_water = g_settings->getBool("opaque_water");
267 bool enable_shaders = g_settings->getBool("enable_shaders");
268 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
269 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
270 bool smooth_lighting = g_settings->getBool("smooth_lighting");
271 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
272 enable_minimap = g_settings->getBool("enable_minimap");
273 node_texture_size = g_settings->getU16("texture_min_size");
274 std::string leaves_style_str = g_settings->get("leaves_style");
275 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
276 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
278 // Mesh cache is not supported in combination with smooth lighting
280 enable_mesh_cache = false;
282 use_normal_texture = enable_shaders &&
283 (enable_bumpmapping || enable_parallax_occlusion);
284 if (leaves_style_str == "fancy") {
285 leaves_style = LEAVES_FANCY;
286 } else if (leaves_style_str == "simple") {
287 leaves_style = LEAVES_SIMPLE;
289 leaves_style = LEAVES_OPAQUE;
292 if (world_aligned_mode_str == "enable")
293 world_aligned_mode = WORLDALIGN_ENABLE;
294 else if (world_aligned_mode_str == "force_solid")
295 world_aligned_mode = WORLDALIGN_FORCE;
296 else if (world_aligned_mode_str == "force_nodebox")
297 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
299 world_aligned_mode = WORLDALIGN_DISABLE;
301 if (autoscale_mode_str == "enable")
302 autoscale_mode = AUTOSCALE_ENABLE;
303 else if (autoscale_mode_str == "force")
304 autoscale_mode = AUTOSCALE_FORCE;
306 autoscale_mode = AUTOSCALE_DISABLE;
313 ContentFeatures::ContentFeatures()
318 ContentFeatures::~ContentFeatures()
321 for (u16 j = 0; j < 6; j++) {
322 delete tiles[j].layers[0].frames;
323 delete tiles[j].layers[1].frames;
325 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
326 delete special_tiles[j].layers[0].frames;
330 void ContentFeatures::reset()
337 visual_solidness = 0;
338 backface_culling = true;
341 has_on_construct = false;
342 has_on_destruct = false;
343 has_after_destruct = false;
347 NOTE: Most of this is always overridden by the default values given
352 // Unknown nodes can be dug
353 groups["dig_immediate"] = 2;
354 drawtype = NDT_NORMAL;
357 for (auto &i : mesh_ptr)
359 minimap_color = video::SColor(0, 0, 0, 0);
362 for (auto &i : tiledef)
364 for (auto &j : tiledef_special)
367 post_effect_color = video::SColor(0, 0, 0, 0);
368 param_type = CPT_NONE;
369 param_type_2 = CPT2_NONE;
370 is_ground_content = false;
371 light_propagates = false;
372 sunlight_propagates = false;
377 buildable_to = false;
379 rightclickable = true;
381 leveled_max = LEVELED_MAX;
382 liquid_type = LIQUID_NONE;
383 liquid_alternative_flowing = "";
384 liquid_alternative_flowing_id = CONTENT_IGNORE;
385 liquid_alternative_source = "";
386 liquid_alternative_source_id = CONTENT_IGNORE;
387 liquid_viscosity = 0;
388 liquid_renewable = true;
389 liquid_range = LIQUID_LEVEL_MAX + 1;
392 damage_per_second = 0;
393 node_box = NodeBox();
394 selection_box = NodeBox();
395 collision_box = NodeBox();
397 legacy_facedir_simple = false;
398 legacy_wallmounted = false;
399 sound_footstep = SimpleSoundSpec();
400 sound_dig = SimpleSoundSpec("__group");
401 sound_dug = SimpleSoundSpec();
403 connects_to_ids.clear();
405 color = video::SColor(0xFFFFFFFF);
408 node_dig_prediction = "air";
411 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
413 const u8 version = CONTENTFEATURES_VERSION;
414 writeU8(os, version);
417 os << serializeString(name);
418 writeU16(os, groups.size());
419 for (const auto &group : groups) {
420 os << serializeString(group.first);
421 writeS16(os, group.second);
423 writeU8(os, param_type);
424 writeU8(os, param_type_2);
427 writeU8(os, drawtype);
428 os << serializeString(mesh);
429 writeF32(os, visual_scale);
431 for (const TileDef &td : tiledef)
432 td.serialize(os, protocol_version);
433 for (const TileDef &td : tiledef_overlay)
434 td.serialize(os, protocol_version);
435 writeU8(os, CF_SPECIAL_COUNT);
436 for (const TileDef &td : tiledef_special) {
437 td.serialize(os, protocol_version);
440 writeU8(os, color.getRed());
441 writeU8(os, color.getGreen());
442 writeU8(os, color.getBlue());
443 os << serializeString(palette_name);
445 writeU8(os, connect_sides);
446 writeU16(os, connects_to_ids.size());
447 for (u16 connects_to_id : connects_to_ids)
448 writeU16(os, connects_to_id);
449 writeARGB8(os, post_effect_color);
450 writeU8(os, leveled);
453 writeU8(os, light_propagates);
454 writeU8(os, sunlight_propagates);
455 writeU8(os, light_source);
458 writeU8(os, is_ground_content);
461 writeU8(os, walkable);
462 writeU8(os, pointable);
463 writeU8(os, diggable);
464 writeU8(os, climbable);
465 writeU8(os, buildable_to);
466 writeU8(os, rightclickable);
467 writeU32(os, damage_per_second);
470 writeU8(os, liquid_type);
471 os << serializeString(liquid_alternative_flowing);
472 os << serializeString(liquid_alternative_source);
473 writeU8(os, liquid_viscosity);
474 writeU8(os, liquid_renewable);
475 writeU8(os, liquid_range);
476 writeU8(os, drowning);
477 writeU8(os, floodable);
480 node_box.serialize(os, protocol_version);
481 selection_box.serialize(os, protocol_version);
482 collision_box.serialize(os, protocol_version);
485 sound_footstep.serialize(os, version);
486 sound_dig.serialize(os, version);
487 sound_dug.serialize(os, version);
490 writeU8(os, legacy_facedir_simple);
491 writeU8(os, legacy_wallmounted);
493 os << serializeString(node_dig_prediction);
494 writeU8(os, leveled_max);
497 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
499 // alpha == 0 means that the node is using texture alpha
500 if (alpha == 0 || alpha == 255)
503 for (int i = 0; i < length; i++) {
504 if (tiles[i].name.empty())
507 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
508 tiles[i].name = s.str();
512 void ContentFeatures::deSerialize(std::istream &is)
515 const u8 version = readU8(is);
516 if (version < CONTENTFEATURES_VERSION)
517 throw SerializationError("unsupported ContentFeatures version");
520 name = deSerializeString(is);
522 u32 groups_size = readU16(is);
523 for (u32 i = 0; i < groups_size; i++) {
524 std::string name = deSerializeString(is);
525 int value = readS16(is);
526 groups[name] = value;
528 param_type = (enum ContentParamType)readU8(is);
529 param_type_2 = (enum ContentParamType2)readU8(is);
532 drawtype = (enum NodeDrawType)readU8(is);
533 mesh = deSerializeString(is);
534 visual_scale = readF32(is);
536 throw SerializationError("unsupported tile count");
537 for (TileDef &td : tiledef)
538 td.deSerialize(is, version, drawtype);
539 for (TileDef &td : tiledef_overlay)
540 td.deSerialize(is, version, drawtype);
541 if (readU8(is) != CF_SPECIAL_COUNT)
542 throw SerializationError("unsupported CF_SPECIAL_COUNT");
543 for (TileDef &td : tiledef_special)
544 td.deSerialize(is, version, drawtype);
546 color.setRed(readU8(is));
547 color.setGreen(readU8(is));
548 color.setBlue(readU8(is));
549 palette_name = deSerializeString(is);
551 connect_sides = readU8(is);
552 u16 connects_to_size = readU16(is);
553 connects_to_ids.clear();
554 for (u16 i = 0; i < connects_to_size; i++)
555 connects_to_ids.push_back(readU16(is));
556 post_effect_color = readARGB8(is);
557 leveled = readU8(is);
560 light_propagates = readU8(is);
561 sunlight_propagates = readU8(is);
562 light_source = readU8(is);
563 light_source = MYMIN(light_source, LIGHT_MAX);
566 is_ground_content = readU8(is);
569 walkable = readU8(is);
570 pointable = readU8(is);
571 diggable = readU8(is);
572 climbable = readU8(is);
573 buildable_to = readU8(is);
574 rightclickable = readU8(is);
575 damage_per_second = readU32(is);
578 liquid_type = (enum LiquidType)readU8(is);
579 liquid_alternative_flowing = deSerializeString(is);
580 liquid_alternative_source = deSerializeString(is);
581 liquid_viscosity = readU8(is);
582 liquid_renewable = readU8(is);
583 liquid_range = readU8(is);
584 drowning = readU8(is);
585 floodable = readU8(is);
588 node_box.deSerialize(is);
589 selection_box.deSerialize(is);
590 collision_box.deSerialize(is);
593 sound_footstep.deSerialize(is, version);
594 sound_dig.deSerialize(is, version);
595 sound_dug.deSerialize(is, version);
597 // read legacy properties
598 legacy_facedir_simple = readU8(is);
599 legacy_wallmounted = readU8(is);
602 node_dig_prediction = deSerializeString(is);
603 u8 tmp_leveled_max = readU8(is);
604 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
605 throw SerializationError("");
606 leveled_max = tmp_leveled_max;
607 } catch (SerializationError &e) {
612 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, const TileSpec &tile,
613 const TileDef &tiledef, video::SColor color, u8 material_type,
614 u32 shader_id, bool backface_culling, const TextureSettings &tsettings)
616 layer->shader_id = shader_id;
617 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
618 layer->material_type = material_type;
620 bool has_scale = tiledef.scale > 0;
621 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
622 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
623 if (use_autoscale && layer->texture) {
624 auto texture_size = layer->texture->getOriginalSize();
625 float base_size = tsettings.node_texture_size;
626 float size = std::fmin(texture_size.Width, texture_size.Height);
627 layer->scale = std::fmax(base_size, size) / base_size;
628 } else if (has_scale) {
629 layer->scale = tiledef.scale;
633 if (!tile.world_aligned)
636 // Normal texture and shader flags texture
637 if (tsettings.use_normal_texture) {
638 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
640 layer->flags_texture =
641 tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
644 layer->material_flags = 0;
645 if (backface_culling)
646 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
647 if (tiledef.animation.type != TAT_NONE)
648 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
649 if (tiledef.tileable_horizontal)
650 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
651 if (tiledef.tileable_vertical)
652 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
655 layer->has_color = tiledef.has_color;
656 if (tiledef.has_color)
657 layer->color = tiledef.color;
659 layer->color = color;
661 // Animation parameters
663 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
665 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
666 &frame_count, &frame_length_ms, NULL);
667 layer->animation_frame_count = frame_count;
668 layer->animation_frame_length_ms = frame_length_ms;
671 if (frame_count == 1) {
672 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
674 std::ostringstream os(std::ios::binary);
675 if (!layer->frames) {
676 layer->frames = new std::vector<FrameSpec>();
678 layer->frames->resize(frame_count);
680 for (int i = 0; i < frame_count; i++) {
686 tiledef.animation.getTextureModifer(
687 os, layer->texture->getOriginalSize(), i);
689 frame.texture = tsrc->getTextureForMesh(
690 os.str(), &frame.texture_id);
691 if (layer->normal_texture)
692 frame.normal_texture = tsrc->getNormalTexture(os.str());
693 frame.flags_texture = layer->flags_texture;
694 (*layer->frames)[i] = frame;
701 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
703 if (style == ALIGN_STYLE_WORLD)
705 if (mode == WORLDALIGN_DISABLE)
707 if (style == ALIGN_STYLE_USER_DEFINED)
709 if (drawtype == NDT_NORMAL)
710 return mode >= WORLDALIGN_FORCE;
711 if (drawtype == NDT_NODEBOX)
712 return mode >= WORLDALIGN_FORCE_NODEBOX;
716 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
717 scene::IMeshManipulator *meshmanip, Client *client,
718 const TextureSettings &tsettings)
720 // minimap pixel color - the average color of a texture
721 if (tsettings.enable_minimap && !tiledef[0].name.empty())
722 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
724 // Figure out the actual tiles to use
726 for (u32 j = 0; j < 6; j++) {
727 tdef[j] = tiledef[j];
728 if (tdef[j].name.empty())
729 tdef[j].name = "unknown_node.png";
731 // also the overlay tiles
732 TileDef tdef_overlay[6];
733 for (u32 j = 0; j < 6; j++)
734 tdef_overlay[j] = tiledef_overlay[j];
735 // also the special tiles
736 TileDef tdef_spec[6];
737 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
738 tdef_spec[j] = tiledef_special[j];
740 bool is_liquid = false;
742 u8 material_type = (alpha == 255) ? TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
747 material_type = (alpha == 255) ? TILE_MATERIAL_OPAQUE
748 : TILE_MATERIAL_ALPHA;
755 assert(liquid_type == LIQUID_SOURCE);
756 if (tsettings.opaque_water)
761 case NDT_FLOWINGLIQUID:
762 assert(liquid_type == LIQUID_FLOWING);
764 if (tsettings.opaque_water)
770 visual_solidness = 1;
772 case NDT_GLASSLIKE_FRAMED:
774 visual_solidness = 1;
776 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
778 visual_solidness = 1;
779 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED
784 visual_solidness = 1;
786 case NDT_ALLFACES_OPTIONAL:
787 if (tsettings.leaves_style == LEAVES_FANCY) {
788 drawtype = NDT_ALLFACES;
790 visual_solidness = 1;
791 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
792 for (u32 j = 0; j < 6; j++) {
793 if (!tdef_spec[j].name.empty())
794 tdef[j].name = tdef_spec[j].name;
796 drawtype = NDT_GLASSLIKE;
798 visual_solidness = 1;
800 drawtype = NDT_NORMAL;
802 for (TileDef &td : tdef)
803 td.name += std::string("^[noalpha");
806 material_type = TILE_MATERIAL_WAVING_LEAVES;
811 material_type = TILE_MATERIAL_WAVING_PLANTS;
820 material_type = TILE_MATERIAL_WAVING_PLANTS;
821 else if (waving == 2)
822 material_type = TILE_MATERIAL_WAVING_LEAVES;
823 else if (waving == 3)
824 material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC;
832 case NDT_PLANTLIKE_ROOTED:
838 // Vertex alpha is no longer supported, correct if necessary.
839 correctAlpha(tdef, 6);
840 correctAlpha(tdef_overlay, 6);
841 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
844 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE
845 : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
847 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE
848 : TILE_MATERIAL_LIQUID_TRANSPARENT;
852 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
854 u8 overlay_material = material_type;
855 if (overlay_material == TILE_MATERIAL_OPAQUE)
856 overlay_material = TILE_MATERIAL_BASIC;
857 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
858 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
861 shdsrc->getShader("nodes_shader", overlay_material, drawtype);
863 // Tiles (fill in f->tiles[])
864 for (u16 j = 0; j < 6; j++) {
865 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
866 tsettings.world_aligned_mode, drawtype);
867 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j], color,
868 material_type, tile_shader, tdef[j].backface_culling,
870 if (!tdef_overlay[j].name.empty())
871 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j],
872 tdef_overlay[j], color, overlay_material,
873 overlay_shader, tdef[j].backface_culling,
877 u8 special_material = material_type;
878 if (drawtype == NDT_PLANTLIKE_ROOTED) {
880 special_material = TILE_MATERIAL_WAVING_PLANTS;
881 else if (waving == 2)
882 special_material = TILE_MATERIAL_WAVING_LEAVES;
885 shdsrc->getShader("nodes_shader", special_material, drawtype);
887 // Special tiles (fill in f->special_tiles[])
888 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
889 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j],
890 tdef_spec[j], color, special_material, special_shader,
891 tdef_spec[j].backface_culling, tsettings);
893 if (param_type_2 == CPT2_COLOR || param_type_2 == CPT2_COLORED_FACEDIR ||
894 param_type_2 == CPT2_COLORED_WALLMOUNTED)
895 palette = tsrc->getPalette(palette_name);
897 if (drawtype == NDT_MESH && !mesh.empty()) {
899 // Read the mesh and apply scale
900 mesh_ptr[0] = client->getMesh(mesh);
902 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
903 scaleMesh(mesh_ptr[0], scale);
904 recalculateBoundingBox(mesh_ptr[0]);
905 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
909 // Cache 6dfacedir and wallmounted rotated clones of meshes
910 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
911 (param_type_2 == CPT2_FACEDIR ||
912 param_type_2 == CPT2_COLORED_FACEDIR)) {
913 for (u16 j = 1; j < 24; j++) {
914 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
915 rotateMeshBy6dFacedir(mesh_ptr[j], j);
916 recalculateBoundingBox(mesh_ptr[j]);
917 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
919 } else if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
920 (param_type_2 == CPT2_WALLMOUNTED ||
921 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
922 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
923 for (u16 j = 1; j < 6; j++) {
924 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
925 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
926 recalculateBoundingBox(mesh_ptr[j]);
927 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
929 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
930 recalculateBoundingBox(mesh_ptr[0]);
931 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
940 NodeDefManager::NodeDefManager()
945 NodeDefManager::~NodeDefManager()
948 for (ContentFeatures &f : m_content_features) {
949 for (auto &j : f.mesh_ptr) {
957 void NodeDefManager::clear()
959 m_content_features.clear();
960 m_name_id_mapping.clear();
961 m_name_id_mapping_with_aliases.clear();
962 m_group_to_items.clear();
964 m_selection_box_union.reset(0, 0, 0);
965 m_selection_box_int_union.reset(0, 0, 0);
967 resetNodeResolveState();
969 u32 initial_length = 0;
970 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
971 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
972 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
973 m_content_features.resize(initial_length);
975 // Set CONTENT_UNKNOWN
979 // Insert directly into containers
980 content_t c = CONTENT_UNKNOWN;
981 m_content_features[c] = f;
982 addNameIdMapping(c, f.name);
989 f.drawtype = NDT_AIRLIKE;
990 f.param_type = CPT_LIGHT;
991 f.light_propagates = true;
992 f.sunlight_propagates = true;
996 f.buildable_to = true;
998 f.is_ground_content = true;
999 // Insert directly into containers
1000 content_t c = CONTENT_AIR;
1001 m_content_features[c] = f;
1002 addNameIdMapping(c, f.name);
1005 // Set CONTENT_IGNORE
1009 f.drawtype = NDT_AIRLIKE;
1010 f.param_type = CPT_NONE;
1011 f.light_propagates = false;
1012 f.sunlight_propagates = false;
1014 f.pointable = false;
1016 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1017 f.is_ground_content = true;
1018 // Insert directly into containers
1019 content_t c = CONTENT_IGNORE;
1020 m_content_features[c] = f;
1021 addNameIdMapping(c, f.name);
1025 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1027 std::unordered_map<std::string, content_t>::const_iterator i =
1028 m_name_id_mapping_with_aliases.find(name);
1029 if (i == m_name_id_mapping_with_aliases.end())
1035 content_t NodeDefManager::getId(const std::string &name) const
1037 content_t id = CONTENT_IGNORE;
1042 bool NodeDefManager::getIds(const std::string &name, std::vector<content_t> &result) const
1044 // TimeTaker t("getIds", NULL, PRECISION_MICRO);
1045 if (name.substr(0, 6) != "group:") {
1046 content_t id = CONTENT_IGNORE;
1047 bool exists = getId(name, id);
1049 result.push_back(id);
1052 std::string group = name.substr(6);
1054 std::unordered_map<std::string, std::vector<content_t>>::const_iterator i =
1055 m_group_to_items.find(group);
1056 if (i == m_group_to_items.end())
1059 const std::vector<content_t> &items = i->second;
1060 result.insert(result.end(), items.begin(), items.end());
1061 // printf("getIds: %dus\n", t.stop());
1065 const ContentFeatures &NodeDefManager::get(const std::string &name) const
1067 content_t id = CONTENT_UNKNOWN;
1072 // returns CONTENT_IGNORE if no free ID found
1073 content_t NodeDefManager::allocateId()
1075 for (content_t id = m_next_id; id >= m_next_id; // overflow?
1077 while (id >= m_content_features.size()) {
1078 m_content_features.emplace_back();
1080 const ContentFeatures &f = m_content_features[id];
1081 if (f.name.empty()) {
1086 // If we arrive here, an overflow occurred in id.
1087 // That means no ID was found
1088 return CONTENT_IGNORE;
1092 * Returns the smallest box that contains all boxes
1093 * in the vector. Box_union is expanded.
1094 * @param[in] boxes the vector containing the boxes
1095 * @param[in, out] box_union the union of the arguments
1097 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1099 for (const aabb3f &box : boxes) {
1100 box_union->addInternalBox(box);
1105 * Returns a box that contains the nodebox in every case.
1106 * The argument node_union is expanded.
1107 * @param[in] nodebox the nodebox to be measured
1108 * @param[in] features used to decide whether the nodebox
1110 * @param[in, out] box_union the union of the arguments
1112 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1115 switch (nodebox.type) {
1117 case NODEBOX_LEVELED: {
1119 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1120 boxVectorUnion(nodebox.fixed, &half_processed);
1121 // Set leveled boxes to maximal
1122 if (nodebox.type == NODEBOX_LEVELED) {
1123 half_processed.MaxEdge.Y = +BS / 2;
1125 if (features.param_type_2 == CPT2_FACEDIR ||
1126 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1127 // Get maximal coordinate
1128 f32 coords[] = {fabsf(half_processed.MinEdge.X),
1129 fabsf(half_processed.MinEdge.Y),
1130 fabsf(half_processed.MinEdge.Z),
1131 fabsf(half_processed.MaxEdge.X),
1132 fabsf(half_processed.MaxEdge.Y),
1133 fabsf(half_processed.MaxEdge.Z)};
1135 for (float coord : coords) {
1140 // Add the union of all possible rotated boxes
1141 box_union->addInternalPoint(-max, -max, -max);
1142 box_union->addInternalPoint(+max, +max, +max);
1144 box_union->addInternalBox(half_processed);
1148 case NODEBOX_WALLMOUNTED: {
1150 box_union->addInternalBox(nodebox.wall_top);
1151 box_union->addInternalBox(nodebox.wall_bottom);
1152 // Find maximal coordinate in the X-Z plane
1153 f32 coords[] = {fabsf(nodebox.wall_side.MinEdge.X),
1154 fabsf(nodebox.wall_side.MinEdge.Z),
1155 fabsf(nodebox.wall_side.MaxEdge.X),
1156 fabsf(nodebox.wall_side.MaxEdge.Z)};
1158 for (float coord : coords) {
1163 // Add the union of all possible rotated boxes
1164 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1165 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1168 case NODEBOX_CONNECTED: {
1169 // Add all possible connected boxes
1170 boxVectorUnion(nodebox.fixed, box_union);
1171 boxVectorUnion(nodebox.connect_top, box_union);
1172 boxVectorUnion(nodebox.connect_bottom, box_union);
1173 boxVectorUnion(nodebox.connect_front, box_union);
1174 boxVectorUnion(nodebox.connect_left, box_union);
1175 boxVectorUnion(nodebox.connect_back, box_union);
1176 boxVectorUnion(nodebox.connect_right, box_union);
1177 boxVectorUnion(nodebox.disconnected_top, box_union);
1178 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1179 boxVectorUnion(nodebox.disconnected_front, box_union);
1180 boxVectorUnion(nodebox.disconnected_left, box_union);
1181 boxVectorUnion(nodebox.disconnected_back, box_union);
1182 boxVectorUnion(nodebox.disconnected_right, box_union);
1183 boxVectorUnion(nodebox.disconnected, box_union);
1184 boxVectorUnion(nodebox.disconnected_sides, box_union);
1189 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1190 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1195 inline void NodeDefManager::fixSelectionBoxIntUnion()
1197 m_selection_box_int_union.MinEdge.X =
1198 floorf(m_selection_box_union.MinEdge.X / BS + 0.5f);
1199 m_selection_box_int_union.MinEdge.Y =
1200 floorf(m_selection_box_union.MinEdge.Y / BS + 0.5f);
1201 m_selection_box_int_union.MinEdge.Z =
1202 floorf(m_selection_box_union.MinEdge.Z / BS + 0.5f);
1203 m_selection_box_int_union.MaxEdge.X =
1204 ceilf(m_selection_box_union.MaxEdge.X / BS - 0.5f);
1205 m_selection_box_int_union.MaxEdge.Y =
1206 ceilf(m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1207 m_selection_box_int_union.MaxEdge.Z =
1208 ceilf(m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1211 void NodeDefManager::eraseIdFromGroups(content_t id)
1213 // For all groups in m_group_to_items...
1214 for (auto iter_groups = m_group_to_items.begin();
1215 iter_groups != m_group_to_items.end();) {
1216 // Get the group items vector.
1217 std::vector<content_t> &items = iter_groups->second;
1219 // Remove any occurence of the id in the group items vector.
1220 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1222 // If group is empty, erase its vector from the map.
1224 iter_groups = m_group_to_items.erase(iter_groups);
1230 // IWritableNodeDefManager
1231 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &d)
1233 ContentFeatures def = d;
1237 assert(name != "ignore");
1238 assert(name == def.name);
1240 content_t id = CONTENT_IGNORE;
1242 if (m_name_id_mapping.getId(name, id)) {
1244 ContentFeatures old_def = get(name);
1245 for (u32 j = 0; j < 6; j++)
1246 if (def.tiledef[j].name.empty())
1247 def.tiledef[j] = old_def.tiledef[j];
1248 for (u32 j = 0; j < 6; j++)
1249 if (def.tiledef_overlay[j].name.empty())
1250 def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
1251 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
1252 if (def.tiledef_special[j].name.empty())
1253 def.tiledef_special[j] = old_def.tiledef_special[j];
1258 if (id == CONTENT_IGNORE) {
1259 warningstream << "NodeDefManager: Absolute "
1262 return CONTENT_IGNORE;
1264 assert(id != CONTENT_IGNORE);
1265 addNameIdMapping(id, name);
1268 // If there is already ContentFeatures registered for this id, clear old groups
1269 if (id < m_content_features.size())
1270 eraseIdFromGroups(id);
1272 m_content_features[id] = def;
1273 verbosestream << "NodeDefManager: registering content id \"" << id
1274 << "\": name=\"" << def.name << "\"" << std::endl;
1276 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1277 fixSelectionBoxIntUnion();
1279 // Add this content to the list of all groups it belongs to
1280 for (const auto &group : def.groups) {
1281 const std::string &group_name = group.first;
1282 m_group_to_items[group_name].push_back(id);
1288 content_t NodeDefManager::allocateDummy(const std::string &name)
1290 assert(name != ""); // Pre-condition
1293 return set(name, f);
1296 void NodeDefManager::removeNode(const std::string &name)
1301 // Erase name from name ID mapping
1302 content_t id = CONTENT_IGNORE;
1303 if (m_name_id_mapping.getId(name, id)) {
1304 m_name_id_mapping.eraseName(name);
1305 m_name_id_mapping_with_aliases.erase(name);
1308 eraseIdFromGroups(id);
1311 void NodeDefManager::updateAliases(IItemDefManager *idef)
1313 std::set<std::string> all;
1315 m_name_id_mapping_with_aliases.clear();
1316 for (const std::string &name : all) {
1317 const std::string &convert_to = idef->getAlias(name);
1319 if (m_name_id_mapping.getId(convert_to, id)) {
1320 m_name_id_mapping_with_aliases.insert(std::make_pair(name, id));
1325 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1327 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1328 "overrides to textures"
1331 for (const TextureOverride &texture_override : overrides) {
1333 if (!getId(texture_override.id, id))
1334 continue; // Ignore unknown node
1336 ContentFeatures &nodedef = m_content_features[id];
1338 if (texture_override.hasTarget(OverrideTarget::TOP))
1339 nodedef.tiledef[0].name = texture_override.texture;
1341 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1342 nodedef.tiledef[1].name = texture_override.texture;
1344 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1345 nodedef.tiledef[2].name = texture_override.texture;
1347 if (texture_override.hasTarget(OverrideTarget::LEFT))
1348 nodedef.tiledef[3].name = texture_override.texture;
1350 if (texture_override.hasTarget(OverrideTarget::BACK))
1351 nodedef.tiledef[4].name = texture_override.texture;
1353 if (texture_override.hasTarget(OverrideTarget::FRONT))
1354 nodedef.tiledef[5].name = texture_override.texture;
1358 void NodeDefManager::updateTextures(IGameDef *gamedef,
1359 void (*progress_callback)(
1360 void *progress_args, u32 progress, u32 max_progress),
1361 void *progress_callback_args)
1364 infostream << "NodeDefManager::updateTextures(): Updating "
1365 "textures in node definitions"
1368 Client *client = (Client *)gamedef;
1369 ITextureSource *tsrc = client->tsrc();
1370 IShaderSource *shdsrc = client->getShaderSource();
1371 scene::IMeshManipulator *meshmanip =
1372 RenderingEngine::get_scene_manager()->getMeshManipulator();
1373 TextureSettings tsettings;
1374 tsettings.readSettings();
1376 u32 size = m_content_features.size();
1378 for (u32 i = 0; i < size; i++) {
1379 ContentFeatures *f = &(m_content_features[i]);
1380 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1381 progress_callback(progress_callback_args, i, size);
1386 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1388 writeU8(os, 1); // version
1390 std::ostringstream os2(std::ios::binary);
1391 for (u32 i = 0; i < m_content_features.size(); i++) {
1392 if (i == CONTENT_IGNORE || i == CONTENT_AIR || 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());
1413 void NodeDefManager::deSerialize(std::istream &is)
1416 int version = readU8(is);
1418 throw SerializationError("unsupported NodeDefinitionManager version");
1419 u16 count = readU16(is);
1420 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1422 for (u16 n = 0; n < count; n++) {
1423 u16 i = readU16(is2);
1425 // Read it from the string wrapper
1426 std::string wrapper = deSerializeString(is2);
1427 std::istringstream wrapper_is(wrapper, std::ios::binary);
1428 f.deSerialize(wrapper_is);
1430 // Check error conditions
1431 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1432 warningstream << "NodeDefManager::deSerialize(): "
1433 "not changing builtin node "
1437 if (f.name.empty()) {
1438 warningstream << "NodeDefManager::deSerialize(): "
1439 "received empty name"
1446 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1447 warningstream << "NodeDefManager::deSerialize(): "
1448 "already defined with different ID: "
1449 << f.name << std::endl;
1453 // All is ok, add node definition with the requested ID
1454 if (i >= m_content_features.size())
1455 m_content_features.resize((u32)(i) + 1);
1456 m_content_features[i] = f;
1457 addNameIdMapping(i, f.name);
1458 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1460 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1461 fixSelectionBoxIntUnion();
1464 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1465 // are not sent, resolve them client-side too.
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));
1475 NodeDefManager *createNodeDefManager()
1477 return new NodeDefManager();
1480 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1483 if (m_node_registration_complete)
1484 nr->nodeResolveInternal();
1486 m_pending_resolve_callbacks.push_back(nr);
1489 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1491 size_t len = m_pending_resolve_callbacks.size();
1492 for (size_t i = 0; i != len; i++) {
1493 if (nr != m_pending_resolve_callbacks[i])
1497 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1498 m_pending_resolve_callbacks.resize(len);
1505 void NodeDefManager::runNodeResolveCallbacks()
1507 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1508 NodeResolver *nr = m_pending_resolve_callbacks[i];
1509 nr->nodeResolveInternal();
1512 m_pending_resolve_callbacks.clear();
1515 void NodeDefManager::resetNodeResolveState()
1517 m_node_registration_complete = false;
1518 m_pending_resolve_callbacks.clear();
1521 static void removeDupes(std::vector<content_t> &list)
1523 std::sort(list.begin(), list.end());
1524 auto new_end = std::unique(list.begin(), list.end());
1525 list.erase(new_end, list.end());
1528 void NodeDefManager::resolveCrossrefs()
1530 for (ContentFeatures &f : m_content_features) {
1531 if (f.liquid_type != LIQUID_NONE) {
1532 f.liquid_alternative_flowing_id =
1533 getId(f.liquid_alternative_flowing);
1534 f.liquid_alternative_source_id =
1535 getId(f.liquid_alternative_source);
1538 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1541 for (const std::string &name : f.connects_to) {
1542 getIds(name, f.connects_to_ids);
1544 removeDupes(f.connects_to_ids);
1548 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) const
1550 const ContentFeatures &f1 = get(from);
1552 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1555 // lookup target in connected set
1556 if (!CONTAINS(f1.connects_to_ids, to.param0))
1559 const ContentFeatures &f2 = get(to);
1561 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1562 // ignores actually looking if back connection exists
1563 return CONTAINS(f2.connects_to_ids, from.param0);
1565 // does to node declare usable faces?
1566 if (f2.connect_sides > 0) {
1567 if ((f2.param_type_2 == CPT2_FACEDIR ||
1568 f2.param_type_2 == CPT2_COLORED_FACEDIR) &&
1569 (connect_face >= 4)) {
1570 static const u8 rot[33 * 4] = {
1571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
1572 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1574 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1577 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1581 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 16,
1584 return (f2.connect_sides &
1585 rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1587 return (f2.connect_sides & connect_face);
1589 // the target is just a regular node, so connect no matter back connection
1597 NodeResolver::NodeResolver()
1599 m_nodenames.reserve(16);
1600 m_nnlistsizes.reserve(4);
1603 NodeResolver::~NodeResolver()
1605 if (!m_resolve_done && m_ndef)
1606 m_ndef->cancelNodeResolveCallback(this);
1609 void NodeResolver::cloneTo(NodeResolver *res) const
1611 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1612 " after resolving has completed");
1613 /* We don't actually do anything significant. Since the node resolving has
1614 * already completed, the class that called us will already have the
1615 * resolved IDs in its data structures (which it copies on its own) */
1616 res->m_ndef = m_ndef;
1617 res->m_resolve_done = true;
1620 void NodeResolver::nodeResolveInternal()
1622 m_nodenames_idx = 0;
1623 m_nnlistsizes_idx = 0;
1626 m_resolve_done = true;
1628 m_nodenames.clear();
1629 m_nnlistsizes.clear();
1632 bool NodeResolver::getIdFromNrBacklog(content_t *result_out, const std::string &node_alt,
1633 content_t c_fallback, bool error_on_fallback)
1635 if (m_nodenames_idx == m_nodenames.size()) {
1636 *result_out = c_fallback;
1637 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1642 std::string name = m_nodenames[m_nodenames_idx++];
1644 bool success = m_ndef->getId(name, c);
1645 if (!success && !node_alt.empty()) {
1647 success = m_ndef->getId(name, c);
1651 if (error_on_fallback)
1652 errorstream << "NodeResolver: failed to resolve node name '"
1653 << name << "'." << std::endl;
1661 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1662 bool all_required, content_t c_fallback)
1664 bool success = true;
1666 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1667 errorstream << "NodeResolver: no more node lists" << std::endl;
1671 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1674 if (m_nodenames_idx == m_nodenames.size()) {
1675 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1680 std::string &name = m_nodenames[m_nodenames_idx++];
1682 if (name.substr(0, 6) != "group:") {
1683 if (m_ndef->getId(name, c)) {
1684 result_out->push_back(c);
1685 } else if (all_required) {
1686 errorstream << "NodeResolver: failed to resolve node "
1688 << name << "'." << std::endl;
1689 result_out->push_back(c_fallback);
1693 m_ndef->getIds(name, *result_out);