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.
26 #include "client/renderingengine.h"
27 #include "client/tile.h"
28 #include <IMeshManipulator.h>
32 #include "nameidmapping.h"
33 #include "util/numeric.h"
34 #include "util/serialize.h"
35 #include "exceptions.h"
39 #include <fstream> // Used in applyTextureOverrides()
47 type = NODEBOX_REGULAR;
50 // default is sign/ladder-like
51 wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
52 wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
53 wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
54 // no default for other parts
56 connect_bottom.clear();
57 connect_front.clear();
60 connect_right.clear();
63 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
67 if (protocol_version >= 27)
75 writeU8(os, NODEBOX_FIXED);
79 writeU16(os, fixed.size());
80 for (std::vector<aabb3f>::const_iterator
82 i != fixed.end(); ++i)
84 writeV3F1000(os, i->MinEdge);
85 writeV3F1000(os, i->MaxEdge);
88 case NODEBOX_WALLMOUNTED:
91 writeV3F1000(os, wall_top.MinEdge);
92 writeV3F1000(os, wall_top.MaxEdge);
93 writeV3F1000(os, wall_bottom.MinEdge);
94 writeV3F1000(os, wall_bottom.MaxEdge);
95 writeV3F1000(os, wall_side.MinEdge);
96 writeV3F1000(os, wall_side.MaxEdge);
98 case NODEBOX_CONNECTED:
100 // send old clients nodes that can't be walked through
102 writeU8(os, NODEBOX_FIXED);
105 writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
106 writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
110 #define WRITEBOX(box) do { \
111 writeU16(os, (box).size()); \
112 for (std::vector<aabb3f>::const_iterator \
114 i != (box).end(); ++i) { \
115 writeV3F1000(os, i->MinEdge); \
116 writeV3F1000(os, i->MaxEdge); \
120 WRITEBOX(connect_top);
121 WRITEBOX(connect_bottom);
122 WRITEBOX(connect_front);
123 WRITEBOX(connect_left);
124 WRITEBOX(connect_back);
125 WRITEBOX(connect_right);
134 void NodeBox::deSerialize(std::istream &is)
136 int version = readU8(is);
137 if (version < 1 || version > 3)
138 throw SerializationError("unsupported NodeBox version");
142 type = (enum NodeBoxType)readU8(is);
144 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
146 u16 fixed_count = readU16(is);
150 box.MinEdge = readV3F1000(is);
151 box.MaxEdge = readV3F1000(is);
152 fixed.push_back(box);
155 else if(type == NODEBOX_WALLMOUNTED)
157 wall_top.MinEdge = readV3F1000(is);
158 wall_top.MaxEdge = readV3F1000(is);
159 wall_bottom.MinEdge = readV3F1000(is);
160 wall_bottom.MaxEdge = readV3F1000(is);
161 wall_side.MinEdge = readV3F1000(is);
162 wall_side.MaxEdge = readV3F1000(is);
164 else if (type == NODEBOX_CONNECTED)
166 #define READBOXES(box) do { \
167 count = readU16(is); \
168 (box).reserve(count); \
170 v3f min = readV3F1000(is); \
171 v3f max = readV3F1000(is); \
172 (box).push_back(aabb3f(min, max)); }; } while (0)
177 READBOXES(connect_top);
178 READBOXES(connect_bottom);
179 READBOXES(connect_front);
180 READBOXES(connect_left);
181 READBOXES(connect_back);
182 READBOXES(connect_right);
190 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
192 if (protocol_version >= 30)
194 else if (protocol_version >= 29)
196 else if (protocol_version >= 26)
201 os << serializeString(name);
202 animation.serialize(os, protocol_version);
203 writeU8(os, backface_culling);
204 if (protocol_version >= 26) {
205 writeU8(os, tileable_horizontal);
206 writeU8(os, tileable_vertical);
208 if (protocol_version >= 30) {
209 writeU8(os, has_color);
211 writeU8(os, color.getRed());
212 writeU8(os, color.getGreen());
213 writeU8(os, color.getBlue());
218 void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
220 int version = readU8(is);
221 name = deSerializeString(is);
222 animation.deSerialize(is, version >= 3 ? 29 : 26);
224 backface_culling = readU8(is);
226 tileable_horizontal = readU8(is);
227 tileable_vertical = readU8(is);
230 has_color = readU8(is);
232 color.setRed(readU8(is));
233 color.setGreen(readU8(is));
234 color.setBlue(readU8(is));
238 if ((contenfeatures_version < 8) &&
239 ((drawtype == NDT_MESH) ||
240 (drawtype == NDT_FIRELIKE) ||
241 (drawtype == NDT_LIQUID) ||
242 (drawtype == NDT_PLANTLIKE)))
243 backface_culling = false;
248 SimpleSoundSpec serialization
251 static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
252 std::ostream &os, u8 version)
254 os<<serializeString(ss.name);
255 writeF1000(os, ss.gain);
258 writeF1000(os, ss.pitch);
260 static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is, u8 version)
262 ss.name = deSerializeString(is);
263 ss.gain = readF1000(is);
266 ss.pitch = readF1000(is);
269 void TextureSettings::readSettings()
271 connected_glass = g_settings->getBool("connected_glass");
272 opaque_water = g_settings->getBool("opaque_water");
273 bool enable_shaders = g_settings->getBool("enable_shaders");
274 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
275 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
276 bool smooth_lighting = g_settings->getBool("smooth_lighting");
277 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
278 enable_minimap = g_settings->getBool("enable_minimap");
279 std::string leaves_style_str = g_settings->get("leaves_style");
281 // Mesh cache is not supported in combination with smooth lighting
283 enable_mesh_cache = false;
285 use_normal_texture = enable_shaders &&
286 (enable_bumpmapping || enable_parallax_occlusion);
287 if (leaves_style_str == "fancy") {
288 leaves_style = LEAVES_FANCY;
289 } else if (leaves_style_str == "simple") {
290 leaves_style = LEAVES_SIMPLE;
292 leaves_style = LEAVES_OPAQUE;
300 ContentFeatures::ContentFeatures()
305 ContentFeatures::~ContentFeatures()
309 void ContentFeatures::reset()
316 visual_solidness = 0;
317 backface_culling = true;
320 has_on_construct = false;
321 has_on_destruct = false;
322 has_after_destruct = false;
326 NOTE: Most of this is always overridden by the default values given
331 // Unknown nodes can be dug
332 groups["dig_immediate"] = 2;
333 drawtype = NDT_NORMAL;
336 for(u32 i = 0; i < 24; i++)
338 minimap_color = video::SColor(0, 0, 0, 0);
341 for(u32 i = 0; i < 6; i++)
342 tiledef[i] = TileDef();
343 for(u16 j = 0; j < CF_SPECIAL_COUNT; j++)
344 tiledef_special[j] = TileDef();
346 post_effect_color = video::SColor(0, 0, 0, 0);
347 param_type = CPT_NONE;
348 param_type_2 = CPT2_NONE;
349 is_ground_content = false;
350 light_propagates = false;
351 sunlight_propagates = false;
356 buildable_to = false;
358 rightclickable = true;
360 liquid_type = LIQUID_NONE;
361 liquid_alternative_flowing = "";
362 liquid_alternative_source = "";
363 liquid_viscosity = 0;
364 liquid_renewable = true;
365 liquid_range = LIQUID_LEVEL_MAX+1;
368 damage_per_second = 0;
369 node_box = NodeBox();
370 selection_box = NodeBox();
371 collision_box = NodeBox();
373 legacy_facedir_simple = false;
374 legacy_wallmounted = false;
375 sound_footstep = SimpleSoundSpec();
376 sound_dig = SimpleSoundSpec("__group");
377 sound_dug = SimpleSoundSpec();
379 connects_to_ids.clear();
381 color = video::SColor(0xFFFFFFFF);
386 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
388 if (protocol_version < 31) {
389 serializeOld(os, protocol_version);
394 u8 version = (protocol_version >= 34) ? 11 : 10;
395 writeU8(os, version);
398 os << serializeString(name);
399 writeU16(os, groups.size());
400 for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
402 os << serializeString(i->first);
403 writeS16(os, i->second);
405 writeU8(os, param_type);
406 writeU8(os, param_type_2);
409 writeU8(os, drawtype);
410 os << serializeString(mesh);
411 writeF1000(os, visual_scale);
413 for (u32 i = 0; i < 6; i++)
414 tiledef[i].serialize(os, protocol_version);
415 for (u32 i = 0; i < 6; i++)
416 tiledef_overlay[i].serialize(os, protocol_version);
417 writeU8(os, CF_SPECIAL_COUNT);
418 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
419 tiledef_special[i].serialize(os, protocol_version);
422 writeU8(os, color.getRed());
423 writeU8(os, color.getGreen());
424 writeU8(os, color.getBlue());
425 os << serializeString(palette_name);
427 writeU8(os, connect_sides);
428 writeU16(os, connects_to_ids.size());
429 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
430 i != connects_to_ids.end(); ++i)
432 writeU8(os, post_effect_color.getAlpha());
433 writeU8(os, post_effect_color.getRed());
434 writeU8(os, post_effect_color.getGreen());
435 writeU8(os, post_effect_color.getBlue());
436 writeU8(os, leveled);
439 writeU8(os, light_propagates);
440 writeU8(os, sunlight_propagates);
441 writeU8(os, light_source);
444 writeU8(os, is_ground_content);
447 writeU8(os, walkable);
448 writeU8(os, pointable);
449 writeU8(os, diggable);
450 writeU8(os, climbable);
451 writeU8(os, buildable_to);
452 writeU8(os, rightclickable);
453 writeU32(os, damage_per_second);
456 writeU8(os, liquid_type);
457 os << serializeString(liquid_alternative_flowing);
458 os << serializeString(liquid_alternative_source);
459 writeU8(os, liquid_viscosity);
460 writeU8(os, liquid_renewable);
461 writeU8(os, liquid_range);
462 writeU8(os, drowning);
463 writeU8(os, floodable);
466 node_box.serialize(os, protocol_version);
467 selection_box.serialize(os, protocol_version);
468 collision_box.serialize(os, protocol_version);
471 serializeSimpleSoundSpec(sound_footstep, os, version);
472 serializeSimpleSoundSpec(sound_dig, os, version);
473 serializeSimpleSoundSpec(sound_dug, os, version);
476 writeU8(os, legacy_facedir_simple);
477 writeU8(os, legacy_wallmounted);
480 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
482 // alpha == 0 means that the node is using texture alpha
483 if (alpha == 0 || alpha == 255)
486 for (int i = 0; i < length; i++) {
487 if (tiles[i].name == "")
490 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
491 tiles[i].name = s.str();
495 void ContentFeatures::deSerialize(std::istream &is)
498 int version = readU8(is);
500 deSerializeOld(is, version);
502 } else if (version > 11) {
503 throw SerializationError("unsupported ContentFeatures version");
507 name = deSerializeString(is);
509 u32 groups_size = readU16(is);
510 for (u32 i = 0; i < groups_size; i++) {
511 std::string name = deSerializeString(is);
512 int value = readS16(is);
513 groups[name] = value;
515 param_type = (enum ContentParamType) readU8(is);
516 param_type_2 = (enum ContentParamType2) readU8(is);
519 drawtype = (enum NodeDrawType) readU8(is);
520 mesh = deSerializeString(is);
521 visual_scale = readF1000(is);
523 throw SerializationError("unsupported tile count");
524 for (u32 i = 0; i < 6; i++)
525 tiledef[i].deSerialize(is, version, drawtype);
527 for (u32 i = 0; i < 6; i++)
528 tiledef_overlay[i].deSerialize(is, version, drawtype);
529 if (readU8(is) != CF_SPECIAL_COUNT)
530 throw SerializationError("unsupported CF_SPECIAL_COUNT");
531 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
532 tiledef_special[i].deSerialize(is, version, drawtype);
534 color.setRed(readU8(is));
535 color.setGreen(readU8(is));
536 color.setBlue(readU8(is));
537 palette_name = deSerializeString(is);
539 connect_sides = readU8(is);
540 u16 connects_to_size = readU16(is);
541 connects_to_ids.clear();
542 for (u16 i = 0; i < connects_to_size; i++)
543 connects_to_ids.insert(readU16(is));
544 post_effect_color.setAlpha(readU8(is));
545 post_effect_color.setRed(readU8(is));
546 post_effect_color.setGreen(readU8(is));
547 post_effect_color.setBlue(readU8(is));
548 leveled = readU8(is);
551 light_propagates = readU8(is);
552 sunlight_propagates = readU8(is);
553 light_source = readU8(is);
554 light_source = MYMIN(light_source, LIGHT_MAX);
557 is_ground_content = readU8(is);
560 walkable = readU8(is);
561 pointable = readU8(is);
562 diggable = readU8(is);
563 climbable = readU8(is);
564 buildable_to = readU8(is);
565 rightclickable = readU8(is);
566 damage_per_second = readU32(is);
569 liquid_type = (enum LiquidType) readU8(is);
570 liquid_alternative_flowing = deSerializeString(is);
571 liquid_alternative_source = deSerializeString(is);
572 liquid_viscosity = readU8(is);
573 liquid_renewable = readU8(is);
574 liquid_range = readU8(is);
575 drowning = readU8(is);
576 floodable = readU8(is);
579 node_box.deSerialize(is);
580 selection_box.deSerialize(is);
581 collision_box.deSerialize(is);
584 deSerializeSimpleSoundSpec(sound_footstep, is, version);
585 deSerializeSimpleSoundSpec(sound_dig, is, version);
586 deSerializeSimpleSoundSpec(sound_dug, is, version);
588 // read legacy properties
589 legacy_facedir_simple = readU8(is);
590 legacy_wallmounted = readU8(is);
594 void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
595 TileDef *tiledef, u32 shader_id, bool use_normal_texture,
596 bool backface_culling, u8 material_type)
598 tile->shader_id = shader_id;
599 tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
600 tile->material_type = material_type;
602 // Normal texture and shader flags texture
603 if (use_normal_texture) {
604 tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
606 tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
609 tile->material_flags = 0;
610 if (backface_culling)
611 tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
612 if (tiledef->animation.type != TAT_NONE)
613 tile->material_flags |= MATERIAL_FLAG_ANIMATION;
614 if (tiledef->tileable_horizontal)
615 tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
616 if (tiledef->tileable_vertical)
617 tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
620 tile->has_color = tiledef->has_color;
621 if (tiledef->has_color)
622 tile->color = tiledef->color;
626 // Animation parameters
628 if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
630 tiledef->animation.determineParams(tile->texture->getOriginalSize(),
631 &frame_count, &frame_length_ms, NULL);
632 tile->animation_frame_count = frame_count;
633 tile->animation_frame_length_ms = frame_length_ms;
636 if (frame_count == 1) {
637 tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
639 std::ostringstream os(std::ios::binary);
640 tile->frames.resize(frame_count);
642 for (int i = 0; i < frame_count; i++) {
648 tiledef->animation.getTextureModifer(os,
649 tile->texture->getOriginalSize(), i);
651 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
652 if (tile->normal_texture)
653 frame.normal_texture = tsrc->getNormalTexture(os.str());
654 frame.flags_texture = tile->flags_texture;
655 tile->frames[i] = frame;
662 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
663 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
665 // minimap pixel color - the average color of a texture
666 if (tsettings.enable_minimap && tiledef[0].name != "")
667 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
669 // Figure out the actual tiles to use
671 for (u32 j = 0; j < 6; j++) {
672 tdef[j] = tiledef[j];
673 if (tdef[j].name == "")
674 tdef[j].name = "unknown_node.png";
676 // also the overlay tiles
677 TileDef tdef_overlay[6];
678 for (u32 j = 0; j < 6; j++)
679 tdef_overlay[j] = tiledef_overlay[j];
680 // also the special tiles
681 TileDef tdef_spec[6];
682 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
683 tdef_spec[j] = tiledef_special[j];
685 bool is_liquid = false;
687 u8 material_type = (alpha == 255) ?
688 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
693 material_type = (alpha == 255) ?
694 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
701 assert(liquid_type == LIQUID_SOURCE);
702 if (tsettings.opaque_water)
707 case NDT_FLOWINGLIQUID:
708 assert(liquid_type == LIQUID_FLOWING);
710 if (tsettings.opaque_water)
716 visual_solidness = 1;
718 case NDT_GLASSLIKE_FRAMED:
720 visual_solidness = 1;
722 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
724 visual_solidness = 1;
725 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
729 visual_solidness = 1;
731 case NDT_ALLFACES_OPTIONAL:
732 if (tsettings.leaves_style == LEAVES_FANCY) {
733 drawtype = NDT_ALLFACES;
735 visual_solidness = 1;
736 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
737 for (u32 j = 0; j < 6; j++) {
738 if (tdef_spec[j].name != "")
739 tdef[j].name = tdef_spec[j].name;
741 drawtype = NDT_GLASSLIKE;
743 visual_solidness = 1;
745 drawtype = NDT_NORMAL;
747 for (u32 i = 0; i < 6; i++)
748 tdef[i].name += std::string("^[noalpha");
751 material_type = TILE_MATERIAL_WAVING_LEAVES;
756 material_type = TILE_MATERIAL_WAVING_PLANTS;
765 material_type = TILE_MATERIAL_WAVING_PLANTS;
766 else if (waving == 2)
767 material_type = TILE_MATERIAL_WAVING_LEAVES;
778 // Vertex alpha is no longer supported, correct if necessary.
779 correctAlpha(tdef, 6);
780 correctAlpha(tdef_overlay, 6);
781 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
782 material_type = (alpha == 255) ?
783 TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
787 for (u16 j = 0; j < 6; j++) {
788 tile_shader[j] = shdsrc->getShader("nodes_shader",
789 material_type, drawtype);
791 u8 overlay_material = material_type;
792 if (overlay_material == TILE_MATERIAL_OPAQUE)
793 overlay_material = TILE_MATERIAL_BASIC;
794 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
795 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
796 u32 overlay_shader[6];
797 for (u16 j = 0; j < 6; j++) {
798 overlay_shader[j] = shdsrc->getShader("nodes_shader",
799 overlay_material, drawtype);
802 // Tiles (fill in f->tiles[])
803 for (u16 j = 0; j < 6; j++) {
804 fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
805 tsettings.use_normal_texture,
806 tdef[j].backface_culling, material_type);
807 if (tdef_overlay[j].name != "")
808 fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
809 overlay_shader[j], tsettings.use_normal_texture,
810 tdef[j].backface_culling, overlay_material);
813 // Special tiles (fill in f->special_tiles[])
814 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
815 fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
816 tile_shader[j], tsettings.use_normal_texture,
817 tdef_spec[j].backface_culling, material_type);
820 if (param_type_2 == CPT2_COLOR ||
821 param_type_2 == CPT2_COLORED_FACEDIR ||
822 param_type_2 == CPT2_COLORED_WALLMOUNTED)
823 palette = tsrc->getPalette(palette_name);
825 if ((drawtype == NDT_MESH) && (mesh != "")) {
827 // Read the mesh and apply scale
828 mesh_ptr[0] = client->getMesh(mesh);
830 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
831 scaleMesh(mesh_ptr[0], scale);
832 recalculateBoundingBox(mesh_ptr[0]);
833 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
835 } else if ((drawtype == NDT_NODEBOX) &&
836 ((node_box.type == NODEBOX_REGULAR) ||
837 (node_box.type == NODEBOX_FIXED)) &&
838 (!node_box.fixed.empty())) {
839 //Convert regular nodebox nodes to meshnodes
840 //Change the drawtype and apply scale
842 mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
843 v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
844 scaleMesh(mesh_ptr[0], scale);
845 recalculateBoundingBox(mesh_ptr[0]);
846 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
849 //Cache 6dfacedir and wallmounted rotated clones of meshes
850 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
851 (param_type_2 == CPT2_FACEDIR
852 || param_type_2 == CPT2_COLORED_FACEDIR)) {
853 for (u16 j = 1; j < 24; j++) {
854 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
855 rotateMeshBy6dFacedir(mesh_ptr[j], j);
856 recalculateBoundingBox(mesh_ptr[j]);
857 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
859 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
860 && (param_type_2 == CPT2_WALLMOUNTED ||
861 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
862 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
863 for (u16 j = 1; j < 6; j++) {
864 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
865 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
866 recalculateBoundingBox(mesh_ptr[j]);
867 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
869 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
870 recalculateBoundingBox(mesh_ptr[0]);
871 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
880 class CNodeDefManager: public IWritableNodeDefManager {
883 virtual ~CNodeDefManager();
885 virtual IWritableNodeDefManager *clone();
886 inline virtual const ContentFeatures& get(content_t c) const;
887 inline virtual const ContentFeatures& get(const MapNode &n) const;
888 virtual bool getId(const std::string &name, content_t &result) const;
889 virtual content_t getId(const std::string &name) const;
890 virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
891 virtual const ContentFeatures& get(const std::string &name) const;
892 content_t allocateId();
893 virtual content_t set(const std::string &name, const ContentFeatures &def);
894 virtual content_t allocateDummy(const std::string &name);
895 virtual void removeNode(const std::string &name);
896 virtual void updateAliases(IItemDefManager *idef);
897 virtual void applyTextureOverrides(const std::string &override_filepath);
898 virtual void updateTextures(IGameDef *gamedef,
899 void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
900 void *progress_cbk_args);
901 void serialize(std::ostream &os, u16 protocol_version) const;
902 void deSerialize(std::istream &is);
904 inline virtual void setNodeRegistrationStatus(bool completed);
906 virtual void pendNodeResolve(NodeResolver *nr);
907 virtual bool cancelNodeResolveCallback(NodeResolver *nr);
908 virtual void runNodeResolveCallbacks();
909 virtual void resetNodeResolveState();
910 virtual void mapNodeboxConnections();
911 virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
912 virtual core::aabbox3d<s16> getSelectionBoxIntUnion() const
914 return m_selection_box_int_union;
918 void addNameIdMapping(content_t i, std::string name);
920 * Recalculates m_selection_box_int_union based on
921 * m_selection_box_union.
923 void fixSelectionBoxIntUnion();
925 // Features indexed by id
926 std::vector<ContentFeatures> m_content_features;
928 // A mapping for fast converting back and forth between names and ids
929 NameIdMapping m_name_id_mapping;
931 // Like m_name_id_mapping, but only from names to ids, and includes
932 // item aliases too. Updated by updateAliases()
933 // Note: Not serialized.
935 std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
937 // A mapping from groups to a list of content_ts (and their levels)
938 // that belong to it. Necessary for a direct lookup in getIds().
939 // Note: Not serialized.
940 std::unordered_map<std::string, GroupItems> m_group_to_items;
942 // Next possibly free id
945 // NodeResolvers to callback once node registration has ended
946 std::vector<NodeResolver *> m_pending_resolve_callbacks;
948 // True when all nodes have been registered
949 bool m_node_registration_complete;
951 //! The union of all nodes' selection boxes.
952 aabb3f m_selection_box_union;
954 * The smallest box in node coordinates that
955 * contains all nodes' selection boxes.
957 core::aabbox3d<s16> m_selection_box_int_union;
961 CNodeDefManager::CNodeDefManager()
967 CNodeDefManager::~CNodeDefManager()
970 for (u32 i = 0; i < m_content_features.size(); i++) {
971 ContentFeatures *f = &m_content_features[i];
972 for (u32 j = 0; j < 24; j++) {
974 f->mesh_ptr[j]->drop();
981 void CNodeDefManager::clear()
983 m_content_features.clear();
984 m_name_id_mapping.clear();
985 m_name_id_mapping_with_aliases.clear();
986 m_group_to_items.clear();
988 m_selection_box_union.reset(0,0,0);
989 m_selection_box_int_union.reset(0,0,0);
991 resetNodeResolveState();
993 u32 initial_length = 0;
994 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
995 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
996 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
997 m_content_features.resize(initial_length);
999 // Set CONTENT_UNKNOWN
1003 // Insert directly into containers
1004 content_t c = CONTENT_UNKNOWN;
1005 m_content_features[c] = f;
1006 addNameIdMapping(c, f.name);
1013 f.drawtype = NDT_AIRLIKE;
1014 f.param_type = CPT_LIGHT;
1015 f.light_propagates = true;
1016 f.sunlight_propagates = true;
1018 f.pointable = false;
1020 f.buildable_to = true;
1022 f.is_ground_content = true;
1023 // Insert directly into containers
1024 content_t c = CONTENT_AIR;
1025 m_content_features[c] = f;
1026 addNameIdMapping(c, f.name);
1029 // Set CONTENT_IGNORE
1033 f.drawtype = NDT_AIRLIKE;
1034 f.param_type = CPT_NONE;
1035 f.light_propagates = false;
1036 f.sunlight_propagates = false;
1038 f.pointable = false;
1040 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1041 f.is_ground_content = true;
1042 // Insert directly into containers
1043 content_t c = CONTENT_IGNORE;
1044 m_content_features[c] = f;
1045 addNameIdMapping(c, f.name);
1050 IWritableNodeDefManager *CNodeDefManager::clone()
1052 CNodeDefManager *mgr = new CNodeDefManager();
1058 inline const ContentFeatures& CNodeDefManager::get(content_t c) const
1060 return c < m_content_features.size()
1061 ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
1065 inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
1067 return get(n.getContent());
1071 bool CNodeDefManager::getId(const std::string &name, content_t &result) const
1073 std::unordered_map<std::string, content_t>::const_iterator
1074 i = m_name_id_mapping_with_aliases.find(name);
1075 if(i == m_name_id_mapping_with_aliases.end())
1082 content_t CNodeDefManager::getId(const std::string &name) const
1084 content_t id = CONTENT_IGNORE;
1090 bool CNodeDefManager::getIds(const std::string &name,
1091 std::set<content_t> &result) const
1093 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1094 if (name.substr(0,6) != "group:") {
1095 content_t id = CONTENT_IGNORE;
1096 bool exists = getId(name, id);
1101 std::string group = name.substr(6);
1103 std::unordered_map<std::string, GroupItems>::const_iterator
1104 i = m_group_to_items.find(group);
1105 if (i == m_group_to_items.end())
1108 const GroupItems &items = i->second;
1109 for (GroupItems::const_iterator j = items.begin();
1110 j != items.end(); ++j) {
1111 if ((*j).second != 0)
1112 result.insert((*j).first);
1114 //printf("getIds: %dus\n", t.stop());
1119 const ContentFeatures& CNodeDefManager::get(const std::string &name) const
1121 content_t id = CONTENT_UNKNOWN;
1127 // returns CONTENT_IGNORE if no free ID found
1128 content_t CNodeDefManager::allocateId()
1130 for (content_t id = m_next_id;
1131 id >= m_next_id; // overflow?
1133 while (id >= m_content_features.size()) {
1134 m_content_features.push_back(ContentFeatures());
1136 const ContentFeatures &f = m_content_features[id];
1142 // If we arrive here, an overflow occurred in id.
1143 // That means no ID was found
1144 return CONTENT_IGNORE;
1149 * Returns the smallest box that contains all boxes
1150 * in the vector. Box_union is expanded.
1151 * @param[in] boxes the vector containing the boxes
1152 * @param[in, out] box_union the union of the arguments
1154 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1156 for (std::vector<aabb3f>::const_iterator it = boxes.begin();
1157 it != boxes.end(); ++it) {
1158 box_union->addInternalBox(*it);
1164 * Returns a box that contains the nodebox in every case.
1165 * The argument node_union is expanded.
1166 * @param[in] nodebox the nodebox to be measured
1167 * @param[in] features used to decide whether the nodebox
1169 * @param[in, out] box_union the union of the arguments
1171 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1174 switch(nodebox.type) {
1176 case NODEBOX_LEVELED: {
1178 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1179 boxVectorUnion(nodebox.fixed, &half_processed);
1180 // Set leveled boxes to maximal
1181 if (nodebox.type == NODEBOX_LEVELED) {
1182 half_processed.MaxEdge.Y = +BS / 2;
1184 if (features.param_type_2 == CPT2_FACEDIR ||
1185 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1186 // Get maximal coordinate
1188 fabsf(half_processed.MinEdge.X),
1189 fabsf(half_processed.MinEdge.Y),
1190 fabsf(half_processed.MinEdge.Z),
1191 fabsf(half_processed.MaxEdge.X),
1192 fabsf(half_processed.MaxEdge.Y),
1193 fabsf(half_processed.MaxEdge.Z) };
1195 for (int i = 0; i < 6; i++) {
1196 if (max < coords[i]) {
1200 // Add the union of all possible rotated boxes
1201 box_union->addInternalPoint(-max, -max, -max);
1202 box_union->addInternalPoint(+max, +max, +max);
1204 box_union->addInternalBox(half_processed);
1208 case NODEBOX_WALLMOUNTED: {
1210 box_union->addInternalBox(nodebox.wall_top);
1211 box_union->addInternalBox(nodebox.wall_bottom);
1212 // Find maximal coordinate in the X-Z plane
1214 fabsf(nodebox.wall_side.MinEdge.X),
1215 fabsf(nodebox.wall_side.MinEdge.Z),
1216 fabsf(nodebox.wall_side.MaxEdge.X),
1217 fabsf(nodebox.wall_side.MaxEdge.Z) };
1219 for (int i = 0; i < 4; i++) {
1220 if (max < coords[i]) {
1224 // Add the union of all possible rotated boxes
1225 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1226 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1229 case NODEBOX_CONNECTED: {
1230 // Add all possible connected boxes
1231 boxVectorUnion(nodebox.fixed, box_union);
1232 boxVectorUnion(nodebox.connect_top, box_union);
1233 boxVectorUnion(nodebox.connect_bottom, box_union);
1234 boxVectorUnion(nodebox.connect_front, box_union);
1235 boxVectorUnion(nodebox.connect_left, box_union);
1236 boxVectorUnion(nodebox.connect_back, box_union);
1237 boxVectorUnion(nodebox.connect_right, box_union);
1242 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1243 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1249 inline void CNodeDefManager::fixSelectionBoxIntUnion()
1251 m_selection_box_int_union.MinEdge.X = floorf(
1252 m_selection_box_union.MinEdge.X / BS + 0.5f);
1253 m_selection_box_int_union.MinEdge.Y = floorf(
1254 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1255 m_selection_box_int_union.MinEdge.Z = floorf(
1256 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1257 m_selection_box_int_union.MaxEdge.X = ceilf(
1258 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1259 m_selection_box_int_union.MaxEdge.Y = ceilf(
1260 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1261 m_selection_box_int_union.MaxEdge.Z = ceilf(
1262 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1266 // IWritableNodeDefManager
1267 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
1271 assert(name == def.name);
1273 // Don't allow redefining ignore (but allow air and unknown)
1274 if (name == "ignore") {
1275 warningstream << "NodeDefManager: Ignoring "
1276 "CONTENT_IGNORE redefinition"<<std::endl;
1277 return CONTENT_IGNORE;
1280 content_t id = CONTENT_IGNORE;
1281 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1284 if (id == CONTENT_IGNORE) {
1285 warningstream << "NodeDefManager: Absolute "
1286 "limit reached" << std::endl;
1287 return CONTENT_IGNORE;
1289 assert(id != CONTENT_IGNORE);
1290 addNameIdMapping(id, name);
1292 m_content_features[id] = def;
1293 verbosestream << "NodeDefManager: registering content id \"" << id
1294 << "\": name=\"" << def.name << "\""<<std::endl;
1296 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1297 fixSelectionBoxIntUnion();
1298 // Add this content to the list of all groups it belongs to
1299 // FIXME: This should remove a node from groups it no longer
1300 // belongs to when a node is re-registered
1301 for (ItemGroupList::const_iterator i = def.groups.begin();
1302 i != def.groups.end(); ++i) {
1303 std::string group_name = i->first;
1305 std::unordered_map<std::string, GroupItems>::iterator
1306 j = m_group_to_items.find(group_name);
1307 if (j == m_group_to_items.end()) {
1308 m_group_to_items[group_name].push_back(
1309 std::make_pair(id, i->second));
1311 GroupItems &items = j->second;
1312 items.push_back(std::make_pair(id, i->second));
1319 content_t CNodeDefManager::allocateDummy(const std::string &name)
1321 assert(name != ""); // Pre-condition
1324 return set(name, f);
1328 void CNodeDefManager::removeNode(const std::string &name)
1333 // Erase name from name ID mapping
1334 content_t id = CONTENT_IGNORE;
1335 if (m_name_id_mapping.getId(name, id)) {
1336 m_name_id_mapping.eraseName(name);
1337 m_name_id_mapping_with_aliases.erase(name);
1340 // Erase node content from all groups it belongs to
1341 for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
1342 m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
1343 GroupItems &items = iter_groups->second;
1344 for (GroupItems::iterator iter_groupitems = items.begin();
1345 iter_groupitems != items.end();) {
1346 if (iter_groupitems->first == id)
1347 items.erase(iter_groupitems++);
1352 // Check if group is empty
1353 if (items.size() == 0)
1354 m_group_to_items.erase(iter_groups++);
1361 void CNodeDefManager::updateAliases(IItemDefManager *idef)
1363 std::set<std::string> all;
1365 m_name_id_mapping_with_aliases.clear();
1366 for (std::set<std::string>::const_iterator
1367 i = all.begin(); i != all.end(); ++i) {
1368 const std::string &name = *i;
1369 const std::string &convert_to = idef->getAlias(name);
1371 if (m_name_id_mapping.getId(convert_to, id)) {
1372 m_name_id_mapping_with_aliases.insert(
1373 std::make_pair(name, id));
1378 void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
1380 infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
1381 "overrides to textures from " << override_filepath << std::endl;
1383 std::ifstream infile(override_filepath.c_str());
1386 while (std::getline(infile, line)) {
1388 if (trim(line) == "")
1390 std::vector<std::string> splitted = str_split(line, ' ');
1391 if (splitted.size() != 3) {
1392 errorstream << override_filepath
1393 << ":" << line_c << " Could not apply texture override \""
1394 << line << "\": Syntax error" << std::endl;
1399 if (!getId(splitted[0], id))
1400 continue; // Ignore unknown node
1402 ContentFeatures &nodedef = m_content_features[id];
1404 if (splitted[1] == "top")
1405 nodedef.tiledef[0].name = splitted[2];
1406 else if (splitted[1] == "bottom")
1407 nodedef.tiledef[1].name = splitted[2];
1408 else if (splitted[1] == "right")
1409 nodedef.tiledef[2].name = splitted[2];
1410 else if (splitted[1] == "left")
1411 nodedef.tiledef[3].name = splitted[2];
1412 else if (splitted[1] == "back")
1413 nodedef.tiledef[4].name = splitted[2];
1414 else if (splitted[1] == "front")
1415 nodedef.tiledef[5].name = splitted[2];
1416 else if (splitted[1] == "all" || splitted[1] == "*")
1417 for (int i = 0; i < 6; i++)
1418 nodedef.tiledef[i].name = splitted[2];
1419 else if (splitted[1] == "sides")
1420 for (int i = 2; i < 6; i++)
1421 nodedef.tiledef[i].name = splitted[2];
1423 errorstream << override_filepath
1424 << ":" << line_c << " Could not apply texture override \""
1425 << line << "\": Unknown node side \""
1426 << splitted[1] << "\"" << std::endl;
1432 void CNodeDefManager::updateTextures(IGameDef *gamedef,
1433 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1434 void *progress_callback_args)
1437 infostream << "CNodeDefManager::updateTextures(): Updating "
1438 "textures in node definitions" << std::endl;
1440 Client *client = (Client *)gamedef;
1441 ITextureSource *tsrc = client->tsrc();
1442 IShaderSource *shdsrc = client->getShaderSource();
1443 scene::IMeshManipulator *meshmanip =
1444 RenderingEngine::get_scene_manager()->getMeshManipulator();
1445 TextureSettings tsettings;
1446 tsettings.readSettings();
1448 u32 size = m_content_features.size();
1450 for (u32 i = 0; i < size; i++) {
1451 ContentFeatures *f = &(m_content_features[i]);
1452 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1453 progress_callback(progress_callback_args, i, size);
1458 void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1460 writeU8(os, 1); // version
1462 std::ostringstream os2(std::ios::binary);
1463 for (u32 i = 0; i < m_content_features.size(); i++) {
1464 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1465 || i == CONTENT_UNKNOWN)
1467 const ContentFeatures *f = &m_content_features[i];
1471 // Wrap it in a string to allow different lengths without
1472 // strict version incompatibilities
1473 std::ostringstream wrapper_os(std::ios::binary);
1474 f->serialize(wrapper_os, protocol_version);
1475 os2<<serializeString(wrapper_os.str());
1477 // must not overflow
1478 u16 next = count + 1;
1479 FATAL_ERROR_IF(next < count, "Overflow");
1482 writeU16(os, count);
1483 os << serializeLongString(os2.str());
1487 void CNodeDefManager::deSerialize(std::istream &is)
1490 int version = readU8(is);
1492 throw SerializationError("unsupported NodeDefinitionManager version");
1493 u16 count = readU16(is);
1494 std::istringstream is2(deSerializeLongString(is), std::ios::binary);
1496 for (u16 n = 0; n < count; n++) {
1497 u16 i = readU16(is2);
1499 // Read it from the string wrapper
1500 std::string wrapper = deSerializeString(is2);
1501 std::istringstream wrapper_is(wrapper, std::ios::binary);
1502 f.deSerialize(wrapper_is);
1504 // Check error conditions
1505 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1506 warningstream << "NodeDefManager::deSerialize(): "
1507 "not changing builtin node " << i << std::endl;
1511 warningstream << "NodeDefManager::deSerialize(): "
1512 "received empty name" << std::endl;
1518 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1519 warningstream << "NodeDefManager::deSerialize(): "
1520 "already defined with different ID: " << f.name << std::endl;
1524 // All is ok, add node definition with the requested ID
1525 if (i >= m_content_features.size())
1526 m_content_features.resize((u32)(i) + 1);
1527 m_content_features[i] = f;
1528 addNameIdMapping(i, f.name);
1529 verbosestream << "deserialized " << f.name << std::endl;
1531 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1532 fixSelectionBoxIntUnion();
1537 void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
1539 m_name_id_mapping.set(i, name);
1540 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1544 IWritableNodeDefManager *createNodeDefManager()
1546 return new CNodeDefManager();
1550 //// Serialization of old ContentFeatures formats
1551 void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
1553 u8 compatible_param_type_2 = param_type_2;
1554 if ((protocol_version < 28)
1555 && (compatible_param_type_2 == CPT2_MESHOPTIONS))
1556 compatible_param_type_2 = CPT2_NONE;
1557 else if (protocol_version < 30) {
1558 if (compatible_param_type_2 == CPT2_COLOR)
1559 compatible_param_type_2 = CPT2_NONE;
1560 else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
1561 compatible_param_type_2 = CPT2_FACEDIR;
1562 else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
1563 compatible_param_type_2 = CPT2_WALLMOUNTED;
1566 float compatible_visual_scale = visual_scale;
1567 if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
1568 compatible_visual_scale = sqrt(visual_scale);
1570 TileDef compatible_tiles[6];
1571 for (u8 i = 0; i < 6; i++) {
1572 compatible_tiles[i] = tiledef[i];
1573 if (tiledef_overlay[i].name != "") {
1574 std::stringstream s;
1575 s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
1577 compatible_tiles[i].name = s.str();
1582 if (protocol_version < 31) {
1583 writeU8(os, protocol_version < 27 ? 7 : 8);
1585 os << serializeString(name);
1586 writeU16(os, groups.size());
1587 for (ItemGroupList::const_iterator i = groups.begin();
1588 i != groups.end(); ++i) {
1589 os << serializeString(i->first);
1590 writeS16(os, i->second);
1592 writeU8(os, drawtype);
1593 writeF1000(os, compatible_visual_scale);
1595 for (u32 i = 0; i < 6; i++)
1596 compatible_tiles[i].serialize(os, protocol_version);
1597 writeU8(os, CF_SPECIAL_COUNT);
1598 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1599 tiledef_special[i].serialize(os, protocol_version);
1601 writeU8(os, post_effect_color.getAlpha());
1602 writeU8(os, post_effect_color.getRed());
1603 writeU8(os, post_effect_color.getGreen());
1604 writeU8(os, post_effect_color.getBlue());
1605 writeU8(os, param_type);
1606 writeU8(os, compatible_param_type_2);
1607 writeU8(os, is_ground_content);
1608 writeU8(os, light_propagates);
1609 writeU8(os, sunlight_propagates);
1610 writeU8(os, walkable);
1611 writeU8(os, pointable);
1612 writeU8(os, diggable);
1613 writeU8(os, climbable);
1614 writeU8(os, buildable_to);
1615 os << serializeString(""); // legacy: used to be metadata_name
1616 writeU8(os, liquid_type);
1617 os << serializeString(liquid_alternative_flowing);
1618 os << serializeString(liquid_alternative_source);
1619 writeU8(os, liquid_viscosity);
1620 writeU8(os, liquid_renewable);
1621 writeU8(os, light_source);
1622 writeU32(os, damage_per_second);
1623 node_box.serialize(os, protocol_version);
1624 selection_box.serialize(os, protocol_version);
1625 writeU8(os, legacy_facedir_simple);
1626 writeU8(os, legacy_wallmounted);
1627 serializeSimpleSoundSpec(sound_footstep, os, 10);
1628 serializeSimpleSoundSpec(sound_dig, os, 10);
1629 serializeSimpleSoundSpec(sound_dug, os, 10);
1630 writeU8(os, rightclickable);
1631 writeU8(os, drowning);
1632 writeU8(os, leveled);
1633 writeU8(os, liquid_range);
1634 writeU8(os, waving);
1635 os << serializeString(mesh);
1636 collision_box.serialize(os, protocol_version);
1637 writeU8(os, floodable);
1638 writeU16(os, connects_to_ids.size());
1639 for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
1640 i != connects_to_ids.end(); ++i)
1642 writeU8(os, connect_sides);
1644 throw SerializationError("ContentFeatures::serialize(): "
1645 "Unsupported version requested");
1649 void ContentFeatures::deSerializeOld(std::istream &is, int version)
1651 if (version == 5) // In PROTOCOL_VERSION 13
1653 name = deSerializeString(is);
1655 u32 groups_size = readU16(is);
1656 for(u32 i=0; i<groups_size; i++){
1657 std::string name = deSerializeString(is);
1658 int value = readS16(is);
1659 groups[name] = value;
1661 drawtype = (enum NodeDrawType)readU8(is);
1663 visual_scale = readF1000(is);
1664 if (readU8(is) != 6)
1665 throw SerializationError("unsupported tile count");
1666 for (u32 i = 0; i < 6; i++)
1667 tiledef[i].deSerialize(is, version, drawtype);
1668 if (readU8(is) != CF_SPECIAL_COUNT)
1669 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1670 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1671 tiledef_special[i].deSerialize(is, version, drawtype);
1673 post_effect_color.setAlpha(readU8(is));
1674 post_effect_color.setRed(readU8(is));
1675 post_effect_color.setGreen(readU8(is));
1676 post_effect_color.setBlue(readU8(is));
1677 param_type = (enum ContentParamType)readU8(is);
1678 param_type_2 = (enum ContentParamType2)readU8(is);
1679 is_ground_content = readU8(is);
1680 light_propagates = readU8(is);
1681 sunlight_propagates = readU8(is);
1682 walkable = readU8(is);
1683 pointable = readU8(is);
1684 diggable = readU8(is);
1685 climbable = readU8(is);
1686 buildable_to = readU8(is);
1687 deSerializeString(is); // legacy: used to be metadata_name
1688 liquid_type = (enum LiquidType)readU8(is);
1689 liquid_alternative_flowing = deSerializeString(is);
1690 liquid_alternative_source = deSerializeString(is);
1691 liquid_viscosity = readU8(is);
1692 light_source = readU8(is);
1693 light_source = MYMIN(light_source, LIGHT_MAX);
1694 damage_per_second = readU32(is);
1695 node_box.deSerialize(is);
1696 selection_box.deSerialize(is);
1697 legacy_facedir_simple = readU8(is);
1698 legacy_wallmounted = readU8(is);
1699 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1700 deSerializeSimpleSoundSpec(sound_dig, is, version);
1701 deSerializeSimpleSoundSpec(sound_dug, is, version);
1702 } else if (version == 6) {
1703 name = deSerializeString(is);
1705 u32 groups_size = readU16(is);
1706 for (u32 i = 0; i < groups_size; i++) {
1707 std::string name = deSerializeString(is);
1708 int value = readS16(is);
1709 groups[name] = value;
1711 drawtype = (enum NodeDrawType)readU8(is);
1712 visual_scale = readF1000(is);
1713 if (readU8(is) != 6)
1714 throw SerializationError("unsupported tile count");
1715 for (u32 i = 0; i < 6; i++)
1716 tiledef[i].deSerialize(is, version, drawtype);
1717 // CF_SPECIAL_COUNT in version 6 = 2
1718 if (readU8(is) != 2)
1719 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1720 for (u32 i = 0; i < 2; i++)
1721 tiledef_special[i].deSerialize(is, version, drawtype);
1723 post_effect_color.setAlpha(readU8(is));
1724 post_effect_color.setRed(readU8(is));
1725 post_effect_color.setGreen(readU8(is));
1726 post_effect_color.setBlue(readU8(is));
1727 param_type = (enum ContentParamType)readU8(is);
1728 param_type_2 = (enum ContentParamType2)readU8(is);
1729 is_ground_content = readU8(is);
1730 light_propagates = readU8(is);
1731 sunlight_propagates = readU8(is);
1732 walkable = readU8(is);
1733 pointable = readU8(is);
1734 diggable = readU8(is);
1735 climbable = readU8(is);
1736 buildable_to = readU8(is);
1737 deSerializeString(is); // legacy: used to be metadata_name
1738 liquid_type = (enum LiquidType)readU8(is);
1739 liquid_alternative_flowing = deSerializeString(is);
1740 liquid_alternative_source = deSerializeString(is);
1741 liquid_viscosity = readU8(is);
1742 liquid_renewable = readU8(is);
1743 light_source = readU8(is);
1744 damage_per_second = readU32(is);
1745 node_box.deSerialize(is);
1746 selection_box.deSerialize(is);
1747 legacy_facedir_simple = readU8(is);
1748 legacy_wallmounted = readU8(is);
1749 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1750 deSerializeSimpleSoundSpec(sound_dig, is, version);
1751 deSerializeSimpleSoundSpec(sound_dug, is, version);
1752 rightclickable = readU8(is);
1753 drowning = readU8(is);
1754 leveled = readU8(is);
1755 liquid_range = readU8(is);
1756 } else if (version == 7 || version == 8){
1757 name = deSerializeString(is);
1759 u32 groups_size = readU16(is);
1760 for (u32 i = 0; i < groups_size; i++) {
1761 std::string name = deSerializeString(is);
1762 int value = readS16(is);
1763 groups[name] = value;
1765 drawtype = (enum NodeDrawType) readU8(is);
1767 visual_scale = readF1000(is);
1768 if (readU8(is) != 6)
1769 throw SerializationError("unsupported tile count");
1770 for (u32 i = 0; i < 6; i++)
1771 tiledef[i].deSerialize(is, version, drawtype);
1772 if (readU8(is) != CF_SPECIAL_COUNT)
1773 throw SerializationError("unsupported CF_SPECIAL_COUNT");
1774 for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
1775 tiledef_special[i].deSerialize(is, version, drawtype);
1777 post_effect_color.setAlpha(readU8(is));
1778 post_effect_color.setRed(readU8(is));
1779 post_effect_color.setGreen(readU8(is));
1780 post_effect_color.setBlue(readU8(is));
1781 param_type = (enum ContentParamType) readU8(is);
1782 param_type_2 = (enum ContentParamType2) readU8(is);
1783 is_ground_content = readU8(is);
1784 light_propagates = readU8(is);
1785 sunlight_propagates = readU8(is);
1786 walkable = readU8(is);
1787 pointable = readU8(is);
1788 diggable = readU8(is);
1789 climbable = readU8(is);
1790 buildable_to = readU8(is);
1791 deSerializeString(is); // legacy: used to be metadata_name
1792 liquid_type = (enum LiquidType) readU8(is);
1793 liquid_alternative_flowing = deSerializeString(is);
1794 liquid_alternative_source = deSerializeString(is);
1795 liquid_viscosity = readU8(is);
1796 liquid_renewable = readU8(is);
1797 light_source = readU8(is);
1798 light_source = MYMIN(light_source, LIGHT_MAX);
1799 damage_per_second = readU32(is);
1800 node_box.deSerialize(is);
1801 selection_box.deSerialize(is);
1802 legacy_facedir_simple = readU8(is);
1803 legacy_wallmounted = readU8(is);
1804 deSerializeSimpleSoundSpec(sound_footstep, is, version);
1805 deSerializeSimpleSoundSpec(sound_dig, is, version);
1806 deSerializeSimpleSoundSpec(sound_dug, is, version);
1807 rightclickable = readU8(is);
1808 drowning = readU8(is);
1809 leveled = readU8(is);
1810 liquid_range = readU8(is);
1811 waving = readU8(is);
1813 mesh = deSerializeString(is);
1814 collision_box.deSerialize(is);
1815 floodable = readU8(is);
1816 u16 connects_to_size = readU16(is);
1817 connects_to_ids.clear();
1818 for (u16 i = 0; i < connects_to_size; i++)
1819 connects_to_ids.insert(readU16(is));
1820 connect_sides = readU8(is);
1821 } catch (SerializationError &e) {};
1823 throw SerializationError("unsupported ContentFeatures version");
1827 inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
1829 m_node_registration_complete = completed;
1833 void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
1836 if (m_node_registration_complete)
1837 nr->nodeResolveInternal();
1839 m_pending_resolve_callbacks.push_back(nr);
1843 bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
1845 size_t len = m_pending_resolve_callbacks.size();
1846 for (size_t i = 0; i != len; i++) {
1847 if (nr != m_pending_resolve_callbacks[i])
1851 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1852 m_pending_resolve_callbacks.resize(len);
1860 void CNodeDefManager::runNodeResolveCallbacks()
1862 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1863 NodeResolver *nr = m_pending_resolve_callbacks[i];
1864 nr->nodeResolveInternal();
1867 m_pending_resolve_callbacks.clear();
1871 void CNodeDefManager::resetNodeResolveState()
1873 m_node_registration_complete = false;
1874 m_pending_resolve_callbacks.clear();
1877 void CNodeDefManager::mapNodeboxConnections()
1879 for (u32 i = 0; i < m_content_features.size(); i++) {
1880 ContentFeatures *f = &m_content_features[i];
1881 if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
1883 for (std::vector<std::string>::iterator it = f->connects_to.begin();
1884 it != f->connects_to.end(); ++it) {
1885 getIds(*it, f->connects_to_ids);
1890 bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
1892 const ContentFeatures &f1 = get(from);
1894 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1897 // lookup target in connected set
1898 if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
1901 const ContentFeatures &f2 = get(to);
1903 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1904 // ignores actually looking if back connection exists
1905 return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
1907 // does to node declare usable faces?
1908 if (f2.connect_sides > 0) {
1909 if ((f2.param_type_2 == CPT2_FACEDIR ||
1910 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1911 && (connect_face >= 4)) {
1912 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1913 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1915 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1917 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1918 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1920 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1921 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1922 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1924 return (f2.connect_sides
1925 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1927 return (f2.connect_sides & connect_face);
1929 // the target is just a regular node, so connect no matter back connection
1937 NodeResolver::NodeResolver()
1939 m_nodenames.reserve(16);
1940 m_nnlistsizes.reserve(4);
1944 NodeResolver::~NodeResolver()
1946 if (!m_resolve_done && m_ndef)
1947 m_ndef->cancelNodeResolveCallback(this);
1951 void NodeResolver::nodeResolveInternal()
1953 m_nodenames_idx = 0;
1954 m_nnlistsizes_idx = 0;
1957 m_resolve_done = true;
1959 m_nodenames.clear();
1960 m_nnlistsizes.clear();
1964 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1965 const std::string &node_alt, content_t c_fallback)
1967 if (m_nodenames_idx == m_nodenames.size()) {
1968 *result_out = c_fallback;
1969 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1974 std::string name = m_nodenames[m_nodenames_idx++];
1976 bool success = m_ndef->getId(name, c);
1977 if (!success && node_alt != "") {
1979 success = m_ndef->getId(name, c);
1983 errorstream << "NodeResolver: failed to resolve node name '" << name
1984 << "'." << std::endl;
1993 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1994 bool all_required, content_t c_fallback)
1996 bool success = true;
1998 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1999 errorstream << "NodeResolver: no more node lists" << std::endl;
2003 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
2006 if (m_nodenames_idx == m_nodenames.size()) {
2007 errorstream << "NodeResolver: no more nodes in list" << std::endl;
2012 std::string &name = m_nodenames[m_nodenames_idx++];
2014 if (name.substr(0,6) != "group:") {
2015 if (m_ndef->getId(name, c)) {
2016 result_out->push_back(c);
2017 } else if (all_required) {
2018 errorstream << "NodeResolver: failed to resolve node name '"
2019 << name << "'." << std::endl;
2020 result_out->push_back(c_fallback);
2024 std::set<content_t> cids;
2025 std::set<content_t>::iterator it;
2026 m_ndef->getIds(name, cids);
2027 for (it = cids.begin(); it != cids.end(); ++it)
2028 result_out->push_back(*it);