Fix unit test if there isn't a localhost address (for example FreeBSD jails), second...
[oweals/minetest.git] / src / nodedef.cpp
index 895761f59e5f213fe1bebe4865c7cc993515c5b2..bcf51a072e1429e9f75d8c93b6e1e1786564fa6b 100644 (file)
@@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "itemdef.h"
 #ifndef SERVER
 #include "tile.h"
+#include "mesh.h"
+#include <IMeshManipulator.h>
 #endif
 #include "log.h"
 #include "settings.h"
@@ -31,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/serialize.h"
 #include "exceptions.h"
 #include "debug.h"
+#include "gamedef.h"
 
 /*
        NodeBox
@@ -195,6 +198,11 @@ void ContentFeatures::reset()
        // Unknown nodes can be dug
        groups["dig_immediate"] = 2;
        drawtype = NDT_NORMAL;
+       mesh = "";
+#ifndef SERVER
+       for(u32 i = 0; i < 24; i++)
+               mesh_ptr[i] = NULL;
+#endif
        visual_scale = 1.0;
        for(u32 i = 0; i < 6; i++)
                tiledef[i] = TileDef();
@@ -219,13 +227,13 @@ void ContentFeatures::reset()
        liquid_alternative_source = "";
        liquid_viscosity = 0;
        liquid_renewable = true;
-       freezemelt = "";
        liquid_range = LIQUID_LEVEL_MAX+1;
        drowning = 0;
        light_source = 0;
        damage_per_second = 0;
        node_box = NodeBox();
        selection_box = NodeBox();
+       collision_box = NodeBox();
        waving = 0;
        legacy_facedir_simple = false;
        legacy_wallmounted = false;
@@ -295,6 +303,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
        writeU8(os, waving);
        // Stuff below should be moved to correct place in a version that otherwise changes
        // the protocol version
+       os<<serializeString(mesh);
+       collision_box.serialize(os, protocol_version);
 }
 
 void ContentFeatures::deSerialize(std::istream &is)
@@ -363,6 +373,8 @@ void ContentFeatures::deSerialize(std::istream &is)
        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) {};
 }
 
@@ -376,8 +388,8 @@ public:
        virtual ~CNodeDefManager();
        void clear();
        virtual IWritableNodeDefManager *clone();
-       virtual const ContentFeatures& get(content_t c) const;
-       virtual const ContentFeatures& get(const MapNode &n) const;
+       inline virtual const ContentFeatures& get(content_t c) const;
+       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<content_t> &result) const;
@@ -386,16 +398,28 @@ public:
        virtual content_t set(const std::string &name, const ContentFeatures &def);
        virtual content_t allocateDummy(const std::string &name);
        virtual void updateAliases(IItemDefManager *idef);
-       virtual void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc);
+       virtual void updateTextures(IGameDef *gamedef);
        void serialize(std::ostream &os, u16 protocol_version);
        void deSerialize(std::istream &is);
 
+       inline virtual bool getNodeRegistrationStatus() const;
+       inline virtual void setNodeRegistrationStatus(bool completed);
+
+       virtual void pendNodeResolve(NodeResolveInfo *nri);
+       virtual void cancelNodeResolve(NodeResolver *resolver);
+       virtual void runNodeResolverCallbacks();
+
+       virtual bool getIdFromResolveInfo(NodeResolveInfo *nri,
+               const std::string &node_alt, content_t c_fallback, content_t &result);
+       virtual bool getIdsFromResolveInfo(NodeResolveInfo *nri,
+               std::vector<content_t> &result);
+
 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, u8 alpha, u8 material_type, 
-               bool backface_culling);
+               u32 shader_id, bool use_normal_texture, bool backface_culling,
+               u8 alpha, u8 material_type);
 #endif
 
        // Features indexed by id
@@ -417,6 +441,12 @@ private:
 
        // Next possibly free id
        content_t m_next_id;
+
+       // List of node strings and node resolver callbacks to perform
+       std::list<NodeResolveInfo *> m_pending_node_lookups;
+
+       // True when all nodes have been registered
+       bool m_node_registration_complete;
 };
 
 
@@ -428,6 +458,15 @@ CNodeDefManager::CNodeDefManager()
 
 CNodeDefManager::~CNodeDefManager()
 {
+#ifndef SERVER
+       for (u32 i = 0; i < m_content_features.size(); i++) {
+               ContentFeatures *f = &m_content_features[i];
+               for (u32 j = 0; j < 24; j++) {
+                       if (f->mesh_ptr[j])
+                               f->mesh_ptr[j]->drop();
+               }
+       }
+#endif
 }
 
 
@@ -439,6 +478,14 @@ void CNodeDefManager::clear()
        m_group_to_items.clear();
        m_next_id = 0;
 
+       m_node_registration_complete = false;
+       for (std::list<NodeResolveInfo *>::iterator
+                       it = m_pending_node_lookups.begin();
+                       it != m_pending_node_lookups.end();
+                       ++it)
+               delete *it;
+       m_pending_node_lookups.clear();
+
        u32 initial_length = 0;
        initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
        initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
@@ -503,16 +550,14 @@ IWritableNodeDefManager *CNodeDefManager::clone()
 }
 
 
-const ContentFeatures& CNodeDefManager::get(content_t c) const
+inline const ContentFeatures& CNodeDefManager::get(content_t c) const
 {
-       if (c < m_content_features.size())
-               return m_content_features[c];
-       else
-               return m_content_features[CONTENT_UNKNOWN];
+       return c < m_content_features.size()
+                       ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN];
 }
 
 
-const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
+inline const ContentFeatures& CNodeDefManager::get(const MapNode &n) const
 {
        return get(n.getContent());
 }
@@ -669,12 +714,17 @@ void CNodeDefManager::updateAliases(IItemDefManager *idef)
 }
 
 
-void CNodeDefManager::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc)
+void CNodeDefManager::updateTextures(IGameDef *gamedef)
 {
 #ifndef SERVER
        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");
@@ -682,6 +732,7 @@ void CNodeDefManager::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
        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);
@@ -771,6 +822,10 @@ void CNodeDefManager::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
                        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:
@@ -801,13 +856,59 @@ void CNodeDefManager::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
                // 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->alpha, material_type, f->backface_culling);
+                               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->alpha, material_type, f->backface_culling);
+                               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);
                }
        }
 #endif
@@ -817,7 +918,7 @@ void CNodeDefManager::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
 #ifndef SERVER
 void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
                TileDef *tiledef, u32 shader_id, bool use_normal_texture,
-               u8 alpha, u8 material_type, bool backface_culling)
+               bool backface_culling, u8 alpha, u8 material_type)
 {
        tile->shader_id     = shader_id;
        tile->texture       = tsrc->getTexture(tiledef->name, &tile->texture_id);
@@ -853,7 +954,10 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
                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("");
@@ -1061,7 +1165,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
                writeU8(os, drowning);
                writeU8(os, leveled);
                writeU8(os, liquid_range);
-       } else 
+       } else
                throw SerializationError("ContentFeatures::serialize(): "
                        "Unsupported version requested");
 }
@@ -1176,3 +1280,130 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version)
                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;
+}
+
+
+void CNodeDefManager::pendNodeResolve(NodeResolveInfo *nri)
+{
+       nri->resolver->m_ndef = this;
+       if (m_node_registration_complete) {
+               nri->resolver->resolveNodeNames(nri);
+               nri->resolver->m_lookup_done = true;
+               delete nri;
+       } else {
+               m_pending_node_lookups.push_back(nri);
+       }
+}
+
+
+void CNodeDefManager::cancelNodeResolve(NodeResolver *resolver)
+{
+       for (std::list<NodeResolveInfo *>::iterator
+                       it = m_pending_node_lookups.begin();
+                       it != m_pending_node_lookups.end();
+                       ++it) {
+               NodeResolveInfo *nri = *it;
+               if (resolver == nri->resolver) {
+                       it = m_pending_node_lookups.erase(it);
+                       delete nri;
+               }
+       }
+}
+
+
+void CNodeDefManager::runNodeResolverCallbacks()
+{
+       while (!m_pending_node_lookups.empty()) {
+               NodeResolveInfo *nri = m_pending_node_lookups.front();
+               m_pending_node_lookups.pop_front();
+               nri->resolver->resolveNodeNames(nri);
+               nri->resolver->m_lookup_done = true;
+               delete nri;
+       }
+}
+
+
+bool CNodeDefManager::getIdFromResolveInfo(NodeResolveInfo *nri,
+       const std::string &node_alt, content_t c_fallback, content_t &result)
+{
+       if (nri->nodenames.empty()) {
+               result = c_fallback;
+               errorstream << "Resolver empty nodename list" << std::endl;
+               return false;
+       }
+
+       content_t c;
+       std::string name = nri->nodenames.front();
+       nri->nodenames.pop_front();
+
+       bool success = getId(name, c);
+       if (!success && node_alt != "") {
+               name = node_alt;
+               success = getId(name, c);
+       }
+
+       if (!success) {
+               errorstream << "Resolver: Failed to resolve node name '" << name
+                       << "'." << std::endl;
+               c = c_fallback;
+       }
+
+       result = c;
+       return success;
+}
+
+
+bool CNodeDefManager::getIdsFromResolveInfo(NodeResolveInfo *nri,
+       std::vector<content_t> &result)
+{
+       bool success = true;
+
+       if (nri->nodelistinfo.empty()) {
+               errorstream << "Resolver: Empty nodelistinfo list" << std::endl;
+               return false;
+       }
+
+       NodeListInfo listinfo = nri->nodelistinfo.front();
+       nri->nodelistinfo.pop_front();
+
+       while (listinfo.length--) {
+               if (nri->nodenames.empty()) {
+                       errorstream << "Resolver: Empty nodename list" << std::endl;
+                       return false;
+               }
+
+               content_t c;
+               std::string name = nri->nodenames.front();
+               nri->nodenames.pop_front();
+
+               if (name.substr(0,6) != "group:") {
+                       if (getId(name, c)) {
+                               result.push_back(c);
+                       } else if (listinfo.all_required) {
+                               errorstream << "Resolver: Failed to resolve node name '" << name
+                                       << "'." << std::endl;
+                               result.push_back(listinfo.c_fallback);
+                               success = false;
+                       }
+               } else {
+                       std::set<content_t> cids;
+                       std::set<content_t>::iterator it;
+                       getIds(name, cids);
+                       for (it = cids.begin(); it != cids.end(); ++it)
+                               result.push_back(*it);
+               }
+       }
+
+       return success;
+}