mirror of
https://github.com/minetest/minetest.git
synced 2024-08-18 20:44:16 +02:00
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:
parent
d4e9dd4643
commit
58d83a7bb2
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 ¶ms);
|
||||
|
||||
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()
|
||||
{
|
||||
/*
|
||||
|
@ -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 ¶ms)=0;
|
||||
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
|
||||
|
@ -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;
|
||||
|
20
src/hud.cpp
20
src/hud.cpp
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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}
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user