Mgv7: Auto-set lowest mountain generation level
[oweals/minetest.git] / src / nodedef.cpp
index 5735ef91465406dc99919c6c485b8ece14b61688..e392f477ab2c3ad9db1881574585e861773c6902 100644 (file)
@@ -19,11 +19,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "nodedef.h"
 
-#include "main.h" // For g_settings
 #include "itemdef.h"
 #ifndef SERVER
-#include "tile.h"
+#include "client/tile.h"
 #include "mesh.h"
+#include <IMeshManipulator.h>
 #endif
 #include "log.h"
 #include "settings.h"
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "exceptions.h"
 #include "debug.h"
 #include "gamedef.h"
+#include <fstream> // Used in applyTextureOverrides()
 
 /*
        NodeBox
@@ -201,6 +202,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++)
@@ -226,7 +228,6 @@ void ContentFeatures::reset()
        liquid_alternative_source = "";
        liquid_viscosity = 0;
        liquid_renewable = true;
-       freezemelt = "";
        liquid_range = LIQUID_LEVEL_MAX+1;
        drowning = 0;
        light_source = 0;
@@ -242,7 +243,7 @@ void ContentFeatures::reset()
        sound_dug = SimpleSoundSpec();
 }
 
-void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
+void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
 {
        if(protocol_version < 24){
                serializeOld(os, protocol_version);
@@ -388,8 +389,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;
@@ -398,10 +399,21 @@ 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(IGameDef *gamedef);
-       void serialize(std::ostream &os, u16 protocol_version);
+       virtual void applyTextureOverrides(const std::string &override_filepath);
+       virtual void updateTextures(IGameDef *gamedef,
+               void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
+               void *progress_cbk_args);
+       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();
+
 private:
        void addNameIdMapping(content_t i, std::string name);
 #ifndef SERVER
@@ -429,6 +441,12 @@ private:
 
        // Next possibly free id
        content_t m_next_id;
+
+       // NodeResolvers to callback once node registration has ended
+       std::vector<NodeResolver *> m_pending_resolve_callbacks;
+
+       // True when all nodes have been registered
+       bool m_node_registration_complete;
 };
 
 
@@ -440,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
 }
 
 
@@ -451,6 +478,8 @@ void CNodeDefManager::clear()
        m_group_to_items.clear();
        m_next_id = 0;
 
+       resetNodeResolveState();
+
        u32 initial_length = 0;
        initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
        initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
@@ -515,16 +544,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());
 }
@@ -608,6 +635,7 @@ content_t CNodeDefManager::allocateId()
 // IWritableNodeDefManager
 content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
 {
+       // Pre-conditions
        assert(name != "");
        assert(name == def.name);
 
@@ -645,7 +673,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
                        j = m_group_to_items.find(group_name);
                if (j == m_group_to_items.end()) {
                        m_group_to_items[group_name].push_back(
-                                       std::make_pair(id, i->second));
+                               std::make_pair(id, i->second));
                } else {
                        GroupItems &items = j->second;
                        items.push_back(std::make_pair(id, i->second));
@@ -657,7 +685,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
 
 content_t CNodeDefManager::allocateDummy(const std::string &name)
 {
-       assert(name != "");
+       assert(name != "");     // Pre-condition
        ContentFeatures f;
        f.name = name;
        return set(name, f);
@@ -675,20 +703,81 @@ void CNodeDefManager::updateAliases(IItemDefManager *idef)
                content_t id;
                if (m_name_id_mapping.getId(convert_to, id)) {
                        m_name_id_mapping_with_aliases.insert(
-                                       std::make_pair(name, id));
+                               std::make_pair(name, id));
                }
        }
 }
 
+void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
+{
+       infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
+               "overrides to textures from " << override_filepath << std::endl;
+
+       std::ifstream infile(override_filepath.c_str());
+       std::string line;
+       int line_c = 0;
+       while (std::getline(infile, line)) {
+               line_c++;
+               if (trim(line) == "")
+                       continue;
+               std::vector<std::string> splitted = str_split(line, ' ');
+               if (splitted.size() != 3) {
+                       errorstream << override_filepath
+                               << ":" << line_c << " Could not apply texture override \""
+                               << line << "\": Syntax error" << std::endl;
+                       continue;
+               }
+
+               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;
+               }
+
+               ContentFeatures &nodedef = m_content_features[id];
+
+               if (splitted[1] == "top")
+                       nodedef.tiledef[0].name = splitted[2];
+               else if (splitted[1] == "bottom")
+                       nodedef.tiledef[1].name = splitted[2];
+               else if (splitted[1] == "right")
+                       nodedef.tiledef[2].name = splitted[2];
+               else if (splitted[1] == "left")
+                       nodedef.tiledef[3].name = splitted[2];
+               else if (splitted[1] == "back")
+                       nodedef.tiledef[4].name = splitted[2];
+               else if (splitted[1] == "front")
+                       nodedef.tiledef[5].name = splitted[2];
+               else if (splitted[1] == "all" || splitted[1] == "*")
+                       for (int i = 0; i < 6; i++)
+                               nodedef.tiledef[i].name = splitted[2];
+               else if (splitted[1] == "sides")
+                       for (int i = 2; i < 6; i++)
+                               nodedef.tiledef[i].name = splitted[2];
+               else {
+                       errorstream << override_filepath
+                               << ":" << line_c << " Could not apply texture override \""
+                               << line << "\": Unknown node side \""
+                               << splitted[1] << "\"" << std::endl;
+                       continue;
+               }
+       }
+}
 
-void CNodeDefManager::updateTextures(IGameDef *gamedef)
+void CNodeDefManager::updateTextures(IGameDef *gamedef,
+       void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
+       void *progress_callback_args)
 {
 #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");
@@ -697,13 +786,21 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef)
        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 enable_minimap            = g_settings->getBool("enable_minimap");
 
        bool use_normal_texture = enable_shaders &&
                (enable_bumpmapping || enable_parallax_occlusion);
 
-       for (u32 i = 0; i < m_content_features.size(); i++) {
+       u32 size = m_content_features.size();
+
+       for (u32 i = 0; i < size; i++) {
                ContentFeatures *f = &m_content_features[i];
 
+               // minimap pixel color - the average color of a texture
+               if (enable_minimap && f->tiledef[0].name != "")
+                       f->minimap_color = tsrc->getTextureAverageColor(f->tiledef[0].name);
+
                // Figure out the actual tiles to use
                TileDef tiledef[6];
                for (u32 j = 0; j < 6; j++) {
@@ -830,33 +927,52 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef)
                                f->tiledef_special[j].backface_culling, f->alpha, material_type);
                }
 
-               // Meshnode drawtype
-               // Read the mesh and apply scale
                if ((f->drawtype == NDT_MESH) && (f->mesh != "")) {
+                       // Meshnode drawtype
+                       // Read the mesh and apply scale
                        f->mesh_ptr[0] = gamedef->getMesh(f->mesh);
-                       scaleMesh(f->mesh_ptr[0], v3f(f->visual_scale,f->visual_scale,f->visual_scale));
-                       recalculateBoundingBox(f->mesh_ptr[0]);
-               }
-
-               //Convert regular nodebox nodes to meshnodes
-               //Change the drawtype and apply scale
-               if ((f->drawtype == NDT_NODEBOX) && 
-                               ((f->node_box.type == NODEBOX_REGULAR) || (f->node_box.type == NODEBOX_FIXED)) &&
+                       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);
-                       scaleMesh(f->mesh_ptr[0], v3f(f->visual_scale,f->visual_scale,f->visual_scale));
+                       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 rotated clones of meshes
-               if (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]);
-                               }
+               //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);
+               }
+
+               progress_callback(progress_callback_args, i, size);
        }
 #endif
 }
@@ -868,7 +984,7 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
                bool backface_culling, u8 alpha, u8 material_type)
 {
        tile->shader_id     = shader_id;
-       tile->texture       = tsrc->getTexture(tiledef->name, &tile->texture_id);
+       tile->texture       = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
        tile->alpha         = alpha;
        tile->material_type = material_type;
 
@@ -901,14 +1017,17 @@ 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("");
                        os << tiledef->name << "^[verticalframe:"
                                << frame_count << ":" << i;
 
-                       frame.texture = tsrc->getTexture(os.str(), &frame.texture_id);
+                       frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
                        if (tile->normal_texture)
                                frame.normal_texture = tsrc->getNormalTexture(os.str());
                        tile->frames[i] = frame;
@@ -918,7 +1037,7 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
 #endif
 
 
-void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
+void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
 {
        writeU8(os, 1); // version
        u16 count = 0;
@@ -927,7 +1046,7 @@ void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
                if (i == CONTENT_IGNORE || i == CONTENT_AIR
                                || i == CONTENT_UNKNOWN)
                        continue;
-               ContentFeatures *f = &m_content_features[i];
+               const ContentFeatures *f = &m_content_features[i];
                if (f->name == "")
                        continue;
                writeU16(os2, i);
@@ -937,7 +1056,9 @@ void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
                f->serialize(wrapper_os, protocol_version);
                os2<<serializeString(wrapper_os.str());
 
-               assert(count + 1 > count); // must not overflow
+               // must not overflow
+               u16 next = count + 1;
+               FATAL_ERROR_IF(next < count, "Overflow");
                count++;
        }
        writeU16(os, count);
@@ -1006,7 +1127,7 @@ IWritableNodeDefManager *createNodeDefManager()
 
 
 //// Serialization of old ContentFeatures formats
-void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
+void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
 {
        if (protocol_version == 13)
        {
@@ -1109,7 +1230,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");
 }
@@ -1224,3 +1345,168 @@ 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(NodeResolver *nr)
+{
+       nr->m_ndef = this;
+       if (m_node_registration_complete)
+               nr->nodeResolveInternal();
+       else
+               m_pending_resolve_callbacks.push_back(nr);
+}
+
+
+bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
+{
+       size_t len = m_pending_resolve_callbacks.size();
+       for (size_t i = 0; i != len; i++) {
+               if (nr != m_pending_resolve_callbacks[i])
+                       continue;
+
+               len--;
+               m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
+               m_pending_resolve_callbacks.resize(len);
+               return true;
+       }
+
+       return false;
+}
+
+
+void CNodeDefManager::runNodeResolveCallbacks()
+{
+       for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
+               NodeResolver *nr = m_pending_resolve_callbacks[i];
+               nr->nodeResolveInternal();
+       }
+
+       m_pending_resolve_callbacks.clear();
+}
+
+
+void CNodeDefManager::resetNodeResolveState()
+{
+       m_node_registration_complete = false;
+       m_pending_resolve_callbacks.clear();
+}
+
+
+////
+//// NodeResolver
+////
+
+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);
+}
+
+
+NodeResolver::~NodeResolver()
+{
+       if (!m_resolve_done && m_ndef)
+               m_ndef->cancelNodeResolveCallback(this);
+}
+
+
+void NodeResolver::nodeResolveInternal()
+{
+       m_nodenames_idx   = 0;
+       m_nnlistsizes_idx = 0;
+
+       resolveNodeNames();
+       m_resolve_done = true;
+
+       m_nodenames.clear();
+       m_nnlistsizes.clear();
+}
+
+
+bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
+       const std::string &node_alt, content_t c_fallback)
+{
+       if (m_nodenames_idx == m_nodenames.size()) {
+               *result_out = c_fallback;
+               errorstream << "NodeResolver: no more nodes in list" << std::endl;
+               return false;
+       }
+
+       content_t c;
+       std::string name = m_nodenames[m_nodenames_idx++];
+
+       bool success = m_ndef->getId(name, c);
+       if (!success && node_alt != "") {
+               name = node_alt;
+               success = m_ndef->getId(name, c);
+       }
+
+       if (!success) {
+               errorstream << "NodeResolver: failed to resolve node name '" << name
+                       << "'." << std::endl;
+               c = c_fallback;
+       }
+
+       *result_out = c;
+       return success;
+}
+
+
+bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
+       bool all_required, content_t c_fallback)
+{
+       bool success = true;
+
+       if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
+               errorstream << "NodeResolver: no more node lists" << std::endl;
+               return false;
+       }
+
+       size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
+
+       while (length--) {
+               if (m_nodenames_idx == m_nodenames.size()) {
+                       errorstream << "NodeResolver: no more nodes in list" << std::endl;
+                       return false;
+               }
+
+               content_t c;
+               std::string &name = m_nodenames[m_nodenames_idx++];
+
+               if (name.substr(0,6) != "group:") {
+                       if (m_ndef->getId(name, c)) {
+                               result_out->push_back(c);
+                       } else if (all_required) {
+                               errorstream << "NodeResolver: failed to resolve node name '"
+                                       << name << "'." << std::endl;
+                               result_out->push_back(c_fallback);
+                               success = false;
+                       }
+               } else {
+                       std::set<content_t> cids;
+                       std::set<content_t>::iterator it;
+                       m_ndef->getIds(name, cids);
+                       for (it = cids.begin(); it != cids.end(); ++it)
+                               result_out->push_back(*it);
+               }
+       }
+
+       return success;
+}