mirror of
https://github.com/Relintai/texture_packer.git
synced 2024-11-12 10:15:16 +01:00
Added TextureLayerMerger.
This commit is contained in:
parent
3597ba1e3d
commit
e865e55db0
25
README.md
25
README.md
@ -67,3 +67,28 @@ Useful for textures you only need for baking, as this class will not register it
|
||||
The module also contains an editor plugin which can import textures as `PackerImageResource` Resource.
|
||||
|
||||
To access it, click on a texture, switch to the import tab, and in the "Import As" Dropdown, select "Packer Image Recource".
|
||||
|
||||
## TextureLayerMerger
|
||||
|
||||
This class can merge together textures as layers. Useful for example to merge together (and color) skin, and clothes for a character.
|
||||
|
||||
It can handle both AtlasTextures and normal Textures.
|
||||
|
||||
Add the layers from bottom to top with the add_texture() method, when you added everything call merge().
|
||||
|
||||
You can set the resulting image's size with the `width`, and `height` properties. If you leave them at 0, they will
|
||||
change to the first added texture's size.
|
||||
|
||||
add_texture looks like this:
|
||||
|
||||
```
|
||||
void add_texture(Ref<Texture> p_texture, Color p_color = Color(1, 1, 1, 1), Vector2 p_position = Vector2(), Rect2 p_rect = Rect2());
|
||||
```
|
||||
|
||||
The color parameter will color the given texture on merge().
|
||||
With the position parameter you can offset your texture (in the resulted texture), and with the rect parameter, you can crop it.
|
||||
|
||||
There are setters to manipulate the added data later.
|
||||
|
||||
After the merge, you can either use `get_result_as_texture()` (it creates an ImageTexture on the fly), or the `data` property to
|
||||
grab the resulting Image.
|
||||
|
2
SCsub
2
SCsub
@ -12,3 +12,5 @@ env.add_source_files(env.modules_sources,"texture_resource/packer_image_resource
|
||||
env.add_source_files(env.modules_sources,"texture_resource/editor_plugin_packer_image_resource.cpp")
|
||||
env.add_source_files(env.modules_sources,"texture_resource/packer_image_resource_importer.cpp")
|
||||
|
||||
env.add_source_files(env.modules_sources,"layers/texture_layer_merger.cpp")
|
||||
|
||||
|
27
doc_classes/TextureLayerMerger.xml
Normal file
27
doc_classes/TextureLayerMerger.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="TextureLayerMerger" inherits="Reference" category="Addon" version="3.2">
|
||||
<brief_description>
|
||||
A class, that can merge textures as layers together.
|
||||
</brief_description>
|
||||
A class, that can merge textures as layers together.
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="add_texture">
|
||||
<return type="AtlasTexture">
|
||||
</return>
|
||||
<argument index="0" name="texture" type="Texture">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="texture_flags" type="int" setter="set_texture_flags" getter="get_texture_flags" default="5">
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
348
layers/texture_layer_merger.cpp
Normal file
348
layers/texture_layer_merger.cpp
Normal file
@ -0,0 +1,348 @@
|
||||
#include "texture_layer_merger.h"
|
||||
|
||||
int TextureLayerMerger::get_width() const {
|
||||
return _width;
|
||||
}
|
||||
|
||||
void TextureLayerMerger::set_width(const int p_value) {
|
||||
_width = p_value;
|
||||
}
|
||||
|
||||
int TextureLayerMerger::get_height() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
void TextureLayerMerger::set_height(const int p_value) {
|
||||
_height = p_value;
|
||||
}
|
||||
|
||||
uint32_t TextureLayerMerger::get_texture_flags() const {
|
||||
return _texture_flags;
|
||||
}
|
||||
void TextureLayerMerger::set_texture_flags(uint32_t p_flags) {
|
||||
_texture_flags = p_flags;
|
||||
}
|
||||
|
||||
Color TextureLayerMerger::get_base_color() const {
|
||||
return _base_color;
|
||||
}
|
||||
void TextureLayerMerger::set_base_color(const Color p_color) {
|
||||
_base_color = p_color;
|
||||
}
|
||||
|
||||
Ref<Image> TextureLayerMerger::get_data() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
void TextureLayerMerger::set_data(const Ref<Image> &p_image) {
|
||||
ERR_FAIL_COND(p_image.is_null());
|
||||
|
||||
_image = p_image;
|
||||
}
|
||||
|
||||
Ref<ImageTexture> TextureLayerMerger::get_result_as_texture() const {
|
||||
ERR_FAIL_COND_V(!_image.is_valid(), Ref<ImageTexture>());
|
||||
|
||||
Ref<ImageTexture> tex;
|
||||
tex.instance();
|
||||
tex->create_from_image(_image);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
void TextureLayerMerger::add_texture(Ref<Texture> p_texture, Color p_color, Vector2 p_position, Rect2 p_rect) {
|
||||
ERR_FAIL_COND(!p_texture.is_valid());
|
||||
|
||||
LayerMergerEntry entry;
|
||||
|
||||
entry.texture = p_texture;
|
||||
entry.color = p_color;
|
||||
entry.position = p_position;
|
||||
|
||||
if (p_rect.size.x <= 0)
|
||||
p_rect.size.x = p_texture->get_width();
|
||||
|
||||
if (p_rect.size.y <= 0)
|
||||
p_rect.size.y = p_texture->get_height();
|
||||
|
||||
entry.rect = p_rect;
|
||||
|
||||
if (_width == 0)
|
||||
_width = p_texture->get_width();
|
||||
|
||||
if (_height == 0)
|
||||
_height = p_texture->get_height();
|
||||
|
||||
_entries.push_back(entry);
|
||||
}
|
||||
|
||||
Ref<Texture> TextureLayerMerger::get_texture(const int p_index) {
|
||||
ERR_FAIL_INDEX_V(p_index, _entries.size(), Ref<Texture>());
|
||||
|
||||
return _entries.get(p_index).texture;
|
||||
}
|
||||
void TextureLayerMerger::set_texture(const int p_index, Ref<Texture> p_texture) {
|
||||
ERR_FAIL_INDEX(p_index, _entries.size());
|
||||
|
||||
_entries.get(p_index).texture = p_texture;
|
||||
}
|
||||
|
||||
Color TextureLayerMerger::get_color(const int p_index) {
|
||||
ERR_FAIL_INDEX_V(p_index, _entries.size(), Color());
|
||||
|
||||
return _entries.get(p_index).color;
|
||||
}
|
||||
void TextureLayerMerger::set_color(const int p_index, Color p_color) {
|
||||
ERR_FAIL_INDEX(p_index, _entries.size());
|
||||
|
||||
_entries.get(p_index).color = p_color;
|
||||
}
|
||||
|
||||
Vector2 TextureLayerMerger::get_position(const int p_index) {
|
||||
ERR_FAIL_INDEX_V(p_index, _entries.size(), Vector2());
|
||||
|
||||
return _entries.get(p_index).position;
|
||||
}
|
||||
void TextureLayerMerger::set_position(const int p_index, Vector2 p_position) {
|
||||
ERR_FAIL_INDEX(p_index, _entries.size());
|
||||
|
||||
_entries.get(p_index).position = p_position;
|
||||
}
|
||||
|
||||
Rect2 TextureLayerMerger::get_rect(const int p_index) {
|
||||
ERR_FAIL_INDEX_V(p_index, _entries.size(), Rect2());
|
||||
|
||||
return _entries.get(p_index).rect;
|
||||
}
|
||||
void TextureLayerMerger::set_rect(const int p_index, Rect2 p_rect) {
|
||||
ERR_FAIL_INDEX(p_index, _entries.size());
|
||||
|
||||
_entries.get(p_index).rect = p_rect;
|
||||
}
|
||||
|
||||
void TextureLayerMerger::remove_texture(const int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, _entries.size());
|
||||
|
||||
return _entries.remove(p_index);
|
||||
}
|
||||
|
||||
int TextureLayerMerger::get_texture_count() {
|
||||
return _entries.size();
|
||||
}
|
||||
|
||||
void TextureLayerMerger::clear() {
|
||||
_entries.clear();
|
||||
}
|
||||
|
||||
void TextureLayerMerger::merge() {
|
||||
ERR_FAIL_COND(_width <= 0 || _height <= 0);
|
||||
|
||||
if (!_image.is_valid())
|
||||
_image.instance();
|
||||
|
||||
PoolVector<uint8_t> data;
|
||||
data.resize(_width * _height * 4);
|
||||
|
||||
write_base_color_to_array(data);
|
||||
|
||||
for (int i = 0; i < _entries.size(); ++i) {
|
||||
LayerMergerEntry &e = _entries.get(i);
|
||||
|
||||
ERR_CONTINUE(!e.texture.is_valid());
|
||||
|
||||
int rx = e.rect.position.x + 0.1;
|
||||
int ry = e.rect.position.y + 0.1;
|
||||
int rw = e.rect.size.x + 0.1;
|
||||
int rh = e.rect.size.y + 0.1;
|
||||
|
||||
int posx = e.position.x + 0.1;
|
||||
int posy = e.position.y + 0.1;
|
||||
|
||||
int atlas_x = 0;
|
||||
int atlas_y = 0;
|
||||
|
||||
if (posx > _width || posy > _height)
|
||||
continue;
|
||||
|
||||
Ref<AtlasTexture> altas_texture = e.texture;
|
||||
Ref<Image> input_image;
|
||||
|
||||
if (altas_texture.is_valid()) {
|
||||
Ref<Texture> atlas = altas_texture->get_atlas();
|
||||
|
||||
ERR_CONTINUE(!atlas.is_valid());
|
||||
|
||||
input_image = atlas->get_data();
|
||||
|
||||
Rect2 region = altas_texture->get_region();
|
||||
|
||||
atlas_x = region.position.x + 0.1;
|
||||
atlas_y = region.position.y + 0.1;
|
||||
|
||||
int atlas_w = region.size.x + 0.1;
|
||||
int atlas_h = region.size.y + 0.1;
|
||||
|
||||
if (rw > atlas_w)
|
||||
rw = atlas_w;
|
||||
|
||||
if (rh > atlas_h)
|
||||
rh = atlas_h;
|
||||
} else {
|
||||
input_image = e.texture->get_data();
|
||||
}
|
||||
|
||||
ERR_CONTINUE(!input_image.is_valid());
|
||||
|
||||
int iiw = input_image->get_width();
|
||||
int iih = input_image->get_height();
|
||||
PoolVector<uint8_t> input_image_data = input_image->get_data();
|
||||
|
||||
Color &blendcolor = e.color;
|
||||
|
||||
float blend_arr[] = {
|
||||
blendcolor.r,
|
||||
blendcolor.g,
|
||||
blendcolor.b,
|
||||
blendcolor.a
|
||||
};
|
||||
|
||||
ERR_CONTINUE(iiw == 0 || iih == 0);
|
||||
ERR_CONTINUE(rx > iiw || ry > iih);
|
||||
|
||||
//clamp width, and height if bigger (for ease of use)
|
||||
if (atlas_x + rx + rw >= iiw)
|
||||
rw -= (atlas_x + rx + rw) - iiw;
|
||||
|
||||
if (atlas_y + ry + rh >= iih)
|
||||
rh -= (atlas_y + ry + rh) - iih;
|
||||
|
||||
//Let's take position into account
|
||||
if (rx + rw + posx >= _width)
|
||||
rw -= (rx + rw + posx) - _width;
|
||||
|
||||
if (ry + rh + posy >= _height)
|
||||
rh -= (ry + rh + posy) - _height;
|
||||
|
||||
if (rw <= 0 || rh <= 0)
|
||||
continue;
|
||||
|
||||
int elen = 0;
|
||||
|
||||
if (input_image->get_format() == Image::FORMAT_RGBA8) {
|
||||
elen = 4;
|
||||
} else if (input_image->get_format() == Image::FORMAT_RGB8) {
|
||||
elen = 3;
|
||||
}
|
||||
|
||||
ERR_CONTINUE_MSG(elen == 0, "Unsupported image format! Format: " + String::num(input_image->get_format()));
|
||||
|
||||
for (int y = 0; y < rh; ++y) {
|
||||
int img_gen_index = (posx + ((y + posy) * _width)) * 4;
|
||||
int img_input_index = (rx + atlas_x + ((y + atlas_y + ry) * iiw)) * elen;
|
||||
|
||||
for (int x = 0; x < rw; ++x) {
|
||||
float blend_alpha = blendcolor.a;
|
||||
|
||||
if (elen == 4) {
|
||||
float orig_alpha = input_image_data.get(img_input_index + 3) / 255.0;
|
||||
|
||||
blend_alpha -= 1.0 - orig_alpha;
|
||||
|
||||
if (blend_alpha < 0)
|
||||
blend_alpha = 0;
|
||||
|
||||
blend_alpha = orig_alpha;
|
||||
}
|
||||
|
||||
for (int sp = 0; sp < elen; ++sp) {
|
||||
int main_index = img_gen_index + sp;
|
||||
|
||||
int main_val = data.get(main_index);
|
||||
int input_val = input_image_data.get(img_input_index + sp);
|
||||
|
||||
main_val = (input_val * blend_arr[sp]) * blend_alpha + (main_val * (1 - blend_alpha));
|
||||
|
||||
if (main_val > 255)
|
||||
main_val = 255;
|
||||
|
||||
data.set(main_index, main_val);
|
||||
}
|
||||
|
||||
img_gen_index += 4;
|
||||
img_input_index += elen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_image->create(_width, _height, (_texture_flags & Texture::FLAG_MIPMAPS) != 0, Image::FORMAT_RGBA8, data);
|
||||
}
|
||||
|
||||
void TextureLayerMerger::write_base_color_to_array(PoolVector<uint8_t> &data) {
|
||||
int cr = _base_color.r * 255;
|
||||
int cg = _base_color.g * 255;
|
||||
int cb = _base_color.b * 255;
|
||||
int ca = _base_color.a * 255;
|
||||
|
||||
for (int i = 0; i < data.size(); i += 4) {
|
||||
data.set(i, cr);
|
||||
data.set(i + 1, cg);
|
||||
data.set(i + 2, cb);
|
||||
data.set(i + 3, ca);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureLayerMerger::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_width"), &TextureLayerMerger::get_width);
|
||||
ClassDB::bind_method(D_METHOD("set_width", "image"), &TextureLayerMerger::set_width);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "width"), "set_width", "get_width");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_height"), &TextureLayerMerger::get_height);
|
||||
ClassDB::bind_method(D_METHOD("set_height", "image"), &TextureLayerMerger::set_height);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_texture_flags"), &TextureLayerMerger::get_texture_flags);
|
||||
ClassDB::bind_method(D_METHOD("set_texture_flags", "image"), &TextureLayerMerger::set_texture_flags);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_texture_flags", "get_texture_flags");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_base_color"), &TextureLayerMerger::get_base_color);
|
||||
ClassDB::bind_method(D_METHOD("set_base_color", "color"), &TextureLayerMerger::set_base_color);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "base_color"), "set_base_color", "get_base_color");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_data"), &TextureLayerMerger::get_data);
|
||||
ClassDB::bind_method(D_METHOD("set_data", "image"), &TextureLayerMerger::set_data);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_data", "get_data");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_result_as_texture"), &TextureLayerMerger::get_result_as_texture);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_texture", "texture", "color", "position", "rect"), &TextureLayerMerger::add_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Vector2()), DEFVAL(Rect2()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_texture", "index"), &TextureLayerMerger::get_texture);
|
||||
ClassDB::bind_method(D_METHOD("set_texture", "index", "texture"), &TextureLayerMerger::set_texture);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_color", "index"), &TextureLayerMerger::get_color);
|
||||
ClassDB::bind_method(D_METHOD("set_color", "index", "color"), &TextureLayerMerger::set_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_position", "index"), &TextureLayerMerger::get_position);
|
||||
ClassDB::bind_method(D_METHOD("set_position", "index", "position"), &TextureLayerMerger::set_position);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_rect", "index"), &TextureLayerMerger::get_rect);
|
||||
ClassDB::bind_method(D_METHOD("set_rect", "index", "rect"), &TextureLayerMerger::set_rect);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("remove_texture", "index"), &TextureLayerMerger::remove_texture);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_count"), &TextureLayerMerger::get_texture_count);
|
||||
ClassDB::bind_method(D_METHOD("clear"), &TextureLayerMerger::clear);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("merge"), &TextureLayerMerger::merge);
|
||||
}
|
||||
|
||||
TextureLayerMerger::TextureLayerMerger() {
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
|
||||
_texture_flags = 0;
|
||||
}
|
||||
|
||||
TextureLayerMerger::~TextureLayerMerger() {
|
||||
_image.unref();
|
||||
_entries.clear();
|
||||
}
|
81
layers/texture_layer_merger.h
Normal file
81
layers/texture_layer_merger.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef TEXTURE_LAYER_MERGER_H
|
||||
#define TEXTURE_LAYER_MERGER_H
|
||||
|
||||
#include "core/reference.h"
|
||||
|
||||
#include "core/math/rect2.h"
|
||||
#include "core/vector.h"
|
||||
#include "core/image.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
class TextureLayerMerger : public Reference {
|
||||
|
||||
GDCLASS(TextureLayerMerger, Reference);
|
||||
|
||||
public:
|
||||
int get_width() const;
|
||||
void set_width(const int p_value);
|
||||
|
||||
int get_height() const;
|
||||
void set_height(const int p_value);
|
||||
|
||||
uint32_t get_texture_flags() const;
|
||||
void set_texture_flags(uint32_t p_flags);
|
||||
|
||||
Color get_base_color() const;
|
||||
void set_base_color(const Color p_color);
|
||||
|
||||
Ref<Image> get_data() const;
|
||||
void set_data(const Ref<Image> &p_image);
|
||||
|
||||
Ref<ImageTexture> get_result_as_texture() const;
|
||||
|
||||
void add_texture(Ref<Texture> p_texture, Color p_color = Color(1, 1, 1, 1), Vector2 p_position = Vector2(), Rect2 p_rect = Rect2());
|
||||
|
||||
Ref<Texture> get_texture(const int p_index);
|
||||
void set_texture(const int p_index, Ref<Texture> p_texture);
|
||||
|
||||
Color get_color(const int p_index);
|
||||
void set_color(const int p_index, Color p_color);
|
||||
|
||||
Vector2 get_position(const int p_index);
|
||||
void set_position(const int p_index, Vector2 p_position);
|
||||
|
||||
Rect2 get_rect(const int p_index);
|
||||
void set_rect(const int p_index, Rect2 p_rect);
|
||||
|
||||
void remove_texture(const int p_index);
|
||||
int get_texture_count();
|
||||
void clear();
|
||||
|
||||
void merge();
|
||||
|
||||
TextureLayerMerger();
|
||||
~TextureLayerMerger();
|
||||
|
||||
protected:
|
||||
void write_base_color_to_array(PoolVector<uint8_t> &data);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
struct LayerMergerEntry {
|
||||
Ref<Texture> texture;
|
||||
Color color;
|
||||
Rect2 rect;
|
||||
Vector2 position;
|
||||
};
|
||||
|
||||
private:
|
||||
int _width;
|
||||
int _height;
|
||||
|
||||
uint32_t _texture_flags;
|
||||
|
||||
Color _base_color;
|
||||
|
||||
Ref<Image> _image;
|
||||
|
||||
Vector<LayerMergerEntry> _entries;
|
||||
};
|
||||
|
||||
#endif
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "texture_resource/packer_image_resource.h"
|
||||
|
||||
#include "layers/texture_layer_merger.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_plugin.h"
|
||||
|
||||
@ -17,6 +19,8 @@ void register_texture_packer_types() {
|
||||
|
||||
ClassDB::register_class<PackerImageResource>();
|
||||
|
||||
ClassDB::register_class<TextureLayerMerger>();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorPlugins::add_by_type<EditorPluginPackerImageResource>();
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user