mirror of
https://github.com/luanti-org/luanti.git
synced 2025-10-28 14:15:18 +01:00
Add inventory image animation API (#16538)
This commit is contained in:
@@ -52,6 +52,7 @@ core.features = {
|
|||||||
particlespawner_exclude_player = true,
|
particlespawner_exclude_player = true,
|
||||||
generate_decorations_biomes = true,
|
generate_decorations_biomes = true,
|
||||||
chunksize_vector = true,
|
chunksize_vector = true,
|
||||||
|
item_inventory_image_animation = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function core.has_feature(arg)
|
function core.has_feature(arg)
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ core.protocol_versions = {
|
|||||||
["5.12.0"] = 48,
|
["5.12.0"] = 48,
|
||||||
["5.13.0"] = 49,
|
["5.13.0"] = 49,
|
||||||
["5.14.0"] = 50,
|
["5.14.0"] = 50,
|
||||||
|
["5.15.0"] = 51,
|
||||||
}
|
}
|
||||||
|
|
||||||
setmetatable(core.protocol_versions, {__newindex = function()
|
setmetatable(core.protocol_versions, {__newindex = function()
|
||||||
|
|||||||
@@ -2708,10 +2708,10 @@ Some of the values in the key-value store are handled specially:
|
|||||||
See also: `get_description` in [`ItemStack`](#itemstack)
|
See also: `get_description` in [`ItemStack`](#itemstack)
|
||||||
* `short_description`: Set the item stack's short description.
|
* `short_description`: Set the item stack's short description.
|
||||||
See also: `get_short_description` in [`ItemStack`](#itemstack)
|
See also: `get_short_description` in [`ItemStack`](#itemstack)
|
||||||
* `inventory_image`: Override inventory_image
|
* `inventory_image`: Override inventory_image.name
|
||||||
* `inventory_overlay`: Override inventory_overlay
|
* `inventory_overlay`: Override inventory_overlay.name
|
||||||
* `wield_image`: Override wield_image
|
* `wield_image`: Override wield_image.name
|
||||||
* `wield_overlay`: Override wield_overlay
|
* `wield_overlay`: Override wield_overlay.name
|
||||||
* `wield_scale`: Override wield_scale, use vector.to_string
|
* `wield_scale`: Override wield_scale, use vector.to_string
|
||||||
* `color`: A `ColorString`, which sets the stack's color.
|
* `color`: A `ColorString`, which sets the stack's color.
|
||||||
* `palette_index`: If the item has a palette, this is used to get the
|
* `palette_index`: If the item has a palette, this is used to get the
|
||||||
@@ -5854,6 +5854,9 @@ Utilities
|
|||||||
generate_decorations_biomes = true,
|
generate_decorations_biomes = true,
|
||||||
-- 'chunksize' mapgen setting can be a vector, instead of a single number (5.15.0)
|
-- 'chunksize' mapgen setting can be a vector, instead of a single number (5.15.0)
|
||||||
chunksize_vector = true,
|
chunksize_vector = true,
|
||||||
|
-- Item definition fields `inventory_image`, `inventory_overlay`, `wield_image`
|
||||||
|
-- and `wield_overlay` accept a table containing animation definitions. (5.16.0)
|
||||||
|
item_image_animation = true,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -9518,6 +9521,7 @@ Player properties need to be saved manually.
|
|||||||
-- `core.itemstring_with_palette()`), the entity will inherit the color.
|
-- `core.itemstring_with_palette()`), the entity will inherit the color.
|
||||||
-- Wielditems are scaled a bit. If you want a wielditem to appear
|
-- Wielditems are scaled a bit. If you want a wielditem to appear
|
||||||
-- to be as large as a node, use `0.667` in `visual_size`
|
-- to be as large as a node, use `0.667` in `visual_size`
|
||||||
|
-- Currently, item image animations are not played. This may change in the future.
|
||||||
-- "item" is similar to "wielditem" but ignores the 'wield_image' parameter.
|
-- "item" is similar to "wielditem" but ignores the 'wield_image' parameter.
|
||||||
-- "node" looks exactly like a node in-world (supported since 5.12.0)
|
-- "node" looks exactly like a node in-world (supported since 5.12.0)
|
||||||
-- Note that visual effects like waving or liquid reflections will not work.
|
-- Note that visual effects like waving or liquid reflections will not work.
|
||||||
@@ -9858,6 +9862,13 @@ Tile animation definition
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Item image definition
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* `"image.png"`
|
||||||
|
* `{name="image.png", animation={Tile Animation definition}}`
|
||||||
|
* Basically a tile definition but for items
|
||||||
|
|
||||||
Item definition
|
Item definition
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@@ -9884,18 +9895,18 @@ Used by `core.register_node`, `core.register_craftitem`, and
|
|||||||
-- {bendy = 2, snappy = 1},
|
-- {bendy = 2, snappy = 1},
|
||||||
-- {hard = 1, metal = 1, spikes = 1}
|
-- {hard = 1, metal = 1, spikes = 1}
|
||||||
|
|
||||||
inventory_image = "",
|
inventory_image = <Item image definition>,
|
||||||
-- Texture shown in the inventory GUI
|
-- Image shown in the inventory GUI
|
||||||
-- Defaults to a 3D rendering of the node if left empty.
|
-- Defaults to a 3D rendering of the node if left empty.
|
||||||
|
|
||||||
inventory_overlay = "",
|
inventory_overlay = <Item image definition>,
|
||||||
-- An overlay texture which is not affected by colorization
|
-- An overlay image which is not affected by colorization
|
||||||
|
|
||||||
wield_image = "",
|
wield_image = <Item image definition>,
|
||||||
-- Texture shown when item is held in hand
|
-- Image shown when item is held in hand
|
||||||
-- Defaults to a 3D rendering of the node if left empty.
|
-- Defaults to a 3D rendering of the node if left empty.
|
||||||
|
|
||||||
wield_overlay = "",
|
wield_overlay = <Item image definition>,
|
||||||
-- Like inventory_overlay but only used in the same situation as wield_image
|
-- Like inventory_overlay but only used in the same situation as wield_image
|
||||||
|
|
||||||
wield_scale = {x = 1, y = 1, z = 1},
|
wield_scale = {x = 1, y = 1, z = 1},
|
||||||
|
|||||||
@@ -177,3 +177,59 @@ core.register_craftitem("testitems:tree_spawner_vmanip", {
|
|||||||
core.register_on_leaveplayer(function(player, timed_out)
|
core.register_on_leaveplayer(function(player, timed_out)
|
||||||
vmanip_for_trees[player:get_player_name()] = nil
|
vmanip_for_trees[player:get_player_name()] = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Animation test items
|
||||||
|
|
||||||
|
local animated_image = {
|
||||||
|
name = "testnodes_anim.png^[invert:rgb",
|
||||||
|
animation = {
|
||||||
|
type = "vertical_frames",
|
||||||
|
aspect_w = 16,
|
||||||
|
aspect_h = 16,
|
||||||
|
length = 4.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local animated_overlay = {
|
||||||
|
name = "testitems_animation_overlay.png",
|
||||||
|
animation = {
|
||||||
|
type = "sheet_2d",
|
||||||
|
frames_w = 1,
|
||||||
|
frames_h = 4,
|
||||||
|
frame_length = 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.register_craftitem("testitems:inventory_image_animation", {
|
||||||
|
description = S("Animated Test Item").."\n"..
|
||||||
|
S("Image animate from A to D in 4s cycle"),
|
||||||
|
inventory_image = animated_image
|
||||||
|
})
|
||||||
|
|
||||||
|
core.register_craftitem("testitems:inventory_image_animation_overlay", {
|
||||||
|
description = S("Animated Test Item With Overlay").."\n"..
|
||||||
|
S("Should be colored red and have green stripes that move").."\n"..
|
||||||
|
S("Image animate from A to D in 4s cycle"),
|
||||||
|
inventory_image = animated_image,
|
||||||
|
inventory_overlay = animated_overlay,
|
||||||
|
color = "#ff0000",
|
||||||
|
})
|
||||||
|
|
||||||
|
core.register_craftitem("testitems:wield_image_animation", {
|
||||||
|
description = S("Wield Animated Test Item").."\n"..
|
||||||
|
S("Looks like the Animated Test Item, "..
|
||||||
|
"but only animated for the wield mesh."),
|
||||||
|
inventory_image = "testnodes_anim.png^[invert:rgb^[verticalframe:4:0",
|
||||||
|
wield_image = animated_image,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.register_craftitem("testitems:wield_image_animation_overlay", {
|
||||||
|
description = S("Wield Animated Test Item With Overlay").."\n"..
|
||||||
|
S("Looks like the animated Test Item With Overlay, "..
|
||||||
|
"but only animated for the wield mesh."),
|
||||||
|
inventory_image = "testnodes_anim.png^[invert:rgb^[verticalframe:4:0",
|
||||||
|
inventory_overlay = "testitems_animation_overlay.png^[verticalframe:4:0",
|
||||||
|
wield_image = animated_image,
|
||||||
|
wield_overlay = animated_overlay,
|
||||||
|
color = "#ff0000",
|
||||||
|
})
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 115 B |
@@ -10,11 +10,29 @@
|
|||||||
#include "itemdef.h"
|
#include "itemdef.h"
|
||||||
#include "inventory.h"
|
#include "inventory.h"
|
||||||
|
|
||||||
ItemVisualsManager::ItemVisuals::~ItemVisuals() {
|
struct ItemVisualsManager::ItemVisuals
|
||||||
if (wield_mesh.mesh)
|
{
|
||||||
wield_mesh.mesh->drop();
|
ItemMesh item_mesh;
|
||||||
|
Palette *palette;
|
||||||
|
|
||||||
|
AnimationInfo inventory_normal;
|
||||||
|
AnimationInfo inventory_overlay;
|
||||||
|
|
||||||
|
ItemVisuals() :
|
||||||
|
palette(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~ItemVisuals()
|
||||||
|
{
|
||||||
|
inventory_normal.freeFrames();
|
||||||
|
inventory_overlay.freeFrames();
|
||||||
|
if (item_mesh.mesh)
|
||||||
|
item_mesh.mesh->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DISABLE_CLASS_COPY(ItemVisuals);
|
||||||
|
};
|
||||||
|
|
||||||
ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const ItemStack &item,
|
ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const ItemStack &item,
|
||||||
Client *client) const
|
Client *client) const
|
||||||
{
|
{
|
||||||
@@ -24,13 +42,18 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
|
|||||||
IItemDefManager *idef = client->idef();
|
IItemDefManager *idef = client->idef();
|
||||||
|
|
||||||
const ItemDefinition &def = item.getDefinition(idef);
|
const ItemDefinition &def = item.getDefinition(idef);
|
||||||
std::string inventory_image = item.getInventoryImage(idef);
|
ItemImageDef inventory_image = item.getInventoryImage(idef);
|
||||||
std::string inventory_overlay = item.getInventoryOverlay(idef);
|
ItemImageDef inventory_overlay = item.getInventoryOverlay(idef);
|
||||||
std::string cache_key = def.name;
|
|
||||||
if (!inventory_image.empty())
|
// Key only consists of item name + image name,
|
||||||
cache_key += "/" + inventory_image;
|
// because animation currently cannot be overridden by meta
|
||||||
if (!inventory_overlay.empty())
|
std::ostringstream os(def.name);
|
||||||
cache_key += ":" + inventory_overlay;
|
if (!inventory_image.name.empty())
|
||||||
|
os << "/" << inventory_image.name;
|
||||||
|
if (!inventory_overlay.name.empty())
|
||||||
|
os << ":" << inventory_overlay.name;
|
||||||
|
std::string cache_key = os.str();
|
||||||
|
|
||||||
|
|
||||||
// Skip if already in cache
|
// Skip if already in cache
|
||||||
auto it = m_cached_item_visuals.find(cache_key);
|
auto it = m_cached_item_visuals.find(cache_key);
|
||||||
@@ -43,36 +66,105 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
|
|||||||
ITextureSource *tsrc = client->getTextureSource();
|
ITextureSource *tsrc = client->getTextureSource();
|
||||||
|
|
||||||
// Create new ItemVisuals
|
// Create new ItemVisuals
|
||||||
auto cc = std::make_unique<ItemVisuals>();
|
auto iv = std::make_unique<ItemVisuals>();
|
||||||
|
|
||||||
cc->inventory_texture = NULL;
|
auto populate_texture_and_animation = [tsrc](
|
||||||
if (!inventory_image.empty())
|
const ItemImageDef &image,
|
||||||
cc->inventory_texture = tsrc->getTexture(inventory_image);
|
AnimationInfo &animation)
|
||||||
getItemMesh(client, item, &(cc->wield_mesh));
|
{
|
||||||
|
int frame_length_ms = 0;
|
||||||
|
auto frames = std::make_unique<std::vector<FrameSpec>>();
|
||||||
|
if (image.name.empty()) {
|
||||||
|
// no-op
|
||||||
|
} else if (image.animation.type == TileAnimationType::TAT_NONE) {
|
||||||
|
frames->push_back({0, tsrc->getTexture(image.name)});
|
||||||
|
} else {
|
||||||
|
// Animated
|
||||||
|
// Get inventory texture frames
|
||||||
|
*frames = createAnimationFrames(tsrc, image.name, image.animation, frame_length_ms);
|
||||||
|
}
|
||||||
|
animation = AnimationInfo(frames.release(), frame_length_ms);
|
||||||
|
// `frames` are freed in `ItemVisuals::~ItemVisuals`
|
||||||
|
};
|
||||||
|
|
||||||
cc->palette = tsrc->getPalette(def.palette_image);
|
populate_texture_and_animation(inventory_image, iv->inventory_normal);
|
||||||
|
populate_texture_and_animation(inventory_overlay, iv->inventory_overlay);
|
||||||
|
|
||||||
|
createItemMesh(client, def,
|
||||||
|
iv->inventory_normal,
|
||||||
|
iv->inventory_overlay,
|
||||||
|
&(iv->item_mesh));
|
||||||
|
|
||||||
|
iv->palette = tsrc->getPalette(def.palette_image);
|
||||||
|
|
||||||
// Put in cache
|
// Put in cache
|
||||||
ItemVisuals *ptr = cc.get();
|
ItemVisuals *ptr = iv.get();
|
||||||
m_cached_item_visuals[cache_key] = std::move(cc);
|
m_cached_item_visuals[cache_key] = std::move(iv);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Needed because `ItemVisuals` is not known in the header.
|
||||||
|
ItemVisualsManager::ItemVisualsManager()
|
||||||
|
{
|
||||||
|
m_main_thread = std::this_thread::get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemVisualsManager::~ItemVisualsManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemVisualsManager::clear()
|
||||||
|
{
|
||||||
|
m_cached_item_visuals.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
video::ITexture *ItemVisualsManager::getInventoryTexture(const ItemStack &item,
|
video::ITexture *ItemVisualsManager::getInventoryTexture(const ItemStack &item,
|
||||||
Client *client) const
|
Client *client) const
|
||||||
{
|
{
|
||||||
ItemVisuals *iv = createItemVisuals(item, client);
|
ItemVisuals *iv = createItemVisuals(item, client);
|
||||||
if (!iv)
|
if (!iv)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return iv->inventory_texture;
|
|
||||||
|
// Texture animation update (if >1 frame)
|
||||||
|
return iv->inventory_normal.getTexture(client->getAnimationTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemMesh* ItemVisualsManager::getWieldMesh(const ItemStack &item, Client *client) const
|
video::ITexture *ItemVisualsManager::getInventoryOverlayTexture(const ItemStack &item,
|
||||||
|
Client *client) const
|
||||||
{
|
{
|
||||||
ItemVisuals *iv = createItemVisuals(item, client);
|
ItemVisuals *iv = createItemVisuals(item, client);
|
||||||
if (!iv)
|
if (!iv)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return &(iv->wield_mesh);
|
|
||||||
|
// Texture animation update (if >1 frame)
|
||||||
|
return iv->inventory_overlay.getTexture(client->getAnimationTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemMesh *ItemVisualsManager::getItemMesh(const ItemStack &item, Client *client) const
|
||||||
|
{
|
||||||
|
ItemVisuals *iv = createItemVisuals(item, client);
|
||||||
|
return iv ? &(iv->item_mesh) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationInfo *ItemVisualsManager::getInventoryAnimation(const ItemStack &item,
|
||||||
|
Client *client) const
|
||||||
|
{
|
||||||
|
ItemVisuals *iv = createItemVisuals(item, client);
|
||||||
|
if (!iv || iv->inventory_normal.getFrameCount() <= 1)
|
||||||
|
return nullptr;
|
||||||
|
return &iv->inventory_normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get item inventory overlay animation
|
||||||
|
// returns nullptr if it is not animated
|
||||||
|
AnimationInfo *ItemVisualsManager::getInventoryOverlayAnimation(const ItemStack &item,
|
||||||
|
Client *client) const
|
||||||
|
{
|
||||||
|
ItemVisuals *iv = createItemVisuals(item, client);
|
||||||
|
if (!iv || iv->inventory_overlay.getFrameCount() <= 1)
|
||||||
|
return nullptr;
|
||||||
|
return &iv->inventory_overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
Palette* ItemVisualsManager::getPalette(const ItemStack &item, Client *client) const
|
Palette* ItemVisualsManager::getPalette(const ItemStack &item, Client *client) const
|
||||||
|
|||||||
@@ -20,21 +20,29 @@ namespace video { class ITexture; }
|
|||||||
|
|
||||||
struct ItemVisualsManager
|
struct ItemVisualsManager
|
||||||
{
|
{
|
||||||
ItemVisualsManager()
|
ItemVisualsManager();
|
||||||
{
|
~ItemVisualsManager();
|
||||||
m_main_thread = std::this_thread::get_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
/// Clears the cached visuals
|
||||||
m_cached_item_visuals.clear();
|
void clear();
|
||||||
}
|
|
||||||
|
|
||||||
// Get item inventory texture
|
// Get item inventory texture
|
||||||
video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const;
|
video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const;
|
||||||
|
|
||||||
// Get item wield mesh
|
// Get item inventory overlay texture
|
||||||
|
video::ITexture* getInventoryOverlayTexture(const ItemStack &item, Client *client) const;
|
||||||
|
|
||||||
|
// Get item inventory animation
|
||||||
|
// returns nullptr if it is not animated
|
||||||
|
AnimationInfo *getInventoryAnimation(const ItemStack &item, Client *client) const;
|
||||||
|
|
||||||
|
// Get item inventory overlay animation
|
||||||
|
// returns nullptr if it is not animated
|
||||||
|
AnimationInfo *getInventoryOverlayAnimation(const ItemStack &item, Client *client) const;
|
||||||
|
|
||||||
|
// Get item mesh
|
||||||
// Once said to return nullptr if there is an inventory image, but this is wrong
|
// Once said to return nullptr if there is an inventory image, but this is wrong
|
||||||
ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const;
|
ItemMesh *getItemMesh(const ItemStack &item, Client *client) const;
|
||||||
|
|
||||||
// Get item palette
|
// Get item palette
|
||||||
Palette* getPalette(const ItemStack &item, Client *client) const;
|
Palette* getPalette(const ItemStack &item, Client *client) const;
|
||||||
@@ -44,21 +52,7 @@ struct ItemVisualsManager
|
|||||||
video::SColor getItemstackColor(const ItemStack &stack, Client *client) const;
|
video::SColor getItemstackColor(const ItemStack &stack, Client *client) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ItemVisuals
|
struct ItemVisuals;
|
||||||
{
|
|
||||||
video::ITexture *inventory_texture;
|
|
||||||
ItemMesh wield_mesh;
|
|
||||||
Palette *palette;
|
|
||||||
|
|
||||||
ItemVisuals():
|
|
||||||
inventory_texture(nullptr),
|
|
||||||
palette(nullptr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~ItemVisuals();
|
|
||||||
|
|
||||||
DISABLE_CLASS_COPY(ItemVisuals);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The id of the thread that is allowed to use irrlicht directly
|
// The id of the thread that is allowed to use irrlicht directly
|
||||||
std::thread::id m_main_thread;
|
std::thread::id m_main_thread;
|
||||||
|
|||||||
@@ -5,17 +5,26 @@
|
|||||||
#include "tile.h"
|
#include "tile.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
video::ITexture *AnimationInfo::getTexture(float animation_time)
|
||||||
|
{
|
||||||
|
if (getFrameCount() == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Figure out current frame
|
||||||
|
u16 frame = (u32)(animation_time * 1000.0f / std::max<u16>(1, m_frame_length_ms))
|
||||||
|
% m_frame_count;
|
||||||
|
|
||||||
|
assert(frame < m_frames->size());
|
||||||
|
return (*m_frames)[frame].texture;
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationInfo::updateTexture(video::SMaterial &material, float animation_time)
|
void AnimationInfo::updateTexture(video::SMaterial &material, float animation_time)
|
||||||
{
|
{
|
||||||
// Figure out current frame
|
video::ITexture *texture = getTexture(animation_time);
|
||||||
u16 frame = (u16)(animation_time * 1000 / m_frame_length_ms) % m_frame_count;
|
if (texture) {
|
||||||
// Only adjust if frame changed
|
material.setTexture(0, texture);
|
||||||
if (frame != m_frame) {
|
}
|
||||||
m_frame = frame;
|
|
||||||
assert(m_frame < m_frames->size());
|
|
||||||
material.setTexture(0, (*m_frames)[m_frame].texture);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
void TileLayer::applyMaterialOptions(video::SMaterial &material, int layer) const
|
void TileLayer::applyMaterialOptions(video::SMaterial &material, int layer) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -158,14 +158,34 @@ struct AnimationInfo {
|
|||||||
m_frames(tile.frames)
|
m_frames(tile.frames)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
AnimationInfo(std::vector<FrameSpec> *frames, u16 frame_length_ms) :
|
||||||
|
m_frame_length_ms(frame_length_ms),
|
||||||
|
m_frame_count(frames->size()),
|
||||||
|
m_frames(frames)
|
||||||
|
{};
|
||||||
|
|
||||||
|
void freeFrames()
|
||||||
|
{
|
||||||
|
delete m_frames;
|
||||||
|
m_frames = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getFrameCount() const
|
||||||
|
{
|
||||||
|
return m_frames ? m_frame_count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
void updateTexture(video::SMaterial &material, float animation_time);
|
void updateTexture(video::SMaterial &material, float animation_time);
|
||||||
|
|
||||||
|
// Returns nullptr if texture did not change since last time
|
||||||
|
video::ITexture *getTexture(float animation_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u16 m_frame = 0; // last animation frame
|
|
||||||
u16 m_frame_length_ms = 0;
|
u16 m_frame_length_ms = 0;
|
||||||
u16 m_frame_count = 1;
|
u16 m_frame_count = 1;
|
||||||
|
|
||||||
/// @note not owned by this struct
|
/// @note by default not owned by this struct
|
||||||
|
/// TODO. Change this to a shared pointer.
|
||||||
std::vector<FrameSpec> *m_frames = nullptr;
|
std::vector<FrameSpec> *m_frames = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -301,17 +301,8 @@ void WieldMeshSceneNode::setExtruded(const TileDef &d0, const TileLayer &l0,
|
|||||||
extractTexture(d1, l1, tsrc), wield_scale);
|
extractTexture(d1, l1, tsrc), wield_scale);
|
||||||
// Add color
|
// Add color
|
||||||
m_buffer_info.clear();
|
m_buffer_info.clear();
|
||||||
m_buffer_info.emplace_back(0, l0.has_color, l0.color);
|
m_buffer_info.emplace_back(0, l0);
|
||||||
m_buffer_info.emplace_back(1, l1.has_color, l1.color);
|
m_buffer_info.emplace_back(1, l1);
|
||||||
}
|
|
||||||
|
|
||||||
void WieldMeshSceneNode::setExtruded(const std::string &image,
|
|
||||||
const std::string &overlay, v3f wield_scale, ITextureSource *tsrc)
|
|
||||||
{
|
|
||||||
video::ITexture *texture = tsrc->getTexture(image);
|
|
||||||
video::ITexture *overlay_texture =
|
|
||||||
overlay.empty() ? nullptr : tsrc->getTexture(overlay);
|
|
||||||
setExtruded(texture, overlay_texture, wield_scale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WieldMeshSceneNode::setExtruded(video::ITexture *texture,
|
void WieldMeshSceneNode::setExtruded(video::ITexture *texture,
|
||||||
@@ -322,11 +313,13 @@ void WieldMeshSceneNode::setExtruded(video::ITexture *texture,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get mesh from cache
|
||||||
core::dimension2d<u32> dim = texture->getSize();
|
core::dimension2d<u32> dim = texture->getSize();
|
||||||
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
||||||
scene::SMesh *mesh = cloneStaticMesh(original);
|
scene::SMesh *mesh = cloneStaticMesh(original);
|
||||||
original->drop();
|
original->drop();
|
||||||
|
|
||||||
|
// Set texture
|
||||||
mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture);
|
mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture);
|
||||||
if (overlay_texture) {
|
if (overlay_texture) {
|
||||||
// duplicate the extruded mesh for the overlay
|
// duplicate the extruded mesh for the overlay
|
||||||
@@ -416,6 +409,38 @@ static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<FrameSpec> createAnimationFrames(ITextureSource *tsrc,
|
||||||
|
const std::string &image_name, const TileAnimationParams &animation,
|
||||||
|
int &result_frame_length_ms)
|
||||||
|
{
|
||||||
|
result_frame_length_ms = 0;
|
||||||
|
|
||||||
|
if (image_name.empty() || animation.type == TileAnimationType::TAT_NONE)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
video::ITexture *orginal_texture = tsrc->getTexture(image_name);
|
||||||
|
if (!orginal_texture)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int frame_count = 1;
|
||||||
|
auto orginal_size = orginal_texture->getOriginalSize();
|
||||||
|
animation.determineParams(orginal_size, &frame_count, &result_frame_length_ms, nullptr);
|
||||||
|
|
||||||
|
std::vector<FrameSpec> frames(frame_count);
|
||||||
|
std::ostringstream os(std::ios::binary);
|
||||||
|
for (int i = 0; i < frame_count; i++) {
|
||||||
|
os.str("");
|
||||||
|
os << image_name;
|
||||||
|
animation.getTextureModifer(os, orginal_size, i);
|
||||||
|
|
||||||
|
u32 id;
|
||||||
|
frames[i].texture = tsrc->getTextureForMesh(os.str(), &id);
|
||||||
|
frames[i].texture_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool check_wield_image)
|
void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool check_wield_image)
|
||||||
{
|
{
|
||||||
ITextureSource *tsrc = client->getTextureSource();
|
ITextureSource *tsrc = client->getTextureSource();
|
||||||
@@ -438,16 +463,46 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||||||
m_buffer_info.clear();
|
m_buffer_info.clear();
|
||||||
m_base_color = item_visuals->getItemstackColor(item, client);
|
m_base_color = item_visuals->getItemstackColor(item, client);
|
||||||
|
|
||||||
const std::string wield_image = item.getWieldImage(idef);
|
const ItemImageDef wield_image = item.getWieldImage(idef);
|
||||||
const std::string wield_overlay = item.getWieldOverlay(idef);
|
const ItemImageDef wield_overlay = item.getWieldOverlay(idef);
|
||||||
const v3f wield_scale = item.getWieldScale(idef);
|
const v3f wield_scale = item.getWieldScale(idef);
|
||||||
|
|
||||||
// If wield_image needs to be checked and is defined, it overrides everything else
|
// If wield_image needs to be checked and is defined, it overrides everything else
|
||||||
if (!wield_image.empty() && check_wield_image) {
|
if (!wield_image.name.empty() && check_wield_image) {
|
||||||
setExtruded(wield_image, wield_overlay, wield_scale, tsrc);
|
video::ITexture *wield_texture;
|
||||||
m_buffer_info.emplace_back(0);
|
video::ITexture *wield_overlay_texture = nullptr;
|
||||||
|
|
||||||
|
int frame_length_ms;
|
||||||
|
m_wield_image_frames = createAnimationFrames(tsrc,
|
||||||
|
wield_image.name, wield_image.animation, frame_length_ms);
|
||||||
|
|
||||||
|
auto &l0 = m_buffer_info.emplace_back(0);
|
||||||
|
if (m_wield_image_frames.empty()) {
|
||||||
|
wield_texture = tsrc->getTexture(wield_image.name);
|
||||||
|
} else {
|
||||||
|
wield_texture = m_wield_image_frames[0].texture;
|
||||||
|
l0.animation_info = std::make_unique<AnimationInfo>(
|
||||||
|
&m_wield_image_frames, frame_length_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overlay
|
||||||
|
if (!wield_overlay.name.empty()) {
|
||||||
|
int overlay_frame_length_ms;
|
||||||
|
m_wield_overlay_frames = createAnimationFrames(tsrc,
|
||||||
|
wield_overlay.name, wield_overlay.animation, overlay_frame_length_ms);
|
||||||
|
|
||||||
// overlay is white, if present
|
// overlay is white, if present
|
||||||
m_buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
auto &l1 = m_buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
||||||
|
if (m_wield_overlay_frames.empty()) {
|
||||||
|
wield_overlay_texture = tsrc->getTexture(wield_overlay.name);
|
||||||
|
} else {
|
||||||
|
wield_overlay_texture = m_wield_overlay_frames[0].texture;
|
||||||
|
l1.animation_info = std::make_unique<AnimationInfo>(
|
||||||
|
&m_wield_overlay_frames, overlay_frame_length_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setExtruded(wield_texture, wield_overlay_texture, wield_scale);
|
||||||
// initialize the color
|
// initialize the color
|
||||||
setColor(video::SColor(0xFFFFFFFF));
|
setColor(video::SColor(0xFFFFFFFF));
|
||||||
return;
|
return;
|
||||||
@@ -457,8 +512,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||||||
if (def.type == ITEM_NODE) {
|
if (def.type == ITEM_NODE) {
|
||||||
switch (f.drawtype) {
|
switch (f.drawtype) {
|
||||||
case NDT_AIRLIKE:
|
case NDT_AIRLIKE:
|
||||||
setExtruded("no_texture_airlike.png", "",
|
setExtruded(tsrc->getTexture("no_texture_airlike.png"), nullptr, v3f(1));
|
||||||
v3f(1), tsrc);
|
|
||||||
break;
|
break;
|
||||||
case NDT_SIGNLIKE:
|
case NDT_SIGNLIKE:
|
||||||
case NDT_TORCHLIKE:
|
case NDT_TORCHLIKE:
|
||||||
@@ -510,17 +564,19 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||||||
setColor(video::SColor(0xFFFFFFFF));
|
setColor(video::SColor(0xFFFFFFFF));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const std::string inventory_image = item.getInventoryImage(idef);
|
video::ITexture* inventory_texture = item_visuals->getInventoryTexture(item, client);
|
||||||
if (!inventory_image.empty()) {
|
if (inventory_texture) {
|
||||||
const std::string inventory_overlay = item.getInventoryOverlay(idef);
|
video::ITexture* inventory_overlay = item_visuals->getInventoryOverlayTexture(item,
|
||||||
setExtruded(inventory_image, inventory_overlay, def.wield_scale, tsrc);
|
client);
|
||||||
|
setExtruded(inventory_texture, inventory_overlay, wield_scale);
|
||||||
} else {
|
} else {
|
||||||
setExtruded("no_texture.png", "", def.wield_scale, tsrc);
|
setExtruded(tsrc->getTexture("no_texture.png"), nullptr, wield_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_buffer_info.emplace_back(0);
|
m_buffer_info.emplace_back(0, item_visuals->getInventoryAnimation(item, client));
|
||||||
// overlay is white, if present
|
// overlay is white, if present
|
||||||
m_buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
m_buffer_info.emplace_back(1, item_visuals->getInventoryOverlayAnimation(item, client),
|
||||||
|
true, video::SColor(0xFFFFFFFF));
|
||||||
|
|
||||||
// initialize the color
|
// initialize the color
|
||||||
setColor(video::SColor(0xFFFFFFFF));
|
setColor(video::SColor(0xFFFFFFFF));
|
||||||
@@ -599,12 +655,13 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
|
|||||||
m_meshnode->setVisible(true);
|
m_meshnode->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
void createItemMesh(Client *client, const ItemDefinition &def,
|
||||||
|
AnimationInfo &animation_normal,
|
||||||
|
AnimationInfo &animation_overlay,
|
||||||
|
ItemMesh *result)
|
||||||
{
|
{
|
||||||
ITextureSource *tsrc = client->getTextureSource();
|
ITextureSource *tsrc = client->getTextureSource();
|
||||||
IItemDefManager *idef = client->getItemDefManager();
|
|
||||||
const NodeDefManager *ndef = client->getNodeDefManager();
|
const NodeDefManager *ndef = client->getNodeDefManager();
|
||||||
const ItemDefinition &def = item.getDefinition(idef);
|
|
||||||
const ContentFeatures &f = ndef->get(def.name);
|
const ContentFeatures &f = ndef->get(def.name);
|
||||||
assert(result);
|
assert(result);
|
||||||
|
|
||||||
@@ -615,19 +672,26 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||||||
// Shading is on by default
|
// Shading is on by default
|
||||||
result->needs_shading = true;
|
result->needs_shading = true;
|
||||||
|
|
||||||
|
video::ITexture *inventory_texture = animation_normal.getTexture(0.0f),
|
||||||
|
*inventory_overlay_texture = animation_overlay.getTexture(0.0f);
|
||||||
|
|
||||||
// If inventory_image is defined, it overrides everything else
|
// If inventory_image is defined, it overrides everything else
|
||||||
const std::string inventory_image = item.getInventoryImage(idef);
|
if (inventory_texture) {
|
||||||
const std::string inventory_overlay = item.getInventoryOverlay(idef);
|
mesh = getExtrudedMesh(inventory_texture, inventory_overlay_texture);
|
||||||
if (!inventory_image.empty()) {
|
|
||||||
mesh = getExtrudedMesh(tsrc, inventory_image, inventory_overlay);
|
result->buffer_info.emplace_back(0, &animation_normal);
|
||||||
result->buffer_info.emplace_back(0);
|
|
||||||
// overlay is white, if present
|
// overlay is white, if present
|
||||||
result->buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
result->buffer_info.emplace_back(1, &animation_overlay,
|
||||||
|
true, video::SColor(0xFFFFFFFF));
|
||||||
|
// Items with inventory images do not need shading
|
||||||
result->needs_shading = false;
|
result->needs_shading = false;
|
||||||
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
|
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
|
||||||
// Fallback image for airlike node
|
// Fallback image for airlike node
|
||||||
mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", inventory_overlay);
|
mesh = getExtrudedMesh(tsrc->getTexture("no_texture_airlike.png"),
|
||||||
|
inventory_overlay_texture);
|
||||||
result->buffer_info.emplace_back(0);
|
result->buffer_info.emplace_back(0);
|
||||||
|
|
||||||
// overlay is white, if present
|
// overlay is white, if present
|
||||||
result->buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
result->buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
||||||
result->needs_shading = false;
|
result->needs_shading = false;
|
||||||
@@ -636,21 +700,17 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||||||
case NDT_PLANTLIKE: {
|
case NDT_PLANTLIKE: {
|
||||||
const TileLayer &l0 = f.tiles[0].layers[0];
|
const TileLayer &l0 = f.tiles[0].layers[0];
|
||||||
const TileLayer &l1 = f.tiles[0].layers[1];
|
const TileLayer &l1 = f.tiles[0].layers[1];
|
||||||
mesh = getExtrudedMesh(
|
mesh = getExtrudedMesh(l0.texture, l1.texture);
|
||||||
extractTexture(f.tiledef[0], l0, tsrc),
|
|
||||||
extractTexture(f.tiledef[1], l1, tsrc));
|
|
||||||
// Add color
|
// Add color
|
||||||
result->buffer_info.emplace_back(0, l0.has_color, l0.color);
|
result->buffer_info.emplace_back(0, l0);
|
||||||
result->buffer_info.emplace_back(1, l1.has_color, l1.color);
|
result->buffer_info.emplace_back(1, l1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NDT_PLANTLIKE_ROOTED: {
|
case NDT_PLANTLIKE_ROOTED: {
|
||||||
// Use the plant tile
|
// Use the plant tile
|
||||||
const TileLayer &l0 = f.special_tiles[0].layers[0];
|
const TileLayer &l0 = f.special_tiles[0].layers[0];
|
||||||
mesh = getExtrudedMesh(
|
mesh = getExtrudedMesh(l0.texture);
|
||||||
extractTexture(f.tiledef_special[0], l0, tsrc), nullptr
|
result->buffer_info.emplace_back(0, l0);
|
||||||
);
|
|
||||||
result->buffer_info.emplace_back(0, l0.has_color, l0.color);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -689,27 +749,19 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||||||
result->mesh = mesh;
|
result->mesh = mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
|
|
||||||
const std::string &imagename, const std::string &overlay_name)
|
|
||||||
{
|
|
||||||
video::ITexture *texture = tsrc->getTexture(imagename);
|
|
||||||
video::ITexture *overlay_texture =
|
|
||||||
overlay_name.empty() ? nullptr : tsrc->getTexture(overlay_name);
|
|
||||||
return getExtrudedMesh(texture, overlay_texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
scene::SMesh *getExtrudedMesh(video::ITexture *texture,
|
scene::SMesh *getExtrudedMesh(video::ITexture *texture,
|
||||||
video::ITexture *overlay_texture)
|
video::ITexture *overlay_texture)
|
||||||
{
|
{
|
||||||
if (!texture)
|
if (!texture)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// get mesh
|
// Get mesh
|
||||||
core::dimension2d<u32> dim = texture->getSize();
|
core::dimension2d<u32> dim = texture->getSize();
|
||||||
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
||||||
scene::SMesh *mesh = cloneStaticMesh(original);
|
scene::SMesh *mesh = cloneStaticMesh(original);
|
||||||
original->drop();
|
original->drop();
|
||||||
|
|
||||||
|
// Set texture
|
||||||
mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture);
|
mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture);
|
||||||
if (overlay_texture) {
|
if (overlay_texture) {
|
||||||
scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
|
scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ struct ItemStack;
|
|||||||
struct TileDef;
|
struct TileDef;
|
||||||
class Client;
|
class Client;
|
||||||
class ITextureSource;
|
class ITextureSource;
|
||||||
|
struct ItemDefinition;
|
||||||
|
struct TileAnimationParams;
|
||||||
class ShadowRenderer;
|
class ShadowRenderer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -56,6 +58,14 @@ public:
|
|||||||
layer(layer)
|
layer(layer)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
ItemMeshBufferInfo(int layer, const AnimationInfo *animation,
|
||||||
|
bool override_c = false, video::SColor color = {}) :
|
||||||
|
override_color(color), override_color_set(override_c), layer(layer)
|
||||||
|
{
|
||||||
|
if (animation)
|
||||||
|
animation_info = std::make_unique<AnimationInfo>(*animation);
|
||||||
|
}
|
||||||
|
|
||||||
ItemMeshBufferInfo(int layer_num, const TileLayer &layer);
|
ItemMeshBufferInfo(int layer_num, const TileLayer &layer);
|
||||||
|
|
||||||
void applyOverride(video::SColor &dest) const {
|
void applyOverride(video::SColor &dest) const {
|
||||||
@@ -109,9 +119,6 @@ public:
|
|||||||
void setExtruded(const TileDef &d0, const TileLayer &l0,
|
void setExtruded(const TileDef &d0, const TileLayer &l0,
|
||||||
const TileDef &d1, const TileLayer &l1,
|
const TileDef &d1, const TileLayer &l1,
|
||||||
v3f wield_scale, ITextureSource *tsrc);
|
v3f wield_scale, ITextureSource *tsrc);
|
||||||
// Set apperance from texture name
|
|
||||||
void setExtruded(const std::string &image, const std::string &overlay,
|
|
||||||
v3f wield_scale, ITextureSource *tsrc);
|
|
||||||
|
|
||||||
void setItem(const ItemStack &item, Client *client,
|
void setItem(const ItemStack &item, Client *client,
|
||||||
bool check_wield_image = true);
|
bool check_wield_image = true);
|
||||||
@@ -153,6 +160,11 @@ private:
|
|||||||
*/
|
*/
|
||||||
video::SColor m_base_color;
|
video::SColor m_base_color;
|
||||||
|
|
||||||
|
// Empty if wield image is empty or not animated
|
||||||
|
// Owned by this class to get AnimationInfo for the mesh buffer info
|
||||||
|
std::vector<FrameSpec> m_wield_image_frames;
|
||||||
|
std::vector<FrameSpec> m_wield_overlay_frames;
|
||||||
|
|
||||||
// Bounding box culling is disabled for this type of scene node,
|
// Bounding box culling is disabled for this type of scene node,
|
||||||
// so this variable is just required so we can implement
|
// so this variable is just required so we can implement
|
||||||
// getBoundingBox() and is set to an empty box.
|
// getBoundingBox() and is set to an empty box.
|
||||||
@@ -161,14 +173,20 @@ private:
|
|||||||
ShadowRenderer *m_shadow;
|
ShadowRenderer *m_shadow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<FrameSpec> createAnimationFrames(ITextureSource *tsrc,
|
||||||
|
const std::string &image_name, const TileAnimationParams &animation,
|
||||||
|
int& result_frame_length_ms);
|
||||||
|
|
||||||
|
scene::SMesh *getExtrudedMesh(video::ITexture *texture,
|
||||||
|
video::ITexture *overlay_texture = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: The item mesh is only suitable for inventory rendering (due to its
|
* NOTE: The item mesh is only suitable for inventory rendering (due to its
|
||||||
* material types). In-world rendering of items must go through WieldMeshSceneNode.
|
* material types). In-world rendering of items must go through WieldMeshSceneNode.
|
||||||
*/
|
*/
|
||||||
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
|
// This is only used to initially generate an ItemMesh
|
||||||
|
// To get the mesh, use ItemVisualsManager::getItemMesh(item, client) instead
|
||||||
scene::SMesh *getExtrudedMesh(video::ITexture *texture,
|
void createItemMesh(Client *client, const ItemDefinition &def,
|
||||||
video::ITexture *overlay_texture);
|
AnimationInfo &animation_normal,
|
||||||
|
AnimationInfo &animation_overlay,
|
||||||
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename,
|
ItemMesh *result);
|
||||||
const std::string &overlay_name);
|
|
||||||
|
|||||||
@@ -48,8 +48,9 @@ void drawItemStack(
|
|||||||
|
|
||||||
bool draw_overlay = false;
|
bool draw_overlay = false;
|
||||||
|
|
||||||
const std::string inventory_image = item.getInventoryImage(idef);
|
// Null if inventory_image is empty
|
||||||
const std::string inventory_overlay = item.getInventoryOverlay(idef);
|
video::ITexture *inventory_texture = item_visuals->getInventoryTexture(item, client);
|
||||||
|
video::ITexture *inventory_overlay = item_visuals->getInventoryOverlayTexture(item, client);
|
||||||
|
|
||||||
bool has_mesh = false;
|
bool has_mesh = false;
|
||||||
ItemMesh *imesh;
|
ItemMesh *imesh;
|
||||||
@@ -59,8 +60,8 @@ void drawItemStack(
|
|||||||
viewrect.clipAgainst(*clip);
|
viewrect.clipAgainst(*clip);
|
||||||
|
|
||||||
// Render as mesh if animated or no inventory image
|
// Render as mesh if animated or no inventory image
|
||||||
if ((enable_animations && rotation_kind < IT_ROT_NONE) || inventory_image.empty()) {
|
if ((enable_animations && rotation_kind < IT_ROT_NONE) || !inventory_texture) {
|
||||||
imesh = item_visuals->getWieldMesh(item, client);
|
imesh = item_visuals->getItemMesh(item, client);
|
||||||
has_mesh = imesh && imesh->mesh;
|
has_mesh = imesh && imesh->mesh;
|
||||||
}
|
}
|
||||||
if (has_mesh) {
|
if (has_mesh) {
|
||||||
@@ -152,36 +153,33 @@ void drawItemStack(
|
|||||||
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
|
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
|
||||||
driver->setViewPort(oldViewPort);
|
driver->setViewPort(oldViewPort);
|
||||||
|
|
||||||
draw_overlay = def.type == ITEM_NODE && inventory_image.empty();
|
draw_overlay = def.type == ITEM_NODE && !inventory_texture;
|
||||||
} else { // Otherwise just draw as 2D
|
} else { // Otherwise just draw as 2D
|
||||||
video::ITexture *texture = item_visuals->getInventoryTexture(item, client);
|
|
||||||
video::SColor color;
|
video::SColor color;
|
||||||
if (texture) {
|
if (inventory_texture) {
|
||||||
color = item_visuals->getItemstackColor(item, client);
|
color = item_visuals->getItemstackColor(item, client);
|
||||||
} else {
|
} else {
|
||||||
color = video::SColor(255, 255, 255, 255);
|
color = video::SColor(255, 255, 255, 255);
|
||||||
ITextureSource *tsrc = client->getTextureSource();
|
ITextureSource *tsrc = client->getTextureSource();
|
||||||
texture = tsrc->getTexture("no_texture.png");
|
inventory_texture = tsrc->getTexture("no_texture.png");
|
||||||
if (!texture)
|
if (!inventory_texture)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const video::SColor colors[] = { color, color, color, color };
|
const video::SColor colors[] = { color, color, color, color };
|
||||||
|
|
||||||
draw2DImageFilterScaled(driver, texture, rect,
|
draw2DImageFilterScaled(driver, inventory_texture, rect,
|
||||||
core::rect<s32>({0, 0}, core::dimension2di(texture->getOriginalSize())),
|
core::rect<s32>({0, 0}, core::dimension2di(inventory_texture->getOriginalSize())),
|
||||||
clip, colors, true);
|
clip, colors, true);
|
||||||
|
|
||||||
draw_overlay = true;
|
draw_overlay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw the inventory_overlay
|
// draw the inventory_overlay
|
||||||
if (!inventory_overlay.empty() && draw_overlay) {
|
if (inventory_overlay && draw_overlay) {
|
||||||
ITextureSource *tsrc = client->getTextureSource();
|
core::dimension2d<u32> dimens = inventory_overlay->getOriginalSize();
|
||||||
video::ITexture *overlay_texture = tsrc->getTexture(inventory_overlay);
|
|
||||||
core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
|
|
||||||
core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
|
core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
|
||||||
draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
|
draw2DImageFilterScaled(driver, inventory_overlay, rect, srcrect, clip, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def.type == ITEM_TOOL && item.wear != 0) {
|
if (def.type == ITEM_TOOL && item.wear != 0) {
|
||||||
|
|||||||
@@ -256,40 +256,44 @@ std::string ItemStack::getShortDescription(const IItemDefManager *itemdef) const
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ItemStack::getInventoryImage(const IItemDefManager *itemdef) const
|
ItemImageDef ItemStack::getInventoryImage(const IItemDefManager *itemdef) const
|
||||||
{
|
{
|
||||||
std::string texture = metadata.getString("inventory_image");
|
ItemImageDef image = getDefinition(itemdef).inventory_image;
|
||||||
if (texture.empty())
|
std::string meta_image = metadata.getString("inventory_image");
|
||||||
texture = getDefinition(itemdef).inventory_image;
|
if (!meta_image.empty())
|
||||||
|
image = meta_image;
|
||||||
|
|
||||||
return texture;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ItemStack::getInventoryOverlay(const IItemDefManager *itemdef) const
|
ItemImageDef ItemStack::getInventoryOverlay(const IItemDefManager *itemdef) const
|
||||||
{
|
{
|
||||||
std::string texture = metadata.getString("inventory_overlay");
|
ItemImageDef image = getDefinition(itemdef).inventory_overlay;
|
||||||
if (texture.empty())
|
std::string meta_image = metadata.getString("inventory_overlay");
|
||||||
texture = getDefinition(itemdef).inventory_overlay;
|
if (!meta_image.empty())
|
||||||
|
image = meta_image;
|
||||||
|
|
||||||
return texture;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ItemStack::getWieldImage(const IItemDefManager *itemdef) const
|
ItemImageDef ItemStack::getWieldImage(const IItemDefManager *itemdef) const
|
||||||
{
|
{
|
||||||
std::string texture = metadata.getString("wield_image");
|
ItemImageDef image = getDefinition(itemdef).wield_image;
|
||||||
if (texture.empty())
|
std::string meta_image = metadata.getString("wield_image");
|
||||||
texture = getDefinition(itemdef).wield_image;
|
if (!meta_image.empty())
|
||||||
|
image = meta_image;
|
||||||
|
|
||||||
return texture;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ItemStack::getWieldOverlay(const IItemDefManager *itemdef) const
|
ItemImageDef ItemStack::getWieldOverlay(const IItemDefManager *itemdef) const
|
||||||
{
|
{
|
||||||
std::string texture = metadata.getString("wield_overlay");
|
ItemImageDef image = getDefinition(itemdef).wield_overlay;
|
||||||
if (texture.empty())
|
std::string meta_image = metadata.getString("wield_overlay");
|
||||||
texture = getDefinition(itemdef).wield_overlay;
|
if (!meta_image.empty())
|
||||||
|
image = meta_image;
|
||||||
|
|
||||||
return texture;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
v3f ItemStack::getWieldScale(const IItemDefManager *itemdef) const
|
v3f ItemStack::getWieldScale(const IItemDefManager *itemdef) const
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ struct ItemStack
|
|||||||
std::string getDescription(const IItemDefManager *itemdef) const;
|
std::string getDescription(const IItemDefManager *itemdef) const;
|
||||||
std::string getShortDescription(const IItemDefManager *itemdef) const;
|
std::string getShortDescription(const IItemDefManager *itemdef) const;
|
||||||
|
|
||||||
std::string getInventoryImage(const IItemDefManager *itemdef) const;
|
ItemImageDef getInventoryImage(const IItemDefManager *itemdef) const;
|
||||||
std::string getInventoryOverlay(const IItemDefManager *itemdef) const;
|
ItemImageDef getInventoryOverlay(const IItemDefManager *itemdef) const;
|
||||||
std::string getWieldImage(const IItemDefManager *itemdef) const;
|
ItemImageDef getWieldImage(const IItemDefManager *itemdef) const;
|
||||||
std::string getWieldOverlay(const IItemDefManager *itemdef) const;
|
ItemImageDef getWieldOverlay(const IItemDefManager *itemdef) const;
|
||||||
v3f getWieldScale(const IItemDefManager *itemdef) const;
|
v3f getWieldScale(const IItemDefManager *itemdef) const;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -82,6 +82,27 @@ void TouchInteraction::deSerialize(std::istream &is)
|
|||||||
pointed_object = (TouchInteractionMode)tmp;
|
pointed_object = (TouchInteractionMode)tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemImageDef::serialize(std::ostream &os, u16 protocol_version) const
|
||||||
|
{
|
||||||
|
if (protocol_version < 51) {
|
||||||
|
// Use first frame if animation is not supported
|
||||||
|
std::string image_to_send = name;
|
||||||
|
animation.extractFirstFrame(image_to_send);
|
||||||
|
os << serializeString16(image_to_send);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
os << serializeString16(name);
|
||||||
|
animation.serialize(os, protocol_version);
|
||||||
|
|
||||||
|
}
|
||||||
|
void ItemImageDef::deSerialize(std::istream &is, u16 protocol_version)
|
||||||
|
{
|
||||||
|
name = deSerializeString16(is);
|
||||||
|
if (protocol_version < 51)
|
||||||
|
return;
|
||||||
|
animation.deSerialize(is, protocol_version);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ItemDefinition
|
ItemDefinition
|
||||||
*/
|
*/
|
||||||
@@ -153,10 +174,10 @@ void ItemDefinition::reset()
|
|||||||
name.clear();
|
name.clear();
|
||||||
description.clear();
|
description.clear();
|
||||||
short_description.clear();
|
short_description.clear();
|
||||||
inventory_image.clear();
|
inventory_image.reset();
|
||||||
inventory_overlay.clear();
|
inventory_overlay.reset();
|
||||||
wield_image.clear();
|
wield_image.reset();
|
||||||
wield_overlay.clear();
|
wield_overlay.reset();
|
||||||
palette_image.clear();
|
palette_image.clear();
|
||||||
color = video::SColor(0xFFFFFFFF);
|
color = video::SColor(0xFFFFFFFF);
|
||||||
wield_scale = v3f(1.0, 1.0, 1.0);
|
wield_scale = v3f(1.0, 1.0, 1.0);
|
||||||
@@ -187,8 +208,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
|
|||||||
writeU8(os, type);
|
writeU8(os, type);
|
||||||
os << serializeString16(name);
|
os << serializeString16(name);
|
||||||
os << serializeString16(description);
|
os << serializeString16(description);
|
||||||
os << serializeString16(inventory_image);
|
inventory_image.serialize(os, protocol_version);
|
||||||
os << serializeString16(wield_image);
|
wield_image.serialize(os, protocol_version);
|
||||||
writeV3F32(os, wield_scale);
|
writeV3F32(os, wield_scale);
|
||||||
writeS16(os, stack_max);
|
writeS16(os, stack_max);
|
||||||
writeU8(os, usable);
|
writeU8(os, usable);
|
||||||
@@ -217,8 +238,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
|
|||||||
writeF32(os, range);
|
writeF32(os, range);
|
||||||
os << serializeString16(palette_image);
|
os << serializeString16(palette_image);
|
||||||
writeARGB8(os, color);
|
writeARGB8(os, color);
|
||||||
os << serializeString16(inventory_overlay);
|
inventory_overlay.serialize(os, protocol_version);
|
||||||
os << serializeString16(wield_overlay);
|
wield_overlay.serialize(os, protocol_version);
|
||||||
|
|
||||||
os << serializeString16(short_description);
|
os << serializeString16(short_description);
|
||||||
|
|
||||||
@@ -273,8 +294,8 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
|
|||||||
|
|
||||||
name = deSerializeString16(is);
|
name = deSerializeString16(is);
|
||||||
description = deSerializeString16(is);
|
description = deSerializeString16(is);
|
||||||
inventory_image = deSerializeString16(is);
|
inventory_image.deSerialize(is, protocol_version);
|
||||||
wield_image = deSerializeString16(is);
|
wield_image.deSerialize(is, protocol_version);
|
||||||
wield_scale = readV3F32(is);
|
wield_scale = readV3F32(is);
|
||||||
stack_max = readS16(is);
|
stack_max = readS16(is);
|
||||||
usable = readU8(is);
|
usable = readU8(is);
|
||||||
@@ -303,8 +324,8 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
|
|||||||
range = readF32(is);
|
range = readF32(is);
|
||||||
palette_image = deSerializeString16(is);
|
palette_image = deSerializeString16(is);
|
||||||
color = readARGB8(is);
|
color = readARGB8(is);
|
||||||
inventory_overlay = deSerializeString16(is);
|
inventory_overlay .deSerialize(is, protocol_version);
|
||||||
wield_overlay = deSerializeString16(is);
|
wield_overlay.deSerialize(is, protocol_version);
|
||||||
|
|
||||||
// If you add anything here, insert it inside the try-catch
|
// If you add anything here, insert it inside the try-catch
|
||||||
// block to not need to increase the version.
|
// block to not need to increase the version.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
#include "util/pointabilities.h"
|
#include "util/pointabilities.h"
|
||||||
#include "util/pointedthing.h"
|
#include "util/pointedthing.h"
|
||||||
|
#include "tileanimation.h"
|
||||||
|
|
||||||
struct ToolCapabilities;
|
struct ToolCapabilities;
|
||||||
struct ItemDefinition;
|
struct ItemDefinition;
|
||||||
@@ -56,6 +57,28 @@ struct TouchInteraction
|
|||||||
void deSerialize(std::istream &is);
|
void deSerialize(std::istream &is);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ItemImageDef
|
||||||
|
{
|
||||||
|
// May be extended to support meshes in the future
|
||||||
|
std::string name;
|
||||||
|
TileAnimationParams animation;
|
||||||
|
|
||||||
|
ItemImageDef &operator=(const std::string &other_name)
|
||||||
|
{
|
||||||
|
this->name = other_name;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
animation.type = TileAnimationType::TAT_NONE;
|
||||||
|
name.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialize(std::ostream &os, u16 protocol_version) const;
|
||||||
|
void deSerialize(std::istream &is, u16 protocol_version);
|
||||||
|
};
|
||||||
|
|
||||||
struct ItemDefinition
|
struct ItemDefinition
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -69,10 +92,10 @@ struct ItemDefinition
|
|||||||
/*
|
/*
|
||||||
Visual properties
|
Visual properties
|
||||||
*/
|
*/
|
||||||
std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems
|
ItemImageDef inventory_image; // Optional for nodes, mandatory for tools/craftitems
|
||||||
std::string inventory_overlay; // Overlay of inventory_image.
|
ItemImageDef inventory_overlay; // Overlay of inventory_image.
|
||||||
std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used
|
ItemImageDef wield_image; // If empty, inventory_image or mesh (only nodes) is used
|
||||||
std::string wield_overlay; // Overlay of wield_image.
|
ItemImageDef wield_overlay; // Overlay of wield_image.
|
||||||
std::string palette_image; // If specified, the item will be colorized based on this
|
std::string palette_image; // If specified, the item will be colorized based on this
|
||||||
video::SColor color; // The fallback color of the node.
|
video::SColor color; // The fallback color of the node.
|
||||||
v3f wield_scale;
|
v3f wield_scale;
|
||||||
|
|||||||
@@ -71,10 +71,13 @@
|
|||||||
PROTOCOL VERSION 50
|
PROTOCOL VERSION 50
|
||||||
Support for TOCLIENT_SPAWN_PARTICLE_BATCH
|
Support for TOCLIENT_SPAWN_PARTICLE_BATCH
|
||||||
[scheduled bump for 5.14.0]
|
[scheduled bump for 5.14.0]
|
||||||
|
PROTOCOL VERSION 51
|
||||||
|
Only send first frame of animated item/wield images to older client
|
||||||
|
[scheduled bump for 5.15.0]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Note: Also update core.protocol_versions in builtin when bumping
|
// Note: Also update core.protocol_versions in builtin when bumping
|
||||||
const u16 LATEST_PROTOCOL_VERSION = 50;
|
const u16 LATEST_PROTOCOL_VERSION = 51;
|
||||||
|
|
||||||
// See also formspec [Version History] in doc/lua_api.md
|
// See also formspec [Version History] in doc/lua_api.md
|
||||||
const u16 FORMSPEC_API_VERSION = 10;
|
const u16 FORMSPEC_API_VERSION = 10;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <IMeshManipulator.h>
|
#include <IMeshManipulator.h>
|
||||||
#include <SMesh.h>
|
#include <SMesh.h>
|
||||||
#include <SkinnedMesh.h>
|
#include <SkinnedMesh.h>
|
||||||
|
#include "client/wieldmesh.h" // createAnimationFrames
|
||||||
#endif
|
#endif
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
@@ -712,34 +713,17 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
|||||||
else
|
else
|
||||||
layer->color = color;
|
layer->color = color;
|
||||||
|
|
||||||
// Animation parameters
|
// Animation
|
||||||
int frame_count = 1;
|
|
||||||
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||||
assert(layer->texture);
|
|
||||||
int frame_length_ms = 0;
|
int frame_length_ms = 0;
|
||||||
tiledef.animation.determineParams(layer->texture->getOriginalSize(),
|
std::vector<FrameSpec> frames = createAnimationFrames(
|
||||||
&frame_count, &frame_length_ms, NULL);
|
tsrc, tiledef.name, tiledef.animation, frame_length_ms);
|
||||||
layer->animation_frame_count = frame_count;
|
if (frames.size() > 1) {
|
||||||
|
layer->frames = new std::vector<FrameSpec>(frames);
|
||||||
|
layer->animation_frame_count = layer->frames->size();
|
||||||
layer->animation_frame_length_ms = frame_length_ms;
|
layer->animation_frame_length_ms = frame_length_ms;
|
||||||
}
|
|
||||||
|
|
||||||
if (frame_count == 1) {
|
|
||||||
layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
|
||||||
} else {
|
} else {
|
||||||
assert(layer->texture);
|
layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
||||||
if (!layer->frames)
|
|
||||||
layer->frames = new std::vector<FrameSpec>();
|
|
||||||
layer->frames->resize(frame_count);
|
|
||||||
|
|
||||||
std::ostringstream os(std::ios::binary);
|
|
||||||
for (int i = 0; i < frame_count; i++) {
|
|
||||||
os.str("");
|
|
||||||
os << tiledef.name;
|
|
||||||
tiledef.animation.getTextureModifer(os,
|
|
||||||
layer->texture->getOriginalSize(), i);
|
|
||||||
|
|
||||||
FrameSpec &frame = (*layer->frames)[i];
|
|
||||||
frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,10 +68,20 @@ void read_item_definition(lua_State* L, int index,
|
|||||||
|
|
||||||
getstringfield(L, index, "description", def.description);
|
getstringfield(L, index, "description", def.description);
|
||||||
getstringfield(L, index, "short_description", def.short_description);
|
getstringfield(L, index, "short_description", def.short_description);
|
||||||
getstringfield(L, index, "inventory_image", def.inventory_image);
|
|
||||||
getstringfield(L, index, "inventory_overlay", def.inventory_overlay);
|
lua_getfield(L, index, "inventory_image");
|
||||||
getstringfield(L, index, "wield_image", def.wield_image);
|
def.inventory_image = read_item_image_definition(L, -1);
|
||||||
getstringfield(L, index, "wield_overlay", def.wield_overlay);
|
lua_pop(L, 1);
|
||||||
|
lua_getfield(L, index, "inventory_overlay");
|
||||||
|
def.inventory_overlay = read_item_image_definition(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_getfield(L, index, "wield_image");
|
||||||
|
def.wield_image = read_item_image_definition(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_getfield(L, index, "wield_overlay");
|
||||||
|
def.wield_overlay = read_item_image_definition(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
getstringfield(L, index, "palette", def.palette_image);
|
getstringfield(L, index, "palette", def.palette_image);
|
||||||
|
|
||||||
// Read item color.
|
// Read item color.
|
||||||
@@ -212,13 +222,13 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
|
|||||||
}
|
}
|
||||||
lua_pushstring(L, type.c_str());
|
lua_pushstring(L, type.c_str());
|
||||||
lua_setfield(L, -2, "type");
|
lua_setfield(L, -2, "type");
|
||||||
lua_pushstring(L, i.inventory_image.c_str());
|
push_item_image_definition(L, i.inventory_image);
|
||||||
lua_setfield(L, -2, "inventory_image");
|
lua_setfield(L, -2, "inventory_image");
|
||||||
lua_pushstring(L, i.inventory_overlay.c_str());
|
push_item_image_definition(L, i.inventory_overlay);
|
||||||
lua_setfield(L, -2, "inventory_overlay");
|
lua_setfield(L, -2, "inventory_overlay");
|
||||||
lua_pushstring(L, i.wield_image.c_str());
|
push_item_image_definition(L, i.wield_image);
|
||||||
lua_setfield(L, -2, "wield_image");
|
lua_setfield(L, -2, "wield_image");
|
||||||
lua_pushstring(L, i.wield_overlay.c_str());
|
push_item_image_definition(L, i.wield_overlay);
|
||||||
lua_setfield(L, -2, "wield_overlay");
|
lua_setfield(L, -2, "wield_overlay");
|
||||||
lua_pushstring(L, i.palette_image.c_str());
|
lua_pushstring(L, i.palette_image.c_str());
|
||||||
lua_setfield(L, -2, "palette_image");
|
lua_setfield(L, -2, "palette_image");
|
||||||
@@ -1570,6 +1580,40 @@ void read_inventory_list(lua_State *L, int tableindex,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
ItemImageDef read_item_image_definition(lua_State *L, int index)
|
||||||
|
{
|
||||||
|
if(index < 0)
|
||||||
|
index = lua_gettop(L) + 1 + index;
|
||||||
|
|
||||||
|
ItemImageDef item_image;
|
||||||
|
|
||||||
|
if(lua_isstring(L, index)){
|
||||||
|
item_image.name = lua_tostring(L, index);
|
||||||
|
}
|
||||||
|
else if(lua_istable(L, index))
|
||||||
|
{
|
||||||
|
getstringfield(L, index, "name", item_image.name);
|
||||||
|
|
||||||
|
lua_getfield(L, index, "animation");
|
||||||
|
item_image.animation = read_animation_definition(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
void push_item_image_definition(lua_State *L, const ItemImageDef &item_image)
|
||||||
|
{
|
||||||
|
/* FIXME: inventory_image.animation, inventory_overlay.animation, wield_image.animation
|
||||||
|
* and wield_overlay.animation, because for nodes we don't push "tiles" (yet) and
|
||||||
|
* we don't have a push_TileAnimationParams function (yet). */
|
||||||
|
|
||||||
|
lua_pushstring(L, item_image.name.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
struct TileAnimationParams read_animation_definition(lua_State *L, int index)
|
struct TileAnimationParams read_animation_definition(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ ItemStack read_item(lua_State *L, int index, IItemDefManager *idef);
|
|||||||
|
|
||||||
struct TileAnimationParams read_animation_definition(lua_State *L, int index);
|
struct TileAnimationParams read_animation_definition(lua_State *L, int index);
|
||||||
|
|
||||||
|
ItemImageDef read_item_image_definition(lua_State *L, int index);
|
||||||
|
void push_item_image_definition(lua_State *L, const ItemImageDef &item_image);
|
||||||
|
|
||||||
PointabilityType read_pointability_type(lua_State *L, int index);
|
PointabilityType read_pointability_type(lua_State *L, int index);
|
||||||
Pointabilities read_pointabilities(lua_State *L, int index);
|
Pointabilities read_pointabilities(lua_State *L, int index);
|
||||||
void push_pointability_type(lua_State *L, PointabilityType pointable);
|
void push_pointability_type(lua_State *L, PointabilityType pointable);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Copyright (C) 2016 sfan5 <sfan5@live.de>
|
// Copyright (C) 2016 sfan5 <sfan5@live.de>
|
||||||
#include "tileanimation.h"
|
#include "tileanimation.h"
|
||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
void TileAnimationParams::serialize(std::ostream &os, u16 protocol_ver) const
|
void TileAnimationParams::serialize(std::ostream &os, u16 protocol_ver) const
|
||||||
{
|
{
|
||||||
@@ -88,6 +89,35 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileAnimationParams::extractFirstFrame(std::string &name) const
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case TAT_VERTICAL_FRAMES: {
|
||||||
|
// Can't use "[verticalframe", since the the server doesn't know the texture size.
|
||||||
|
std::ostringstream oss;
|
||||||
|
str_texture_modifiers_escape(name);
|
||||||
|
oss << "[combine:" <<
|
||||||
|
vertical_frames.aspect_w << "x" <<
|
||||||
|
vertical_frames.aspect_h <<
|
||||||
|
":0,0=" << name;
|
||||||
|
name = oss.str();
|
||||||
|
break;
|
||||||
|
} case TAT_SHEET_2D: {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << name << "^[sheet:" <<
|
||||||
|
sheet_2d.frames_w << "x" <<
|
||||||
|
sheet_2d.frames_h << ":0,0";
|
||||||
|
name = oss.str();
|
||||||
|
break;
|
||||||
|
} case TAT_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
v2f TileAnimationParams::getTextureCoords(v2u32 texture_size, int frame) const
|
v2f TileAnimationParams::getTextureCoords(v2u32 texture_size, int frame) const
|
||||||
{
|
{
|
||||||
v2u32 ret(0, 0);
|
v2u32 ret(0, 0);
|
||||||
|
|||||||
@@ -37,8 +37,13 @@ struct TileAnimationParams
|
|||||||
|
|
||||||
void serialize(std::ostream &os, u16 protocol_ver) const;
|
void serialize(std::ostream &os, u16 protocol_ver) const;
|
||||||
void deSerialize(std::istream &is, u16 protocol_ver);
|
void deSerialize(std::istream &is, u16 protocol_ver);
|
||||||
|
|
||||||
void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms,
|
void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms,
|
||||||
v2u32 *frame_size) const;
|
v2u32 *frame_size) const;
|
||||||
void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const;
|
void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const;
|
||||||
v2f getTextureCoords(v2u32 texture_size, int frame) const;
|
v2f getTextureCoords(v2u32 texture_size, int frame) const;
|
||||||
|
|
||||||
|
// Modifies the texture name such that it only contains the first frame
|
||||||
|
// If the texture_size is know (client code), getTextureModifer should be used instead
|
||||||
|
void extractFirstFrame(std::string &name) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -500,6 +500,16 @@ inline void str_formspec_escape(std::string &str)
|
|||||||
str_replace(str, "$", "\\$");
|
str_replace(str, "$", "\\$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes characters to nest texture modifiers
|
||||||
|
*/
|
||||||
|
inline void str_texture_modifiers_escape(std::string &str)
|
||||||
|
{
|
||||||
|
str_replace(str, "\\", "\\\\");
|
||||||
|
str_replace(str, "^", "\\^");
|
||||||
|
str_replace(str, ":", "\\:");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace all occurrences of the character \p from in \p str with \p to.
|
* Replace all occurrences of the character \p from in \p str with \p to.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user