1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "nodedef.h"
21
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>
43
44 /*
45 NodeBox
46 */
47
reset()48 void NodeBox::reset()
49 {
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();
72 }
73
serialize(std::ostream & os,u16 protocol_version) const74 void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
75 {
76 // Protocol >= 36
77 const u8 version = 6;
78 writeU8(os, version);
79
80 switch (type) {
81 case NODEBOX_LEVELED:
82 case NODEBOX_FIXED:
83 writeU8(os, type);
84
85 writeU16(os, fixed.size());
86 for (const aabb3f &nodebox : fixed) {
87 writeV3F32(os, nodebox.MinEdge);
88 writeV3F32(os, nodebox.MaxEdge);
89 }
90 break;
91 case NODEBOX_WALLMOUNTED:
92 writeU8(os, type);
93
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);
103
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); \
109 };
110
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;
130 }
131 }
132
deSerialize(std::istream & is)133 void NodeBox::deSerialize(std::istream &is)
134 {
135 int version = readU8(is);
136 if (version < 6)
137 throw SerializationError("unsupported NodeBox version");
138
139 reset();
140
141 type = (enum NodeBoxType)readU8(is);
142
143 if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
144 {
145 u16 fixed_count = readU16(is);
146 while(fixed_count--)
147 {
148 aabb3f box;
149 box.MinEdge = readV3F32(is);
150 box.MaxEdge = readV3F32(is);
151 fixed.push_back(box);
152 }
153 }
154 else if(type == NODEBOX_WALLMOUNTED)
155 {
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);
162 }
163 else if (type == NODEBOX_CONNECTED)
164 {
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); }; }
172
173 u16 count;
174
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);
190 }
191 }
192
193 /*
194 TileDef
195 */
196
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)
203
serialize(std::ostream & os,u16 protocol_version) const204 void TileDef::serialize(std::ostream &os, u16 protocol_version) const
205 {
206 // protocol_version >= 36
207 u8 version = 6;
208 writeU8(os, version);
209
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());
231 }
232 if (has_scale)
233 writeU8(os, scale);
234 if (align_style != ALIGN_STYLE_NODE)
235 writeU8(os, align_style);
236 }
237
deSerialize(std::istream & is,u8 contentfeatures_version,NodeDrawType drawtype)238 void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
239 NodeDrawType drawtype)
240 {
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));
257 }
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;
263 }
264
readSettings()265 void TextureSettings::readSettings()
266 {
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");
276
277 // Mesh cache is not supported in combination with smooth lighting
278 if (smooth_lighting)
279 enable_mesh_cache = false;
280
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;
287 }
288
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;
297
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;
304 }
305
306 /*
307 ContentFeatures
308 */
309
ContentFeatures()310 ContentFeatures::ContentFeatures()
311 {
312 reset();
313 }
314
~ContentFeatures()315 ContentFeatures::~ContentFeatures()
316 {
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;
321 }
322 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
323 delete special_tiles[j].layers[0].frames;
324 #endif
325 }
326
reset()327 void ContentFeatures::reset()
328 {
329 /*
330 Cached stuff
331 */
332 #ifndef SERVER
333 solidness = 2;
334 visual_solidness = 0;
335 backface_culling = true;
336
337 #endif
338 has_on_construct = false;
339 has_on_destruct = false;
340 has_after_destruct = false;
341 /*
342 Actual data
343
344 NOTE: Most of this is always overridden by the default values given
345 in builtin.lua
346 */
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 = ALPHAMODE_OPAQUE;
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";
406 }
407
setAlphaFromLegacy(u8 legacy_alpha)408 void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
409 {
410 // No special handling for nodebox/mesh here as it doesn't make sense to
411 // throw warnings when the server is too old to support the "correct" way
412 switch (drawtype) {
413 case NDT_NORMAL:
414 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
415 break;
416 case NDT_LIQUID:
417 case NDT_FLOWINGLIQUID:
418 alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
419 break;
420 default:
421 alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
422 break;
423 }
424 }
425
getAlphaForLegacy() const426 u8 ContentFeatures::getAlphaForLegacy() const
427 {
428 // This is so simple only because 255 and 0 mean wildly different things
429 // depending on drawtype...
430 return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
431 }
432
serialize(std::ostream & os,u16 protocol_version) const433 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
434 {
435 const u8 version = CONTENTFEATURES_VERSION;
436 writeU8(os, version);
437
438 // general
439 os << serializeString16(name);
440 writeU16(os, groups.size());
441 for (const auto &group : groups) {
442 os << serializeString16(group.first);
443 writeS16(os, group.second);
444 }
445 writeU8(os, param_type);
446 writeU8(os, param_type_2);
447
448 // visual
449 writeU8(os, drawtype);
450 os << serializeString16(mesh);
451 writeF32(os, visual_scale);
452 writeU8(os, 6);
453 for (const TileDef &td : tiledef)
454 td.serialize(os, protocol_version);
455 for (const TileDef &td : tiledef_overlay)
456 td.serialize(os, protocol_version);
457 writeU8(os, CF_SPECIAL_COUNT);
458 for (const TileDef &td : tiledef_special) {
459 td.serialize(os, protocol_version);
460 }
461 writeU8(os, getAlphaForLegacy());
462 writeU8(os, color.getRed());
463 writeU8(os, color.getGreen());
464 writeU8(os, color.getBlue());
465 os << serializeString16(palette_name);
466 writeU8(os, waving);
467 writeU8(os, connect_sides);
468 writeU16(os, connects_to_ids.size());
469 for (u16 connects_to_id : connects_to_ids)
470 writeU16(os, connects_to_id);
471 writeARGB8(os, post_effect_color);
472 writeU8(os, leveled);
473
474 // lighting
475 writeU8(os, light_propagates);
476 writeU8(os, sunlight_propagates);
477 writeU8(os, light_source);
478
479 // map generation
480 writeU8(os, is_ground_content);
481
482 // interaction
483 writeU8(os, walkable);
484 writeU8(os, pointable);
485 writeU8(os, diggable);
486 writeU8(os, climbable);
487 writeU8(os, buildable_to);
488 writeU8(os, rightclickable);
489 writeU32(os, damage_per_second);
490
491 // liquid
492 writeU8(os, liquid_type);
493 os << serializeString16(liquid_alternative_flowing);
494 os << serializeString16(liquid_alternative_source);
495 writeU8(os, liquid_viscosity);
496 writeU8(os, liquid_renewable);
497 writeU8(os, liquid_range);
498 writeU8(os, drowning);
499 writeU8(os, floodable);
500
501 // node boxes
502 node_box.serialize(os, protocol_version);
503 selection_box.serialize(os, protocol_version);
504 collision_box.serialize(os, protocol_version);
505
506 // sound
507 sound_footstep.serialize(os, version);
508 sound_dig.serialize(os, version);
509 sound_dug.serialize(os, version);
510
511 // legacy
512 writeU8(os, legacy_facedir_simple);
513 writeU8(os, legacy_wallmounted);
514
515 os << serializeString16(node_dig_prediction);
516 writeU8(os, leveled_max);
517 writeU8(os, alpha);
518 }
519
deSerialize(std::istream & is)520 void ContentFeatures::deSerialize(std::istream &is)
521 {
522 // version detection
523 const u8 version = readU8(is);
524 if (version < CONTENTFEATURES_VERSION)
525 throw SerializationError("unsupported ContentFeatures version");
526
527 // general
528 name = deSerializeString16(is);
529 groups.clear();
530 u32 groups_size = readU16(is);
531 for (u32 i = 0; i < groups_size; i++) {
532 std::string name = deSerializeString16(is);
533 int value = readS16(is);
534 groups[name] = value;
535 }
536 param_type = (enum ContentParamType) readU8(is);
537 param_type_2 = (enum ContentParamType2) readU8(is);
538
539 // visual
540 drawtype = (enum NodeDrawType) readU8(is);
541 mesh = deSerializeString16(is);
542 visual_scale = readF32(is);
543 if (readU8(is) != 6)
544 throw SerializationError("unsupported tile count");
545 for (TileDef &td : tiledef)
546 td.deSerialize(is, version, drawtype);
547 for (TileDef &td : tiledef_overlay)
548 td.deSerialize(is, version, drawtype);
549 if (readU8(is) != CF_SPECIAL_COUNT)
550 throw SerializationError("unsupported CF_SPECIAL_COUNT");
551 for (TileDef &td : tiledef_special)
552 td.deSerialize(is, version, drawtype);
553 setAlphaFromLegacy(readU8(is));
554 color.setRed(readU8(is));
555 color.setGreen(readU8(is));
556 color.setBlue(readU8(is));
557 palette_name = deSerializeString16(is);
558 waving = readU8(is);
559 connect_sides = readU8(is);
560 u16 connects_to_size = readU16(is);
561 connects_to_ids.clear();
562 for (u16 i = 0; i < connects_to_size; i++)
563 connects_to_ids.push_back(readU16(is));
564 post_effect_color = readARGB8(is);
565 leveled = readU8(is);
566
567 // lighting-related
568 light_propagates = readU8(is);
569 sunlight_propagates = readU8(is);
570 light_source = readU8(is);
571 light_source = MYMIN(light_source, LIGHT_MAX);
572
573 // map generation
574 is_ground_content = readU8(is);
575
576 // interaction
577 walkable = readU8(is);
578 pointable = readU8(is);
579 diggable = readU8(is);
580 climbable = readU8(is);
581 buildable_to = readU8(is);
582 rightclickable = readU8(is);
583 damage_per_second = readU32(is);
584
585 // liquid
586 liquid_type = (enum LiquidType) readU8(is);
587 liquid_alternative_flowing = deSerializeString16(is);
588 liquid_alternative_source = deSerializeString16(is);
589 liquid_viscosity = readU8(is);
590 liquid_renewable = readU8(is);
591 liquid_range = readU8(is);
592 drowning = readU8(is);
593 floodable = readU8(is);
594
595 // node boxes
596 node_box.deSerialize(is);
597 selection_box.deSerialize(is);
598 collision_box.deSerialize(is);
599
600 // sounds
601 sound_footstep.deSerialize(is, version);
602 sound_dig.deSerialize(is, version);
603 sound_dug.deSerialize(is, version);
604
605 // read legacy properties
606 legacy_facedir_simple = readU8(is);
607 legacy_wallmounted = readU8(is);
608
609 try {
610 node_dig_prediction = deSerializeString16(is);
611
612 u8 tmp = readU8(is);
613 if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
614 throw SerializationError("");
615 leveled_max = tmp;
616
617 tmp = readU8(is);
618 if (is.eof())
619 throw SerializationError("");
620 alpha = static_cast<enum AlphaMode>(tmp);
621 } catch(SerializationError &e) {};
622 }
623
624 #ifndef SERVER
fillTileAttribs(ITextureSource * tsrc,TileLayer * layer,const TileSpec & tile,const TileDef & tiledef,video::SColor color,u8 material_type,u32 shader_id,bool backface_culling,const TextureSettings & tsettings)625 static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
626 const TileSpec &tile, const TileDef &tiledef, video::SColor color,
627 u8 material_type, u32 shader_id, bool backface_culling,
628 const TextureSettings &tsettings)
629 {
630 layer->shader_id = shader_id;
631 layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
632 layer->material_type = material_type;
633
634 bool has_scale = tiledef.scale > 0;
635 bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
636 (tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
637 if (use_autoscale) {
638 auto texture_size = layer->texture->getOriginalSize();
639 float base_size = tsettings.node_texture_size;
640 float size = std::fmin(texture_size.Width, texture_size.Height);
641 layer->scale = std::fmax(base_size, size) / base_size;
642 } else if (has_scale) {
643 layer->scale = tiledef.scale;
644 } else {
645 layer->scale = 1;
646 }
647 if (!tile.world_aligned)
648 layer->scale = 1;
649
650 layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
651
652 // Material flags
653 layer->material_flags = 0;
654 if (backface_culling)
655 layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
656 if (tiledef.animation.type != TAT_NONE)
657 layer->material_flags |= MATERIAL_FLAG_ANIMATION;
658 if (tiledef.tileable_horizontal)
659 layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
660 if (tiledef.tileable_vertical)
661 layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
662
663 // Color
664 layer->has_color = tiledef.has_color;
665 if (tiledef.has_color)
666 layer->color = tiledef.color;
667 else
668 layer->color = color;
669
670 // Animation parameters
671 int frame_count = 1;
672 if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
673 int frame_length_ms;
674 tiledef.animation.determineParams(layer->texture->getOriginalSize(),
675 &frame_count, &frame_length_ms, NULL);
676 layer->animation_frame_count = frame_count;
677 layer->animation_frame_length_ms = frame_length_ms;
678 }
679
680 if (frame_count == 1) {
681 layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
682 } else {
683 std::ostringstream os(std::ios::binary);
684 if (!layer->frames) {
685 layer->frames = new std::vector<FrameSpec>();
686 }
687 layer->frames->resize(frame_count);
688
689 for (int i = 0; i < frame_count; i++) {
690
691 FrameSpec frame;
692
693 os.str("");
694 os << tiledef.name;
695 tiledef.animation.getTextureModifer(os,
696 layer->texture->getOriginalSize(), i);
697
698 frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
699 if (layer->normal_texture)
700 frame.normal_texture = tsrc->getNormalTexture(os.str());
701 frame.flags_texture = layer->flags_texture;
702 (*layer->frames)[i] = frame;
703 }
704 }
705 }
706
textureAlphaCheck(ITextureSource * tsrc,const TileDef * tiles,int length)707 bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
708 {
709 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
710 static thread_local bool long_warning_printed = false;
711 std::set<std::string> seen;
712
713 for (int i = 0; i < length; i++) {
714 if (seen.find(tiles[i].name) != seen.end())
715 continue;
716 seen.insert(tiles[i].name);
717
718 // Load the texture and see if there's any transparent pixels
719 video::ITexture *texture = tsrc->getTexture(tiles[i].name);
720 video::IImage *image = driver->createImage(texture,
721 core::position2d<s32>(0, 0), texture->getOriginalSize());
722 if (!image)
723 continue;
724 core::dimension2d<u32> dim = image->getDimension();
725 bool ok = true;
726 for (u16 x = 0; x < dim.Width; x++) {
727 for (u16 y = 0; y < dim.Height; y++) {
728 if (image->getPixel(x, y).getAlpha() < 255) {
729 ok = false;
730 goto break_loop;
731 }
732 }
733 }
734
735 break_loop:
736 image->drop();
737 if (ok)
738 continue;
739 warningstream << "Texture \"" << tiles[i].name << "\" of "
740 << name << " has transparency, assuming "
741 "use_texture_alpha = \"clip\"." << std::endl;
742 if (!long_warning_printed) {
743 warningstream << " This warning can be a false-positive if "
744 "unused pixels in the texture are transparent. However if "
745 "it is meant to be transparent, you *MUST* update the "
746 "nodedef and set use_texture_alpha = \"clip\"! This "
747 "compatibility code will be removed in a few releases."
748 << std::endl;
749 long_warning_printed = true;
750 }
751 return true;
752 }
753 return false;
754 }
755
isWorldAligned(AlignStyle style,WorldAlignMode mode,NodeDrawType drawtype)756 bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
757 {
758 if (style == ALIGN_STYLE_WORLD)
759 return true;
760 if (mode == WORLDALIGN_DISABLE)
761 return false;
762 if (style == ALIGN_STYLE_USER_DEFINED)
763 return true;
764 if (drawtype == NDT_NORMAL)
765 return mode >= WORLDALIGN_FORCE;
766 if (drawtype == NDT_NODEBOX)
767 return mode >= WORLDALIGN_FORCE_NODEBOX;
768 return false;
769 }
770
updateTextures(ITextureSource * tsrc,IShaderSource * shdsrc,scene::IMeshManipulator * meshmanip,Client * client,const TextureSettings & tsettings)771 void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
772 scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
773 {
774 // minimap pixel color - the average color of a texture
775 if (tsettings.enable_minimap && !tiledef[0].name.empty())
776 minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
777
778 // Figure out the actual tiles to use
779 TileDef tdef[6];
780 for (u32 j = 0; j < 6; j++) {
781 tdef[j] = tiledef[j];
782 if (tdef[j].name.empty())
783 tdef[j].name = "unknown_node.png";
784 }
785 // also the overlay tiles
786 TileDef tdef_overlay[6];
787 for (u32 j = 0; j < 6; j++)
788 tdef_overlay[j] = tiledef_overlay[j];
789 // also the special tiles
790 TileDef tdef_spec[6];
791 for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
792 tdef_spec[j] = tiledef_special[j];
793
794 bool is_liquid = false;
795
796 if (alpha == ALPHAMODE_LEGACY_COMPAT) {
797 // Before working with the alpha mode, resolve any legacy kludges
798 alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
799 }
800
801 MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
802 TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
803 TILE_MATERIAL_ALPHA);
804
805 switch (drawtype) {
806 default:
807 case NDT_NORMAL:
808 solidness = 2;
809 break;
810 case NDT_AIRLIKE:
811 solidness = 0;
812 break;
813 case NDT_LIQUID:
814 if (tsettings.opaque_water)
815 alpha = ALPHAMODE_OPAQUE;
816 solidness = 1;
817 is_liquid = true;
818 break;
819 case NDT_FLOWINGLIQUID:
820 solidness = 0;
821 if (tsettings.opaque_water)
822 alpha = ALPHAMODE_OPAQUE;
823 is_liquid = true;
824 break;
825 case NDT_GLASSLIKE:
826 solidness = 0;
827 visual_solidness = 1;
828 break;
829 case NDT_GLASSLIKE_FRAMED:
830 solidness = 0;
831 visual_solidness = 1;
832 break;
833 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
834 solidness = 0;
835 visual_solidness = 1;
836 drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
837 break;
838 case NDT_ALLFACES:
839 solidness = 0;
840 visual_solidness = 1;
841 break;
842 case NDT_ALLFACES_OPTIONAL:
843 if (tsettings.leaves_style == LEAVES_FANCY) {
844 drawtype = NDT_ALLFACES;
845 solidness = 0;
846 visual_solidness = 1;
847 } else if (tsettings.leaves_style == LEAVES_SIMPLE) {
848 for (u32 j = 0; j < 6; j++) {
849 if (!tdef_spec[j].name.empty())
850 tdef[j].name = tdef_spec[j].name;
851 }
852 drawtype = NDT_GLASSLIKE;
853 solidness = 0;
854 visual_solidness = 1;
855 } else {
856 drawtype = NDT_NORMAL;
857 solidness = 2;
858 for (TileDef &td : tdef)
859 td.name += std::string("^[noalpha");
860 }
861 if (waving >= 1)
862 material_type = TILE_MATERIAL_WAVING_LEAVES;
863 break;
864 case NDT_PLANTLIKE:
865 solidness = 0;
866 if (waving >= 1)
867 material_type = TILE_MATERIAL_WAVING_PLANTS;
868 break;
869 case NDT_FIRELIKE:
870 solidness = 0;
871 break;
872 case NDT_MESH:
873 case NDT_NODEBOX:
874 solidness = 0;
875 if (waving == 1) {
876 material_type = TILE_MATERIAL_WAVING_PLANTS;
877 } else if (waving == 2) {
878 material_type = TILE_MATERIAL_WAVING_LEAVES;
879 } else if (waving == 3) {
880 material_type = alpha == ALPHAMODE_OPAQUE ?
881 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
882 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
883 }
884 break;
885 case NDT_TORCHLIKE:
886 case NDT_SIGNLIKE:
887 case NDT_FENCELIKE:
888 case NDT_RAILLIKE:
889 solidness = 0;
890 break;
891 case NDT_PLANTLIKE_ROOTED:
892 solidness = 2;
893 break;
894 }
895
896 if (is_liquid) {
897 if (waving == 3) {
898 material_type = alpha == ALPHAMODE_OPAQUE ?
899 TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
900 TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
901 } else {
902 material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
903 TILE_MATERIAL_LIQUID_TRANSPARENT;
904 }
905 }
906
907 u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
908
909 MaterialType overlay_material = material_type;
910 if (overlay_material == TILE_MATERIAL_OPAQUE)
911 overlay_material = TILE_MATERIAL_BASIC;
912 else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
913 overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
914
915 u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
916
917 // Tiles (fill in f->tiles[])
918 for (u16 j = 0; j < 6; j++) {
919 tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
920 tsettings.world_aligned_mode, drawtype);
921 fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
922 color, material_type, tile_shader,
923 tdef[j].backface_culling, tsettings);
924 if (!tdef_overlay[j].name.empty())
925 fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
926 color, overlay_material, overlay_shader,
927 tdef[j].backface_culling, tsettings);
928 }
929
930 MaterialType special_material = material_type;
931 if (drawtype == NDT_PLANTLIKE_ROOTED) {
932 if (waving == 1)
933 special_material = TILE_MATERIAL_WAVING_PLANTS;
934 else if (waving == 2)
935 special_material = TILE_MATERIAL_WAVING_LEAVES;
936 }
937 u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
938
939 // Special tiles (fill in f->special_tiles[])
940 for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
941 fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
942 color, special_material, special_shader,
943 tdef_spec[j].backface_culling, tsettings);
944
945 if (param_type_2 == CPT2_COLOR ||
946 param_type_2 == CPT2_COLORED_FACEDIR ||
947 param_type_2 == CPT2_COLORED_WALLMOUNTED)
948 palette = tsrc->getPalette(palette_name);
949
950 if (drawtype == NDT_MESH && !mesh.empty()) {
951 // Meshnode drawtype
952 // Read the mesh and apply scale
953 mesh_ptr[0] = client->getMesh(mesh);
954 if (mesh_ptr[0]){
955 v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale;
956 scaleMesh(mesh_ptr[0], scale);
957 recalculateBoundingBox(mesh_ptr[0]);
958 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
959 }
960 }
961
962 //Cache 6dfacedir and wallmounted rotated clones of meshes
963 if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
964 (param_type_2 == CPT2_FACEDIR
965 || param_type_2 == CPT2_COLORED_FACEDIR)) {
966 for (u16 j = 1; j < 24; j++) {
967 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
968 rotateMeshBy6dFacedir(mesh_ptr[j], j);
969 recalculateBoundingBox(mesh_ptr[j]);
970 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
971 }
972 } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
973 && (param_type_2 == CPT2_WALLMOUNTED ||
974 param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
975 static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
976 for (u16 j = 1; j < 6; j++) {
977 mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
978 rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
979 recalculateBoundingBox(mesh_ptr[j]);
980 meshmanip->recalculateNormals(mesh_ptr[j], true, false);
981 }
982 rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]);
983 recalculateBoundingBox(mesh_ptr[0]);
984 meshmanip->recalculateNormals(mesh_ptr[0], true, false);
985 }
986 }
987 #endif
988
989 /*
990 NodeDefManager
991 */
992
993
994
995
NodeDefManager()996 NodeDefManager::NodeDefManager()
997 {
998 clear();
999 }
1000
1001
~NodeDefManager()1002 NodeDefManager::~NodeDefManager()
1003 {
1004 #ifndef SERVER
1005 for (ContentFeatures &f : m_content_features) {
1006 for (auto &j : f.mesh_ptr) {
1007 if (j)
1008 j->drop();
1009 }
1010 }
1011 #endif
1012 }
1013
1014
clear()1015 void NodeDefManager::clear()
1016 {
1017 m_content_features.clear();
1018 m_name_id_mapping.clear();
1019 m_name_id_mapping_with_aliases.clear();
1020 m_group_to_items.clear();
1021 m_next_id = 0;
1022 m_selection_box_union.reset(0,0,0);
1023 m_selection_box_int_union.reset(0,0,0);
1024
1025 resetNodeResolveState();
1026
1027 u32 initial_length = 0;
1028 initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
1029 initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
1030 initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
1031 m_content_features.resize(initial_length);
1032
1033 // Set CONTENT_UNKNOWN
1034 {
1035 ContentFeatures f;
1036 f.name = "unknown";
1037 // Insert directly into containers
1038 content_t c = CONTENT_UNKNOWN;
1039 m_content_features[c] = f;
1040 addNameIdMapping(c, f.name);
1041 }
1042
1043 // Set CONTENT_AIR
1044 {
1045 ContentFeatures f;
1046 f.name = "air";
1047 f.drawtype = NDT_AIRLIKE;
1048 f.param_type = CPT_LIGHT;
1049 f.light_propagates = true;
1050 f.sunlight_propagates = true;
1051 f.walkable = false;
1052 f.pointable = false;
1053 f.diggable = false;
1054 f.buildable_to = true;
1055 f.floodable = true;
1056 f.is_ground_content = true;
1057 // Insert directly into containers
1058 content_t c = CONTENT_AIR;
1059 m_content_features[c] = f;
1060 addNameIdMapping(c, f.name);
1061 }
1062
1063 // Set CONTENT_IGNORE
1064 {
1065 ContentFeatures f;
1066 f.name = "ignore";
1067 f.drawtype = NDT_AIRLIKE;
1068 f.param_type = CPT_NONE;
1069 f.light_propagates = false;
1070 f.sunlight_propagates = false;
1071 f.walkable = false;
1072 f.pointable = false;
1073 f.diggable = false;
1074 f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
1075 f.is_ground_content = true;
1076 // Insert directly into containers
1077 content_t c = CONTENT_IGNORE;
1078 m_content_features[c] = f;
1079 addNameIdMapping(c, f.name);
1080 }
1081 }
1082
1083
getId(const std::string & name,content_t & result) const1084 bool NodeDefManager::getId(const std::string &name, content_t &result) const
1085 {
1086 std::unordered_map<std::string, content_t>::const_iterator
1087 i = m_name_id_mapping_with_aliases.find(name);
1088 if(i == m_name_id_mapping_with_aliases.end())
1089 return false;
1090 result = i->second;
1091 return true;
1092 }
1093
1094
getId(const std::string & name) const1095 content_t NodeDefManager::getId(const std::string &name) const
1096 {
1097 content_t id = CONTENT_IGNORE;
1098 getId(name, id);
1099 return id;
1100 }
1101
1102
getIds(const std::string & name,std::vector<content_t> & result) const1103 bool NodeDefManager::getIds(const std::string &name,
1104 std::vector<content_t> &result) const
1105 {
1106 //TimeTaker t("getIds", NULL, PRECISION_MICRO);
1107 if (name.substr(0,6) != "group:") {
1108 content_t id = CONTENT_IGNORE;
1109 bool exists = getId(name, id);
1110 if (exists)
1111 result.push_back(id);
1112 return exists;
1113 }
1114 std::string group = name.substr(6);
1115
1116 std::unordered_map<std::string, std::vector<content_t>>::const_iterator
1117 i = m_group_to_items.find(group);
1118 if (i == m_group_to_items.end())
1119 return true;
1120
1121 const std::vector<content_t> &items = i->second;
1122 result.insert(result.end(), items.begin(), items.end());
1123 //printf("getIds: %dus\n", t.stop());
1124 return true;
1125 }
1126
1127
get(const std::string & name) const1128 const ContentFeatures& NodeDefManager::get(const std::string &name) const
1129 {
1130 content_t id = CONTENT_UNKNOWN;
1131 getId(name, id);
1132 return get(id);
1133 }
1134
1135
1136 // returns CONTENT_IGNORE if no free ID found
allocateId()1137 content_t NodeDefManager::allocateId()
1138 {
1139 for (content_t id = m_next_id;
1140 id >= m_next_id; // overflow?
1141 ++id) {
1142 while (id >= m_content_features.size()) {
1143 m_content_features.emplace_back();
1144 }
1145 const ContentFeatures &f = m_content_features[id];
1146 if (f.name.empty()) {
1147 m_next_id = id + 1;
1148 return id;
1149 }
1150 }
1151 // If we arrive here, an overflow occurred in id.
1152 // That means no ID was found
1153 return CONTENT_IGNORE;
1154 }
1155
1156
1157 /*!
1158 * Returns the smallest box that contains all boxes
1159 * in the vector. Box_union is expanded.
1160 * @param[in] boxes the vector containing the boxes
1161 * @param[in, out] box_union the union of the arguments
1162 */
boxVectorUnion(const std::vector<aabb3f> & boxes,aabb3f * box_union)1163 void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
1164 {
1165 for (const aabb3f &box : boxes) {
1166 box_union->addInternalBox(box);
1167 }
1168 }
1169
1170
1171 /*!
1172 * Returns a box that contains the nodebox in every case.
1173 * The argument node_union is expanded.
1174 * @param[in] nodebox the nodebox to be measured
1175 * @param[in] features used to decide whether the nodebox
1176 * can be rotated
1177 * @param[in, out] box_union the union of the arguments
1178 */
getNodeBoxUnion(const NodeBox & nodebox,const ContentFeatures & features,aabb3f * box_union)1179 void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
1180 aabb3f *box_union)
1181 {
1182 switch(nodebox.type) {
1183 case NODEBOX_FIXED:
1184 case NODEBOX_LEVELED: {
1185 // Raw union
1186 aabb3f half_processed(0, 0, 0, 0, 0, 0);
1187 boxVectorUnion(nodebox.fixed, &half_processed);
1188 // Set leveled boxes to maximal
1189 if (nodebox.type == NODEBOX_LEVELED) {
1190 half_processed.MaxEdge.Y = +BS / 2;
1191 }
1192 if (features.param_type_2 == CPT2_FACEDIR ||
1193 features.param_type_2 == CPT2_COLORED_FACEDIR) {
1194 // Get maximal coordinate
1195 f32 coords[] = {
1196 fabsf(half_processed.MinEdge.X),
1197 fabsf(half_processed.MinEdge.Y),
1198 fabsf(half_processed.MinEdge.Z),
1199 fabsf(half_processed.MaxEdge.X),
1200 fabsf(half_processed.MaxEdge.Y),
1201 fabsf(half_processed.MaxEdge.Z) };
1202 f32 max = 0;
1203 for (float coord : coords) {
1204 if (max < coord) {
1205 max = coord;
1206 }
1207 }
1208 // Add the union of all possible rotated boxes
1209 box_union->addInternalPoint(-max, -max, -max);
1210 box_union->addInternalPoint(+max, +max, +max);
1211 } else {
1212 box_union->addInternalBox(half_processed);
1213 }
1214 break;
1215 }
1216 case NODEBOX_WALLMOUNTED: {
1217 // Add fix boxes
1218 box_union->addInternalBox(nodebox.wall_top);
1219 box_union->addInternalBox(nodebox.wall_bottom);
1220 // Find maximal coordinate in the X-Z plane
1221 f32 coords[] = {
1222 fabsf(nodebox.wall_side.MinEdge.X),
1223 fabsf(nodebox.wall_side.MinEdge.Z),
1224 fabsf(nodebox.wall_side.MaxEdge.X),
1225 fabsf(nodebox.wall_side.MaxEdge.Z) };
1226 f32 max = 0;
1227 for (float coord : coords) {
1228 if (max < coord) {
1229 max = coord;
1230 }
1231 }
1232 // Add the union of all possible rotated boxes
1233 box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max);
1234 box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max);
1235 break;
1236 }
1237 case NODEBOX_CONNECTED: {
1238 // Add all possible connected boxes
1239 boxVectorUnion(nodebox.fixed, box_union);
1240 boxVectorUnion(nodebox.connect_top, box_union);
1241 boxVectorUnion(nodebox.connect_bottom, box_union);
1242 boxVectorUnion(nodebox.connect_front, box_union);
1243 boxVectorUnion(nodebox.connect_left, box_union);
1244 boxVectorUnion(nodebox.connect_back, box_union);
1245 boxVectorUnion(nodebox.connect_right, box_union);
1246 boxVectorUnion(nodebox.disconnected_top, box_union);
1247 boxVectorUnion(nodebox.disconnected_bottom, box_union);
1248 boxVectorUnion(nodebox.disconnected_front, box_union);
1249 boxVectorUnion(nodebox.disconnected_left, box_union);
1250 boxVectorUnion(nodebox.disconnected_back, box_union);
1251 boxVectorUnion(nodebox.disconnected_right, box_union);
1252 boxVectorUnion(nodebox.disconnected, box_union);
1253 boxVectorUnion(nodebox.disconnected_sides, box_union);
1254 break;
1255 }
1256 default: {
1257 // NODEBOX_REGULAR
1258 box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2);
1259 box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2);
1260 }
1261 }
1262 }
1263
1264
fixSelectionBoxIntUnion()1265 inline void NodeDefManager::fixSelectionBoxIntUnion()
1266 {
1267 m_selection_box_int_union.MinEdge.X = floorf(
1268 m_selection_box_union.MinEdge.X / BS + 0.5f);
1269 m_selection_box_int_union.MinEdge.Y = floorf(
1270 m_selection_box_union.MinEdge.Y / BS + 0.5f);
1271 m_selection_box_int_union.MinEdge.Z = floorf(
1272 m_selection_box_union.MinEdge.Z / BS + 0.5f);
1273 m_selection_box_int_union.MaxEdge.X = ceilf(
1274 m_selection_box_union.MaxEdge.X / BS - 0.5f);
1275 m_selection_box_int_union.MaxEdge.Y = ceilf(
1276 m_selection_box_union.MaxEdge.Y / BS - 0.5f);
1277 m_selection_box_int_union.MaxEdge.Z = ceilf(
1278 m_selection_box_union.MaxEdge.Z / BS - 0.5f);
1279 }
1280
1281
eraseIdFromGroups(content_t id)1282 void NodeDefManager::eraseIdFromGroups(content_t id)
1283 {
1284 // For all groups in m_group_to_items...
1285 for (auto iter_groups = m_group_to_items.begin();
1286 iter_groups != m_group_to_items.end();) {
1287 // Get the group items vector.
1288 std::vector<content_t> &items = iter_groups->second;
1289
1290 // Remove any occurence of the id in the group items vector.
1291 items.erase(std::remove(items.begin(), items.end(), id), items.end());
1292
1293 // If group is empty, erase its vector from the map.
1294 if (items.empty())
1295 iter_groups = m_group_to_items.erase(iter_groups);
1296 else
1297 ++iter_groups;
1298 }
1299 }
1300
1301
1302 // IWritableNodeDefManager
set(const std::string & name,const ContentFeatures & def)1303 content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
1304 {
1305 // Pre-conditions
1306 assert(name != "");
1307 assert(name != "ignore");
1308 assert(name == def.name);
1309
1310 content_t id = CONTENT_IGNORE;
1311 if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
1312 // Get new id
1313 id = allocateId();
1314 if (id == CONTENT_IGNORE) {
1315 warningstream << "NodeDefManager: Absolute "
1316 "limit reached" << std::endl;
1317 return CONTENT_IGNORE;
1318 }
1319 assert(id != CONTENT_IGNORE);
1320 addNameIdMapping(id, name);
1321 }
1322
1323 // If there is already ContentFeatures registered for this id, clear old groups
1324 if (id < m_content_features.size())
1325 eraseIdFromGroups(id);
1326
1327 m_content_features[id] = def;
1328 verbosestream << "NodeDefManager: registering content id \"" << id
1329 << "\": name=\"" << def.name << "\""<<std::endl;
1330
1331 getNodeBoxUnion(def.selection_box, def, &m_selection_box_union);
1332 fixSelectionBoxIntUnion();
1333
1334 // Add this content to the list of all groups it belongs to
1335 for (const auto &group : def.groups) {
1336 const std::string &group_name = group.first;
1337 m_group_to_items[group_name].push_back(id);
1338 }
1339
1340 return id;
1341 }
1342
1343
allocateDummy(const std::string & name)1344 content_t NodeDefManager::allocateDummy(const std::string &name)
1345 {
1346 assert(name != ""); // Pre-condition
1347 ContentFeatures f;
1348 f.name = name;
1349 return set(name, f);
1350 }
1351
1352
removeNode(const std::string & name)1353 void NodeDefManager::removeNode(const std::string &name)
1354 {
1355 // Pre-condition
1356 assert(name != "");
1357
1358 // Erase name from name ID mapping
1359 content_t id = CONTENT_IGNORE;
1360 if (m_name_id_mapping.getId(name, id)) {
1361 m_name_id_mapping.eraseName(name);
1362 m_name_id_mapping_with_aliases.erase(name);
1363 }
1364
1365 eraseIdFromGroups(id);
1366 }
1367
1368
updateAliases(IItemDefManager * idef)1369 void NodeDefManager::updateAliases(IItemDefManager *idef)
1370 {
1371 std::set<std::string> all;
1372 idef->getAll(all);
1373 m_name_id_mapping_with_aliases.clear();
1374 for (const std::string &name : all) {
1375 const std::string &convert_to = idef->getAlias(name);
1376 content_t id;
1377 if (m_name_id_mapping.getId(convert_to, id)) {
1378 m_name_id_mapping_with_aliases.insert(
1379 std::make_pair(name, id));
1380 }
1381 }
1382 }
1383
applyTextureOverrides(const std::vector<TextureOverride> & overrides)1384 void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &overrides)
1385 {
1386 infostream << "NodeDefManager::applyTextureOverrides(): Applying "
1387 "overrides to textures" << std::endl;
1388
1389 for (const TextureOverride& texture_override : overrides) {
1390 content_t id;
1391 if (!getId(texture_override.id, id))
1392 continue; // Ignore unknown node
1393
1394 ContentFeatures &nodedef = m_content_features[id];
1395
1396 // Override tiles
1397 if (texture_override.hasTarget(OverrideTarget::TOP))
1398 nodedef.tiledef[0].name = texture_override.texture;
1399
1400 if (texture_override.hasTarget(OverrideTarget::BOTTOM))
1401 nodedef.tiledef[1].name = texture_override.texture;
1402
1403 if (texture_override.hasTarget(OverrideTarget::RIGHT))
1404 nodedef.tiledef[2].name = texture_override.texture;
1405
1406 if (texture_override.hasTarget(OverrideTarget::LEFT))
1407 nodedef.tiledef[3].name = texture_override.texture;
1408
1409 if (texture_override.hasTarget(OverrideTarget::BACK))
1410 nodedef.tiledef[4].name = texture_override.texture;
1411
1412 if (texture_override.hasTarget(OverrideTarget::FRONT))
1413 nodedef.tiledef[5].name = texture_override.texture;
1414
1415
1416 // Override special tiles, if applicable
1417 if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
1418 nodedef.tiledef_special[0].name = texture_override.texture;
1419
1420 if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
1421 nodedef.tiledef_special[1].name = texture_override.texture;
1422
1423 if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
1424 nodedef.tiledef_special[2].name = texture_override.texture;
1425
1426 if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
1427 nodedef.tiledef_special[3].name = texture_override.texture;
1428
1429 if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
1430 nodedef.tiledef_special[4].name = texture_override.texture;
1431
1432 if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
1433 nodedef.tiledef_special[5].name = texture_override.texture;
1434 }
1435 }
1436
updateTextures(IGameDef * gamedef,void (* progress_callback)(void * progress_args,u32 progress,u32 max_progress),void * progress_callback_args)1437 void NodeDefManager::updateTextures(IGameDef *gamedef,
1438 void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
1439 void *progress_callback_args)
1440 {
1441 #ifndef SERVER
1442 infostream << "NodeDefManager::updateTextures(): Updating "
1443 "textures in node definitions" << std::endl;
1444
1445 Client *client = (Client *)gamedef;
1446 ITextureSource *tsrc = client->tsrc();
1447 IShaderSource *shdsrc = client->getShaderSource();
1448 scene::IMeshManipulator *meshmanip =
1449 RenderingEngine::get_scene_manager()->getMeshManipulator();
1450 TextureSettings tsettings;
1451 tsettings.readSettings();
1452
1453 u32 size = m_content_features.size();
1454
1455 for (u32 i = 0; i < size; i++) {
1456 ContentFeatures *f = &(m_content_features[i]);
1457 f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
1458 progress_callback(progress_callback_args, i, size);
1459 }
1460 #endif
1461 }
1462
serialize(std::ostream & os,u16 protocol_version) const1463 void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
1464 {
1465 writeU8(os, 1); // version
1466 u16 count = 0;
1467 std::ostringstream os2(std::ios::binary);
1468 for (u32 i = 0; i < m_content_features.size(); i++) {
1469 if (i == CONTENT_IGNORE || i == CONTENT_AIR
1470 || i == CONTENT_UNKNOWN)
1471 continue;
1472 const ContentFeatures *f = &m_content_features[i];
1473 if (f->name.empty())
1474 continue;
1475 writeU16(os2, i);
1476 // Wrap it in a string to allow different lengths without
1477 // strict version incompatibilities
1478 std::ostringstream wrapper_os(std::ios::binary);
1479 f->serialize(wrapper_os, protocol_version);
1480 os2<<serializeString16(wrapper_os.str());
1481
1482 // must not overflow
1483 u16 next = count + 1;
1484 FATAL_ERROR_IF(next < count, "Overflow");
1485 count++;
1486 }
1487 writeU16(os, count);
1488 os << serializeString32(os2.str());
1489 }
1490
1491
deSerialize(std::istream & is)1492 void NodeDefManager::deSerialize(std::istream &is)
1493 {
1494 clear();
1495 int version = readU8(is);
1496 if (version != 1)
1497 throw SerializationError("unsupported NodeDefinitionManager version");
1498 u16 count = readU16(is);
1499 std::istringstream is2(deSerializeString32(is), std::ios::binary);
1500 ContentFeatures f;
1501 for (u16 n = 0; n < count; n++) {
1502 u16 i = readU16(is2);
1503
1504 // Read it from the string wrapper
1505 std::string wrapper = deSerializeString16(is2);
1506 std::istringstream wrapper_is(wrapper, std::ios::binary);
1507 f.deSerialize(wrapper_is);
1508
1509 // Check error conditions
1510 if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
1511 warningstream << "NodeDefManager::deSerialize(): "
1512 "not changing builtin node " << i << std::endl;
1513 continue;
1514 }
1515 if (f.name.empty()) {
1516 warningstream << "NodeDefManager::deSerialize(): "
1517 "received empty name" << std::endl;
1518 continue;
1519 }
1520
1521 // Ignore aliases
1522 u16 existing_id;
1523 if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
1524 warningstream << "NodeDefManager::deSerialize(): "
1525 "already defined with different ID: " << f.name << std::endl;
1526 continue;
1527 }
1528
1529 // All is ok, add node definition with the requested ID
1530 if (i >= m_content_features.size())
1531 m_content_features.resize((u32)(i) + 1);
1532 m_content_features[i] = f;
1533 addNameIdMapping(i, f.name);
1534 TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl);
1535
1536 getNodeBoxUnion(f.selection_box, f, &m_selection_box_union);
1537 fixSelectionBoxIntUnion();
1538 }
1539
1540 // Since liquid_alternative_flowing_id and liquid_alternative_source_id
1541 // are not sent, resolve them client-side too.
1542 resolveCrossrefs();
1543 }
1544
1545
addNameIdMapping(content_t i,std::string name)1546 void NodeDefManager::addNameIdMapping(content_t i, std::string name)
1547 {
1548 m_name_id_mapping.set(i, name);
1549 m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
1550 }
1551
1552
createNodeDefManager()1553 NodeDefManager *createNodeDefManager()
1554 {
1555 return new NodeDefManager();
1556 }
1557
1558
pendNodeResolve(NodeResolver * nr) const1559 void NodeDefManager::pendNodeResolve(NodeResolver *nr) const
1560 {
1561 nr->m_ndef = this;
1562 if (m_node_registration_complete)
1563 nr->nodeResolveInternal();
1564 else
1565 m_pending_resolve_callbacks.push_back(nr);
1566 }
1567
1568
cancelNodeResolveCallback(NodeResolver * nr) const1569 bool NodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) const
1570 {
1571 size_t len = m_pending_resolve_callbacks.size();
1572 for (size_t i = 0; i != len; i++) {
1573 if (nr != m_pending_resolve_callbacks[i])
1574 continue;
1575
1576 len--;
1577 m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
1578 m_pending_resolve_callbacks.resize(len);
1579 return true;
1580 }
1581
1582 return false;
1583 }
1584
1585
runNodeResolveCallbacks()1586 void NodeDefManager::runNodeResolveCallbacks()
1587 {
1588 for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
1589 NodeResolver *nr = m_pending_resolve_callbacks[i];
1590 nr->nodeResolveInternal();
1591 }
1592
1593 m_pending_resolve_callbacks.clear();
1594 }
1595
1596
resetNodeResolveState()1597 void NodeDefManager::resetNodeResolveState()
1598 {
1599 m_node_registration_complete = false;
1600 m_pending_resolve_callbacks.clear();
1601 }
1602
removeDupes(std::vector<content_t> & list)1603 static void removeDupes(std::vector<content_t> &list)
1604 {
1605 std::sort(list.begin(), list.end());
1606 auto new_end = std::unique(list.begin(), list.end());
1607 list.erase(new_end, list.end());
1608 }
1609
resolveCrossrefs()1610 void NodeDefManager::resolveCrossrefs()
1611 {
1612 for (ContentFeatures &f : m_content_features) {
1613 if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
1614 f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
1615 f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
1616 continue;
1617 }
1618 if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
1619 continue;
1620
1621 for (const std::string &name : f.connects_to) {
1622 getIds(name, f.connects_to_ids);
1623 }
1624 removeDupes(f.connects_to_ids);
1625 }
1626 }
1627
nodeboxConnects(MapNode from,MapNode to,u8 connect_face) const1628 bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to,
1629 u8 connect_face) const
1630 {
1631 const ContentFeatures &f1 = get(from);
1632
1633 if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
1634 return false;
1635
1636 // lookup target in connected set
1637 if (!CONTAINS(f1.connects_to_ids, to.param0))
1638 return false;
1639
1640 const ContentFeatures &f2 = get(to);
1641
1642 if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
1643 // ignores actually looking if back connection exists
1644 return CONTAINS(f2.connects_to_ids, from.param0);
1645
1646 // does to node declare usable faces?
1647 if (f2.connect_sides > 0) {
1648 if ((f2.param_type_2 == CPT2_FACEDIR ||
1649 f2.param_type_2 == CPT2_COLORED_FACEDIR)
1650 && (connect_face >= 4)) {
1651 static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1652 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1653 0, // 4 - back
1654 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1655 0, // 8 - right
1656 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
1657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1658 0, // 16 - front
1659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1661 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
1662 };
1663 return (f2.connect_sides
1664 & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
1665 }
1666 return (f2.connect_sides & connect_face);
1667 }
1668 // the target is just a regular node, so connect no matter back connection
1669 return true;
1670 }
1671
1672 ////
1673 //// NodeResolver
1674 ////
1675
NodeResolver()1676 NodeResolver::NodeResolver()
1677 {
1678 m_nodenames.reserve(16);
1679 m_nnlistsizes.reserve(4);
1680 }
1681
1682
~NodeResolver()1683 NodeResolver::~NodeResolver()
1684 {
1685 if (!m_resolve_done && m_ndef)
1686 m_ndef->cancelNodeResolveCallback(this);
1687 }
1688
1689
cloneTo(NodeResolver * res) const1690 void NodeResolver::cloneTo(NodeResolver *res) const
1691 {
1692 FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
1693 " after resolving has completed");
1694 /* We don't actually do anything significant. Since the node resolving has
1695 * already completed, the class that called us will already have the
1696 * resolved IDs in its data structures (which it copies on its own) */
1697 res->m_ndef = m_ndef;
1698 res->m_resolve_done = true;
1699 }
1700
1701
nodeResolveInternal()1702 void NodeResolver::nodeResolveInternal()
1703 {
1704 m_nodenames_idx = 0;
1705 m_nnlistsizes_idx = 0;
1706
1707 resolveNodeNames();
1708 m_resolve_done = true;
1709
1710 m_nodenames.clear();
1711 m_nnlistsizes.clear();
1712 }
1713
1714
getIdFromNrBacklog(content_t * result_out,const std::string & node_alt,content_t c_fallback,bool error_on_fallback)1715 bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
1716 const std::string &node_alt, content_t c_fallback, bool error_on_fallback)
1717 {
1718 if (m_nodenames_idx == m_nodenames.size()) {
1719 *result_out = c_fallback;
1720 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1721 return false;
1722 }
1723
1724 content_t c;
1725 std::string name = m_nodenames[m_nodenames_idx++];
1726
1727 bool success = m_ndef->getId(name, c);
1728 if (!success && !node_alt.empty()) {
1729 name = node_alt;
1730 success = m_ndef->getId(name, c);
1731 }
1732
1733 if (!success) {
1734 if (error_on_fallback)
1735 errorstream << "NodeResolver: failed to resolve node name '" << name
1736 << "'." << std::endl;
1737 c = c_fallback;
1738 }
1739
1740 *result_out = c;
1741 return success;
1742 }
1743
1744
getIdsFromNrBacklog(std::vector<content_t> * result_out,bool all_required,content_t c_fallback)1745 bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
1746 bool all_required, content_t c_fallback)
1747 {
1748 bool success = true;
1749
1750 if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
1751 errorstream << "NodeResolver: no more node lists" << std::endl;
1752 return false;
1753 }
1754
1755 size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
1756
1757 while (length--) {
1758 if (m_nodenames_idx == m_nodenames.size()) {
1759 errorstream << "NodeResolver: no more nodes in list" << std::endl;
1760 return false;
1761 }
1762
1763 content_t c;
1764 std::string &name = m_nodenames[m_nodenames_idx++];
1765
1766 if (name.substr(0,6) != "group:") {
1767 if (m_ndef->getId(name, c)) {
1768 result_out->push_back(c);
1769 } else if (all_required) {
1770 errorstream << "NodeResolver: failed to resolve node name '"
1771 << name << "'." << std::endl;
1772 result_out->push_back(c_fallback);
1773 success = false;
1774 }
1775 } else {
1776 m_ndef->getIds(name, *result_out);
1777 }
1778 }
1779
1780 return success;
1781 }
1782