Hardware coloring for itemstacks
authorDániel Juhász <juhdanad@gmail.com>
Fri, 10 Mar 2017 17:25:58 +0000 (18:25 +0100)
committerAuke Kok <sofar@foo-projects.org>
Sun, 9 Apr 2017 01:39:15 +0000 (18:39 -0700)
Adds the possibility to colorize item stacks based on their metadata.

In the item/node definition you can specify palette (an image file)
and color (fallback color if the item has no palette or metadata).
Then you can add palette_index to the metadata.

Dropped itemstacks with different colors do not merge.

23 files changed:
builtin/game/item_entity.lua
doc/lua_api.txt
src/camera.cpp
src/client/tile.cpp
src/client/tile.h
src/content_cao.cpp
src/hud.cpp
src/itemdef.cpp
src/itemdef.h
src/nodedef.cpp
src/object_properties.cpp
src/object_properties.h
src/script/common/c_content.cpp
src/script/common/c_content.h
src/script/cpp_api/s_entity.cpp
src/script/lua_api/l_itemstackmeta.cpp
src/script/lua_api/l_metadata.cpp
src/script/lua_api/l_metadata.h
src/script/lua_api/l_nodemeta.cpp
src/script/lua_api/l_object.cpp
src/script/lua_api/l_storage.cpp
src/wieldmesh.cpp
src/wieldmesh.h

index be158c119b24ccdf5df6e27fc4fefaf412fb7e5a..7b8247116ab3b1ad8be674d20c672a8bae561350 100644 (file)
@@ -53,6 +53,8 @@ core.register_entity(":__builtin:item", {
                if itemtable then
                        itemname = stack:to_table().name
                end
+               -- Backwards compatibility: old clients use the texture
+               -- to get the type of the item
                local item_texture = nil
                local item_type = ""
                if core.registered_items[itemname] then
@@ -66,6 +68,7 @@ core.register_entity(":__builtin:item", {
                        visual_size = {x = s, y = s},
                        collisionbox = {-c, -c, -c, c, c, c},
                        automatic_rotate = math.pi * 0.5,
+                       wield_item = itemstring,
                }
                self.object:set_properties(prop)
        end,
@@ -101,31 +104,39 @@ core.register_entity(":__builtin:item", {
                self:set_item(self.itemstring)
        end,
 
+  -- moves items from this stack to an other stack
        try_merge_with = function(self, own_stack, object, obj)
+         -- other item's stack
                local stack = ItemStack(obj.itemstring)
-               if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then
+               -- only merge if items are the same
+               if own_stack:get_name() == stack:get_name() and
+                               own_stack:get_meta() == stack:get_meta() and
+                               own_stack:get_wear() == stack:get_wear() and
+                               stack:get_free_space() > 0 then
                        local overflow = false
                        local count = stack:get_count() + own_stack:get_count()
                        local max_count = stack:get_stack_max()
                        if count > max_count then
                                overflow = true
+                               stack:set_count(max_count)
                                count = count - max_count
+                               own_stack:set_count(count)
                        else
                                self.itemstring = ''
+                               stack:set_count(count)
                        end
                        local pos = object:getpos()
                        pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15
                        object:moveto(pos, false)
                        local s, c
-                       local max_count = stack:get_stack_max()
-                       local name = stack:get_name()
                        if not overflow then
-                               obj.itemstring = name .. " " .. count
+                               obj.itemstring = stack:to_string()
                                s = 0.2 + 0.1 * (count / max_count)
                                c = s
                                object:set_properties({
                                        visual_size = {x = s, y = s},
-                                       collisionbox = {-c, -c, -c, c, c, c}
+                                       collisionbox = {-c, -c, -c, c, c, c},
+                                       wield_item = obj.itemstring
                                })
                                self.object:remove()
                                -- merging succeeded
@@ -133,18 +144,20 @@ core.register_entity(":__builtin:item", {
                        else
                                s = 0.4
                                c = 0.3
+                               obj.itemstring = stack:to_string()
                                object:set_properties({
                                        visual_size = {x = s, y = s},
-                                       collisionbox = {-c, -c, -c, c, c, c}
+                                       collisionbox = {-c, -c, -c, c, c, c},
+                                       wield_item = obj.itemstring
                                })
-                               obj.itemstring = name .. " " .. max_count
                                s = 0.2 + 0.1 * (count / max_count)
                                c = s
+                               self.itemstring = own_stack:to_string()
                                self.object:set_properties({
                                        visual_size = {x = s, y = s},
-                                       collisionbox = {-c, -c, -c, c, c, c}
+                                       collisionbox = {-c, -c, -c, c, c, c},
+                                       wield_item = self.itemstring
                                })
-                               self.itemstring = name .. " " .. count
                        end
                end
                -- merging didn't succeed
index ca1b5d14c446e6fd572b0999fe138cf1ed1d4b91..721f5448a3e5e26a10d756a2e02fb2096235d4ff 100644 (file)
@@ -1480,6 +1480,9 @@ Item metadata only contains a key-value store.
 Some of the values in the key-value store are handled specially:
 
 * `description`: Set the itemstack's description. Defaults to idef.description
+* `color`: A `ColorString`, which sets the stack's color.
+* `palette_index`: If the item has a palette, this is used to get the
+                  current color from the palette.
 
 Example stuff:
 
@@ -2855,6 +2858,8 @@ See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`.
     * Any non-table value will clear the metadata
     * See "Node Metadata" for an example
     * returns `true` on success
+* `equals(other)`
+    * returns `true` if this metadata has the same key-value pairs as `other`
 
 ### `NodeMetaRef`
 Node metadata: reference extra data and functionality stored in a node.
@@ -3735,6 +3740,19 @@ Definition tables
                             {hard = 1, metal = 1, spikes = 1}
         inventory_image = "default_tool_steelaxe.png",
         wield_image = "",
+        palette = "",
+        --[[
+        ^ An image file containing the palette of a node.
+        ^ You can set the currently used color as the
+        ^ "palette_index" field of the item stack metadata.
+        ^ The palette is always stretched to fit indices
+        ^ between 0 and 255, to ensure compatibility with
+        ^ "colorfacedir" and "colorwallmounted" nodes.
+        ]]
+        color = "0xFFFFFFFF",
+        --[[
+        ^ The color of the item. The palette overrides this.
+        ]]
         wield_scale = {x = 1, y = 1, z = 1},
         stack_max = 99,
         range = 4.0,
index d5c337fe82e6ffd671af4622237ecac7e26f50e0..7e83dadebf949ef6010a24f197df51a493e373e4 100644 (file)
@@ -501,7 +501,8 @@ void Camera::setDigging(s32 button)
 
 void Camera::wield(const ItemStack &item)
 {
-       if (item.name != m_wield_item_next.name) {
+       if (item.name != m_wield_item_next.name ||
+                       item.metadata != m_wield_item_next.metadata) {
                m_wield_item_next = item;
                if (m_wield_change_timer > 0)
                        m_wield_change_timer = -m_wield_change_timer;
index 86ca7d42221daf9dd0b7fe90490efab286efef49..0aa06980cceaae3b4b059b5b9ce6eddd6f3a1431 100644 (file)
@@ -341,6 +341,8 @@ public:
        */
        video::ITexture* getTextureForMesh(const std::string &name, u32 *id);
 
+       virtual Palette* getPalette(const std::string &name);
+
        // Returns a pointer to the irrlicht device
        virtual IrrlichtDevice* getDevice()
        {
@@ -377,8 +379,6 @@ public:
        video::ITexture* generateTextureFromMesh(
                        const TextureFromMeshParams &params);
 
-       video::IImage* generateImage(const std::string &name);
-
        video::ITexture* getNormalTexture(const std::string &name);
        video::SColor getTextureAverageColor(const std::string &name);
        video::ITexture *getShaderFlagsTexture(bool normamap_present);
@@ -401,6 +401,13 @@ private:
        // if baseimg is NULL, it is created. Otherwise stuff is made on it.
        bool generateImagePart(std::string part_of_name, video::IImage *& baseimg);
 
+       /*! Generates an image from a full string like
+        * "stone.png^mineral_coal.png^[crack:1:0".
+        * Shall be called from the main thread.
+        * The returned Image should be dropped.
+        */
+       video::IImage* generateImage(const std::string &name);
+
        // Thread-safe cache of what source images are known (true = known)
        MutexedMap<std::string, bool> m_source_image_existence;
 
@@ -419,6 +426,9 @@ private:
        // but can't be deleted because the ITexture* might still be used
        std::vector<video::ITexture*> m_texture_trash;
 
+       // Maps image file names to loaded palettes.
+       UNORDERED_MAP<std::string, Palette> m_palettes;
+
        // Cached settings needed for making textures from meshes
        bool m_setting_trilinear_filter;
        bool m_setting_bilinear_filter;
@@ -682,6 +692,61 @@ video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *
        return getTexture(name + "^[applyfiltersformesh", id);
 }
 
+Palette* TextureSource::getPalette(const std::string &name)
+{
+       // Only the main thread may load images
+       sanity_check(thr_is_current_thread(m_main_thread));
+
+       if (name == "")
+               return NULL;
+
+       UNORDERED_MAP<std::string, Palette>::iterator it = m_palettes.find(name);
+       if (it == m_palettes.end()) {
+               // Create palette
+               video::IImage *img = generateImage(name);
+               if (!img) {
+                       warningstream << "TextureSource::getPalette(): palette \"" << name
+                               << "\" could not be loaded." << std::endl;
+                       return NULL;
+               }
+               Palette new_palette;
+               u32 w = img->getDimension().Width;
+               u32 h = img->getDimension().Height;
+               // Real area of the image
+               u32 area = h * w;
+               if (area == 0)
+                       return NULL;
+               if (area > 256) {
+                       warningstream << "TextureSource::getPalette(): the specified"
+                               << " palette image \"" << name << "\" is larger than 256"
+                               << " pixels, using the first 256." << std::endl;
+                       area = 256;
+               } else if (256 % area != 0)
+                       warningstream << "TextureSource::getPalette(): the "
+                               << "specified palette image \"" << name << "\" does not "
+                               << "contain power of two pixels." << std::endl;
+               // We stretch the palette so it will fit 256 values
+               // This many param2 values will have the same color
+               u32 step = 256 / area;
+               // For each pixel in the image
+               for (u32 i = 0; i < area; i++) {
+                       video::SColor c = img->getPixel(i % w, i / w);
+                       // Fill in palette with 'step' colors
+                       for (u32 j = 0; j < step; j++)
+                               new_palette.push_back(c);
+               }
+               img->drop();
+               // Fill in remaining elements
+               while (new_palette.size() < 256)
+                       new_palette.push_back(video::SColor(0xFFFFFFFF));
+               m_palettes[name] = new_palette;
+               it = m_palettes.find(name);
+       }
+       if (it != m_palettes.end())
+               return &((*it).second);
+       return NULL;
+}
+
 void TextureSource::processQueue()
 {
        /*
index d04ab918a27cac08dc2563ded6e064da7ec41450..5eec0f2ea843afb77adb5e20875a80b1205af69a 100644 (file)
@@ -33,6 +33,8 @@ class IGameDef;
 struct TileSpec;
 struct TileDef;
 
+typedef std::vector<video::SColor> Palette;
+
 /*
        tile.{h,cpp}: Texture handling stuff.
 */
@@ -106,14 +108,15 @@ public:
                        const std::string &name, u32 *id = NULL)=0;
        virtual video::ITexture* getTextureForMesh(
                        const std::string &name, u32 *id = NULL) = 0;
+       /*!
+        * Returns a palette from the given texture name.
+        * The pointer is valid until the texture source is
+        * destructed.
+        * Should be called from the main thread.
+        */
+       virtual Palette* getPalette(const std::string &name) = 0;
        virtual IrrlichtDevice* getDevice()=0;
        virtual bool isKnownSourceImage(const std::string &name)=0;
-       /*! Generates an image from a full string like
-        * "stone.png^mineral_coal.png^[crack:1:0".
-        * Shall be called from the main thread.
-        * The returned Image should be dropped.
-        */
-       virtual video::IImage* generateImage(const std::string &name)=0;
        virtual video::ITexture* generateTextureFromMesh(
                        const TextureFromMeshParams &params)=0;
        virtual video::ITexture* getNormalTexture(const std::string &name)=0;
index e0b1c4cd2e841e87201b2b46230771af29c90dde..84f198b75f1d0c917de6daf7ebc6329965358fa0 100644 (file)
@@ -933,23 +933,30 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr,
                        errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
        }
        else if(m_prop.visual == "wielditem") {
-               infostream<<"GenericCAO::addToScene(): wielditem"<<std::endl;
-               infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
-               if(m_prop.textures.size() >= 1){
-                       infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
-                       IItemDefManager *idef = m_client->idef();
-                       ItemStack item(m_prop.textures[0], 1, 0, idef);
-
-                       m_wield_meshnode = new WieldMeshSceneNode(
-                                       smgr->getRootSceneNode(), smgr, -1);
-                       m_wield_meshnode->setItem(item, m_client);
-
-                       m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2,
-                                       m_prop.visual_size.Y/2,
-                                       m_prop.visual_size.X/2));
-                       u8 li = m_last_light;
-                       m_wield_meshnode->setColor(video::SColor(255,li,li,li));
+               ItemStack item;
+               infostream << "GenericCAO::addToScene(): wielditem" << std::endl;
+               if (m_prop.wield_item == "") {
+                       // Old format, only textures are specified.
+                       infostream << "textures: " << m_prop.textures.size() << std::endl;
+                       if (m_prop.textures.size() >= 1) {
+                               infostream << "textures[0]: " << m_prop.textures[0]
+                                       << std::endl;
+                               IItemDefManager *idef = m_client->idef();
+                               item = ItemStack(m_prop.textures[0], 1, 0, idef);
+                       }
+               } else {
+                       infostream << "serialized form: " << m_prop.wield_item << std::endl;
+                       item.deSerialize(m_prop.wield_item, m_client->idef());
                }
+               m_wield_meshnode = new WieldMeshSceneNode(smgr->getRootSceneNode(),
+                       smgr, -1);
+               m_wield_meshnode->setItem(item, m_client);
+
+               m_wield_meshnode->setScale(
+                       v3f(m_prop.visual_size.X / 2, m_prop.visual_size.Y / 2,
+                               m_prop.visual_size.X / 2));
+               u8 li = m_last_light;
+               m_wield_meshnode->setColor(video::SColor(255, li, li, li));
        } else {
                infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
                                <<"\" not supported"<<std::endl;
index a602125e364fe2fe3072ab1f3afddb188ec9f171..f558acf1ea24c62cc951d675d86a1b3621630a25 100644 (file)
@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "fontengine.h"
 #include "guiscalingfilter.h"
 #include "mesh.h"
+#include "wieldmesh.h"
 #include <IGUIStaticText.h>
 
 #ifdef HAVE_TOUCHSCREENGUI
@@ -642,9 +643,10 @@ void drawItemStack(video::IVideoDriver *driver,
        }
 
        const ItemDefinition &def = item.getDefinition(client->idef());
-       scene::IMesh* mesh = client->idef()->getWieldMesh(def.name, client);
+       ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
 
-       if (mesh) {
+       if (imesh && imesh->mesh) {
+               scene::IMesh *mesh = imesh->mesh;
                driver->clearZBuffer();
                s32 delta = 0;
                if (rotation_kind < IT_ROT_NONE) {
@@ -667,16 +669,28 @@ void drawItemStack(video::IVideoDriver *driver,
                matrix.makeIdentity();
 
                if (enable_animations) {
-                       float timer_f = (float)delta / 5000.0;
+                       float timer_f = (float) delta / 5000.0;
                        matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
                }
 
                driver->setTransform(video::ETS_WORLD, matrix);
                driver->setViewPort(rect);
 
+               video::SColor basecolor =
+                       client->idef()->getItemstackColor(item, client);
+
                u32 mc = mesh->getMeshBufferCount();
                for (u32 j = 0; j < mc; ++j) {
                        scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+                       // we can modify vertices relatively fast,
+                       // because these meshes are not buffered.
+                       assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
+                       video::SColor c = basecolor;
+                       if (imesh->buffer_colors.size() > j) {
+                               std::pair<bool, video::SColor> p = imesh->buffer_colors[j];
+                               c = p.first ? p.second : basecolor;
+                       }
+                       colorizeMeshBuffer(buf, &c);
                        video::SMaterial &material = buf->getMaterial();
                        material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
                        material.Lighting = false;
index 627ad1b6c45164a7e57d6999b39d6978bb3bf1dc..51d8f1d5d662045511a4183825ddc38c54d4e048 100644 (file)
@@ -82,6 +82,8 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
        sound_place = def.sound_place;
        sound_place_failed = def.sound_place_failed;
        range = def.range;
+       palette_image = def.palette_image;
+       color = def.color;
        return *this;
 }
 
@@ -104,6 +106,8 @@ void ItemDefinition::reset()
        description = "";
        inventory_image = "";
        wield_image = "";
+       palette_image = "";
+       color = video::SColor(0xFFFFFFFF);
        wield_scale = v3f(1.0, 1.0, 1.0);
        stack_max = 99;
        usable = false;
@@ -153,6 +157,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
        writeF1000(os, range);
        os << serializeString(sound_place_failed.name);
        writeF1000(os, sound_place_failed.gain);
+       os << serializeString(palette_image);
+       writeU32(os, color.color);
 }
 
 void ItemDefinition::deSerialize(std::istream &is)
@@ -209,6 +215,8 @@ void ItemDefinition::deSerialize(std::istream &is)
        try {
                sound_place_failed.name = deSerializeString(is);
                sound_place_failed.gain = readF1000(is);
+               palette_image = deSerializeString(is);
+               color.set(readU32(is));
        } catch(SerializationError &e) {};
 }
 
@@ -224,11 +232,13 @@ class CItemDefManager: public IWritableItemDefManager
        struct ClientCached
        {
                video::ITexture *inventory_texture;
-               scene::IMesh *wield_mesh;
+               ItemMesh wield_mesh;
+               Palette *palette;
 
                ClientCached():
                        inventory_texture(NULL),
-                       wield_mesh(NULL)
+                       wield_mesh(),
+                       palette(NULL)
                {}
        };
 #endif
@@ -250,8 +260,8 @@ public:
                                i = values.begin(); i != values.end(); ++i)
                {
                        ClientCached *cc = *i;
-                       if (cc->wield_mesh)
-                               cc->wield_mesh->drop();
+                       if (cc->wield_mesh.mesh)
+                               cc->wield_mesh.mesh->drop();
                        delete cc;
                }
 
@@ -335,8 +345,9 @@ public:
                ItemStack item = ItemStack();
                item.name = def.name;
 
-               scene::IMesh *mesh = getItemMesh(client, item);
-               cc->wield_mesh = mesh;
+               getItemMesh(client, item, &(cc->wield_mesh));
+
+               cc->palette = tsrc->getPalette(def.palette_image);
 
                // Put in cache
                m_clientcached.set(name, cc);
@@ -390,13 +401,41 @@ public:
                return cc->inventory_texture;
        }
        // Get item wield mesh
-       virtual scene::IMesh* getWieldMesh(const std::string &name,
+       virtual ItemMesh* getWieldMesh(const std::string &name,
                        Client *client) const
        {
                ClientCached *cc = getClientCached(name, client);
                if(!cc)
                        return NULL;
-               return cc->wield_mesh;
+               return &(cc->wield_mesh);
+       }
+
+       // Get item palette
+       virtual Palette* getPalette(const std::string &name,
+                       Client *client) const
+       {
+               ClientCached *cc = getClientCached(name, client);
+               if(!cc)
+                       return NULL;
+               return cc->palette;
+       }
+
+       virtual video::SColor getItemstackColor(const ItemStack &stack,
+               Client *client) const
+       {
+               // Look for direct color definition
+               const std::string &colorstring = stack.metadata.getString("color", 0);
+               video::SColor directcolor;
+               if ((colorstring != "")
+                               && parseColorString(colorstring, directcolor, true))
+                       return directcolor;
+               // See if there is a palette
+               Palette *palette = getPalette(stack.name, client);
+               const std::string &index = stack.metadata.getString("palette_index", 0);
+               if ((palette != NULL) && (index != ""))
+                       return (*palette)[mystoi(index, 0, 255)];
+               // Fallback color
+               return get(stack.name).color;
        }
 #endif
        void clear()
index 01ec4fa2fed37623c606cc472b0553043bc7add7..2d7ff570dee3f828ee848d4f9363d2cc0aa36b96 100644 (file)
@@ -30,6 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class IGameDef;
 class Client;
 struct ToolCapabilities;
+#ifndef SERVER
+#include "client/tile.h"
+struct ItemMesh;
+struct ItemStack;
+#endif
 
 /*
        Base item definition
@@ -57,6 +62,8 @@ struct ItemDefinition
        */
        std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems
        std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used
+       std::string palette_image; // If specified, the item will be colorized based on this
+       video::SColor color; // The fallback color of the node.
        v3f wield_scale;
 
        /*
@@ -110,8 +117,15 @@ public:
        virtual video::ITexture* getInventoryTexture(const std::string &name,
                        Client *client) const=0;
        // Get item wield mesh
-       virtual scene::IMesh* getWieldMesh(const std::string &name,
+       virtual ItemMesh* getWieldMesh(const std::string &name,
                Client *client) const=0;
+       // Get item palette
+       virtual Palette* getPalette(const std::string &name,
+               Client *client) const = 0;
+       // Returns the base color of an item stack: the color of all
+       // tiles that do not define their own color.
+       virtual video::SColor getItemstackColor(const ItemStack &stack,
+               Client *client) const = 0;
 #endif
 
        virtual void serialize(std::ostream &os, u16 protocol_version)=0;
@@ -136,7 +150,7 @@ public:
        virtual video::ITexture* getInventoryTexture(const std::string &name,
                        Client *client) const=0;
        // Get item wield mesh
-       virtual scene::IMesh* getWieldMesh(const std::string &name,
+       virtual ItemMesh* getWieldMesh(const std::string &name,
                Client *client) const=0;
 #endif
 
index 2745a45e8cb1644f9eee09f7f93998ba1743f68b..558acafd6dbd093491e2ae71078754f6464108a6 100644 (file)
@@ -786,6 +786,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
                        tiledef_special[j].backface_culling, material_type);
        }
 
+       palette = tsrc->getPalette(palette_name);
+
        if ((drawtype == NDT_MESH) && (mesh != "")) {
                // Meshnode drawtype
                // Read the mesh and apply scale
@@ -859,9 +861,6 @@ public:
        virtual void removeNode(const std::string &name);
        virtual void updateAliases(IItemDefManager *idef);
        virtual void applyTextureOverrides(const std::string &override_filepath);
-       //! Returns a palette or NULL if not found. Only on client.
-       std::vector<video::SColor> *getPalette(const ContentFeatures &f,
-               const IGameDef *gamedef);
        virtual void updateTextures(IGameDef *gamedef,
                void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
                void *progress_cbk_args);
@@ -910,9 +909,6 @@ private:
        // Next possibly free id
        content_t m_next_id;
 
-       // Maps image file names to loaded palettes.
-       UNORDERED_MAP<std::string, std::vector<video::SColor> > m_palettes;
-
        // NodeResolvers to callback once node registration has ended
        std::vector<NodeResolver *> m_pending_resolve_callbacks;
 
@@ -1401,78 +1397,6 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath
        }
 }
 
-std::vector<video::SColor> *CNodeDefManager::getPalette(
-       const ContentFeatures &f, const IGameDef *gamedef)
-{
-#ifndef SERVER
-       // This works because colors always use the most significant bits
-       // of param2. If you add a new colored type which uses param2
-       // in a more advanced way, you should change this code, too.
-       u32 palette_pixels = 0;
-       switch (f.param_type_2) {
-               case CPT2_COLOR:
-                       palette_pixels = 256;
-                       break;
-               case CPT2_COLORED_FACEDIR:
-                       palette_pixels = 8;
-                       break;
-               case CPT2_COLORED_WALLMOUNTED:
-                       palette_pixels = 32;
-                       break;
-               default:
-                       return NULL;
-       }
-       // This many param2 values will have the same color
-       u32 step = 256 / palette_pixels;
-       const std::string &name = f.palette_name;
-       if (name == "")
-               return NULL;
-       Client *client = (Client *) gamedef;
-       ITextureSource *tsrc = client->tsrc();
-
-       UNORDERED_MAP<std::string, std::vector<video::SColor> >::iterator it =
-       m_palettes.find(name);
-       if (it == m_palettes.end()) {
-               // Create palette
-               if (!tsrc->isKnownSourceImage(name)) {
-                       warningstream << "CNodeDefManager::getPalette(): palette \"" << name
-                               << "\" could not be loaded." << std::endl;
-                       return NULL;
-               }
-               video::IImage *img = tsrc->generateImage(name);
-               std::vector<video::SColor> new_palette;
-               u32 w = img->getDimension().Width;
-               u32 h = img->getDimension().Height;
-               // Real area of the image
-               u32 area = h * w;
-               if (area != palette_pixels)
-                       warningstream << "CNodeDefManager::getPalette(): the "
-                               << "specified palette image \"" << name << "\" does not "
-                               << "contain exactly " << palette_pixels
-                               << " pixels." << std::endl;
-               if (area > palette_pixels)
-                       area = palette_pixels;
-               // For each pixel in the image
-               for (u32 i = 0; i < area; i++) {
-                       video::SColor c = img->getPixel(i % w, i / w);
-                       // Fill in palette with 'step' colors
-                       for (u32 j = 0; j < step; j++)
-                               new_palette.push_back(c);
-               }
-               img->drop();
-               // Fill in remaining elements
-               while (new_palette.size() < 256)
-                       new_palette.push_back(video::SColor(0xFFFFFFFF));
-               m_palettes[name] = new_palette;
-               it = m_palettes.find(name);
-       }
-       if (it != m_palettes.end())
-               return &((*it).second);
-
-#endif
-       return NULL;
-}
-
 void CNodeDefManager::updateTextures(IGameDef *gamedef,
        void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
        void *progress_callback_args)
@@ -1489,12 +1413,10 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
        TextureSettings tsettings;
        tsettings.readSettings();
 
-       m_palettes.clear();
        u32 size = m_content_features.size();
 
        for (u32 i = 0; i < size; i++) {
                ContentFeatures *f = &(m_content_features[i]);
-               f->palette = getPalette(*f, gamedef);
                f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
                progress_callback(progress_callback_args, i, size);
        }
index f4e4953babe6380f9037ca0f632341ccdff3b847..a77368151db64753f26314cf7b7fffbbfd53117d 100644 (file)
@@ -117,6 +117,7 @@ void ObjectProperties::serialize(std::ostream &os) const
        writeARGB8(os, nametag_color);
        writeF1000(os, automatic_face_movement_max_rotation_per_sec);
        os << serializeString(infotext);
+       os << serializeString(wield_item);
 
        // Add stuff only at the bottom.
        // Never remove anything, because we don't want new versions of this
@@ -159,6 +160,7 @@ void ObjectProperties::deSerialize(std::istream &is)
                        nametag_color = readARGB8(is);
                        automatic_face_movement_max_rotation_per_sec = readF1000(is);
                        infotext = deSerializeString(is);
+                       wield_item = deSerializeString(is);
                }catch(SerializationError &e){}
        }
        else
index 082d9a5290ea945e0f7cb6b418acbd9a369fe4b5..908757a644752d8fa0f29df058cd7ac35128df6d 100644 (file)
@@ -52,6 +52,8 @@ struct ObjectProperties
        video::SColor nametag_color;
        f32 automatic_face_movement_max_rotation_per_sec;
        std::string infotext;
+       //! For dropped items, this contains item information.
+       std::string wield_item;
 
        ObjectProperties();
        std::string dump();
index 99e12cd829bbd8b1044eaa94642969f0c288bf37..bcae874b99e09eb9ed6422ecb30368bfdb813873 100644 (file)
@@ -58,6 +58,12 @@ ItemDefinition read_item_definition(lua_State* L,int index,
        getstringfield(L, index, "description", def.description);
        getstringfield(L, index, "inventory_image", def.inventory_image);
        getstringfield(L, index, "wield_image", def.wield_image);
+       getstringfield(L, index, "palette", def.palette_image);
+
+       // Read item color.
+       lua_getfield(L, index, "color");
+       read_color(L, -1, &def.color);
+       lua_pop(L, 1);
 
        lua_getfield(L, index, "wield_scale");
        if(lua_istable(L, -1)){
@@ -118,7 +124,7 @@ ItemDefinition read_item_definition(lua_State* L,int index,
 
 /******************************************************************************/
 void read_object_properties(lua_State *L, int index,
-               ObjectProperties *prop)
+               ObjectProperties *prop, IItemDefManager *idef)
 {
        if(index < 0)
                index = lua_gettop(L) + 1 + index;
@@ -216,6 +222,10 @@ void read_object_properties(lua_State *L, int index,
        }
        lua_pop(L, 1);
        getstringfield(L, -1, "infotext", prop->infotext);
+       lua_getfield(L, -1, "wield_item");
+       if (!lua_isnil(L, -1))
+               prop->wield_item = read_item(L, -1, idef).getItemString();
+       lua_pop(L, 1);
 }
 
 /******************************************************************************/
@@ -284,6 +294,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
        lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec");
        lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size());
        lua_setfield(L, -2, "infotext");
+       lua_pushlstring(L, prop->wield_item.c_str(), prop->wield_item.size());
+       lua_setfield(L, -2, "wield_item");
 }
 
 /******************************************************************************/
index 10cccbb0173c83c9e07c42ef0d5d642f07499737..949b136eb35ad3b7f6a441c14253ee9d98bde866 100644 (file)
@@ -89,7 +89,8 @@ void               push_tool_capabilities    (lua_State *L,
 ItemDefinition     read_item_definition      (lua_State *L, int index,
                                               ItemDefinition default_def);
 void               read_object_properties    (lua_State *L, int index,
-                                              ObjectProperties *prop);
+                                              ObjectProperties *prop,
+                                              IItemDefManager *idef);
 void               push_object_properties    (lua_State *L,
                                               ObjectProperties *prop);
 
index 9e2193970c28d45b61abf101cd5f8b05f28a28a8..2e1d277e48d4261b99f2cebe0f86c1f55b979e9e 100644 (file)
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "object_properties.h"
 #include "common/c_converter.h"
 #include "common/c_content.h"
+#include "server.h"
 
 bool ScriptApiEntity::luaentity_Add(u16 id, const char *name)
 {
@@ -187,11 +188,11 @@ void ScriptApiEntity::luaentity_GetProperties(u16 id,
        getstringfield(L, -1, "mesh", prop->mesh);
 
        // Deprecated: read object properties directly
-       read_object_properties(L, -1, prop);
+       read_object_properties(L, -1, prop, getServer()->idef());
 
        // Read initial_properties
        lua_getfield(L, -1, "initial_properties");
-       read_object_properties(L, -1, prop);
+       read_object_properties(L, -1, prop, getServer()->idef());
        lua_pop(L, 1);
 }
 
index efdd77b516302923214ab35de8d2c5ec313b2a16..c37a82116d83bac99a0e7a193021afd83c8bd6eb 100644 (file)
@@ -92,6 +92,10 @@ void ItemStackMetaRef::Register(lua_State *L)
        lua_pushcfunction(L, gc_object);
        lua_settable(L, metatable);
 
+       lua_pushliteral(L, "__eq");
+       lua_pushcfunction(L, l_equals);
+       lua_settable(L, metatable);
+
        lua_pop(L, 1);  // drop metatable
 
        luaL_openlib(L, 0, methods, 0);  // fill methodtable
@@ -111,5 +115,6 @@ const luaL_Reg ItemStackMetaRef::methods[] = {
        luamethod(MetaDataRef, set_float),
        luamethod(MetaDataRef, to_table),
        luamethod(MetaDataRef, from_table),
+       luamethod(MetaDataRef, equals),
        {0,0}
 };
index b54005bacdcf1c41a90a67c55d10b257af0faa05..5f4e984cb59c9fce79bf82250c3c140f247b3d0e 100644 (file)
@@ -250,3 +250,17 @@ bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta)
 
        return true;
 }
+
+// equals(self, other)
+int MetaDataRef::l_equals(lua_State *L)
+{
+       MetaDataRef *ref1 = checkobject(L, 1);
+       Metadata *data1 = ref1->getmeta(false);
+       MetaDataRef *ref2 = checkobject(L, 2);
+       Metadata *data2 = ref2->getmeta(false);
+       if (data1 == NULL || data2 == NULL)
+               lua_pushboolean(L, data1 == data2);
+       else
+               lua_pushboolean(L, *data1 == *data2);
+       return 1;
+}
index be31d95ad8b9aa88ebea146bbd79e60f181b4485..a4d8214d323ba7889bf5541c0ae5d058c43be8ed 100644 (file)
@@ -67,6 +67,9 @@ protected:
 
        // from_table(self, table)
        static int l_from_table(lua_State *L);
+
+       // equals(self, other)
+       static int l_equals(lua_State *L);
 };
 
 #endif /* L_NODEMETA_H_ */
index 55d11fc13354a986ee471bbf9364acd098ead6f0..c65d56f14800f50564202ab027f04e822b504796 100644 (file)
@@ -204,6 +204,10 @@ void NodeMetaRef::RegisterCommon(lua_State *L)
        lua_pushcfunction(L, gc_object);
        lua_settable(L, metatable);
 
+       lua_pushliteral(L, "__eq");
+       lua_pushcfunction(L, l_equals);
+       lua_settable(L, metatable);
+
        lua_pop(L, 1);  // drop metatable
 }
 
@@ -225,6 +229,7 @@ const luaL_Reg NodeMetaRef::methodsServer[] = {
        luamethod(MetaDataRef, to_table),
        luamethod(MetaDataRef, from_table),
        luamethod(NodeMetaRef, get_inventory),
+       luamethod(MetaDataRef, equals),
        {0,0}
 };
 
index f9d2754e77303d4cd0fd61e0722fe8d55905cd83..0699705cbfab5afee746a5949ef2977ef72391b2 100644 (file)
@@ -737,7 +737,7 @@ int ObjectRef::l_set_properties(lua_State *L)
        ObjectProperties *prop = co->accessObjectProperties();
        if (!prop)
                return 0;
-       read_object_properties(L, 2, prop);
+       read_object_properties(L, 2, prop, getServer(L)->idef());
        co->notifyObjectPropertiesModified();
        return 0;
 }
index 59906dda5804b3c6d575ba1bd6b31220ba9b8b1f..4c6b2a1827df56ea5a3cbeab1b218b008c9cd9df 100644 (file)
@@ -98,6 +98,10 @@ void StorageRef::Register(lua_State *L)
        lua_pushcfunction(L, gc_object);
        lua_settable(L, metatable);
 
+       lua_pushliteral(L, "__eq");
+       lua_pushcfunction(L, l_equals);
+       lua_settable(L, metatable);
+
        lua_pop(L, 1);  // drop metatable
 
        luaL_openlib(L, 0, methods, 0);  // fill methodtable
@@ -138,5 +142,6 @@ const luaL_Reg StorageRef::methods[] = {
        luamethod(MetaDataRef, set_float),
        luamethod(MetaDataRef, to_table),
        luamethod(MetaDataRef, from_table),
+       luamethod(MetaDataRef, equals),
        {0,0}
 };
index 089a67f338a8fad3b920d41229e5db9c0b4201d3..40af0be5f345616881c4ac81adbdbd5056cea633 100644 (file)
@@ -318,11 +318,15 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
                u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
                m_material_type = shdrsrc->getShaderInfo(shader_id).material;
        }
+
+       // Color-related
        m_colors.clear();
+       video::SColor basecolor = idef->getItemstackColor(item, client);
 
        // If wield_image is defined, it overrides everything else
        if (def.wield_image != "") {
                setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
+               m_colors.push_back(basecolor);
                return;
        }
        // Handle nodes
@@ -371,7 +375,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
                        } else {
                                material.setTexture(0, tile->texture);
                        }
-                       m_colors.push_back(tile->color);
+                       m_colors.push_back(tile->has_color ? tile->color : basecolor);
                        material.MaterialType = m_material_type;
                        if (m_enable_shaders) {
                                if (tile->normal_texture) {
@@ -389,6 +393,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
        }
        else if (def.inventory_image != "") {
                setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
+               m_colors.push_back(basecolor);
                return;
        }
 
@@ -455,7 +460,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
        m_meshnode->setVisible(true);
 }
 
-scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
+void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
 {
        ITextureSource *tsrc = client->getTextureSource();
        IItemDefManager *idef = client->getItemDefManager();
@@ -475,12 +480,13 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
        // If inventory_image is defined, it overrides everything else
        if (def.inventory_image != "") {
                mesh = getExtrudedMesh(tsrc, def.inventory_image);
-               return mesh;
+               result->mesh = mesh;
+               result->buffer_colors.push_back(
+                       std::pair<bool, video::SColor>(false, video::SColor(0xFFFFFFFF)));
        } else if (def.type == ITEM_NODE) {
                if (f.mesh_ptr[0]) {
                        mesh = cloneMesh(f.mesh_ptr[0]);
                        scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
-                       setMeshColor(mesh, video::SColor (255, 255, 255, 255));
                } else if (f.drawtype == NDT_PLANTLIKE) {
                        mesh = getExtrudedMesh(tsrc,
                                tsrc->getTextureName(f.tiles[0].texture_id));
@@ -515,6 +521,8 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
                for (u32 i = 0; i < mc; ++i) {
                        const TileSpec *tile = &(f.tiles[i]);
                        scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+                       result->buffer_colors.push_back(
+                               std::pair<bool, video::SColor>(tile->has_color, tile->color));
                        colorizeMeshBuffer(buf, &tile->color);
                        video::SMaterial &material = buf->getMaterial();
                        material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
@@ -532,9 +540,8 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
 
                rotateMeshXZby(mesh, -45);
                rotateMeshYZby(mesh, -30);
-               return mesh;
+               result->mesh = mesh;
        }
-       return NULL;
 }
 
 scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
index 200587058cad9315ccac19b09b14fc37acd96f19..94edb1de6e23124d4d91c179bb4bbf46d4e1f7dc 100644 (file)
@@ -28,6 +28,22 @@ class Client;
 class ITextureSource;
 struct TileSpec;
 
+struct ItemMesh
+{
+       scene::IMesh* mesh;
+       /*!
+        * Stores the color of each mesh buffer.
+        * If the boolean is true, the color is fixed, else
+        * palettes can modify it.
+        */
+       std::vector<std::pair<bool, video::SColor> > buffer_colors;
+
+       ItemMesh():
+               mesh(NULL),
+               buffer_colors()
+       {}
+};
+
 /*
        Wield item scene node, renders the wield mesh of some item
 */
@@ -79,7 +95,7 @@ private:
        aabb3f m_bounding_box;
 };
 
-scene::IMesh *getItemMesh(Client *client, const ItemStack &item);
+void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
 
 scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
 #endif