3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "client/mesh.h"
25 #include "client/shader.h"
26 #include "client/client.h"
27 #include "client/renderingengine.h"
28 #include "client/tile.h"
29 #include <IMeshManipulator.h>
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
40 #include <fstream> // Used in applyTextureOverrides()
50 type = NODEBOX_REGULAR;
53 // default is sign/ladder-like
54 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
55 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
56 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
57 // no default for other parts
59 connect_bottom.clear();
60 connect_front.clear();
63 connect_right.clear();
64 disconnected_top.clear();
65 disconnected_bottom.clear();
66 disconnected_front.clear();
67 disconnected_left.clear();
68 disconnected_back.clear();
69 disconnected_right.clear();
71 disconnected_sides.clear();
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
91 case NODEBOX_WALLMOUNTED:
94 writeV3F32(os, wall_top.MinEdge);
95 writeV3F32(os, wall_top.MaxEdge);
96 writeV3F32(os, wall_bottom.MinEdge);
97 writeV3F32(os, wall_bottom.MaxEdge);
98 writeV3F32(os, wall_side.MinEdge);
99 writeV3F32(os, wall_side.MaxEdge);
101 case NODEBOX_CONNECTED:
104 #define WRITEBOX(box) \
105 writeU16(os, (box).size()); \
106 for (const aabb3f &i: (box)) { \
107 writeV3F32(os, i.MinEdge); \
108 writeV3F32(os, i.MaxEdge); \
112 WRITEBOX(connect_top);
113 WRITEBOX(connect_bottom);
114 WRITEBOX(connect_front);
115 WRITEBOX(connect_left);
116 WRITEBOX(connect_back);
117 WRITEBOX(connect_right);
118 WRITEBOX(disconnected_top);
119 WRITEBOX(disconnected_bottom);
120 WRITEBOX(disconnected_front);
121 WRITEBOX(disconnected_left);
122 WRITEBOX(disconnected_back);
123 WRITEBOX(disconnected_right);
124 WRITEBOX(disconnected);
125 WRITEBOX(disconnected_sides);
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
137 throw SerializationError("unsupported NodeBox version");
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
149 box.MinEdge = readV3F32(is);
150 box.MaxEdge = readV3F32(is);
151 fixed.push_back(box);
154 else if(type == NODEBOX_WALLMOUNTED)
156 wall_top.MinEdge = readV3F32(is);
157 wall_top.MaxEdge = readV3F32(is);
158 wall_bottom.MinEdge = readV3F32(is);
159 wall_bottom.MaxEdge = readV3F32(is);
160 wall_side.MinEdge = readV3F32(is);
161 wall_side.MaxEdge = readV3F32(is);
163 else if (type == NODEBOX_CONNECTED)
165 #define READBOXES(box) { \
166 count = readU16(is); \
167 (box).reserve(count); \
169 v3f min = readV3F32(is); \
170 v3f max = readV3F32(is); \
171 (box).emplace_back(min, max); }; }
176 READBOXES(connect_top);
177 READBOXES(connect_bottom);
178 READBOXES(connect_front);
179 READBOXES(connect_left);
180 READBOXES(connect_back);
181 READBOXES(connect_right);
182 READBOXES(disconnected_top);
183 READBOXES(disconnected_bottom);
184 READBOXES(disconnected_front);
185 READBOXES(disconnected_left);
186 READBOXES(disconnected_back);
187 READBOXES(disconnected_right);
188 READBOXES(disconnected);
189 READBOXES(disconnected_sides);
197 #define TILE_FLAG_BACKFACE_CULLING (1 << 0)
198 #define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
199 #define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
200 #define TILE_FLAG_HAS_COLOR (1 << 3)
201 #define TILE_FLAG_HAS_SCALE (1 << 4)
202 #define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
206 // protocol_version >= 36
208 writeU8(os, version);
210 os << serializeString(name);
211 animation.serialize(os, version);
212 bool has_scale = scale > 0;
214 if (backface_culling)
215 flags |= TILE_FLAG_BACKFACE_CULLING;
216 if (tileable_horizontal)
217 flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
218 if (tileable_vertical)
219 flags |= TILE_FLAG_TILEABLE_VERTICAL;
221 flags |= TILE_FLAG_HAS_COLOR;
223 flags |= TILE_FLAG_HAS_SCALE;
224 if (align_style != ALIGN_STYLE_NODE)
225 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
228 writeU8(os, color.getRed());
229 writeU8(os, color.getGreen());
230 writeU8(os, color.getBlue());
234 if (align_style != ALIGN_STYLE_NODE)
235 writeU8(os, align_style);
238 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
239 NodeDrawType drawtype)
241 int version = readU8(is);
243 throw SerializationError("unsupported TileDef version");
244 name = deSerializeString(is);
245 animation.deSerialize(is, version);
246 u16 flags = readU16(is);
247 backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
248 tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
249 tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
250 has_color = flags & TILE_FLAG_HAS_COLOR;
251 bool has_scale = flags & TILE_FLAG_HAS_SCALE;
252 bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
254 color.setRed(readU8(is));
255 color.setGreen(readU8(is));
256 color.setBlue(readU8(is));
258 scale = has_scale ? readU8(is) : 0;
260 align_style = static_cast<AlignStyle>(readU8(is));
262 align_style = ALIGN_STYLE_NODE;
265 void TextureSettings::readSettings()
267 connected_glass = g_settings->getBool("connected_glass");
268 opaque_water = g_settings->getBool("opaque_water");
269 bool enable_shaders = g_settings->getBool("enable_shaders");
270 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
271 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
272 bool smooth_lighting = g_settings->getBool("smooth_lighting");
273 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
274 enable_minimap = g_settings->getBool("enable_minimap");
275 node_texture_size = g_settings->getU16("texture_min_size");
276 std::string leaves_style_str = g_settings->get("leaves_style");
277 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
278 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
280 // Mesh cache is not supported in combination with smooth lighting
282 enable_mesh_cache = false;
284 use_normal_texture = enable_shaders &&
285 (enable_bumpmapping || enable_parallax_occlusion);
286 if (leaves_style_str == "fancy") {
287 leaves_style = LEAVES_FANCY;
288 } else if (leaves_style_str == "simple") {
289 leaves_style = LEAVES_SIMPLE;
291 leaves_style = LEAVES_OPAQUE;
294 if (world_aligned_mode_str == "enable")
295 world_aligned_mode = WORLDALIGN_ENABLE;
296 else if (world_aligned_mode_str == "force_solid")
297 world_aligned_mode = WORLDALIGN_FORCE;
298 else if (world_aligned_mode_str == "force_nodebox")
299 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
301 world_aligned_mode = WORLDALIGN_DISABLE;
303 if (autoscale_mode_str == "enable")
304 autoscale_mode = AUTOSCALE_ENABLE;
305 else if (autoscale_mode_str == "force")
306 autoscale_mode = AUTOSCALE_FORCE;
308 autoscale_mode = AUTOSCALE_DISABLE;
315 ContentFeatures::ContentFeatures()
320 ContentFeatures::~ContentFeatures()
323 for (u16 j = 0; j < 6; j++) {
324 delete tiles[j].layers[0].frames;
325 delete tiles[j].layers[1].frames;
327 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
328 delete special_tiles[j].layers[0].frames;
332 void ContentFeatures::reset()
339 visual_solidness = 0;
340 backface_culling = true;
343 has_on_construct = false;
344 has_on_destruct = false;
345 has_after_destruct = false;
349 NOTE: Most of this is always overridden by the default values given
354 // Unknown nodes can be dug
355 groups["dig_immediate"] = 2;
356 drawtype = NDT_NORMAL;
359 for (auto &i : mesh_ptr)
361 minimap_color = video::SColor(0, 0, 0, 0);
364 for (auto &i : tiledef)
366 for (auto &j : tiledef_special)
369 post_effect_color = video::SColor(0, 0, 0, 0);
370 param_type = CPT_NONE;
371 param_type_2 = CPT2_NONE;
372 is_ground_content = false;
373 light_propagates = false;
374 sunlight_propagates = false;
379 buildable_to = false;
381 rightclickable = true;
383 leveled_max = LEVELED_MAX;
384 liquid_type = LIQUID_NONE;
385 liquid_alternative_flowing = "";
386 liquid_alternative_flowing_id = CONTENT_IGNORE;
387 liquid_alternative_source = "";
388 liquid_alternative_source_id = CONTENT_IGNORE;
389 liquid_viscosity = 0;
390 liquid_renewable = true;
391 liquid_range = LIQUID_LEVEL_MAX+1;
394 damage_per_second = 0;
395 node_box = NodeBox();
396 selection_box = NodeBox();
397 collision_box = NodeBox();
399 legacy_facedir_simple = false;
400 legacy_wallmounted = false;
401 sound_footstep = SimpleSoundSpec();
402 sound_dig = SimpleSoundSpec("__group");
403 sound_dug = SimpleSoundSpec();
405 connects_to_ids.clear();
407 color = video::SColor(0xFFFFFFFF);
410 node_dig_prediction = "air";
413 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
415 const u8 version = CONTENTFEATURES_VERSION;
416 writeU8(os, version);
419 os << serializeString(name);
420 writeU16(os, groups.size());
421 for (const auto &group : groups) {
422 os << serializeString(group.first);
423 writeS16(os, group.second);
425 writeU8(os, param_type);
426 writeU8(os, param_type_2);
429 writeU8(os, drawtype);
430 os << serializeString(mesh);
431 writeF32(os, visual_scale);
433 for (const TileDef &td : tiledef)
434 td.serialize(os, protocol_version);
435 for (const TileDef &td : tiledef_overlay)
436 td.serialize(os, protocol_version);
437 writeU8(os, CF_SPECIAL_COUNT);
438 for (const TileDef &td : tiledef_special) {
439 td.serialize(os, protocol_version);
442 writeU8(os, color.getRed());
443 writeU8(os, color.getGreen());
444 writeU8(os, color.getBlue());
445 os << serializeString(palette_name);
447 writeU8(os, connect_sides);
448 writeU16(os, connects_to_ids.size());
449 for (u16 connects_to_id : connects_to_ids)
450 writeU16(os, connects_to_id);
451 writeARGB8(os, post_effect_color);
452 writeU8(os, leveled);
455 writeU8(os, light_propagates);
456 writeU8(os, sunlight_propagates);
457 writeU8(os, light_source);
460 writeU8(os, is_ground_content);
463 writeU8(os, walkable);
464 writeU8(os, pointable);
465 writeU8(os, diggable);
466 writeU8(os, climbable);
467 writeU8(os, buildable_to);
468 writeU8(os, rightclickable);
469 writeU32(os, damage_per_second);
472 writeU8(os, liquid_type);
473 os << serializeString(liquid_alternative_flowing);
474 os << serializeString(liquid_alternative_source);
475 writeU8(os, liquid_viscosity);
476 writeU8(os, liquid_renewable);
477 writeU8(os, liquid_range);
478 writeU8(os, drowning);
479 writeU8(os, floodable);
482 node_box.serialize(os, protocol_version);
483 selection_box.serialize(os, protocol_version);
484 collision_box.serialize(os, protocol_version);
487 sound_footstep.serialize(os, version);
488 sound_dig.serialize(os, version);
489 sound_dug.serialize(os, version);
492 writeU8(os, legacy_facedir_simple);
493 writeU8(os, legacy_wallmounted);
495 os << serializeString(node_dig_prediction);
496 writeU8(os, leveled_max);
499 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
501 // alpha == 0 means that the node is using texture alpha
502 if (alpha == 0 || alpha == 255)
505 for (int i = 0; i < length; i++) {
506 if (tiles[i].name.empty())
509 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
510 tiles[i].name = s.str();
514 void ContentFeatures::deSerialize(std::istream &is)
517 const u8 version = readU8(is);
518 if (version < CONTENTFEATURES_VERSION)
519 throw SerializationError("unsupported ContentFeatures version");
522 name = deSerializeString(is);
524 u32 groups_size = readU16(is);
525 for (u32 i = 0; i < groups_size; i++) {
526 std::string name = deSerializeString(is);
527 int value = readS16(is);
528 groups[name] = value;
530 param_type = (enum ContentParamType) readU8(is);
531 param_type_2 = (enum ContentParamType2) readU8(is);
534 drawtype = (enum NodeDrawType) readU8(is);
535 mesh = deSerializeString(is);
536 visual_scale = readF32(is);
538 throw SerializationError("unsupported tile count");
539 for (TileDef &td : tiledef)
540 td.deSerialize(is, version, drawtype);
541 for (TileDef &td : tiledef_overlay)
542 td.deSerialize(is, version, drawtype);
543 if (readU8(is) != CF_SPECIAL_COUNT)
544 throw SerializationError("unsupported CF_SPECIAL_COUNT");
545 for (TileDef &td : tiledef_special)
546 td.deSerialize(is, version, drawtype);
548 color.setRed(readU8(is));
549 color.setGreen(readU8(is));
550 color.setBlue(readU8(is));
551 palette_name = deSerializeString(is);
553 connect_sides = readU8(is);
554 u16 connects_to_size = readU16(is);
555 connects_to_ids.clear();
556 for (u16 i = 0; i < connects_to_size; i++)
557 connects_to_ids.push_back(readU16(is));
558 post_effect_color = readARGB8(is);
559 leveled = readU8(is);
562 light_propagates = readU8(is);
563 sunlight_propagates = readU8(is);
564 light_source = readU8(is);
565 light_source = MYMIN(light_source, LIGHT_MAX);
568 is_ground_content = readU8(is);
571 walkable = readU8(is);
572 pointable = readU8(is);
573 diggable = readU8(is);
574 climbable = readU8(is);
575 buildable_to = readU8(is);
576 rightclickable = readU8(is);
577 damage_per_second = readU32(is);
580 liquid_type = (enum LiquidType) readU8(is);
581 liquid_alternative_flowing = deSerializeString(is);
582 liquid_alternative_source = deSerializeString(is);
583 liquid_viscosity = readU8(is);
584 liquid_renewable = readU8(is);
585 liquid_range = readU8(is);
586 drowning = readU8(is);
587 floodable = readU8(is);
590 node_box.deSerialize(is);
591 selection_box.deSerialize(is);
592 collision_box.deSerialize(is);
595 sound_footstep.deSerialize(is, version);
596 sound_dig.deSerialize(is, version);
597 sound_dug.deSerialize(is, version);
599 // read legacy properties
600 legacy_facedir_simple = readU8(is);
601 legacy_wallmounted = readU8(is);
604 node_dig_prediction = deSerializeString(is);
605 u8 tmp_leveled_max = readU8(is);
606 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
607 throw SerializationError("");
608 leveled_max = tmp_leveled_max;
609 } catch(SerializationError &e) {};
613 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
614 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
615 u8 material_type, u32 shader_id, bool backface_culling,
616 const TextureSettings &tsettings)
618 layer->shader_id = shader_id;
619 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
620 layer->material_type = material_type;
622 bool has_scale = tiledef.scale > 0;
623 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
624 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
625 if (use_autoscale && layer->texture) {
626 auto texture_size = layer->texture->getOriginalSize();
627 float base_size = tsettings.node_texture_size;
628 float size = std::fmin(texture_size.Width, texture_size.Height);
629 layer->scale = std::fmax(base_size, size) / base_size;
630 } else if (has_scale) {
631 layer->scale = tiledef.scale;
635 if (!tile.world_aligned)
638 // Normal texture and shader flags texture
639 if (tsettings.use_normal_texture) {
640 layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
642 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
645 layer->material_flags = 0;
646 if (backface_culling)
647 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
648 if (tiledef.animation.type != TAT_NONE)
649 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
650 if (tiledef.tileable_horizontal)
651 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
652 if (tiledef.tileable_vertical)
653 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
656 layer->has_color = tiledef.has_color;
657 if (tiledef.has_color)
658 layer->color = tiledef.color;
660 layer->color = color;
662 // Animation parameters
664 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
666 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
667 &frame_count, &frame_length_ms, NULL);
668 layer->animation_frame_count = frame_count;
669 layer->animation_frame_length_ms = frame_length_ms;
672 if (frame_count == 1) {
673 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
675 std::ostringstream os(std::ios::binary);
676 if (!layer->frames) {
677 layer->frames = new std::vector<FrameSpec>();
679 layer->frames->resize(frame_count);
681 for (int i = 0; i < frame_count; i++) {
687 tiledef.animation.getTextureModifer(os,
688 layer->texture->getOriginalSize(), i);
690 frame.texture = tsrc->getTextureForMesh(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, const TextureSettings &tsettings)
719 // minimap pixel color - the average color of a texture
720 if (tsettings.enable_minimap && !tiledef[0].name.empty())
721 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
723 // Figure out the actual tiles to use
725 for (u32 j = 0; j < 6; j++) {
726 tdef[j] = tiledef[j];
727 if (tdef[j].name.empty())
728 tdef[j].name = "unknown_node.png";
730 // also the overlay tiles
731 TileDef tdef_overlay[6];
732 for (u32 j = 0; j < 6; j++)
733 tdef_overlay[j] = tiledef_overlay[j];
734 // also the special tiles
735 TileDef tdef_spec[6];
736 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
737 tdef_spec[j] = tiledef_special[j];
739 bool is_liquid = false;
741 u8 material_type = (alpha == 255) ?
742 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
747 material_type = (alpha == 255) ?
748 TILE_MATERIAL_OPAQUE : 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 : NDT_GLASSLIKE;
783 visual_solidness = 1;
785 case NDT_ALLFACES_OPTIONAL:
786 if (tsettings.leaves_style == LEAVES_FANCY) {
787 drawtype = NDT_ALLFACES;
789 visual_solidness = 1;
790 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
791 for (u32 j = 0; j < 6; j++) {
792 if (!tdef_spec[j].name.empty())
793 tdef[j].name = tdef_spec[j].name;
795 drawtype = NDT_GLASSLIKE;
797 visual_solidness = 1;
799 drawtype = NDT_NORMAL;
801 for (TileDef &td : tdef)
802 td.name += std::string("^[noalpha");
805 material_type = TILE_MATERIAL_WAVING_LEAVES;
810 material_type = TILE_MATERIAL_WAVING_PLANTS;
819 material_type = TILE_MATERIAL_WAVING_PLANTS;
820 else if (waving == 2)
821 material_type = TILE_MATERIAL_WAVING_LEAVES;
822 else if (waving == 3)
823 material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC;
831 case NDT_PLANTLIKE_ROOTED:
837 // Vertex alpha is no longer supported, correct if necessary.
838 correctAlpha(tdef, 6);
839 correctAlpha(tdef_overlay, 6);
840 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
843 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
844 TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
846 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
847 TILE_MATERIAL_LIQUID_TRANSPARENT;
851 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
853 u8 overlay_material = material_type;
854 if (overlay_material == TILE_MATERIAL_OPAQUE)
855 overlay_material = TILE_MATERIAL_BASIC;
856 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
857 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
859 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
861 // Tiles (fill in f->tiles[])
862 for (u16 j = 0; j < 6; j++) {
863 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
864 tsettings.world_aligned_mode, drawtype);
865 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
866 color, material_type, tile_shader,
867 tdef[j].backface_culling, tsettings);
868 if (!tdef_overlay[j].name.empty())
869 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
870 color, overlay_material, overlay_shader,
871 tdef[j].backface_culling, tsettings);
874 u8 special_material = material_type;
875 if (drawtype == NDT_PLANTLIKE_ROOTED) {
877 special_material = TILE_MATERIAL_WAVING_PLANTS;
878 else if (waving == 2)
879 special_material = TILE_MATERIAL_WAVING_LEAVES;
881 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
883 // Special tiles (fill in f->special_tiles[])
884 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
885 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
886 color, special_material, special_shader,
887 tdef_spec[j].backface_culling, tsettings);
889 if (param_type_2 == CPT2_COLOR ||
890 param_type_2 == CPT2_COLORED_FACEDIR ||
891 param_type_2 == CPT2_COLORED_WALLMOUNTED)
892 palette = tsrc->getPalette(palette_name);
894 if (drawtype == NDT_MESH && !mesh.empty()) {
896 // Read the mesh and apply scale
897 mesh_ptr[0] = client->getMesh(mesh);
899 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
900 scaleMesh(mesh_ptr[0], scale);
901 recalculateBoundingBox(mesh_ptr[0]);
902 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
906 //Cache 6dfacedir and wallmounted rotated clones of meshes
907 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
908 (param_type_2 == CPT2_FACEDIR
909 || param_type_2 == CPT2_COLORED_FACEDIR)) {
910 for (u16 j = 1; j < 24; j++) {
911 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
912 rotateMeshBy6dFacedir(mesh_ptr[j], j);
913 recalculateBoundingBox(mesh_ptr[j]);
914 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
916 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
917 && (param_type_2 == CPT2_WALLMOUNTED ||
918 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
919 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
920 for (u16 j = 1; j < 6; j++) {
921 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
922 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
923 recalculateBoundingBox(mesh_ptr[j]);
924 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
926 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
927 recalculateBoundingBox(mesh_ptr[0]);
928 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
940 NodeDefManager::NodeDefManager()
946 NodeDefManager::~NodeDefManager()
949 for (ContentFeatures &f : m_content_features) {
950 for (auto &j : f.mesh_ptr) {
959 void NodeDefManager::clear()
961 m_content_features.clear();
962 m_name_id_mapping.clear();
963 m_name_id_mapping_with_aliases.clear();
964 m_group_to_items.clear();
966 m_selection_box_union.reset(0,0,0);
967 m_selection_box_int_union.reset(0,0,0);
969 resetNodeResolveState();
971 u32 initial_length = 0;
972 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
973 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
974 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
975 m_content_features.resize(initial_length);
977 // Set CONTENT_UNKNOWN
981 // Insert directly into containers
982 content_t c = CONTENT_UNKNOWN;
983 m_content_features[c] = f;
984 addNameIdMapping(c, f.name);
991 f.drawtype = NDT_AIRLIKE;
992 f.param_type = CPT_LIGHT;
993 f.light_propagates = true;
994 f.sunlight_propagates = true;
998 f.buildable_to = true;
1000 f.is_ground_content = true;
1001 // Insert directly into containers
1002 content_t c = CONTENT_AIR;
1003 m_content_features[c] = f;
1004 addNameIdMapping(c, f.name);
1007 // Set CONTENT_IGNORE
1011 f.drawtype = NDT_AIRLIKE;
1012 f.param_type = CPT_NONE;
1013 f.light_propagates = false;
1014 f.sunlight_propagates = false;
1016 f.pointable = false;
1018 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1019 f.is_ground_content = true;
1020 // Insert directly into containers
1021 content_t c = CONTENT_IGNORE;
1022 m_content_features[c] = f;
1023 addNameIdMapping(c, f.name);
1028 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1030 std::unordered_map<std::string, content_t>::const_iterator
1031 i = m_name_id_mapping_with_aliases.find(name);
1032 if(i == m_name_id_mapping_with_aliases.end())
1039 content_t NodeDefManager::getId(const std::string &name) const
1041 content_t id = CONTENT_IGNORE;
1047 bool NodeDefManager::getIds(const std::string &name,
1048 std::vector<content_t> &result) const
1050 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1051 if (name.substr(0,6) != "group:") {
1052 content_t id = CONTENT_IGNORE;
1053 bool exists = getId(name, id);
1055 result.push_back(id);
1058 std::string group = name.substr(6);
1060 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1061 i = m_group_to_items.find(group);
1062 if (i == m_group_to_items.end())
1065 const std::vector<content_t> &items = i->second;
1066 result.insert(result.end(), items.begin(), items.end());
1067 //printf("getIds: %dus\n", t.stop());
1072 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1074 content_t id = CONTENT_UNKNOWN;
1080 // returns CONTENT_IGNORE if no free ID found
1081 content_t NodeDefManager::allocateId()
1083 for (content_t id = m_next_id;
1084 id >= m_next_id; // overflow?
1086 while (id >= m_content_features.size()) {
1087 m_content_features.emplace_back();
1089 const ContentFeatures &f = m_content_features[id];
1090 if (f.name.empty()) {
1095 // If we arrive here, an overflow occurred in id.
1096 // That means no ID was found
1097 return CONTENT_IGNORE;
1102 * Returns the smallest box that contains all boxes
1103 * in the vector. Box_union is expanded.
1104 * @param[in] boxes the vector containing the boxes
1105 * @param[in, out] box_union the union of the arguments
1107 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1109 for (const aabb3f &box : boxes) {
1110 box_union->addInternalBox(box);
1116 * Returns a box that contains the nodebox in every case.
1117 * The argument node_union is expanded.
1118 * @param[in] nodebox the nodebox to be measured
1119 * @param[in] features used to decide whether the nodebox
1121 * @param[in, out] box_union the union of the arguments
1123 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1126 switch(nodebox.type) {
1128 case NODEBOX_LEVELED: {
1130 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1131 boxVectorUnion(nodebox.fixed, &half_processed);
1132 // Set leveled boxes to maximal
1133 if (nodebox.type == NODEBOX_LEVELED) {
1134 half_processed.MaxEdge.Y = +BS / 2;
1136 if (features.param_type_2 == CPT2_FACEDIR ||
1137 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1138 // Get maximal coordinate
1140 fabsf(half_processed.MinEdge.X),
1141 fabsf(half_processed.MinEdge.Y),
1142 fabsf(half_processed.MinEdge.Z),
1143 fabsf(half_processed.MaxEdge.X),
1144 fabsf(half_processed.MaxEdge.Y),
1145 fabsf(half_processed.MaxEdge.Z) };
1147 for (float coord : coords) {
1152 // Add the union of all possible rotated boxes
1153 box_union->addInternalPoint(-max, -max, -max);
1154 box_union->addInternalPoint(+max, +max, +max);
1156 box_union->addInternalBox(half_processed);
1160 case NODEBOX_WALLMOUNTED: {
1162 box_union->addInternalBox(nodebox.wall_top);
1163 box_union->addInternalBox(nodebox.wall_bottom);
1164 // Find maximal coordinate in the X-Z plane
1166 fabsf(nodebox.wall_side.MinEdge.X),
1167 fabsf(nodebox.wall_side.MinEdge.Z),
1168 fabsf(nodebox.wall_side.MaxEdge.X),
1169 fabsf(nodebox.wall_side.MaxEdge.Z) };
1171 for (float coord : coords) {
1176 // Add the union of all possible rotated boxes
1177 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1178 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1181 case NODEBOX_CONNECTED: {
1182 // Add all possible connected boxes
1183 boxVectorUnion(nodebox.fixed, box_union);
1184 boxVectorUnion(nodebox.connect_top, box_union);
1185 boxVectorUnion(nodebox.connect_bottom, box_union);
1186 boxVectorUnion(nodebox.connect_front, box_union);
1187 boxVectorUnion(nodebox.connect_left, box_union);
1188 boxVectorUnion(nodebox.connect_back, box_union);
1189 boxVectorUnion(nodebox.connect_right, box_union);
1190 boxVectorUnion(nodebox.disconnected_top, box_union);
1191 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1192 boxVectorUnion(nodebox.disconnected_front, box_union);
1193 boxVectorUnion(nodebox.disconnected_left, box_union);
1194 boxVectorUnion(nodebox.disconnected_back, box_union);
1195 boxVectorUnion(nodebox.disconnected_right, box_union);
1196 boxVectorUnion(nodebox.disconnected, box_union);
1197 boxVectorUnion(nodebox.disconnected_sides, box_union);
1202 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1203 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1209 inline void NodeDefManager::fixSelectionBoxIntUnion()
1211 m_selection_box_int_union.MinEdge.X = floorf(
1212 m_selection_box_union.MinEdge.X / BS + 0.5f);
1213 m_selection_box_int_union.MinEdge.Y = floorf(
1214 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1215 m_selection_box_int_union.MinEdge.Z = floorf(
1216 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1217 m_selection_box_int_union.MaxEdge.X = ceilf(
1218 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1219 m_selection_box_int_union.MaxEdge.Y = ceilf(
1220 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1221 m_selection_box_int_union.MaxEdge.Z = ceilf(
1222 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1226 void NodeDefManager::eraseIdFromGroups(content_t id)
1228 // For all groups in m_group_to_items...
1229 for (auto iter_groups = m_group_to_items.begin();
1230 iter_groups != m_group_to_items.end();) {
1231 // Get the group items vector.
1232 std::vector<content_t> &items = iter_groups->second;
1234 // Remove any occurence of the id in the group items vector.
1235 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1237 // If group is empty, erase its vector from the map.
1239 iter_groups = m_group_to_items.erase(iter_groups);
1246 // IWritableNodeDefManager
1247 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1251 assert(name != "ignore");
1252 assert(name == def.name);
1254 content_t id = CONTENT_IGNORE;
1255 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1258 if (id == CONTENT_IGNORE) {
1259 warningstream << "NodeDefManager: Absolute "
1260 "limit reached" << std::endl;
1261 return CONTENT_IGNORE;
1263 assert(id != CONTENT_IGNORE);
1264 addNameIdMapping(id, name);
1267 // If there is already ContentFeatures registered for this id, clear old groups
1268 if (id < m_content_features.size())
1269 eraseIdFromGroups(id);
1271 m_content_features[id] = def;
1272 verbosestream << "NodeDefManager: registering content id \"" << id
1273 << "\": name=\"" << def.name << "\""<<std::endl;
1275 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1276 fixSelectionBoxIntUnion();
1278 // Add this content to the list of all groups it belongs to
1279 for (const auto &group : def.groups) {
1280 const std::string &group_name = group.first;
1281 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);
1297 void NodeDefManager::removeNode(const std::string &name)
1302 // Erase name from name ID mapping
1303 content_t id = CONTENT_IGNORE;
1304 if (m_name_id_mapping.getId(name, id)) {
1305 m_name_id_mapping.eraseName(name);
1306 m_name_id_mapping_with_aliases.erase(name);
1309 eraseIdFromGroups(id);
1313 void NodeDefManager::updateAliases(IItemDefManager *idef)
1315 std::set<std::string> all;
1317 m_name_id_mapping_with_aliases.clear();
1318 for (const std::string &name : all) {
1319 const std::string &convert_to = idef->getAlias(name);
1321 if (m_name_id_mapping.getId(convert_to, id)) {
1322 m_name_id_mapping_with_aliases.insert(
1323 std::make_pair(name, id));
1328 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1330 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1331 "overrides to textures" << std::endl;
1333 for (const TextureOverride& texture_override : overrides) {
1335 if (!getId(texture_override.id, id))
1336 continue; // Ignore unknown node
1338 ContentFeatures &nodedef = m_content_features[id];
1341 if (texture_override.hasTarget(OverrideTarget::TOP))
1342 nodedef.tiledef[0].name = texture_override.texture;
1344 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1345 nodedef.tiledef[1].name = texture_override.texture;
1347 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1348 nodedef.tiledef[2].name = texture_override.texture;
1350 if (texture_override.hasTarget(OverrideTarget::LEFT))
1351 nodedef.tiledef[3].name = texture_override.texture;
1353 if (texture_override.hasTarget(OverrideTarget::BACK))
1354 nodedef.tiledef[4].name = texture_override.texture;
1356 if (texture_override.hasTarget(OverrideTarget::FRONT))
1357 nodedef.tiledef[5].name = texture_override.texture;
1360 // Override special tiles, if applicable
1361 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1362 nodedef.tiledef_special[0].name = texture_override.texture;
1364 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1365 nodedef.tiledef_special[1].name = texture_override.texture;
1367 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1368 nodedef.tiledef_special[2].name = texture_override.texture;
1370 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1371 nodedef.tiledef_special[3].name = texture_override.texture;
1373 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1374 nodedef.tiledef_special[4].name = texture_override.texture;
1376 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1377 nodedef.tiledef_special[5].name = texture_override.texture;
1381 void NodeDefManager::updateTextures(IGameDef *gamedef,
1382 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1383 void *progress_callback_args)
1386 infostream << "NodeDefManager::updateTextures(): Updating "
1387 "textures in node definitions" << std::endl;
1389 Client *client = (Client *)gamedef;
1390 ITextureSource *tsrc = client->tsrc();
1391 IShaderSource *shdsrc = client->getShaderSource();
1392 scene::IMeshManipulator *meshmanip =
1393 RenderingEngine::get_scene_manager()->getMeshManipulator();
1394 TextureSettings tsettings;
1395 tsettings.readSettings();
1397 u32 size = m_content_features.size();
1399 for (u32 i = 0; i < size; i++) {
1400 ContentFeatures *f = &(m_content_features[i]);
1401 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1402 progress_callback(progress_callback_args, i, size);
1407 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1409 writeU8(os, 1); // version
1411 std::ostringstream os2(std::ios::binary);
1412 for (u32 i = 0; i < m_content_features.size(); i++) {
1413 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1414 || i == CONTENT_UNKNOWN)
1416 const ContentFeatures *f = &m_content_features[i];
1417 if (f->name.empty())
1420 // Wrap it in a string to allow different lengths without
1421 // strict version incompatibilities
1422 std::ostringstream wrapper_os(std::ios::binary);
1423 f->serialize(wrapper_os, protocol_version);
1424 os2<<serializeString(wrapper_os.str());
1426 // must not overflow
1427 u16 next = count + 1;
1428 FATAL_ERROR_IF(next < count, "Overflow");
1431 writeU16(os, count);
1432 os << serializeLongString(os2.str());
1436 void NodeDefManager::deSerialize(std::istream &is)
1439 int version = readU8(is);
1441 throw SerializationError("unsupported NodeDefinitionManager version");
1442 u16 count = readU16(is);
1443 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1445 for (u16 n = 0; n < count; n++) {
1446 u16 i = readU16(is2);
1448 // Read it from the string wrapper
1449 std::string wrapper = deSerializeString(is2);
1450 std::istringstream wrapper_is(wrapper, std::ios::binary);
1451 f.deSerialize(wrapper_is);
1453 // Check error conditions
1454 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1455 warningstream << "NodeDefManager::deSerialize(): "
1456 "not changing builtin node " << i << std::endl;
1459 if (f.name.empty()) {
1460 warningstream << "NodeDefManager::deSerialize(): "
1461 "received empty name" << std::endl;
1467 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1468 warningstream << "NodeDefManager::deSerialize(): "
1469 "already defined with different ID: " << f.name << std::endl;
1473 // All is ok, add node definition with the requested ID
1474 if (i >= m_content_features.size())
1475 m_content_features.resize((u32)(i) + 1);
1476 m_content_features[i] = f;
1477 addNameIdMapping(i, f.name);
1478 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1480 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1481 fixSelectionBoxIntUnion();
1484 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1485 // are not sent, resolve them client-side too.
1490 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1492 m_name_id_mapping.set(i, name);
1493 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1497 NodeDefManager *createNodeDefManager()
1499 return new NodeDefManager();
1503 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1506 if (m_node_registration_complete)
1507 nr->nodeResolveInternal();
1509 m_pending_resolve_callbacks.push_back(nr);
1513 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1515 size_t len = m_pending_resolve_callbacks.size();
1516 for (size_t i = 0; i != len; i++) {
1517 if (nr != m_pending_resolve_callbacks[i])
1521 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1522 m_pending_resolve_callbacks.resize(len);
1530 void NodeDefManager::runNodeResolveCallbacks()
1532 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1533 NodeResolver *nr = m_pending_resolve_callbacks[i];
1534 nr->nodeResolveInternal();
1537 m_pending_resolve_callbacks.clear();
1541 void NodeDefManager::resetNodeResolveState()
1543 m_node_registration_complete = false;
1544 m_pending_resolve_callbacks.clear();
1547 static void removeDupes(std::vector<content_t> &list)
1549 std::sort(list.begin(), list.end());
1550 auto new_end = std::unique(list.begin(), list.end());
1551 list.erase(new_end, list.end());
1554 void NodeDefManager::resolveCrossrefs()
1556 for (ContentFeatures &f : m_content_features) {
1557 if (f.liquid_type != LIQUID_NONE) {
1558 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1559 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1562 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1565 for (const std::string &name : f.connects_to) {
1566 getIds(name, f.connects_to_ids);
1568 removeDupes(f.connects_to_ids);
1572 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1573 u8 connect_face) const
1575 const ContentFeatures &f1 = get(from);
1577 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1580 // lookup target in connected set
1581 if (!CONTAINS(f1.connects_to_ids, to.param0))
1584 const ContentFeatures &f2 = get(to);
1586 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1587 // ignores actually looking if back connection exists
1588 return CONTAINS(f2.connects_to_ids, from.param0);
1590 // does to node declare usable faces?
1591 if (f2.connect_sides > 0) {
1592 if ((f2.param_type_2 == CPT2_FACEDIR ||
1593 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1594 && (connect_face >= 4)) {
1595 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1596 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1598 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1600 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1601 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1603 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1604 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1605 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1607 return (f2.connect_sides
1608 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1610 return (f2.connect_sides & connect_face);
1612 // the target is just a regular node, so connect no matter back connection
1620 NodeResolver::NodeResolver()
1622 m_nodenames.reserve(16);
1623 m_nnlistsizes.reserve(4);
1627 NodeResolver::~NodeResolver()
1629 if (!m_resolve_done && m_ndef)
1630 m_ndef->cancelNodeResolveCallback(this);
1634 void NodeResolver::cloneTo(NodeResolver *res) const
1636 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1637 " after resolving has completed");
1638 /* We don't actually do anything significant. Since the node resolving has
1639 * already completed, the class that called us will already have the
1640 * resolved IDs in its data structures (which it copies on its own) */
1641 res->m_ndef = m_ndef;
1642 res->m_resolve_done = true;
1646 void NodeResolver::nodeResolveInternal()
1648 m_nodenames_idx = 0;
1649 m_nnlistsizes_idx = 0;
1652 m_resolve_done = true;
1654 m_nodenames.clear();
1655 m_nnlistsizes.clear();
1659 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1660 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1662 if (m_nodenames_idx == m_nodenames.size()) {
1663 *result_out = c_fallback;
1664 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1669 std::string name = m_nodenames[m_nodenames_idx++];
1671 bool success = m_ndef->getId(name, c);
1672 if (!success && !node_alt.empty()) {
1674 success = m_ndef->getId(name, c);
1678 if (error_on_fallback)
1679 errorstream << "NodeResolver: failed to resolve node name '" << name
1680 << "'." << std::endl;
1689 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1690 bool all_required, content_t c_fallback)
1692 bool success = true;
1694 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1695 errorstream << "NodeResolver: no more node lists" << std::endl;
1699 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1702 if (m_nodenames_idx == m_nodenames.size()) {
1703 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1708 std::string &name = m_nodenames[m_nodenames_idx++];
1710 if (name.substr(0,6) != "group:") {
1711 if (m_ndef->getId(name, c)) {
1712 result_out->push_back(c);
1713 } else if (all_required) {
1714 errorstream << "NodeResolver: failed to resolve node name '"
1715 << name << "'." << std::endl;
1716 result_out->push_back(c_fallback);
1720 m_ndef->getIds(name, *result_out);