X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=src%2Fnodedef.cpp;h=651418cd80c46029020f29c1563b46098d6b3e9b;hb=4a1265ceb5919c53efaa8c50bbb0fe7955aa47aa;hp=6c2e96c4f47d4d5f554b2f1c25c965be83896188;hpb=603297cc352cab685dd01dcd645999624ad17c0b;p=dragonfireclient.git diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 6c2e96c4f..651418cd8 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -21,8 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #ifndef SERVER -#include "client/tile.h" #include "mesh.h" +#include "shader.h" +#include "client.h" +#include "client/renderingengine.h" +#include "client/tile.h" #include #endif #include "log.h" @@ -33,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include "debug.h" #include "gamedef.h" +#include "mapnode.h" #include // Used in applyTextureOverrides() /* @@ -48,44 +52,90 @@ void NodeBox::reset() wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2); wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2); wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2); + // no default for other parts + connect_top.clear(); + connect_bottom.clear(); + connect_front.clear(); + connect_left.clear(); + connect_back.clear(); + connect_right.clear(); } void NodeBox::serialize(std::ostream &os, u16 protocol_version) const { - int version = protocol_version >= 21 ? 2 : 1; + // Protocol >= 21 + int version = 2; + if (protocol_version >= 27) + version = 3; writeU8(os, version); - if (version == 1 && type == NODEBOX_LEVELED) - writeU8(os, NODEBOX_FIXED); - else - writeU8(os, type); + switch (type) { + case NODEBOX_LEVELED: + case NODEBOX_FIXED: + if (version == 1) + writeU8(os, NODEBOX_FIXED); + else + writeU8(os, type); - if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED) - { writeU16(os, fixed.size()); - for(std::vector::const_iterator + for (std::vector::const_iterator i = fixed.begin(); - i != fixed.end(); i++) + i != fixed.end(); ++i) { writeV3F1000(os, i->MinEdge); writeV3F1000(os, i->MaxEdge); } - } - else if(type == NODEBOX_WALLMOUNTED) - { + break; + case NODEBOX_WALLMOUNTED: + writeU8(os, type); + writeV3F1000(os, wall_top.MinEdge); writeV3F1000(os, wall_top.MaxEdge); writeV3F1000(os, wall_bottom.MinEdge); writeV3F1000(os, wall_bottom.MaxEdge); writeV3F1000(os, wall_side.MinEdge); writeV3F1000(os, wall_side.MaxEdge); + break; + case NODEBOX_CONNECTED: + if (version <= 2) { + // send old clients nodes that can't be walked through + // to prevent abuse + writeU8(os, NODEBOX_FIXED); + + writeU16(os, 1); + writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2)); + writeV3F1000(os, v3f(BS/2, BS/2, BS/2)); + } else { + writeU8(os, type); + +#define WRITEBOX(box) do { \ + writeU16(os, (box).size()); \ + for (std::vector::const_iterator \ + i = (box).begin(); \ + i != (box).end(); ++i) { \ + writeV3F1000(os, i->MinEdge); \ + writeV3F1000(os, i->MaxEdge); \ + }; } while (0) + + WRITEBOX(fixed); + WRITEBOX(connect_top); + WRITEBOX(connect_bottom); + WRITEBOX(connect_front); + WRITEBOX(connect_left); + WRITEBOX(connect_back); + WRITEBOX(connect_right); + } + break; + default: + writeU8(os, type); + break; } } void NodeBox::deSerialize(std::istream &is) { int version = readU8(is); - if(version < 1 || version > 2) + if (version < 1 || version > 3) throw SerializationError("unsupported NodeBox version"); reset(); @@ -112,6 +162,26 @@ void NodeBox::deSerialize(std::istream &is) wall_side.MinEdge = readV3F1000(is); wall_side.MaxEdge = readV3F1000(is); } + else if (type == NODEBOX_CONNECTED) + { +#define READBOXES(box) do { \ + count = readU16(is); \ + (box).reserve(count); \ + while (count--) { \ + v3f min = readV3F1000(is); \ + v3f max = readV3F1000(is); \ + (box).push_back(aabb3f(min, max)); }; } while (0) + + u16 count; + + READBOXES(fixed); + READBOXES(connect_top); + READBOXES(connect_bottom); + READBOXES(connect_front); + READBOXES(connect_left); + READBOXES(connect_back); + READBOXES(connect_right); + } } /* @@ -120,45 +190,108 @@ void NodeBox::deSerialize(std::istream &is) void TileDef::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version >= 17) - writeU8(os, 1); + if (protocol_version >= 30) + writeU8(os, 4); + else if (protocol_version >= 29) + writeU8(os, 3); + else if (protocol_version >= 26) + writeU8(os, 2); else - writeU8(os, 0); - os<= 17) - writeU8(os, backface_culling); + writeU8(os, 1); + + os << serializeString(name); + animation.serialize(os, protocol_version); + writeU8(os, backface_culling); + if (protocol_version >= 26) { + writeU8(os, tileable_horizontal); + writeU8(os, tileable_vertical); + } + if (protocol_version >= 30) { + writeU8(os, has_color); + if (has_color) { + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + } + } } -void TileDef::deSerialize(std::istream &is) +void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype) { int version = readU8(is); name = deSerializeString(is); - animation.type = (TileAnimationType)readU8(is); - animation.aspect_w = readU16(is); - animation.aspect_h = readU16(is); - animation.length = readF1000(is); - if(version >= 1) + animation.deSerialize(is, version >= 3 ? 29 : 26); + if (version >= 1) backface_culling = readU8(is); + if (version >= 2) { + tileable_horizontal = readU8(is); + tileable_vertical = readU8(is); + } + if (version >= 4) { + has_color = readU8(is); + if (has_color) { + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + } + } + + if ((contenfeatures_version < 8) && + ((drawtype == NDT_MESH) || + (drawtype == NDT_FIRELIKE) || + (drawtype == NDT_LIQUID) || + (drawtype == NDT_PLANTLIKE))) + backface_culling = false; } + /* SimpleSoundSpec serialization */ static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss, - std::ostream &os) + std::ostream &os, u8 version) { os<= 11) + writeF1000(os, ss.pitch); } -static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is) +static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is, u8 version) { ss.name = deSerializeString(is); ss.gain = readF1000(is); + + if (version >= 11) + ss.pitch = readF1000(is); +} + +void TextureSettings::readSettings() +{ + connected_glass = g_settings->getBool("connected_glass"); + opaque_water = g_settings->getBool("opaque_water"); + bool enable_shaders = g_settings->getBool("enable_shaders"); + bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); + bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); + bool smooth_lighting = g_settings->getBool("smooth_lighting"); + enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); + enable_minimap = g_settings->getBool("enable_minimap"); + std::string leaves_style_str = g_settings->get("leaves_style"); + + // Mesh cache is not supported in combination with smooth lighting + if (smooth_lighting) + enable_mesh_cache = false; + + use_normal_texture = enable_shaders && + (enable_bumpmapping || enable_parallax_occlusion); + if (leaves_style_str == "fancy") { + leaves_style = LEAVES_FANCY; + } else if (leaves_style_str == "simple") { + leaves_style = LEAVES_SIMPLE; + } else { + leaves_style = LEAVES_OPAQUE; + } } /* @@ -183,6 +316,7 @@ void ContentFeatures::reset() solidness = 2; visual_solidness = 0; backface_culling = true; + #endif has_on_construct = false; has_on_destruct = false; @@ -202,6 +336,7 @@ void ContentFeatures::reset() #ifndef SERVER for(u32 i = 0; i < 24; i++) mesh_ptr[i] = NULL; + minimap_color = video::SColor(0, 0, 0, 0); #endif visual_scale = 1.0; for(u32 i = 0; i < 6; i++) @@ -220,6 +355,7 @@ void ContentFeatures::reset() diggable = true; climbable = false; buildable_to = false; + floodable = false; rightclickable = true; leveled = 0; liquid_type = LIQUID_NONE; @@ -240,144 +376,513 @@ void ContentFeatures::reset() sound_footstep = SimpleSoundSpec(); sound_dig = SimpleSoundSpec("__group"); sound_dug = SimpleSoundSpec(); + connects_to.clear(); + connects_to_ids.clear(); + connect_sides = 0; + color = video::SColor(0xFFFFFFFF); + palette_name = ""; + palette = NULL; } void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version < 24){ + if (protocol_version < 31) { serializeOld(os, protocol_version); return; } - writeU8(os, 7); // version - os<= 34) ? 11 : 10; + writeU8(os, version); + + // general + os << serializeString(name); writeU16(os, groups.size()); - for(ItemGroupList::const_iterator - i = groups.begin(); i != groups.end(); i++){ - os<first); + for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end(); + ++i) { + os << serializeString(i->first); writeS16(os, i->second); } + writeU8(os, param_type); + writeU8(os, param_type_2); + + // visual writeU8(os, drawtype); + os << serializeString(mesh); writeF1000(os, visual_scale); writeU8(os, 6); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); + for (u32 i = 0; i < 6; i++) + tiledef_overlay[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){ + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + os << serializeString(palette_name); + writeU8(os, waving); + writeU8(os, connect_sides); + writeU16(os, connects_to_ids.size()); + for (std::set::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); writeU8(os, post_effect_color.getAlpha()); writeU8(os, post_effect_color.getRed()); writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); - writeU8(os, param_type); - writeU8(os, param_type_2); - writeU8(os, is_ground_content); + writeU8(os, leveled); + + // lighting writeU8(os, light_propagates); writeU8(os, sunlight_propagates); + writeU8(os, light_source); + + // map generation + writeU8(os, is_ground_content); + + // interaction writeU8(os, walkable); writeU8(os, pointable); writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); - os< 11) { + throw SerializationError("unsupported ContentFeatures version"); } + // general name = deSerializeString(is); groups.clear(); u32 groups_size = readU16(is); - for(u32 i = 0; i < groups_size; i++){ + for (u32 i = 0; i < groups_size; i++) { std::string name = deSerializeString(is); int value = readS16(is); groups[name] = value; } - drawtype = (enum NodeDrawType)readU8(is); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + + // visual + drawtype = (enum NodeDrawType) readU8(is); + mesh = deSerializeString(is); visual_scale = readF1000(is); - if(readU8(is) != 6) + if (readU8(is) != 6) throw SerializationError("unsupported tile count"); - for(u32 i = 0; i < 6; i++) - tiledef[i].deSerialize(is); - if(readU8(is) != CF_SPECIAL_COUNT) + for (u32 i = 0; i < 6; i++) + tiledef[i].deSerialize(is, version, drawtype); + if (version >= 10) + for (u32 i = 0; i < 6; i++) + tiledef_overlay[i].deSerialize(is, version, drawtype); + if (readU8(is) != CF_SPECIAL_COUNT) throw SerializationError("unsupported CF_SPECIAL_COUNT"); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++) - tiledef_special[i].deSerialize(is); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].deSerialize(is, version, drawtype); alpha = readU8(is); + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + palette_name = deSerializeString(is); + waving = readU8(is); + connect_sides = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); post_effect_color.setAlpha(readU8(is)); post_effect_color.setRed(readU8(is)); post_effect_color.setGreen(readU8(is)); post_effect_color.setBlue(readU8(is)); - param_type = (enum ContentParamType)readU8(is); - param_type_2 = (enum ContentParamType2)readU8(is); - is_ground_content = readU8(is); + leveled = readU8(is); + + // lighting-related light_propagates = readU8(is); sunlight_propagates = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + + // map generation + is_ground_content = readU8(is); + + // interaction walkable = readU8(is); pointable = readU8(is); diggable = readU8(is); climbable = readU8(is); buildable_to = readU8(is); - deSerializeString(is); // legacy: used to be metadata_name - liquid_type = (enum LiquidType)readU8(is); + rightclickable = readU8(is); + damage_per_second = readU32(is); + + // liquid + liquid_type = (enum LiquidType) readU8(is); liquid_alternative_flowing = deSerializeString(is); liquid_alternative_source = deSerializeString(is); liquid_viscosity = readU8(is); liquid_renewable = readU8(is); - light_source = readU8(is); - damage_per_second = readU32(is); + liquid_range = readU8(is); + drowning = readU8(is); + floodable = readU8(is); + + // node boxes node_box.deSerialize(is); selection_box.deSerialize(is); + collision_box.deSerialize(is); + + // sounds + deSerializeSimpleSoundSpec(sound_footstep, is, version); + deSerializeSimpleSoundSpec(sound_dig, is, version); + deSerializeSimpleSoundSpec(sound_dug, is, version); + + // read legacy properties legacy_facedir_simple = readU8(is); legacy_wallmounted = readU8(is); - deSerializeSimpleSoundSpec(sound_footstep, is); - deSerializeSimpleSoundSpec(sound_dig, is); - deSerializeSimpleSoundSpec(sound_dug, is); - rightclickable = readU8(is); - drowning = readU8(is); - leveled = readU8(is); - liquid_range = readU8(is); - waving = readU8(is); - // If you add anything here, insert it primarily inside the try-catch - // block to not need to increase the version. - try{ - // Stuff below should be moved to correct place in a version that - // otherwise changes the protocol version - mesh = deSerializeString(is); - collision_box.deSerialize(is); - }catch(SerializationError &e) {}; } +#ifndef SERVER +void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, + TileDef *tiledef, u32 shader_id, bool use_normal_texture, + bool backface_culling, u8 material_type) +{ + tile->shader_id = shader_id; + tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); + tile->material_type = material_type; + + // Normal texture and shader flags texture + if (use_normal_texture) { + tile->normal_texture = tsrc->getNormalTexture(tiledef->name); + } + tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false); + + // Material flags + tile->material_flags = 0; + if (backface_culling) + tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + if (tiledef->animation.type != TAT_NONE) + tile->material_flags |= MATERIAL_FLAG_ANIMATION; + if (tiledef->tileable_horizontal) + tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; + if (tiledef->tileable_vertical) + tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; + + // Color + tile->has_color = tiledef->has_color; + if (tiledef->has_color) + tile->color = tiledef->color; + else + tile->color = color; + + // Animation parameters + int frame_count = 1; + if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { + int frame_length_ms; + tiledef->animation.determineParams(tile->texture->getOriginalSize(), + &frame_count, &frame_length_ms, NULL); + tile->animation_frame_count = frame_count; + tile->animation_frame_length_ms = frame_length_ms; + } + + if (frame_count == 1) { + tile->material_flags &= ~MATERIAL_FLAG_ANIMATION; + } else { + std::ostringstream os(std::ios::binary); + if (!tile->frames) { + tile->frames = std::make_shared>(); + } + tile->frames->resize(frame_count); + + for (int i = 0; i < frame_count; i++) { + + FrameSpec frame; + + os.str(""); + os << tiledef->name; + tiledef->animation.getTextureModifer(os, + tile->texture->getOriginalSize(), i); + + frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); + if (tile->normal_texture) + frame.normal_texture = tsrc->getNormalTexture(os.str()); + frame.flags_texture = tile->flags_texture; + (*tile->frames)[i] = frame; + } + } +} +#endif + +#ifndef SERVER +void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings) +{ + // minimap pixel color - the average color of a texture + if (tsettings.enable_minimap && tiledef[0].name != "") + minimap_color = tsrc->getTextureAverageColor(tiledef[0].name); + + // Figure out the actual tiles to use + TileDef tdef[6]; + for (u32 j = 0; j < 6; j++) { + tdef[j] = tiledef[j]; + if (tdef[j].name == "") + tdef[j].name = "unknown_node.png"; + } + // also the overlay tiles + TileDef tdef_overlay[6]; + for (u32 j = 0; j < 6; j++) + tdef_overlay[j] = tiledef_overlay[j]; + // also the special tiles + TileDef tdef_spec[6]; + for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) + tdef_spec[j] = tiledef_special[j]; + + bool is_liquid = false; + + u8 material_type = (alpha == 255) ? + TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA; + + switch (drawtype) { + default: + case NDT_NORMAL: + material_type = (alpha == 255) ? + TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA; + solidness = 2; + break; + case NDT_AIRLIKE: + solidness = 0; + break; + case NDT_LIQUID: + assert(liquid_type == LIQUID_SOURCE); + if (tsettings.opaque_water) + alpha = 255; + solidness = 1; + is_liquid = true; + break; + case NDT_FLOWINGLIQUID: + assert(liquid_type == LIQUID_FLOWING); + solidness = 0; + if (tsettings.opaque_water) + alpha = 255; + is_liquid = true; + break; + case NDT_GLASSLIKE: + solidness = 0; + visual_solidness = 1; + break; + case NDT_GLASSLIKE_FRAMED: + solidness = 0; + visual_solidness = 1; + break; + case NDT_GLASSLIKE_FRAMED_OPTIONAL: + solidness = 0; + visual_solidness = 1; + drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE; + break; + case NDT_ALLFACES: + solidness = 0; + visual_solidness = 1; + break; + case NDT_ALLFACES_OPTIONAL: + if (tsettings.leaves_style == LEAVES_FANCY) { + drawtype = NDT_ALLFACES; + solidness = 0; + visual_solidness = 1; + } else if (tsettings.leaves_style == LEAVES_SIMPLE) { + for (u32 j = 0; j < 6; j++) { + if (tdef_spec[j].name != "") + tdef[j].name = tdef_spec[j].name; + } + drawtype = NDT_GLASSLIKE; + solidness = 0; + visual_solidness = 1; + } else { + drawtype = NDT_NORMAL; + solidness = 2; + for (u32 i = 0; i < 6; i++) + tdef[i].name += std::string("^[noalpha"); + } + if (waving >= 1) + material_type = TILE_MATERIAL_WAVING_LEAVES; + break; + case NDT_PLANTLIKE: + solidness = 0; + if (waving >= 1) + material_type = TILE_MATERIAL_WAVING_PLANTS; + break; + case NDT_FIRELIKE: + solidness = 0; + break; + case NDT_MESH: + case NDT_NODEBOX: + solidness = 0; + if (waving == 1) + material_type = TILE_MATERIAL_WAVING_PLANTS; + else if (waving == 2) + material_type = TILE_MATERIAL_WAVING_LEAVES; + break; + case NDT_TORCHLIKE: + case NDT_SIGNLIKE: + case NDT_FENCELIKE: + case NDT_RAILLIKE: + solidness = 0; + break; + case NDT_PLANTLIKE_ROOTED: + solidness = 2; + break; + } + + if (is_liquid) { + // Vertex alpha is no longer supported, correct if necessary. + correctAlpha(tdef, 6); + correctAlpha(tdef_overlay, 6); + correctAlpha(tdef_spec, CF_SPECIAL_COUNT); + material_type = (alpha == 255) ? + TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT; + } + + u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype); + + u8 overlay_material = material_type; + if (overlay_material == TILE_MATERIAL_OPAQUE) + overlay_material = TILE_MATERIAL_BASIC; + else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE) + overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT; + + u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype); + + // Tiles (fill in f->tiles[]) + for (u16 j = 0; j < 6; j++) { + fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader, + tsettings.use_normal_texture, + tdef[j].backface_culling, material_type); + if (tdef_overlay[j].name != "") + fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j], + overlay_shader, tsettings.use_normal_texture, + tdef[j].backface_culling, overlay_material); + } + + u8 special_material = material_type; + if (drawtype == NDT_PLANTLIKE_ROOTED) { + if (waving == 1) + special_material = TILE_MATERIAL_WAVING_PLANTS; + else if (waving == 2) + special_material = TILE_MATERIAL_WAVING_LEAVES; + } + u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype); + + // Special tiles (fill in f->special_tiles[]) + for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { + fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j], + special_shader, tsettings.use_normal_texture, + tdef_spec[j].backface_culling, special_material); + } + + if (param_type_2 == CPT2_COLOR || + param_type_2 == CPT2_COLORED_FACEDIR || + param_type_2 == CPT2_COLORED_WALLMOUNTED) + palette = tsrc->getPalette(palette_name); + + if ((drawtype == NDT_MESH) && (mesh != "")) { + // Meshnode drawtype + // Read the mesh and apply scale + mesh_ptr[0] = client->getMesh(mesh); + if (mesh_ptr[0]){ + v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale; + scaleMesh(mesh_ptr[0], scale); + recalculateBoundingBox(mesh_ptr[0]); + meshmanip->recalculateNormals(mesh_ptr[0], true, false); + } + } else if ((drawtype == NDT_NODEBOX) && + ((node_box.type == NODEBOX_REGULAR) || + (node_box.type == NODEBOX_FIXED)) && + (!node_box.fixed.empty())) { + //Convert regular nodebox nodes to meshnodes + //Change the drawtype and apply scale + drawtype = NDT_MESH; + mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed); + v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale; + scaleMesh(mesh_ptr[0], scale); + recalculateBoundingBox(mesh_ptr[0]); + meshmanip->recalculateNormals(mesh_ptr[0], true, false); + } + + //Cache 6dfacedir and wallmounted rotated clones of meshes + if (tsettings.enable_mesh_cache && mesh_ptr[0] && + (param_type_2 == CPT2_FACEDIR + || param_type_2 == CPT2_COLORED_FACEDIR)) { + for (u16 j = 1; j < 24; j++) { + mesh_ptr[j] = cloneMesh(mesh_ptr[0]); + rotateMeshBy6dFacedir(mesh_ptr[j], j); + recalculateBoundingBox(mesh_ptr[j]); + meshmanip->recalculateNormals(mesh_ptr[j], true, false); + } + } else if (tsettings.enable_mesh_cache && mesh_ptr[0] + && (param_type_2 == CPT2_WALLMOUNTED || + param_type_2 == CPT2_COLORED_WALLMOUNTED)) { + static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 }; + for (u16 j = 1; j < 6; j++) { + mesh_ptr[j] = cloneMesh(mesh_ptr[0]); + rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]); + recalculateBoundingBox(mesh_ptr[j]); + meshmanip->recalculateNormals(mesh_ptr[j], true, false); + } + rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]); + recalculateBoundingBox(mesh_ptr[0]); + meshmanip->recalculateNormals(mesh_ptr[0], true, false); + } +} +#endif + /* CNodeDefManager */ @@ -392,11 +897,12 @@ class CNodeDefManager: public IWritableNodeDefManager { inline virtual const ContentFeatures& get(const MapNode &n) const; virtual bool getId(const std::string &name, content_t &result) const; virtual content_t getId(const std::string &name) const; - virtual void getIds(const std::string &name, std::set &result) const; + virtual bool getIds(const std::string &name, std::set &result) const; virtual const ContentFeatures& get(const std::string &name) const; content_t allocateId(); virtual content_t set(const std::string &name, const ContentFeatures &def); virtual content_t allocateDummy(const std::string &name); + virtual void removeNode(const std::string &name); virtual void updateAliases(IItemDefManager *idef); virtual void applyTextureOverrides(const std::string &override_filepath); virtual void updateTextures(IGameDef *gamedef, @@ -405,21 +911,26 @@ class CNodeDefManager: public IWritableNodeDefManager { void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); - inline virtual bool getNodeRegistrationStatus() const; inline virtual void setNodeRegistrationStatus(bool completed); virtual void pendNodeResolve(NodeResolver *nr); virtual bool cancelNodeResolveCallback(NodeResolver *nr); virtual void runNodeResolveCallbacks(); virtual void resetNodeResolveState(); + virtual void mapNodeboxConnections(); + virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face); + virtual core::aabbox3d getSelectionBoxIntUnion() const + { + return m_selection_box_int_union; + } private: void addNameIdMapping(content_t i, std::string name); -#ifndef SERVER - void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, - u32 shader_id, bool use_normal_texture, bool backface_culling, - u8 alpha, u8 material_type); -#endif + /*! + * Recalculates m_selection_box_int_union based on + * m_selection_box_union. + */ + void fixSelectionBoxIntUnion(); // Features indexed by id std::vector m_content_features; @@ -431,12 +942,12 @@ class CNodeDefManager: public IWritableNodeDefManager { // item aliases too. Updated by updateAliases() // Note: Not serialized. - std::map m_name_id_mapping_with_aliases; + std::unordered_map m_name_id_mapping_with_aliases; // A mapping from groups to a list of content_ts (and their levels) // that belong to it. Necessary for a direct lookup in getIds(). // Note: Not serialized. - std::map m_group_to_items; + std::unordered_map m_group_to_items; // Next possibly free id content_t m_next_id; @@ -446,6 +957,14 @@ class CNodeDefManager: public IWritableNodeDefManager { // True when all nodes have been registered bool m_node_registration_complete; + + //! The union of all nodes' selection boxes. + aabb3f m_selection_box_union; + /*! + * The smallest box in node coordinates that + * contains all nodes' selection boxes. + */ + core::aabbox3d m_selection_box_int_union; }; @@ -476,6 +995,8 @@ void CNodeDefManager::clear() m_name_id_mapping_with_aliases.clear(); m_group_to_items.clear(); m_next_id = 0; + m_selection_box_union.reset(0,0,0); + m_selection_box_int_union.reset(0,0,0); resetNodeResolveState(); @@ -507,6 +1028,7 @@ void CNodeDefManager::clear() f.pointable = false; f.diggable = false; f.buildable_to = true; + f.floodable = true; f.is_ground_content = true; // Insert directly into containers content_t c = CONTENT_AIR; @@ -558,7 +1080,7 @@ inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const bool CNodeDefManager::getId(const std::string &name, content_t &result) const { - std::map::const_iterator + std::unordered_map::const_iterator i = m_name_id_mapping_with_aliases.find(name); if(i == m_name_id_mapping_with_aliases.end()) return false; @@ -575,22 +1097,23 @@ content_t CNodeDefManager::getId(const std::string &name) const } -void CNodeDefManager::getIds(const std::string &name, +bool CNodeDefManager::getIds(const std::string &name, std::set &result) const { //TimeTaker t("getIds", NULL, PRECISION_MICRO); if (name.substr(0,6) != "group:") { content_t id = CONTENT_IGNORE; - if(getId(name, id)) + bool exists = getId(name, id); + if (exists) result.insert(id); - return; + return exists; } std::string group = name.substr(6); - std::map::const_iterator + std::unordered_map::const_iterator i = m_group_to_items.find(group); if (i == m_group_to_items.end()) - return; + return true; const GroupItems &items = i->second; for (GroupItems::const_iterator j = items.begin(); @@ -599,6 +1122,7 @@ void CNodeDefManager::getIds(const std::string &name, result.insert((*j).first); } //printf("getIds: %dus\n", t.stop()); + return true; } @@ -631,6 +1155,124 @@ content_t CNodeDefManager::allocateId() } +/*! + * Returns the smallest box that contains all boxes + * in the vector. Box_union is expanded. + * @param[in] boxes the vector containing the boxes + * @param[in, out] box_union the union of the arguments + */ +void boxVectorUnion(const std::vector &boxes, aabb3f *box_union) +{ + for (std::vector::const_iterator it = boxes.begin(); + it != boxes.end(); ++it) { + box_union->addInternalBox(*it); + } +} + + +/*! + * Returns a box that contains the nodebox in every case. + * The argument node_union is expanded. + * @param[in] nodebox the nodebox to be measured + * @param[in] features used to decide whether the nodebox + * can be rotated + * @param[in, out] box_union the union of the arguments + */ +void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features, + aabb3f *box_union) +{ + switch(nodebox.type) { + case NODEBOX_FIXED: + case NODEBOX_LEVELED: { + // Raw union + aabb3f half_processed(0, 0, 0, 0, 0, 0); + boxVectorUnion(nodebox.fixed, &half_processed); + // Set leveled boxes to maximal + if (nodebox.type == NODEBOX_LEVELED) { + half_processed.MaxEdge.Y = +BS / 2; + } + if (features.param_type_2 == CPT2_FACEDIR || + features.param_type_2 == CPT2_COLORED_FACEDIR) { + // Get maximal coordinate + f32 coords[] = { + fabsf(half_processed.MinEdge.X), + fabsf(half_processed.MinEdge.Y), + fabsf(half_processed.MinEdge.Z), + fabsf(half_processed.MaxEdge.X), + fabsf(half_processed.MaxEdge.Y), + fabsf(half_processed.MaxEdge.Z) }; + f32 max = 0; + for (int i = 0; i < 6; i++) { + if (max < coords[i]) { + max = coords[i]; + } + } + // Add the union of all possible rotated boxes + box_union->addInternalPoint(-max, -max, -max); + box_union->addInternalPoint(+max, +max, +max); + } else { + box_union->addInternalBox(half_processed); + } + break; + } + case NODEBOX_WALLMOUNTED: { + // Add fix boxes + box_union->addInternalBox(nodebox.wall_top); + box_union->addInternalBox(nodebox.wall_bottom); + // Find maximal coordinate in the X-Z plane + f32 coords[] = { + fabsf(nodebox.wall_side.MinEdge.X), + fabsf(nodebox.wall_side.MinEdge.Z), + fabsf(nodebox.wall_side.MaxEdge.X), + fabsf(nodebox.wall_side.MaxEdge.Z) }; + f32 max = 0; + for (int i = 0; i < 4; i++) { + if (max < coords[i]) { + max = coords[i]; + } + } + // Add the union of all possible rotated boxes + box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max); + box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max); + break; + } + case NODEBOX_CONNECTED: { + // Add all possible connected boxes + boxVectorUnion(nodebox.fixed, box_union); + boxVectorUnion(nodebox.connect_top, box_union); + boxVectorUnion(nodebox.connect_bottom, box_union); + boxVectorUnion(nodebox.connect_front, box_union); + boxVectorUnion(nodebox.connect_left, box_union); + boxVectorUnion(nodebox.connect_back, box_union); + boxVectorUnion(nodebox.connect_right, box_union); + break; + } + default: { + // NODEBOX_REGULAR + box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2); + box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2); + } + } +} + + +inline void CNodeDefManager::fixSelectionBoxIntUnion() +{ + m_selection_box_int_union.MinEdge.X = floorf( + m_selection_box_union.MinEdge.X / BS + 0.5f); + m_selection_box_int_union.MinEdge.Y = floorf( + m_selection_box_union.MinEdge.Y / BS + 0.5f); + m_selection_box_int_union.MinEdge.Z = floorf( + m_selection_box_union.MinEdge.Z / BS + 0.5f); + m_selection_box_int_union.MaxEdge.X = ceilf( + m_selection_box_union.MaxEdge.X / BS - 0.5f); + m_selection_box_int_union.MaxEdge.Y = ceilf( + m_selection_box_union.MaxEdge.Y / BS - 0.5f); + m_selection_box_int_union.MaxEdge.Z = ceilf( + m_selection_box_union.MaxEdge.Z / BS - 0.5f); +} + + // IWritableNodeDefManager content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def) { @@ -640,7 +1282,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d // Don't allow redefining ignore (but allow air and unknown) if (name == "ignore") { - infostream << "NodeDefManager: WARNING: Ignoring " + warningstream << "NodeDefManager: Ignoring " "CONTENT_IGNORE redefinition"<first; - std::map::iterator + std::unordered_map::iterator j = m_group_to_items.find(group_name); if (j == m_group_to_items.end()) { m_group_to_items[group_name].push_back( @@ -691,14 +1335,48 @@ content_t CNodeDefManager::allocateDummy(const std::string &name) } +void CNodeDefManager::removeNode(const std::string &name) +{ + // Pre-condition + assert(name != ""); + + // Erase name from name ID mapping + content_t id = CONTENT_IGNORE; + if (m_name_id_mapping.getId(name, id)) { + m_name_id_mapping.eraseName(name); + m_name_id_mapping_with_aliases.erase(name); + } + + // Erase node content from all groups it belongs to + for (std::unordered_map::iterator iter_groups = + m_group_to_items.begin(); iter_groups != m_group_to_items.end();) { + GroupItems &items = iter_groups->second; + for (GroupItems::iterator iter_groupitems = items.begin(); + iter_groupitems != items.end();) { + if (iter_groupitems->first == id) + items.erase(iter_groupitems++); + else + ++iter_groupitems; + } + + // Check if group is empty + if (items.size() == 0) + m_group_to_items.erase(iter_groups++); + else + ++iter_groups; + } +} + + void CNodeDefManager::updateAliases(IItemDefManager *idef) { - std::set all = idef->getAll(); + std::set all; + idef->getAll(all); m_name_id_mapping_with_aliases.clear(); - for (std::set::iterator - i = all.begin(); i != all.end(); i++) { - std::string name = *i; - std::string convert_to = idef->getAlias(name); + for (std::set::const_iterator + i = all.begin(); i != all.end(); ++i) { + const std::string &name = *i; + const std::string &convert_to = idef->getAlias(name); content_t id; if (m_name_id_mapping.getId(convert_to, id)) { m_name_id_mapping_with_aliases.insert( @@ -728,13 +1406,8 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath } content_t id; - if (!getId(splitted[0], id)) { - errorstream << override_filepath - << ":" << line_c << " Could not apply texture override \"" - << line << "\": Unknown node \"" - << splitted[0] << "\"" << std::endl; - continue; - } + if (!getId(splitted[0], id)) + continue; // Ignore unknown node ContentFeatures &nodedef = m_content_features[id]; @@ -766,7 +1439,6 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath } } - void CNodeDefManager::updateTextures(IGameDef *gamedef, void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress), void *progress_callback_args) @@ -775,264 +1447,24 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, infostream << "CNodeDefManager::updateTextures(): Updating " "textures in node definitions" << std::endl; - ITextureSource *tsrc = gamedef->tsrc(); - IShaderSource *shdsrc = gamedef->getShaderSource(); - scene::ISceneManager* smgr = gamedef->getSceneManager(); - scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); - - bool new_style_water = g_settings->getBool("new_style_water"); - bool new_style_leaves = g_settings->getBool("new_style_leaves"); - bool connected_glass = g_settings->getBool("connected_glass"); - bool opaque_water = g_settings->getBool("opaque_water"); - bool enable_shaders = g_settings->getBool("enable_shaders"); - bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); - bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); - bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); - - bool use_normal_texture = enable_shaders && - (enable_bumpmapping || enable_parallax_occlusion); + Client *client = (Client *)gamedef; + ITextureSource *tsrc = client->tsrc(); + IShaderSource *shdsrc = client->getShaderSource(); + scene::IMeshManipulator *meshmanip = + RenderingEngine::get_scene_manager()->getMeshManipulator(); + TextureSettings tsettings; + tsettings.readSettings(); u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - ContentFeatures *f = &m_content_features[i]; - - // Figure out the actual tiles to use - TileDef tiledef[6]; - for (u32 j = 0; j < 6; j++) { - tiledef[j] = f->tiledef[j]; - if (tiledef[j].name == "") - tiledef[j].name = "unknown_node.png"; - } - - bool is_liquid = false; - bool is_water_surface = false; - - u8 material_type = (f->alpha == 255) ? - TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA; - - switch (f->drawtype) { - default: - case NDT_NORMAL: - f->solidness = 2; - break; - case NDT_AIRLIKE: - f->solidness = 0; - break; - case NDT_LIQUID: - assert(f->liquid_type == LIQUID_SOURCE); - if (opaque_water) - f->alpha = 255; - if (new_style_water){ - f->solidness = 0; - } else { - f->solidness = 1; - f->backface_culling = false; - } - is_liquid = true; - break; - case NDT_FLOWINGLIQUID: - assert(f->liquid_type == LIQUID_FLOWING); - f->solidness = 0; - if (opaque_water) - f->alpha = 255; - is_liquid = true; - break; - case NDT_GLASSLIKE: - f->solidness = 0; - f->visual_solidness = 1; - break; - case NDT_GLASSLIKE_FRAMED: - f->solidness = 0; - f->visual_solidness = 1; - break; - case NDT_GLASSLIKE_FRAMED_OPTIONAL: - f->solidness = 0; - f->visual_solidness = 1; - f->drawtype = connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE; - break; - case NDT_ALLFACES: - f->solidness = 0; - f->visual_solidness = 1; - break; - case NDT_ALLFACES_OPTIONAL: - if (new_style_leaves) { - f->drawtype = NDT_ALLFACES; - f->solidness = 0; - f->visual_solidness = 1; - } else { - f->drawtype = NDT_NORMAL; - f->solidness = 2; - for (u32 i = 0; i < 6; i++) - tiledef[i].name += std::string("^[noalpha"); - } - if (f->waving == 1) - material_type = TILE_MATERIAL_WAVING_LEAVES; - break; - case NDT_PLANTLIKE: - f->solidness = 0; - f->backface_culling = false; - if (f->waving == 1) - material_type = TILE_MATERIAL_WAVING_PLANTS; - break; - case NDT_FIRELIKE: - f->backface_culling = false; - f->solidness = 0; - break; - case NDT_MESH: - f->solidness = 0; - f->backface_culling = false; - break; - case NDT_TORCHLIKE: - case NDT_SIGNLIKE: - case NDT_FENCELIKE: - case NDT_RAILLIKE: - case NDT_NODEBOX: - f->solidness = 0; - break; - } - - if (is_liquid) { - material_type = (f->alpha == 255) ? - TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT; - if (f->name == "default:water_source") - is_water_surface = true; - } - - u32 tile_shader[6]; - for (u16 j = 0; j < 6; j++) { - tile_shader[j] = shdsrc->getShader("nodes_shader", - material_type, f->drawtype); - } - - if (is_water_surface) { - tile_shader[0] = shdsrc->getShader("water_surface_shader", - material_type, f->drawtype); - } - - // Tiles (fill in f->tiles[]) - for (u16 j = 0; j < 6; j++) { - fillTileAttribs(tsrc, &f->tiles[j], &tiledef[j], tile_shader[j], - use_normal_texture, f->backface_culling, f->alpha, material_type); - } - - // Special tiles (fill in f->special_tiles[]) - for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { - fillTileAttribs(tsrc, &f->special_tiles[j], &f->tiledef_special[j], - tile_shader[j], use_normal_texture, - f->tiledef_special[j].backface_culling, f->alpha, material_type); - } - - if ((f->drawtype == NDT_MESH) && (f->mesh != "")) { - // Meshnode drawtype - // Read the mesh and apply scale - f->mesh_ptr[0] = gamedef->getMesh(f->mesh); - if (f->mesh_ptr[0]){ - v3f scale = v3f(1.0, 1.0, 1.0) * BS * f->visual_scale; - scaleMesh(f->mesh_ptr[0], scale); - recalculateBoundingBox(f->mesh_ptr[0]); - meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); - } - } else if ((f->drawtype == NDT_NODEBOX) && - ((f->node_box.type == NODEBOX_REGULAR) || - (f->node_box.type == NODEBOX_FIXED)) && - (!f->node_box.fixed.empty())) { - //Convert regular nodebox nodes to meshnodes - //Change the drawtype and apply scale - f->drawtype = NDT_MESH; - f->mesh_ptr[0] = convertNodeboxNodeToMesh(f); - v3f scale = v3f(1.0, 1.0, 1.0) * f->visual_scale; - scaleMesh(f->mesh_ptr[0], scale); - recalculateBoundingBox(f->mesh_ptr[0]); - meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); - } - - //Cache 6dfacedir and wallmounted rotated clones of meshes - if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_FACEDIR)) { - for (u16 j = 1; j < 24; j++) { - f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]); - rotateMeshBy6dFacedir(f->mesh_ptr[j], j); - recalculateBoundingBox(f->mesh_ptr[j]); - meshmanip->recalculateNormals(f->mesh_ptr[j], true, false); - } - } else if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_WALLMOUNTED)) { - static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; - for (u16 j = 1; j < 6; j++) { - f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]); - rotateMeshBy6dFacedir(f->mesh_ptr[j], wm_to_6d[j]); - recalculateBoundingBox(f->mesh_ptr[j]); - meshmanip->recalculateNormals(f->mesh_ptr[j], true, false); - } - rotateMeshBy6dFacedir(f->mesh_ptr[0], wm_to_6d[0]); - recalculateBoundingBox(f->mesh_ptr[0]); - meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); - } - + ContentFeatures *f = &(m_content_features[i]); + f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } #endif } - -#ifndef SERVER -void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, - TileDef *tiledef, u32 shader_id, bool use_normal_texture, - bool backface_culling, u8 alpha, u8 material_type) -{ - tile->shader_id = shader_id; - tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); - tile->alpha = alpha; - tile->material_type = material_type; - - // Normal texture - if (use_normal_texture) - tile->normal_texture = tsrc->getNormalTexture(tiledef->name); - - // Material flags - tile->material_flags = 0; - if (backface_culling) - tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if (tiledef->animation.type == TAT_VERTICAL_FRAMES) - tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; - - // Animation parameters - int frame_count = 1; - if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { - // Get texture size to determine frame count by aspect ratio - v2u32 size = tile->texture->getOriginalSize(); - int frame_height = (float)size.X / - (float)tiledef->animation.aspect_w * - (float)tiledef->animation.aspect_h; - frame_count = size.Y / frame_height; - int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count; - tile->animation_frame_count = frame_count; - tile->animation_frame_length_ms = frame_length_ms; - } - - if (frame_count == 1) { - tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; - } else { - std::ostringstream os(std::ios::binary); - tile->frames.resize(frame_count); - - for (int i = 0; i < frame_count; i++) { - - FrameSpec frame; - - os.str(""); - os << tiledef->name << "^[verticalframe:" - << frame_count << ":" << i; - - frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); - if (tile->normal_texture) - frame.normal_texture = tsrc->getNormalTexture(os.str()); - tile->frames[i] = frame; - } - } -} -#endif - - void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const { writeU8(os, 1); // version @@ -1081,12 +1513,12 @@ void CNodeDefManager::deSerialize(std::istream &is) // Check error conditions if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) { - infostream << "NodeDefManager::deSerialize(): WARNING: " + warningstream << "NodeDefManager::deSerialize(): " "not changing builtin node " << i << std::endl; continue; } if (f.name == "") { - infostream << "NodeDefManager::deSerialize(): WARNING: " + warningstream << "NodeDefManager::deSerialize(): " "received empty name" << std::endl; continue; } @@ -1094,7 +1526,7 @@ void CNodeDefManager::deSerialize(std::istream &is) // Ignore aliases u16 existing_id; if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) { - infostream << "NodeDefManager::deSerialize(): WARNING: " + warningstream << "NodeDefManager::deSerialize(): " "already defined with different ID: " << f.name << std::endl; continue; } @@ -1105,6 +1537,9 @@ void CNodeDefManager::deSerialize(std::istream &is) m_content_features[i] = f; addNameIdMapping(i, f.name); verbosestream << "deserialized " << f.name << std::endl; + + getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); + fixSelectionBoxIntUnion(); } } @@ -1125,72 +1560,52 @@ IWritableNodeDefManager *createNodeDefManager() //// Serialization of old ContentFeatures formats void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const { - if (protocol_version == 13) - { - writeU8(os, 5); // version - os<first); - writeS16(os, i->second); + u8 compatible_param_type_2 = param_type_2; + if ((protocol_version < 28) + && (compatible_param_type_2 == CPT2_MESHOPTIONS)) + compatible_param_type_2 = CPT2_NONE; + else if (protocol_version < 30) { + if (compatible_param_type_2 == CPT2_COLOR) + compatible_param_type_2 = CPT2_NONE; + else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR) + compatible_param_type_2 = CPT2_FACEDIR; + else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED) + compatible_param_type_2 = CPT2_WALLMOUNTED; + } + + float compatible_visual_scale = visual_scale; + if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) + compatible_visual_scale = sqrt(visual_scale); + + TileDef compatible_tiles[6]; + for (u8 i = 0; i < 6; i++) { + compatible_tiles[i] = tiledef[i]; + if (tiledef_overlay[i].name != "") { + std::stringstream s; + s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name + << ")"; + compatible_tiles[i].name = s.str(); } - writeU8(os, drawtype); - writeF1000(os, visual_scale); - writeU8(os, 6); - for (u32 i = 0; i < 6; i++) - tiledef[i].serialize(os, protocol_version); - //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24 - writeU8(os, 2); - for (u32 i = 0; i < 2; i++) - tiledef_special[i].serialize(os, protocol_version); - writeU8(os, alpha); - writeU8(os, post_effect_color.getAlpha()); - writeU8(os, post_effect_color.getRed()); - writeU8(os, post_effect_color.getGreen()); - writeU8(os, post_effect_color.getBlue()); - writeU8(os, param_type); - writeU8(os, param_type_2); - writeU8(os, is_ground_content); - writeU8(os, light_propagates); - writeU8(os, sunlight_propagates); - writeU8(os, walkable); - writeU8(os, pointable); - writeU8(os, diggable); - writeU8(os, climbable); - writeU8(os, buildable_to); - os< 13 && protocol_version < 24) { - writeU8(os, 6); // version - os<= 24 + if (protocol_version < 31) { + writeU8(os, protocol_version < 27 ? 7 : 8); + + os << serializeString(name); writeU16(os, groups.size()); - for (ItemGroupList::const_iterator - i = groups.begin(); i != groups.end(); i++) { - os<first); - writeS16(os, i->second); + for (ItemGroupList::const_iterator i = groups.begin(); + i != groups.end(); ++i) { + os << serializeString(i->first); + writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) - tiledef[i].serialize(os, protocol_version); - //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24 - writeU8(os, 2); - for (u32 i = 0; i < 2; i++) + compatible_tiles[i].serialize(os, protocol_version); + writeU8(os, CF_SPECIAL_COUNT); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) tiledef_special[i].serialize(os, protocol_version); writeU8(os, alpha); writeU8(os, post_effect_color.getAlpha()); @@ -1198,7 +1613,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + writeU8(os, compatible_param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); @@ -1207,10 +1622,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); - os<::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); + writeU8(os, connect_sides); + } else { throw SerializationError("ContentFeatures::serialize(): " "Unsupported version requested"); + } } - void ContentFeatures::deSerializeOld(std::istream &is, int version) { if (version == 5) // In PROTOCOL_VERSION 13 @@ -1245,15 +1669,16 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) groups[name] = value; } drawtype = (enum NodeDrawType)readU8(is); + visual_scale = readF1000(is); if (readU8(is) != 6) throw SerializationError("unsupported tile count"); for (u32 i = 0; i < 6; i++) - tiledef[i].deSerialize(is); + tiledef[i].deSerialize(is, version, drawtype); if (readU8(is) != CF_SPECIAL_COUNT) throw SerializationError("unsupported CF_SPECIAL_COUNT"); for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) - tiledef_special[i].deSerialize(is); + tiledef_special[i].deSerialize(is, version, drawtype); alpha = readU8(is); post_effect_color.setAlpha(readU8(is)); post_effect_color.setRed(readU8(is)); @@ -1275,14 +1700,15 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) liquid_alternative_source = deSerializeString(is); liquid_viscosity = readU8(is); light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); damage_per_second = readU32(is); node_box.deSerialize(is); selection_box.deSerialize(is); legacy_facedir_simple = readU8(is); legacy_wallmounted = readU8(is); - deSerializeSimpleSoundSpec(sound_footstep, is); - deSerializeSimpleSoundSpec(sound_dig, is); - deSerializeSimpleSoundSpec(sound_dug, is); + deSerializeSimpleSoundSpec(sound_footstep, is, version); + deSerializeSimpleSoundSpec(sound_dig, is, version); + deSerializeSimpleSoundSpec(sound_dug, is, version); } else if (version == 6) { name = deSerializeString(is); groups.clear(); @@ -1297,12 +1723,12 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) if (readU8(is) != 6) throw SerializationError("unsupported tile count"); for (u32 i = 0; i < 6; i++) - tiledef[i].deSerialize(is); + tiledef[i].deSerialize(is, version, drawtype); // CF_SPECIAL_COUNT in version 6 = 2 if (readU8(is) != 2) throw SerializationError("unsupported CF_SPECIAL_COUNT"); for (u32 i = 0; i < 2; i++) - tiledef_special[i].deSerialize(is); + tiledef_special[i].deSerialize(is, version, drawtype); alpha = readU8(is); post_effect_color.setAlpha(readU8(is)); post_effect_color.setRed(readU8(is)); @@ -1330,25 +1756,84 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) selection_box.deSerialize(is); legacy_facedir_simple = readU8(is); legacy_wallmounted = readU8(is); - deSerializeSimpleSoundSpec(sound_footstep, is); - deSerializeSimpleSoundSpec(sound_dig, is); - deSerializeSimpleSoundSpec(sound_dug, is); + deSerializeSimpleSoundSpec(sound_footstep, is, version); + deSerializeSimpleSoundSpec(sound_dig, is, version); + deSerializeSimpleSoundSpec(sound_dug, is, version); rightclickable = readU8(is); drowning = readU8(is); leveled = readU8(is); liquid_range = readU8(is); - } else { + } else if (version == 7 || version == 8){ + name = deSerializeString(is); + groups.clear(); + u32 groups_size = readU16(is); + for (u32 i = 0; i < groups_size; i++) { + std::string name = deSerializeString(is); + int value = readS16(is); + groups[name] = value; + } + drawtype = (enum NodeDrawType) readU8(is); + + visual_scale = readF1000(is); + if (readU8(is) != 6) + throw SerializationError("unsupported tile count"); + for (u32 i = 0; i < 6; i++) + tiledef[i].deSerialize(is, version, drawtype); + if (readU8(is) != CF_SPECIAL_COUNT) + throw SerializationError("unsupported CF_SPECIAL_COUNT"); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].deSerialize(is, version, drawtype); + alpha = readU8(is); + post_effect_color.setAlpha(readU8(is)); + post_effect_color.setRed(readU8(is)); + post_effect_color.setGreen(readU8(is)); + post_effect_color.setBlue(readU8(is)); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + is_ground_content = readU8(is); + light_propagates = readU8(is); + sunlight_propagates = readU8(is); + walkable = readU8(is); + pointable = readU8(is); + diggable = readU8(is); + climbable = readU8(is); + buildable_to = readU8(is); + deSerializeString(is); // legacy: used to be metadata_name + liquid_type = (enum LiquidType) readU8(is); + liquid_alternative_flowing = deSerializeString(is); + liquid_alternative_source = deSerializeString(is); + liquid_viscosity = readU8(is); + liquid_renewable = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + damage_per_second = readU32(is); + node_box.deSerialize(is); + selection_box.deSerialize(is); + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); + deSerializeSimpleSoundSpec(sound_footstep, is, version); + deSerializeSimpleSoundSpec(sound_dig, is, version); + deSerializeSimpleSoundSpec(sound_dug, is, version); + rightclickable = readU8(is); + drowning = readU8(is); + leveled = readU8(is); + liquid_range = readU8(is); + waving = readU8(is); + try { + mesh = deSerializeString(is); + collision_box.deSerialize(is); + floodable = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); + connect_sides = readU8(is); + } catch (SerializationError &e) {}; + }else{ throw SerializationError("unsupported ContentFeatures version"); } } - -inline bool CNodeDefManager::getNodeRegistrationStatus() const -{ - return m_node_registration_complete; -} - - inline void CNodeDefManager::setNodeRegistrationStatus(bool completed) { m_node_registration_complete = completed; @@ -1399,6 +1884,61 @@ void CNodeDefManager::resetNodeResolveState() m_pending_resolve_callbacks.clear(); } +void CNodeDefManager::mapNodeboxConnections() +{ + for (u32 i = 0; i < m_content_features.size(); i++) { + ContentFeatures *f = &m_content_features[i]; + if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED)) + continue; + for (std::vector::iterator it = f->connects_to.begin(); + it != f->connects_to.end(); ++it) { + getIds(*it, f->connects_to_ids); + } + } +} + +bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) +{ + const ContentFeatures &f1 = get(from); + + if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED)) + return false; + + // lookup target in connected set + if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end()) + return false; + + const ContentFeatures &f2 = get(to); + + if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED)) + // ignores actually looking if back connection exists + return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end()); + + // does to node declare usable faces? + if (f2.connect_sides > 0) { + if ((f2.param_type_2 == CPT2_FACEDIR || + f2.param_type_2 == CPT2_COLORED_FACEDIR) + && (connect_face >= 4)) { + static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 4 - back + 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 8 - right + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 16 - front + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left + }; + return (f2.connect_sides + & rot[(connect_face * 4) + (to.param2 & 0x1F)]); + } + return (f2.connect_sides & connect_face); + } + // the target is just a regular node, so connect no matter back connection + return true; +} //// //// NodeResolver @@ -1406,11 +1946,6 @@ void CNodeDefManager::resetNodeResolveState() NodeResolver::NodeResolver() { - m_ndef = NULL; - m_nodenames_idx = 0; - m_nnlistsizes_idx = 0; - m_resolve_done = false; - m_nodenames.reserve(16); m_nnlistsizes.reserve(4); }