Hardware coloring for itemstacks

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.
This commit is contained in:
Dániel Juhász 2017-03-10 18:25:58 +01:00 committed by Auke Kok
parent d4e9dd4643
commit 58d83a7bb2
23 changed files with 308 additions and 139 deletions

View 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

View 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,

View 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;

View 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()
{
/*

View 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;

View 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;

View 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;

View 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()

View 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

View 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);
}

View 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

View 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();

View 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");
}
/******************************************************************************/

View 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);

View 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);
}

View 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}
};

View 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;
}

View 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_ */

View 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}
};

View 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;
}

View 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}
};

View 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,

View 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