cleanup
[waspsaliva.git] / src / nodedef.cpp
blob4d4fc7a7abae5c839c1cc3d34e52a29cefbdfd42
1 /*
2 Minetest
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.
20 #include "nodedef.h"
22 #include "itemdef.h"
23 #ifndef SERVER
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>
30 #endif
31 #include "log.h"
32 #include "settings.h"
33 #include "nameidmapping.h"
34 #include "util/numeric.h"
35 #include "util/serialize.h"
36 #include "exceptions.h"
37 #include "debug.h"
38 #include "gamedef.h"
39 #include "mapnode.h"
40 #include <fstream> // Used in applyTextureOverrides()
41 #include <algorithm>
42 #include <cmath>
45 NodeBox
48 void NodeBox::reset()
50 type = NODEBOX_REGULAR;
51 // default is empty
52 fixed.clear();
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
58 connect_top.clear();
59 connect_bottom.clear();
60 connect_front.clear();
61 connect_left.clear();
62 connect_back.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();
70 disconnected.clear();
71 disconnected_sides.clear();
74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
76 // Protocol >= 36
77 const u8 version = 6;
78 writeU8(os, version);
80 switch (type) {
81 case NODEBOX_LEVELED:
82 case NODEBOX_FIXED:
83 writeU8(os, type);
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
90 break;
91 case NODEBOX_WALLMOUNTED:
92 writeU8(os, type);
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);
100 break;
101 case NODEBOX_CONNECTED:
102 writeU8(os, type);
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); \
111 WRITEBOX(fixed);
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);
126 break;
127 default:
128 writeU8(os, type);
129 break;
133 void NodeBox::deSerialize(std::istream &is)
135 int version = readU8(is);
136 if (version < 6)
137 throw SerializationError("unsupported NodeBox version");
139 reset();
141 type = (enum NodeBoxType)readU8(is);
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
145 u16 fixed_count = readU16(is);
146 while(fixed_count--)
148 aabb3f box;
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); \
168 while (count--) { \
169 v3f min = readV3F32(is); \
170 v3f max = readV3F32(is); \
171 (box).emplace_back(min, max); }; }
173 u16 count;
175 READBOXES(fixed);
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);
194 TileDef
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
207 u8 version = 6;
208 writeU8(os, version);
210 os << serializeString16(name);
211 animation.serialize(os, version);
212 bool has_scale = scale > 0;
213 u16 flags = 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;
220 if (has_color)
221 flags |= TILE_FLAG_HAS_COLOR;
222 if (has_scale)
223 flags |= TILE_FLAG_HAS_SCALE;
224 if (align_style != ALIGN_STYLE_NODE)
225 flags |= TILE_FLAG_HAS_ALIGN_STYLE;
226 writeU16(os, flags);
227 if (has_color) {
228 writeU8(os, color.getRed());
229 writeU8(os, color.getGreen());
230 writeU8(os, color.getBlue());
232 if (has_scale)
233 writeU8(os, scale);
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);
242 if (version < 6)
243 throw SerializationError("unsupported TileDef version");
244 name = deSerializeString16(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;
253 if (has_color) {
254 color.setRed(readU8(is));
255 color.setGreen(readU8(is));
256 color.setBlue(readU8(is));
258 scale = has_scale ? readU8(is) : 0;
259 if (has_align_style)
260 align_style = static_cast<AlignStyle>(readU8(is));
261 else
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 smooth_lighting = g_settings->getBool("smooth_lighting");
270 enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
271 enable_minimap = g_settings->getBool("enable_minimap");
272 node_texture_size = g_settings->getU16("texture_min_size");
273 std::string leaves_style_str = g_settings->get("leaves_style");
274 std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
275 std::string autoscale_mode_str = g_settings->get("autoscale_mode");
277 // Mesh cache is not supported in combination with smooth lighting
278 if (smooth_lighting)
279 enable_mesh_cache = false;
281 if (leaves_style_str == "fancy") {
282 leaves_style = LEAVES_FANCY;
283 } else if (leaves_style_str == "simple") {
284 leaves_style = LEAVES_SIMPLE;
285 } else {
286 leaves_style = LEAVES_OPAQUE;
289 if (world_aligned_mode_str == "enable")
290 world_aligned_mode = WORLDALIGN_ENABLE;
291 else if (world_aligned_mode_str == "force_solid")
292 world_aligned_mode = WORLDALIGN_FORCE;
293 else if (world_aligned_mode_str == "force_nodebox")
294 world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
295 else
296 world_aligned_mode = WORLDALIGN_DISABLE;
298 if (autoscale_mode_str == "enable")
299 autoscale_mode = AUTOSCALE_ENABLE;
300 else if (autoscale_mode_str == "force")
301 autoscale_mode = AUTOSCALE_FORCE;
302 else
303 autoscale_mode = AUTOSCALE_DISABLE;
307 ContentFeatures
310 ContentFeatures::ContentFeatures()
312 reset();
315 ContentFeatures::~ContentFeatures()
317 #ifndef SERVER
318 for (u16 j = 0; j < 6; j++) {
319 delete tiles[j].layers[0].frames;
320 delete tiles[j].layers[1].frames;
322 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
323 delete special_tiles[j].layers[0].frames;
324 #endif
327 void ContentFeatures::reset()
330 Cached stuff
332 #ifndef SERVER
333 solidness = 0;
334 visual_solidness = 0;
335 backface_culling = true;
337 #endif
338 has_on_construct = false;
339 has_on_destruct = false;
340 has_after_destruct = false;
342 Actual data
344 NOTE: Most of this is always overridden by the default values given
345 in builtin.lua
347 name = "";
348 groups.clear();
349 // Unknown nodes can be dug
350 groups["dig_immediate"] = 2;
351 drawtype = NDT_NORMAL;
352 mesh = "";
353 #ifndef SERVER
354 for (auto &i : mesh_ptr)
355 i = NULL;
356 minimap_color = video::SColor(0, 0, 0, 0);
357 #endif
358 visual_scale = 1.0;
359 for (auto &i : tiledef)
360 i = TileDef();
361 for (auto &j : tiledef_special)
362 j = TileDef();
363 alpha = 255;
364 post_effect_color = video::SColor(0, 0, 0, 0);
365 param_type = CPT_NONE;
366 param_type_2 = CPT2_NONE;
367 is_ground_content = false;
368 light_propagates = false;
369 sunlight_propagates = false;
370 walkable = true;
371 pointable = true;
372 diggable = true;
373 climbable = false;
374 buildable_to = false;
375 floodable = false;
376 rightclickable = true;
377 leveled = 0;
378 leveled_max = LEVELED_MAX;
379 liquid_type = LIQUID_NONE;
380 liquid_alternative_flowing = "";
381 liquid_alternative_flowing_id = CONTENT_IGNORE;
382 liquid_alternative_source = "";
383 liquid_alternative_source_id = CONTENT_IGNORE;
384 liquid_viscosity = 0;
385 liquid_renewable = true;
386 liquid_range = LIQUID_LEVEL_MAX+1;
387 drowning = 0;
388 light_source = 0;
389 damage_per_second = 0;
390 node_box = NodeBox();
391 selection_box = NodeBox();
392 collision_box = NodeBox();
393 waving = 0;
394 legacy_facedir_simple = false;
395 legacy_wallmounted = false;
396 sound_footstep = SimpleSoundSpec();
397 sound_dig = SimpleSoundSpec("__group");
398 sound_dug = SimpleSoundSpec();
399 connects_to.clear();
400 connects_to_ids.clear();
401 connect_sides = 0;
402 color = video::SColor(0xFFFFFFFF);
403 palette_name = "";
404 palette = NULL;
405 node_dig_prediction = "air";
408 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
410 const u8 version = CONTENTFEATURES_VERSION;
411 writeU8(os, version);
413 // general
414 os << serializeString16(name);
415 writeU16(os, groups.size());
416 for (const auto &group : groups) {
417 os << serializeString16(group.first);
418 writeS16(os, group.second);
420 writeU8(os, param_type);
421 writeU8(os, param_type_2);
423 // visual
424 writeU8(os, drawtype);
425 os << serializeString16(mesh);
426 writeF32(os, visual_scale);
427 writeU8(os, 6);
428 for (const TileDef &td : tiledef)
429 td.serialize(os, protocol_version);
430 for (const TileDef &td : tiledef_overlay)
431 td.serialize(os, protocol_version);
432 writeU8(os, CF_SPECIAL_COUNT);
433 for (const TileDef &td : tiledef_special) {
434 td.serialize(os, protocol_version);
436 writeU8(os, alpha);
437 writeU8(os, color.getRed());
438 writeU8(os, color.getGreen());
439 writeU8(os, color.getBlue());
440 os << serializeString16(palette_name);
441 writeU8(os, waving);
442 writeU8(os, connect_sides);
443 writeU16(os, connects_to_ids.size());
444 for (u16 connects_to_id : connects_to_ids)
445 writeU16(os, connects_to_id);
446 writeARGB8(os, post_effect_color);
447 writeU8(os, leveled);
449 // lighting
450 writeU8(os, light_propagates);
451 writeU8(os, sunlight_propagates);
452 writeU8(os, light_source);
454 // map generation
455 writeU8(os, is_ground_content);
457 // interaction
458 writeU8(os, walkable);
459 writeU8(os, pointable);
460 writeU8(os, diggable);
461 writeU8(os, climbable);
462 writeU8(os, buildable_to);
463 writeU8(os, rightclickable);
464 writeU32(os, damage_per_second);
466 // liquid
467 writeU8(os, liquid_type);
468 os << serializeString16(liquid_alternative_flowing);
469 os << serializeString16(liquid_alternative_source);
470 writeU8(os, liquid_viscosity);
471 writeU8(os, liquid_renewable);
472 writeU8(os, liquid_range);
473 writeU8(os, drowning);
474 writeU8(os, floodable);
476 // node boxes
477 node_box.serialize(os, protocol_version);
478 selection_box.serialize(os, protocol_version);
479 collision_box.serialize(os, protocol_version);
481 // sound
482 sound_footstep.serialize(os, version);
483 sound_dig.serialize(os, version);
484 sound_dug.serialize(os, version);
486 // legacy
487 writeU8(os, legacy_facedir_simple);
488 writeU8(os, legacy_wallmounted);
490 os << serializeString16(node_dig_prediction);
491 writeU8(os, leveled_max);
494 void ContentFeatures::correctAlpha(TileDef *tiles, int length)
496 // alpha == 0 means that the node is using texture alpha
497 if (alpha == 0 || alpha == 255)
498 return;
500 for (int i = 0; i < length; i++) {
501 if (tiles[i].name.empty())
502 continue;
503 std::stringstream s;
504 s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
505 tiles[i].name = s.str();
509 void ContentFeatures::deSerialize(std::istream &is)
511 // version detection
512 const u8 version = readU8(is);
513 if (version < CONTENTFEATURES_VERSION)
514 throw SerializationError("unsupported ContentFeatures version");
516 // general
517 name = deSerializeString16(is);
518 groups.clear();
519 u32 groups_size = readU16(is);
520 for (u32 i = 0; i < groups_size; i++) {
521 std::string name = deSerializeString16(is);
522 int value = readS16(is);
523 groups[name] = value;
525 param_type = (enum ContentParamType) readU8(is);
526 param_type_2 = (enum ContentParamType2) readU8(is);
528 // visual
529 drawtype = (enum NodeDrawType) readU8(is);
530 mesh = deSerializeString16(is);
531 visual_scale = readF32(is);
532 if (readU8(is) != 6)
533 throw SerializationError("unsupported tile count");
534 for (TileDef &td : tiledef)
535 td.deSerialize(is, version, drawtype);
536 for (TileDef &td : tiledef_overlay)
537 td.deSerialize(is, version, drawtype);
538 if (readU8(is) != CF_SPECIAL_COUNT)
539 throw SerializationError("unsupported CF_SPECIAL_COUNT");
540 for (TileDef &td : tiledef_special)
541 td.deSerialize(is, version, drawtype);
542 alpha = readU8(is);
543 color.setRed(readU8(is));
544 color.setGreen(readU8(is));
545 color.setBlue(readU8(is));
546 palette_name = deSerializeString16(is);
547 waving = readU8(is);
548 connect_sides = readU8(is);
549 u16 connects_to_size = readU16(is);
550 connects_to_ids.clear();
551 for (u16 i = 0; i < connects_to_size; i++)
552 connects_to_ids.push_back(readU16(is));
553 post_effect_color = readARGB8(is);
554 leveled = readU8(is);
556 // lighting-related
557 light_propagates = readU8(is);
558 sunlight_propagates = readU8(is);
559 light_source = readU8(is);
560 light_source = MYMIN(light_source, LIGHT_MAX);
562 // map generation
563 is_ground_content = readU8(is);
565 // interaction
566 walkable = readU8(is);
567 pointable = readU8(is);
568 diggable = readU8(is);
569 climbable = readU8(is);
570 buildable_to = readU8(is);
571 rightclickable = readU8(is);
572 damage_per_second = readU32(is);
574 // liquid
575 liquid_type = (enum LiquidType) readU8(is);
576 liquid_alternative_flowing = deSerializeString16(is);
577 liquid_alternative_source = deSerializeString16(is);
578 liquid_viscosity = readU8(is);
579 liquid_renewable = readU8(is);
580 liquid_range = readU8(is);
581 drowning = readU8(is);
582 floodable = readU8(is);
584 // node boxes
585 node_box.deSerialize(is);
586 selection_box.deSerialize(is);
587 collision_box.deSerialize(is);
589 // sounds
590 sound_footstep.deSerialize(is, version);
591 sound_dig.deSerialize(is, version);
592 sound_dug.deSerialize(is, version);
594 // read legacy properties
595 legacy_facedir_simple = readU8(is);
596 legacy_wallmounted = readU8(is);
598 try {
599 node_dig_prediction = deSerializeString16(is);
600 u8 tmp_leveled_max = readU8(is);
601 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
602 throw SerializationError("");
603 leveled_max = tmp_leveled_max;
604 } catch(SerializationError &e) {};
607 #ifndef SERVER
608 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
609 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
610 u8 material_type, u32 shader_id, bool backface_culling,
611 const TextureSettings &tsettings)
613 layer->shader_id = shader_id;
614 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
615 layer->material_type = material_type;
617 bool has_scale = tiledef.scale > 0;
618 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
619 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
620 if (use_autoscale && layer->texture) {
621 auto texture_size = layer->texture->getOriginalSize();
622 float base_size = tsettings.node_texture_size;
623 float size = std::fmin(texture_size.Width, texture_size.Height);
624 layer->scale = std::fmax(base_size, size) / base_size;
625 } else if (has_scale) {
626 layer->scale = tiledef.scale;
627 } else {
628 layer->scale = 1;
630 if (!tile.world_aligned)
631 layer->scale = 1;
633 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
635 // Material flags
636 layer->material_flags = 0;
637 if (backface_culling)
638 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
639 if (tiledef.animation.type != TAT_NONE)
640 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
641 if (tiledef.tileable_horizontal)
642 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
643 if (tiledef.tileable_vertical)
644 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
646 // Color
647 layer->has_color = tiledef.has_color;
648 if (tiledef.has_color)
649 layer->color = tiledef.color;
650 else
651 layer->color = color;
653 // Animation parameters
654 int frame_count = 1;
655 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
656 int frame_length_ms;
657 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
658 &frame_count, &frame_length_ms, NULL);
659 layer->animation_frame_count = frame_count;
660 layer->animation_frame_length_ms = frame_length_ms;
663 if (frame_count == 1) {
664 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
665 } else {
666 std::ostringstream os(std::ios::binary);
667 if (!layer->frames) {
668 layer->frames = new std::vector<FrameSpec>();
670 layer->frames->resize(frame_count);
672 for (int i = 0; i < frame_count; i++) {
674 FrameSpec frame;
676 os.str("");
677 os << tiledef.name;
678 tiledef.animation.getTextureModifer(os,
679 layer->texture->getOriginalSize(), i);
681 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
682 if (layer->normal_texture)
683 frame.normal_texture = tsrc->getNormalTexture(os.str());
684 frame.flags_texture = layer->flags_texture;
685 (*layer->frames)[i] = frame;
690 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
692 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
693 static thread_local bool long_warning_printed = false;
694 std::set<std::string> seen;
695 for (int i = 0; i < length; i++) {
696 if (seen.find(tiles[i].name) != seen.end())
697 continue;
698 seen.insert(tiles[i].name);
700 // Load the texture and see if there's any transparent pixels
701 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
702 video::IImage *image = driver->createImage(texture,
703 core::position2d<s32>(0, 0), texture->getOriginalSize());
704 if (!image)
705 continue;
706 core::dimension2d<u32> dim = image->getDimension();
707 bool ok = true;
708 for (u16 x = 0; x < dim.Width; x++) {
709 for (u16 y = 0; y < dim.Height; y++) {
710 if (image->getPixel(x, y).getAlpha() < 255) {
711 ok = false;
712 goto break_loop;
717 break_loop:
718 image->drop();
719 if (!ok) {
720 warningstream << "Texture \"" << tiles[i].name << "\" of "
721 << name << " has transparent pixels, assuming "
722 "use_texture_alpha = true." << std::endl;
723 if (!long_warning_printed) {
724 warningstream << " This warning can be a false-positive if "
725 "unused pixels in the texture are transparent. However if "
726 "it is meant to be transparent, you *MUST* update the "
727 "nodedef and set use_texture_alpha = true! This compatibility "
728 "code will be removed in a few releases." << std::endl;
729 long_warning_printed = true;
731 return true;
734 return false;
737 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
739 if (style == ALIGN_STYLE_WORLD)
740 return true;
741 if (mode == WORLDALIGN_DISABLE)
742 return false;
743 if (style == ALIGN_STYLE_USER_DEFINED)
744 return true;
745 if (drawtype == NDT_NORMAL)
746 return mode >= WORLDALIGN_FORCE;
747 if (drawtype == NDT_NODEBOX)
748 return mode >= WORLDALIGN_FORCE_NODEBOX;
749 return false;
752 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
753 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
755 // minimap pixel color - the average color of a texture
756 if (tsettings.enable_minimap && !tiledef[0].name.empty())
757 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
759 // Figure out the actual tiles to use
760 TileDef tdef[6];
761 for (u32 j = 0; j < 6; j++) {
762 tdef[j] = tiledef[j];
763 if (tdef[j].name.empty())
764 tdef[j].name = "unknown_node.png";
766 // also the overlay tiles
767 TileDef tdef_overlay[6];
768 for (u32 j = 0; j < 6; j++)
769 tdef_overlay[j] = tiledef_overlay[j];
770 // also the special tiles
771 TileDef tdef_spec[6];
772 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
773 tdef_spec[j] = tiledef_special[j];
775 bool is_liquid = false;
777 u8 material_type = (alpha == 255) ?
778 TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
780 switch (drawtype) {
781 default:
782 case NDT_NORMAL:
783 material_type = (alpha == 255) ?
784 TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
785 solidness = 2;
786 break;
787 case NDT_AIRLIKE:
788 solidness = 0;
789 break;
790 case NDT_LIQUID:
791 assert(liquid_type == LIQUID_SOURCE);
792 if (tsettings.opaque_water)
793 alpha = 255;
794 solidness = 1;
795 is_liquid = true;
796 break;
797 case NDT_FLOWINGLIQUID:
798 assert(liquid_type == LIQUID_FLOWING);
799 solidness = 0;
800 if (tsettings.opaque_water)
801 alpha = 255;
802 is_liquid = true;
803 break;
804 case NDT_GLASSLIKE:
805 solidness = 0;
806 visual_solidness = 1;
807 break;
808 case NDT_GLASSLIKE_FRAMED:
809 solidness = 0;
810 visual_solidness = 1;
811 break;
812 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
813 solidness = 0;
814 visual_solidness = 1;
815 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
816 break;
817 case NDT_ALLFACES:
818 solidness = 0;
819 visual_solidness = 1;
820 break;
821 case NDT_ALLFACES_OPTIONAL:
822 if (tsettings.leaves_style == LEAVES_FANCY) {
823 drawtype = NDT_ALLFACES;
824 solidness = 0;
825 visual_solidness = 1;
826 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
827 for (u32 j = 0; j < 6; j++) {
828 if (!tdef_spec[j].name.empty())
829 tdef[j].name = tdef_spec[j].name;
831 drawtype = NDT_GLASSLIKE;
832 solidness = 0;
833 visual_solidness = 1;
834 } else {
835 drawtype = NDT_NORMAL;
836 solidness = 2;
837 for (TileDef &td : tdef)
838 td.name += std::string("^[noalpha");
840 if (waving >= 1)
841 material_type = TILE_MATERIAL_WAVING_LEAVES;
842 break;
843 case NDT_PLANTLIKE:
844 solidness = 0;
845 if (waving >= 1)
846 material_type = TILE_MATERIAL_WAVING_PLANTS;
847 break;
848 case NDT_FIRELIKE:
849 solidness = 0;
850 break;
851 case NDT_MESH:
852 case NDT_NODEBOX:
853 if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
854 alpha = 0;
856 solidness = 0;
857 if (waving == 1)
858 material_type = TILE_MATERIAL_WAVING_PLANTS;
859 else if (waving == 2)
860 material_type = TILE_MATERIAL_WAVING_LEAVES;
861 else if (waving == 3)
862 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
863 TILE_MATERIAL_WAVING_LIQUID_BASIC;
864 else if (alpha == 255)
865 material_type = TILE_MATERIAL_OPAQUE;
866 break;
867 case NDT_TORCHLIKE:
868 case NDT_SIGNLIKE:
869 case NDT_FENCELIKE:
870 case NDT_RAILLIKE:
871 solidness = 0;
872 break;
873 case NDT_PLANTLIKE_ROOTED:
874 solidness = 2;
875 break;
878 if (is_liquid) {
879 // Vertex alpha is no longer supported, correct if necessary.
880 correctAlpha(tdef, 6);
881 correctAlpha(tdef_overlay, 6);
882 correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
884 if (waving == 3) {
885 material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
886 TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
887 } else {
888 material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
889 TILE_MATERIAL_LIQUID_TRANSPARENT;
893 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
895 u8 overlay_material = material_type;
896 if (overlay_material == TILE_MATERIAL_OPAQUE)
897 overlay_material = TILE_MATERIAL_BASIC;
898 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
899 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
901 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
903 // Tiles (fill in f->tiles[])
904 for (u16 j = 0; j < 6; j++) {
905 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
906 tsettings.world_aligned_mode, drawtype);
907 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
908 color, material_type, tile_shader,
909 tdef[j].backface_culling, tsettings);
910 if (!tdef_overlay[j].name.empty())
911 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
912 color, overlay_material, overlay_shader,
913 tdef[j].backface_culling, tsettings);
916 u8 special_material = material_type;
917 if (drawtype == NDT_PLANTLIKE_ROOTED) {
918 if (waving == 1)
919 special_material = TILE_MATERIAL_WAVING_PLANTS;
920 else if (waving == 2)
921 special_material = TILE_MATERIAL_WAVING_LEAVES;
923 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
925 // Special tiles (fill in f->special_tiles[])
926 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
927 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
928 color, special_material, special_shader,
929 tdef_spec[j].backface_culling, tsettings);
931 if (param_type_2 == CPT2_COLOR ||
932 param_type_2 == CPT2_COLORED_FACEDIR ||
933 param_type_2 == CPT2_COLORED_WALLMOUNTED)
934 palette = tsrc->getPalette(palette_name);
936 if (drawtype == NDT_MESH && !mesh.empty()) {
937 // Meshnode drawtype
938 // Read the mesh and apply scale
939 mesh_ptr[0] = client->getMesh(mesh);
940 if (mesh_ptr[0]){
941 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
942 scaleMesh(mesh_ptr[0], scale);
943 recalculateBoundingBox(mesh_ptr[0]);
944 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
948 //Cache 6dfacedir and wallmounted rotated clones of meshes
949 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
950 (param_type_2 == CPT2_FACEDIR
951 || param_type_2 == CPT2_COLORED_FACEDIR)) {
952 for (u16 j = 1; j < 24; j++) {
953 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
954 rotateMeshBy6dFacedir(mesh_ptr[j], j);
955 recalculateBoundingBox(mesh_ptr[j]);
956 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
958 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
959 && (param_type_2 == CPT2_WALLMOUNTED ||
960 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
961 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
962 for (u16 j = 1; j < 6; j++) {
963 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
964 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
965 recalculateBoundingBox(mesh_ptr[j]);
966 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
968 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
969 recalculateBoundingBox(mesh_ptr[0]);
970 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
973 #endif
976 NodeDefManager
982 NodeDefManager::NodeDefManager()
984 clear();
988 NodeDefManager::~NodeDefManager()
990 #ifndef SERVER
991 for (ContentFeatures &f : m_content_features) {
992 for (auto &j : f.mesh_ptr) {
993 if (j)
994 j->drop();
997 #endif
1001 void NodeDefManager::clear()
1003 m_content_features.clear();
1004 m_name_id_mapping.clear();
1005 m_name_id_mapping_with_aliases.clear();
1006 m_group_to_items.clear();
1007 m_next_id = 0;
1008 m_selection_box_union.reset(0,0,0);
1009 m_selection_box_int_union.reset(0,0,0);
1011 resetNodeResolveState();
1013 u32 initial_length = 0;
1014 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1015 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1016 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1017 m_content_features.resize(initial_length);
1019 // Set CONTENT_UNKNOWN
1021 ContentFeatures f;
1022 f.name = "unknown";
1023 // Insert directly into containers
1024 content_t c = CONTENT_UNKNOWN;
1025 m_content_features[c] = f;
1026 addNameIdMapping(c, f.name);
1029 // Set CONTENT_AIR
1031 ContentFeatures f;
1032 f.name = "air";
1033 f.drawtype = NDT_AIRLIKE;
1034 f.param_type = CPT_LIGHT;
1035 f.light_propagates = true;
1036 f.sunlight_propagates = true;
1037 f.walkable = false;
1038 f.pointable = false;
1039 f.diggable = false;
1040 f.buildable_to = true;
1041 f.floodable = true;
1042 f.is_ground_content = true;
1043 // Insert directly into containers
1044 content_t c = CONTENT_AIR;
1045 m_content_features[c] = f;
1046 addNameIdMapping(c, f.name);
1049 // Set CONTENT_IGNORE
1051 ContentFeatures f;
1052 f.name = "ignore";
1053 f.drawtype = NDT_AIRLIKE;
1054 f.param_type = CPT_NONE;
1055 f.light_propagates = false;
1056 f.sunlight_propagates = false;
1057 f.walkable = false;
1058 f.pointable = false;
1059 f.diggable = false;
1060 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1061 f.is_ground_content = true;
1062 // Insert directly into containers
1063 content_t c = CONTENT_IGNORE;
1064 m_content_features[c] = f;
1065 addNameIdMapping(c, f.name);
1070 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1072 std::unordered_map<std::string, content_t>::const_iterator
1073 i = m_name_id_mapping_with_aliases.find(name);
1074 if(i == m_name_id_mapping_with_aliases.end())
1075 return false;
1076 result = i->second;
1077 return true;
1081 content_t NodeDefManager::getId(const std::string &name) const
1083 content_t id = CONTENT_IGNORE;
1084 getId(name, id);
1085 return id;
1089 bool NodeDefManager::getIds(const std::string &name,
1090 std::vector<content_t> &result) const
1092 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1093 if (name.substr(0,6) != "group:") {
1094 content_t id = CONTENT_IGNORE;
1095 bool exists = getId(name, id);
1096 if (exists)
1097 result.push_back(id);
1098 return exists;
1100 std::string group = name.substr(6);
1102 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1103 i = m_group_to_items.find(group);
1104 if (i == m_group_to_items.end())
1105 return true;
1107 const std::vector<content_t> &items = i->second;
1108 result.insert(result.end(), items.begin(), items.end());
1109 //printf("getIds: %dus\n", t.stop());
1110 return true;
1114 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1116 content_t id = CONTENT_UNKNOWN;
1117 getId(name, id);
1118 return get(id);
1122 // returns CONTENT_IGNORE if no free ID found
1123 content_t NodeDefManager::allocateId()
1125 for (content_t id = m_next_id;
1126 id >= m_next_id; // overflow?
1127 ++id) {
1128 while (id >= m_content_features.size()) {
1129 m_content_features.emplace_back();
1131 const ContentFeatures &f = m_content_features[id];
1132 if (f.name.empty()) {
1133 m_next_id = id + 1;
1134 return id;
1137 // If we arrive here, an overflow occurred in id.
1138 // That means no ID was found
1139 return CONTENT_IGNORE;
1144 * Returns the smallest box that contains all boxes
1145 * in the vector. Box_union is expanded.
1146 * @param[in] boxes the vector containing the boxes
1147 * @param[in, out] box_union the union of the arguments
1149 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1151 for (const aabb3f &box : boxes) {
1152 box_union->addInternalBox(box);
1158 * Returns a box that contains the nodebox in every case.
1159 * The argument node_union is expanded.
1160 * @param[in] nodebox the nodebox to be measured
1161 * @param[in] features used to decide whether the nodebox
1162 * can be rotated
1163 * @param[in, out] box_union the union of the arguments
1165 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1166 aabb3f *box_union)
1168 switch(nodebox.type) {
1169 case NODEBOX_FIXED:
1170 case NODEBOX_LEVELED: {
1171 // Raw union
1172 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1173 boxVectorUnion(nodebox.fixed, &half_processed);
1174 // Set leveled boxes to maximal
1175 if (nodebox.type == NODEBOX_LEVELED) {
1176 half_processed.MaxEdge.Y = +BS / 2;
1178 if (features.param_type_2 == CPT2_FACEDIR ||
1179 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1180 // Get maximal coordinate
1181 f32 coords[] = {
1182 fabsf(half_processed.MinEdge.X),
1183 fabsf(half_processed.MinEdge.Y),
1184 fabsf(half_processed.MinEdge.Z),
1185 fabsf(half_processed.MaxEdge.X),
1186 fabsf(half_processed.MaxEdge.Y),
1187 fabsf(half_processed.MaxEdge.Z) };
1188 f32 max = 0;
1189 for (float coord : coords) {
1190 if (max < coord) {
1191 max = coord;
1194 // Add the union of all possible rotated boxes
1195 box_union->addInternalPoint(-max, -max, -max);
1196 box_union->addInternalPoint(+max, +max, +max);
1197 } else {
1198 box_union->addInternalBox(half_processed);
1200 break;
1202 case NODEBOX_WALLMOUNTED: {
1203 // Add fix boxes
1204 box_union->addInternalBox(nodebox.wall_top);
1205 box_union->addInternalBox(nodebox.wall_bottom);
1206 // Find maximal coordinate in the X-Z plane
1207 f32 coords[] = {
1208 fabsf(nodebox.wall_side.MinEdge.X),
1209 fabsf(nodebox.wall_side.MinEdge.Z),
1210 fabsf(nodebox.wall_side.MaxEdge.X),
1211 fabsf(nodebox.wall_side.MaxEdge.Z) };
1212 f32 max = 0;
1213 for (float coord : coords) {
1214 if (max < coord) {
1215 max = coord;
1218 // Add the union of all possible rotated boxes
1219 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1220 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1221 break;
1223 case NODEBOX_CONNECTED: {
1224 // Add all possible connected boxes
1225 boxVectorUnion(nodebox.fixed, box_union);
1226 boxVectorUnion(nodebox.connect_top, box_union);
1227 boxVectorUnion(nodebox.connect_bottom, box_union);
1228 boxVectorUnion(nodebox.connect_front, box_union);
1229 boxVectorUnion(nodebox.connect_left, box_union);
1230 boxVectorUnion(nodebox.connect_back, box_union);
1231 boxVectorUnion(nodebox.connect_right, box_union);
1232 boxVectorUnion(nodebox.disconnected_top, box_union);
1233 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1234 boxVectorUnion(nodebox.disconnected_front, box_union);
1235 boxVectorUnion(nodebox.disconnected_left, box_union);
1236 boxVectorUnion(nodebox.disconnected_back, box_union);
1237 boxVectorUnion(nodebox.disconnected_right, box_union);
1238 boxVectorUnion(nodebox.disconnected, box_union);
1239 boxVectorUnion(nodebox.disconnected_sides, box_union);
1240 break;
1242 default: {
1243 // NODEBOX_REGULAR
1244 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1245 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1251 inline void NodeDefManager::fixSelectionBoxIntUnion()
1253 m_selection_box_int_union.MinEdge.X = floorf(
1254 m_selection_box_union.MinEdge.X / BS + 0.5f);
1255 m_selection_box_int_union.MinEdge.Y = floorf(
1256 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1257 m_selection_box_int_union.MinEdge.Z = floorf(
1258 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1259 m_selection_box_int_union.MaxEdge.X = ceilf(
1260 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1261 m_selection_box_int_union.MaxEdge.Y = ceilf(
1262 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1263 m_selection_box_int_union.MaxEdge.Z = ceilf(
1264 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1268 void NodeDefManager::eraseIdFromGroups(content_t id)
1270 // For all groups in m_group_to_items...
1271 for (auto iter_groups = m_group_to_items.begin();
1272 iter_groups != m_group_to_items.end();) {
1273 // Get the group items vector.
1274 std::vector<content_t> &items = iter_groups->second;
1276 // Remove any occurence of the id in the group items vector.
1277 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1279 // If group is empty, erase its vector from the map.
1280 if (items.empty())
1281 iter_groups = m_group_to_items.erase(iter_groups);
1282 else
1283 ++iter_groups;
1288 // IWritableNodeDefManager
1289 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &d)
1291 ContentFeatures def = d;
1293 // Pre-conditions
1294 assert(name != "");
1295 assert(name != "ignore");
1296 assert(name == def.name);
1298 content_t id = CONTENT_IGNORE;
1300 if (m_name_id_mapping.getId(name, id)) {
1301 #ifndef SERVER
1302 ContentFeatures old_def = get(name);
1303 for (u32 j = 0; j < 6; j++)
1304 if (def.tiledef[j].name.empty())
1305 def.tiledef[j] = old_def.tiledef[j];
1306 for (u32 j = 0; j < 6; j++)
1307 if (def.tiledef_overlay[j].name.empty())
1308 def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
1309 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
1310 if (def.tiledef_special[j].name.empty())
1311 def.tiledef_special[j] = old_def.tiledef_special[j];
1312 #endif
1313 } else {
1314 // Get new id
1315 id = allocateId();
1316 if (id == CONTENT_IGNORE) {
1317 warningstream << "NodeDefManager: Absolute "
1318 "limit reached" << std::endl;
1319 return CONTENT_IGNORE;
1321 assert(id != CONTENT_IGNORE);
1322 addNameIdMapping(id, name);
1325 // If there is already ContentFeatures registered for this id, clear old groups
1326 if (id < m_content_features.size())
1327 eraseIdFromGroups(id);
1329 m_content_features[id] = def;
1330 verbosestream << "NodeDefManager: registering content id \"" << id
1331 << "\": name=\"" << def.name << "\""<<std::endl;
1333 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1334 fixSelectionBoxIntUnion();
1336 // Add this content to the list of all groups it belongs to
1337 for (const auto &group : def.groups) {
1338 const std::string &group_name = group.first;
1339 m_group_to_items[group_name].push_back(id);
1342 return id;
1346 content_t NodeDefManager::allocateDummy(const std::string &name)
1348 assert(name != ""); // Pre-condition
1349 ContentFeatures f;
1350 f.name = name;
1351 return set(name, f);
1355 void NodeDefManager::removeNode(const std::string &name)
1357 // Pre-condition
1358 assert(name != "");
1360 // Erase name from name ID mapping
1361 content_t id = CONTENT_IGNORE;
1362 if (m_name_id_mapping.getId(name, id)) {
1363 m_name_id_mapping.eraseName(name);
1364 m_name_id_mapping_with_aliases.erase(name);
1367 eraseIdFromGroups(id);
1371 void NodeDefManager::updateAliases(IItemDefManager *idef)
1373 std::set<std::string> all;
1374 idef->getAll(all);
1375 m_name_id_mapping_with_aliases.clear();
1376 for (const std::string &name : all) {
1377 const std::string &convert_to = idef->getAlias(name);
1378 content_t id;
1379 if (m_name_id_mapping.getId(convert_to, id)) {
1380 m_name_id_mapping_with_aliases.insert(
1381 std::make_pair(name, id));
1386 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1388 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1389 "overrides to textures" << std::endl;
1391 for (const TextureOverride& texture_override : overrides) {
1392 content_t id;
1393 if (!getId(texture_override.id, id))
1394 continue; // Ignore unknown node
1396 ContentFeatures &nodedef = m_content_features[id];
1398 // Override tiles
1399 if (texture_override.hasTarget(OverrideTarget::TOP))
1400 nodedef.tiledef[0].name = texture_override.texture;
1402 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1403 nodedef.tiledef[1].name = texture_override.texture;
1405 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1406 nodedef.tiledef[2].name = texture_override.texture;
1408 if (texture_override.hasTarget(OverrideTarget::LEFT))
1409 nodedef.tiledef[3].name = texture_override.texture;
1411 if (texture_override.hasTarget(OverrideTarget::BACK))
1412 nodedef.tiledef[4].name = texture_override.texture;
1414 if (texture_override.hasTarget(OverrideTarget::FRONT))
1415 nodedef.tiledef[5].name = texture_override.texture;
1418 // Override special tiles, if applicable
1419 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1420 nodedef.tiledef_special[0].name = texture_override.texture;
1422 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1423 nodedef.tiledef_special[1].name = texture_override.texture;
1425 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1426 nodedef.tiledef_special[2].name = texture_override.texture;
1428 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1429 nodedef.tiledef_special[3].name = texture_override.texture;
1431 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1432 nodedef.tiledef_special[4].name = texture_override.texture;
1434 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1435 nodedef.tiledef_special[5].name = texture_override.texture;
1439 void NodeDefManager::updateTextures(IGameDef *gamedef,
1440 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1441 void *progress_callback_args)
1443 #ifndef SERVER
1444 infostream << "NodeDefManager::updateTextures(): Updating "
1445 "textures in node definitions" << std::endl;
1447 Client *client = (Client *)gamedef;
1448 ITextureSource *tsrc = client->tsrc();
1449 IShaderSource *shdsrc = client->getShaderSource();
1450 scene::IMeshManipulator *meshmanip =
1451 RenderingEngine::get_scene_manager()->getMeshManipulator();
1452 TextureSettings tsettings;
1453 tsettings.readSettings();
1455 u32 size = m_content_features.size();
1457 for (u32 i = 0; i < size; i++) {
1458 ContentFeatures *f = &(m_content_features[i]);
1459 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1460 progress_callback(progress_callback_args, i, size);
1462 #endif
1465 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1467 writeU8(os, 1); // version
1468 u16 count = 0;
1469 std::ostringstream os2(std::ios::binary);
1470 for (u32 i = 0; i < m_content_features.size(); i++) {
1471 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1472 || i == CONTENT_UNKNOWN)
1473 continue;
1474 const ContentFeatures *f = &m_content_features[i];
1475 if (f->name.empty())
1476 continue;
1477 writeU16(os2, i);
1478 // Wrap it in a string to allow different lengths without
1479 // strict version incompatibilities
1480 std::ostringstream wrapper_os(std::ios::binary);
1481 f->serialize(wrapper_os, protocol_version);
1482 os2<<serializeString16(wrapper_os.str());
1484 // must not overflow
1485 u16 next = count + 1;
1486 FATAL_ERROR_IF(next < count, "Overflow");
1487 count++;
1489 writeU16(os, count);
1490 os << serializeString32(os2.str());
1494 void NodeDefManager::deSerialize(std::istream &is)
1496 clear();
1497 int version = readU8(is);
1498 if (version != 1)
1499 throw SerializationError("unsupported NodeDefinitionManager version");
1500 u16 count = readU16(is);
1501 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1502 ContentFeatures f;
1503 for (u16 n = 0; n < count; n++) {
1504 u16 i = readU16(is2);
1506 // Read it from the string wrapper
1507 std::string wrapper = deSerializeString16(is2);
1508 std::istringstream wrapper_is(wrapper, std::ios::binary);
1509 f.deSerialize(wrapper_is);
1511 // Check error conditions
1512 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1513 warningstream << "NodeDefManager::deSerialize(): "
1514 "not changing builtin node " << i << std::endl;
1515 continue;
1517 if (f.name.empty()) {
1518 warningstream << "NodeDefManager::deSerialize(): "
1519 "received empty name" << std::endl;
1520 continue;
1523 // Ignore aliases
1524 u16 existing_id;
1525 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1526 warningstream << "NodeDefManager::deSerialize(): "
1527 "already defined with different ID: " << f.name << std::endl;
1528 continue;
1531 // All is ok, add node definition with the requested ID
1532 if (i >= m_content_features.size())
1533 m_content_features.resize((u32)(i) + 1);
1534 m_content_features[i] = f;
1535 addNameIdMapping(i, f.name);
1536 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1538 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1539 fixSelectionBoxIntUnion();
1542 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1543 // are not sent, resolve them client-side too.
1544 resolveCrossrefs();
1548 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1550 m_name_id_mapping.set(i, name);
1551 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1555 NodeDefManager *createNodeDefManager()
1557 return new NodeDefManager();
1561 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1563 nr->m_ndef = this;
1564 if (m_node_registration_complete)
1565 nr->nodeResolveInternal();
1566 else
1567 m_pending_resolve_callbacks.push_back(nr);
1571 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1573 size_t len = m_pending_resolve_callbacks.size();
1574 for (size_t i = 0; i != len; i++) {
1575 if (nr != m_pending_resolve_callbacks[i])
1576 continue;
1578 len--;
1579 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1580 m_pending_resolve_callbacks.resize(len);
1581 return true;
1584 return false;
1588 void NodeDefManager::runNodeResolveCallbacks()
1590 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1591 NodeResolver *nr = m_pending_resolve_callbacks[i];
1592 nr->nodeResolveInternal();
1595 m_pending_resolve_callbacks.clear();
1599 void NodeDefManager::resetNodeResolveState()
1601 m_node_registration_complete = false;
1602 m_pending_resolve_callbacks.clear();
1605 static void removeDupes(std::vector<content_t> &list)
1607 std::sort(list.begin(), list.end());
1608 auto new_end = std::unique(list.begin(), list.end());
1609 list.erase(new_end, list.end());
1612 void NodeDefManager::resolveCrossrefs()
1614 for (ContentFeatures &f : m_content_features) {
1615 if (f.liquid_type != LIQUID_NONE) {
1616 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1617 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1618 continue;
1620 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1621 continue;
1623 for (const std::string &name : f.connects_to) {
1624 getIds(name, f.connects_to_ids);
1626 removeDupes(f.connects_to_ids);
1630 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1631 u8 connect_face) const
1633 const ContentFeatures &f1 = get(from);
1635 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1636 return false;
1638 // lookup target in connected set
1639 if (!CONTAINS(f1.connects_to_ids, to.param0))
1640 return false;
1642 const ContentFeatures &f2 = get(to);
1644 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1645 // ignores actually looking if back connection exists
1646 return CONTAINS(f2.connects_to_ids, from.param0);
1648 // does to node declare usable faces?
1649 if (f2.connect_sides > 0) {
1650 if ((f2.param_type_2 == CPT2_FACEDIR ||
1651 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1652 && (connect_face >= 4)) {
1653 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1654 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1655 0, // 4 - back
1656 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1657 0, // 8 - right
1658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1660 0, // 16 - front
1661 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1662 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1663 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1665 return (f2.connect_sides
1666 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1668 return (f2.connect_sides & connect_face);
1670 // the target is just a regular node, so connect no matter back connection
1671 return true;
1674 ////
1675 //// NodeResolver
1676 ////
1678 NodeResolver::NodeResolver()
1680 m_nodenames.reserve(16);
1681 m_nnlistsizes.reserve(4);
1685 NodeResolver::~NodeResolver()
1687 if (!m_resolve_done && m_ndef)
1688 m_ndef->cancelNodeResolveCallback(this);
1692 void NodeResolver::cloneTo(NodeResolver *res) const
1694 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1695 " after resolving has completed");
1696 /* We don't actually do anything significant. Since the node resolving has
1697 * already completed, the class that called us will already have the
1698 * resolved IDs in its data structures (which it copies on its own) */
1699 res->m_ndef = m_ndef;
1700 res->m_resolve_done = true;
1704 void NodeResolver::nodeResolveInternal()
1706 m_nodenames_idx = 0;
1707 m_nnlistsizes_idx = 0;
1709 resolveNodeNames();
1710 m_resolve_done = true;
1712 m_nodenames.clear();
1713 m_nnlistsizes.clear();
1717 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1718 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1720 if (m_nodenames_idx == m_nodenames.size()) {
1721 *result_out = c_fallback;
1722 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1723 return false;
1726 content_t c;
1727 std::string name = m_nodenames[m_nodenames_idx++];
1729 bool success = m_ndef->getId(name, c);
1730 if (!success && !node_alt.empty()) {
1731 name = node_alt;
1732 success = m_ndef->getId(name, c);
1735 if (!success) {
1736 if (error_on_fallback)
1737 errorstream << "NodeResolver: failed to resolve node name '" << name
1738 << "'." << std::endl;
1739 c = c_fallback;
1742 *result_out = c;
1743 return success;
1747 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1748 bool all_required, content_t c_fallback)
1750 bool success = true;
1752 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1753 errorstream << "NodeResolver: no more node lists" << std::endl;
1754 return false;
1757 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1759 while (length--) {
1760 if (m_nodenames_idx == m_nodenames.size()) {
1761 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1762 return false;
1765 content_t c;
1766 std::string &name = m_nodenames[m_nodenames_idx++];
1768 if (name.substr(0,6) != "group:") {
1769 if (m_ndef->getId(name, c)) {
1770 result_out->push_back(c);
1771 } else if (all_required) {
1772 errorstream << "NodeResolver: failed to resolve node name '"
1773 << name << "'." << std::endl;
1774 result_out->push_back(c_fallback);
1775 success = false;
1777 } else {
1778 m_ndef->getIds(name, *result_out);
1782 return success;