├── .gitignore ├── config.py ├── SCsub ├── doc_classes ├── PackerImageResource.xml ├── TexturePacker.xml ├── TextureLayerMerger.xml └── TextureMerger.xml ├── LICENSE ├── rectpack2D ├── LICENSE ├── README.md ├── pack.h └── pack.cpp ├── register_types.h ├── editor ├── editor_plugin_packer_image_resource.cpp ├── editor_plugin_packer_image_resource.h ├── packer_image_resource_importer.h └── packer_image_resource_importer.cpp ├── register_types.cpp ├── texture_resource ├── packer_image_resource.h └── packer_image_resource.cpp ├── texture_packer.h ├── layers ├── texture_layer_merger.h └── texture_layer_merger.cpp ├── README.md ├── texture_merger.h ├── texture_merger.cpp └── texture_packer.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .import 2 | *.d 3 | *.o 4 | *.meta 5 | *.pyc 6 | *.obj 7 | *.bc 8 | *.os 9 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | def can_build(env, platform): 2 | return True 3 | 4 | 5 | def configure(env): 6 | pass 7 | 8 | 9 | def get_doc_classes(): 10 | return [ 11 | "TextureMerger", 12 | "TexturePacker", 13 | "TextureLayerMerger", 14 | "PackerImageResource", 15 | ] 16 | 17 | 18 | def get_doc_path(): 19 | return "doc_classes" 20 | -------------------------------------------------------------------------------- /SCsub: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | Import("env") 4 | 5 | module_env = env.Clone() 6 | 7 | sources = [ 8 | "register_types.cpp", 9 | "texture_packer.cpp", 10 | "rectpack2D/pack.cpp", 11 | "texture_merger.cpp", 12 | "texture_resource/packer_image_resource.cpp", 13 | "layers/texture_layer_merger.cpp", 14 | ] 15 | 16 | module_env.add_source_files(env.modules_sources, sources) 17 | 18 | if env.editor_build: 19 | module_env.add_source_files(env.modules_sources, "editor/*.cpp") 20 | -------------------------------------------------------------------------------- /doc_classes/PackerImageResource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A simple [Texture] class containing an imported [Image] without drawing logic. 5 | 6 | 7 | PackerImageResource is a Texture class designed for baking textures. It contains an imported [Image] and does not register its data in the [RenderingServer]. This makes it useful for textures that are only needed for baking. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Sets the image data for the [PackerImageResource]. 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Péter Magyar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rectpack2D/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Team Hypersomnia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /register_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef TEXTURE_PACKER_REGISTER_TYPES_H 24 | #define TEXTURE_PACKER_REGISTER_TYPES_H 25 | 26 | #include "modules/register_module_types.h" 27 | 28 | void initialize_texture_packer_module(ModuleInitializationLevel p_level); 29 | void uninitialize_texture_packer_module(ModuleInitializationLevel p_level); 30 | 31 | #endif // TEXTURE_PACKER_REGISTER_TYPES_H 32 | -------------------------------------------------------------------------------- /rectpack2D/README.md: -------------------------------------------------------------------------------- 1 | [Check out this modern re-implementation of the library.](https://github.com/TeamHypersomnia/rectpack2D/tree/master) 2 | 3 | # rectpack2D 4 | 5 | Tiny rectangle packing library allowing multiple, dynamic-sized bins. 6 | 7 | The code is ugly, I know, I was learning the language at the time. 8 | Nevertheless, it's been years since I used it for the first time in my open-source shooter [Hypersomnia][3]. 9 | No crashes so far and works quite fast. 10 | 11 | Copied from: http://gamedev.stackexchange.com/a/34193/16982 12 | 13 | [This algorithm][1] should meet every gamedeving needs. 14 | It's very, very efficient, lightweight, and I've myself improved it with searching the best possible sorting function 15 | (whether it's by area, perimeter, width, height, max(width, height)) 16 | and the best possible bin size so **you don't have to hardcode the width/height yourself anymore**. 17 | 18 | It's also easy to design it so it automatically portions out your rectangles into more bins 19 | if one with fixed maximum size is not sufficient, so you probably want to pass all your textures to it 20 | and pass a maximum texture size as the value for maximum bins' dimension, and BAM ! 21 | You have your texture atlases ready to be uploaded to GPU. Same goes for font packing. 22 | 23 | 400 random rectangles, automatically divided into 3 bins of maximum 400x400 size: 24 | ![enter image description here][2] 25 | 26 | 27 | [1]: http://www.blackpawn.com/texts/lightmaps/default.html 28 | [2]: http://i.stack.imgur.com/mOgcn.png 29 | [3]: https://github.com/TeamHypersomnia/Hypersomnia 30 | -------------------------------------------------------------------------------- /editor/editor_plugin_packer_image_resource.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "editor_plugin_packer_image_resource.h" 24 | 25 | #include "core/version.h" 26 | 27 | void EditorPluginPackerImageResource::_notification(int p_what) { 28 | switch (p_what) { 29 | case NOTIFICATION_ENTER_TREE: 30 | _importer.instantiate(); 31 | add_import_plugin(_importer); 32 | 33 | break; 34 | case NOTIFICATION_EXIT_TREE: 35 | remove_import_plugin(_importer); 36 | 37 | _importer.unref(); 38 | 39 | break; 40 | } 41 | } 42 | 43 | EditorPluginPackerImageResource::EditorPluginPackerImageResource() { 44 | } 45 | -------------------------------------------------------------------------------- /editor/editor_plugin_packer_image_resource.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef EDITOR_PLUGIN_PACKER_IMAGE_RESOURCE_H 24 | #define EDITOR_PLUGIN_PACKER_IMAGE_RESOURCE_H 25 | 26 | #include "core/string/ustring.h" 27 | #include "core/version.h" 28 | #include "editor/editor_plugin.h" 29 | 30 | #include "packer_image_resource_importer.h" 31 | 32 | class EditorPluginPackerImageResource : public EditorPlugin { 33 | GDCLASS(EditorPluginPackerImageResource, EditorPlugin); 34 | 35 | public: 36 | EditorPluginPackerImageResource(); 37 | 38 | protected: 39 | void _notification(int p_what); 40 | 41 | private: 42 | Ref _importer; 43 | }; 44 | 45 | #endif // EDITOR_PLUGIN_PACKER_IMAGE_RESOURCE_H 46 | -------------------------------------------------------------------------------- /register_types.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "register_types.h" 24 | #include "core/object/class_db.h" 25 | 26 | #include "layers/texture_layer_merger.h" 27 | #include "texture_merger.h" 28 | #include "texture_packer.h" 29 | #include "texture_resource/packer_image_resource.h" 30 | 31 | #ifdef TOOLS_ENABLED 32 | #include "editor/editor_plugin_packer_image_resource.h" 33 | #endif 34 | 35 | void initialize_texture_packer_module(ModuleInitializationLevel p_level) { 36 | if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { 37 | GDREGISTER_CLASS(TexturePacker); 38 | GDREGISTER_CLASS(TextureMerger); 39 | 40 | GDREGISTER_CLASS(PackerImageResource); 41 | 42 | GDREGISTER_CLASS(TextureLayerMerger); 43 | } 44 | 45 | #ifdef TOOLS_ENABLED 46 | if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { 47 | EditorPlugins::add_by_type(); 48 | } 49 | #endif 50 | } 51 | 52 | void uninitialize_texture_packer_module(ModuleInitializationLevel p_level) { 53 | } 54 | -------------------------------------------------------------------------------- /editor/packer_image_resource_importer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef PACKER_IMAGE_RESOURCE_H 24 | #define PACKER_IMAGE_RESOURCE_H 25 | 26 | #include "core/io/image.h" 27 | #include "core/io/image_loader.h" 28 | #include "core/io/resource_saver.h" 29 | #include "core/string/ustring.h" 30 | #include "core/version.h" 31 | 32 | #include "editor/import/editor_import_plugin.h" 33 | 34 | #include "../texture_resource/packer_image_resource.h" 35 | 36 | class PackerImageResourceImporter : public EditorImportPlugin { 37 | GDCLASS(PackerImageResourceImporter, EditorImportPlugin); 38 | 39 | public: 40 | virtual String get_importer_name() const override; 41 | virtual String get_visible_name() const override; 42 | virtual void get_recognized_extensions(List *p_extensions) const override; 43 | virtual String get_save_extension() const override; 44 | virtual String get_resource_type() const override; 45 | virtual float get_priority() const override; 46 | 47 | virtual int get_preset_count() const override; 48 | virtual String get_preset_name(int p_idx) const override; 49 | 50 | virtual void get_import_options(const String &p_path, List *r_options, int p_preset) const override; 51 | virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const override; 52 | 53 | virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files = NULL, Variant *r_metadata = NULL) override; 54 | 55 | PackerImageResourceImporter(); 56 | ~PackerImageResourceImporter(); 57 | 58 | private: 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /texture_resource/packer_image_resource.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef PACKER_IMAGE_REOURCE_H 24 | #define PACKER_IMAGE_REOURCE_H 25 | 26 | #include "core/version.h" 27 | 28 | #if VERSION_MAJOR > 3 29 | #include "core/io/image.h" 30 | #else 31 | #include "core/image.h" 32 | #endif 33 | 34 | #include "scene/resources/texture.h" 35 | 36 | class PackerImageResource : public Texture { 37 | GDCLASS(PackerImageResource, Texture); 38 | 39 | RES_BASE_EXTENSION("restex"); 40 | 41 | public: 42 | int get_width() const; 43 | int get_height() const; 44 | 45 | RID get_rid() const override; 46 | bool has_alpha() const; 47 | 48 | void set_flags(uint32_t p_flags); 49 | uint32_t get_flags() const; 50 | 51 | void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref()) const; 52 | void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref()) const; 53 | void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref &p_normal_map = Ref(), bool p_clip_uv = true) const; 54 | bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const; 55 | 56 | void set_data(const Ref &p_image); 57 | Ref get_data() const; 58 | 59 | PackerImageResource(); 60 | ~PackerImageResource(); 61 | 62 | protected: 63 | static void _bind_methods(); 64 | 65 | private: 66 | Ref _image; 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /texture_resource/packer_image_resource.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "../texture_resource/packer_image_resource.h" 24 | 25 | #include "core/version.h" 26 | 27 | int PackerImageResource::get_width() const { 28 | if (_image.is_valid()) { 29 | return _image->get_width(); 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | int PackerImageResource::get_height() const { 36 | if (_image.is_valid()) { 37 | return _image->get_height(); 38 | } 39 | 40 | return 0; 41 | } 42 | 43 | RID PackerImageResource::get_rid() const { 44 | return RID(); 45 | } 46 | bool PackerImageResource::has_alpha() const { 47 | return true; 48 | } 49 | 50 | void PackerImageResource::set_flags(uint32_t p_flags) { 51 | } 52 | uint32_t PackerImageResource::get_flags() const { 53 | return 0; 54 | } 55 | 56 | void PackerImageResource::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map) const { 57 | } 58 | void PackerImageResource::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map) const { 59 | } 60 | void PackerImageResource::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref &p_normal_map, bool p_clip_uv) const { 61 | } 62 | bool PackerImageResource::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const { 63 | return false; 64 | } 65 | 66 | void PackerImageResource::set_data(const Ref &p_image) { 67 | _image = p_image; 68 | } 69 | 70 | Ref PackerImageResource::get_data() const { 71 | return _image; 72 | } 73 | 74 | void PackerImageResource::_bind_methods() { 75 | ClassDB::bind_method(D_METHOD("set_data", "image"), &PackerImageResource::set_data); 76 | #if VERSION_MAJOR < 4 77 | //for some reason this crashes on 4.0. Not yet sure why 78 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_DEFAULT), "set_data", "get_data"); 79 | #endif 80 | } 81 | 82 | PackerImageResource::PackerImageResource() { 83 | } 84 | 85 | PackerImageResource::~PackerImageResource() { 86 | _image.unref(); 87 | } 88 | -------------------------------------------------------------------------------- /texture_packer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef TEXTURE_PACKER_H 24 | #define TEXTURE_PACKER_H 25 | 26 | #include "core/version.h" 27 | 28 | #include "core/io/image.h" 29 | #include "core/object/ref_counted.h" 30 | #include "core/templates/vector.h" 31 | #include "core/string/ustring.h" 32 | #include "core/math/color.h" 33 | 34 | #include "scene/resources/texture.h" 35 | #include 36 | 37 | #include "rectpack2D/pack.h" 38 | 39 | class TexturePacker : public RefCounted { 40 | GDCLASS(TexturePacker, RefCounted); 41 | 42 | public: 43 | int get_texture_flags() const; 44 | void set_texture_flags(const int flags); 45 | 46 | int get_max_atlas_size() const; 47 | void set_max_atlas_size(const int size); 48 | 49 | bool get_keep_original_atlases() const; 50 | void set_keep_original_atlases(const bool value); 51 | 52 | Color get_background_color() const; 53 | void set_background_color(const Color &color); 54 | 55 | int get_margin() const; 56 | void set_margin(const int margin); 57 | 58 | Ref add_texture(const Ref &texture); 59 | 60 | Ref get_texture(const Ref &texture); 61 | Ref get_texture_index(const int index); 62 | 63 | Ref get_original_texture(const int index); 64 | 65 | bool contains_texture(const Ref &texture); 66 | 67 | bool unref_texture_index(const int index); 68 | bool unref_texture(const Ref &texture); 69 | void remove_texture_index(const int index); 70 | void remove_texture(const Ref &texture); 71 | 72 | int get_texture_count(); 73 | void clear(); 74 | 75 | Ref get_generated_texture(const int index); 76 | int get_generated_texture_count(); 77 | 78 | void merge(); 79 | 80 | int get_offset_for_format(const Image::Format format); 81 | 82 | TexturePacker(); 83 | ~TexturePacker(); 84 | 85 | protected: 86 | static void _bind_methods(); 87 | 88 | private: 89 | int _texture_flags; 90 | int _max_atlas_size; 91 | bool _keep_original_atlases; 92 | Color _background_color; 93 | int _margin; 94 | 95 | Vector _rects; 96 | Vector > _generated_textures; 97 | }; 98 | 99 | #endif // TEXTURE_PACKER_H 100 | -------------------------------------------------------------------------------- /layers/texture_layer_merger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef TEXTURE_LAYER_MERGER_H 24 | #define TEXTURE_LAYER_MERGER_H 25 | 26 | #include "core/version.h" 27 | 28 | #include "core/templates/vector.h" 29 | #include "core/io/image.h" 30 | 31 | #include "core/math/rect2.h" 32 | #include "scene/resources/texture.h" 33 | 34 | #include "core/version.h" 35 | 36 | class TextureLayerMerger : public RefCounted { 37 | 38 | GDCLASS(TextureLayerMerger, RefCounted); 39 | 40 | public: 41 | int get_width() const; 42 | void set_width(const int p_value); 43 | 44 | int get_height() const; 45 | void set_height(const int p_value); 46 | 47 | uint32_t get_texture_flags() const; 48 | void set_texture_flags(const uint32_t p_flags); 49 | 50 | Color get_base_color() const; 51 | void set_base_color(const Color &p_color); 52 | 53 | Ref get_data() const; 54 | void set_data(const Ref &p_image); 55 | 56 | Ref get_result_as_texture() const; 57 | 58 | void add_texture(const Ref &p_texture, const Color &p_color = Color(1, 1, 1, 1), const Vector2 &p_position = Vector2(), Rect2 p_rect = Rect2()); 59 | 60 | Ref get_texture(const int p_index); 61 | void set_texture(const int p_index, const Ref &p_texture); 62 | 63 | Color get_color(const int p_index); 64 | void set_color(const int p_index, const Color &p_color); 65 | 66 | Vector2 get_position(const int p_index); 67 | void set_position(const int p_index, const Vector2 &p_position); 68 | 69 | Rect2 get_rect(const int p_index); 70 | void set_rect(const int p_index, const Rect2 &p_rect); 71 | 72 | void remove_texture(const int p_index); 73 | int get_texture_count(); 74 | void clear(); 75 | 76 | void merge(); 77 | 78 | TextureLayerMerger(); 79 | ~TextureLayerMerger(); 80 | 81 | protected: 82 | void write_base_color_to_array(Vector &data); 83 | 84 | static void _bind_methods(); 85 | 86 | struct LayerMergerEntry { 87 | Ref texture; 88 | Color color; 89 | Rect2 rect; 90 | Vector2 position; 91 | }; 92 | 93 | private: 94 | int _width; 95 | int _height; 96 | 97 | uint32_t _texture_flags; 98 | 99 | Color _base_color; 100 | 101 | Ref _image; 102 | 103 | Vector _entries; 104 | }; 105 | 106 | #endif // TEXTURE_LAYER_MERGER_H 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Texture Packer for the Godot Engine 2 | 3 | This is a texture packer engine module, for the Godot Engine. 4 | 5 | It can create texture atlases for you even in the running game. 6 | 7 | It uses the legacy version of [rectpack2D](https://github.com/TeamHypersomnia/rectpack2D/tree/legacy) 8 | 9 | It should work on all platforms. 10 | 11 | # Building 12 | 13 | Build Godot. [Tutorial](https://docs.godotengine.org/en/latest/development/compiling/index.html) 14 | 15 | # Features and Usage 16 | 17 | ## TexturePacker 18 | 19 | This is the class that can merge textures. Add every texture you want into it using it's API (add_texture()), and then call merge(). 20 | 21 | add_texture() will return an AtlasTexture, this is the texture you want to use in your classes. It is immediately usable, it will just contain the original texture. Calling merge() will change it, to point to the new (merged) texture. 22 | 23 | Supports filters, custom background color, margins. 24 | 25 | ### The keep_original_atlases option: 26 | 27 | If you set this to true, and then add AtlasTextures, TexturePacker will change these ones (the ones you actually added) 28 | after the bake. 29 | 30 | You can use this to bake gui textures together, without changing the resources everywhere at runtime. 31 | Think of rpgs, when you have a huge number of potential icons that the player can put on his or her actionbars. 32 | You can take look at Tales of Maj'Eyal or pretty much every actually complex MMORPGs as an example. 33 | 34 | Note: Doing something like this in only recommended, if you can't pre-make the atlases (or it's really unfeasible), you are better off 35 | making the atlases yourself during development. 36 | 37 | ## TextureMerger 38 | 39 | A Node that can bake textures for you. It uses TexturePacker internally. 40 | 41 | It has an exposed Array, so you can assign textures to it in the editor. 42 | 43 | ## PackerImageResource 44 | 45 | This is a simple Texture, which just contains an imported Image. It has no logic for drawing. 46 | 47 | Useful for textures you only need for baking, as this class will not register it's data into the VisualServer. 48 | 49 | The module also contains an editor plugin which can import textures as `PackerImageResource` Resource. 50 | 51 | To access it, click on a texture, switch to the import tab, and in the "Import As" Dropdown, select "Packer Image Recource". 52 | 53 | ## TextureLayerMerger 54 | 55 | This class can merge together textures as layers. Useful for example to merge together (and color) skin, and clothes for a character. 56 | 57 | It can handle both AtlasTextures and normal Textures. 58 | 59 | Add the layers from bottom to top with the add_texture() method, when you added everything call merge(). 60 | 61 | You can set the resulting image's size with the `width`, and `height` properties. If you leave them at 0, they will 62 | change to the first added texture's size. 63 | 64 | add_texture looks like this: 65 | 66 | ``` 67 | void add_texture(Ref p_texture, Color p_color = Color(1, 1, 1, 1), Vector2 p_position = Vector2(), Rect2 p_rect = Rect2()); 68 | ``` 69 | 70 | The color parameter will color the given texture on merge(). 71 | With the position parameter you can offset your texture (in the resulted texture), and with the rect parameter, you can crop it. 72 | 73 | There are setters to manipulate the added data later. 74 | 75 | After the merge, you can either use `get_result_as_texture()` (it creates an ImageTexture on the fly), or the `data` property to 76 | grab the resulting Image. 77 | -------------------------------------------------------------------------------- /editor/packer_image_resource_importer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "packer_image_resource_importer.h" 24 | 25 | #include "core/version.h" 26 | 27 | String PackerImageResourceImporter::get_importer_name() const { 28 | return "packer_image_resource"; 29 | } 30 | 31 | String PackerImageResourceImporter::get_visible_name() const { 32 | return "Packer Image Resource"; 33 | } 34 | 35 | void PackerImageResourceImporter::get_recognized_extensions(List *p_extensions) const { 36 | ImageLoader::get_recognized_extensions(p_extensions); 37 | } 38 | 39 | String PackerImageResourceImporter::get_save_extension() const { 40 | return "restex"; 41 | } 42 | 43 | String PackerImageResourceImporter::get_resource_type() const { 44 | return "PackerImageResource"; 45 | } 46 | 47 | float PackerImageResourceImporter::get_priority() const { 48 | return 1.0; 49 | } 50 | 51 | int PackerImageResourceImporter::get_preset_count() const { 52 | return 0; 53 | } 54 | 55 | String PackerImageResourceImporter::get_preset_name(int p_idx) const { 56 | return ""; 57 | } 58 | 59 | void PackerImageResourceImporter::get_import_options(const String &p_path, List *r_options, int p_preset) const { 60 | r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "hdr_as_srgb"), false)); 61 | r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "scale"), 1.0)); 62 | } 63 | 64 | bool PackerImageResourceImporter::get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const { 65 | return true; 66 | } 67 | 68 | Error PackerImageResourceImporter::import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) { 69 | bool hdr_as_srgb = p_options["hdr_as_srgb"]; 70 | float scale = p_options["scale"]; 71 | Ref image; 72 | image.instantiate(); 73 | Error err = ImageLoader::load_image(p_source_file, image, nullptr, hdr_as_srgb, scale); 74 | if (err != OK) { 75 | return err; 76 | } 77 | Ref res; 78 | res.instantiate(); 79 | res->set_data(image); 80 | return ResourceSaver::save(res, p_save_path + "." + get_save_extension()); 81 | } 82 | 83 | PackerImageResourceImporter::PackerImageResourceImporter() { 84 | } 85 | 86 | PackerImageResourceImporter::~PackerImageResourceImporter() { 87 | } 88 | -------------------------------------------------------------------------------- /texture_merger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef TEXTURE_MERGER_H 24 | #define TEXTURE_MERGER_H 25 | 26 | #include "core/version.h" 27 | 28 | #include "core/templates/vector.h" 29 | #include "core/config/engine.h" 30 | 31 | #include "scene/main/node.h" 32 | 33 | #include "texture_packer.h" 34 | 35 | class TextureMerger : public Node { 36 | GDCLASS(TextureMerger, Node); 37 | 38 | public: 39 | bool get_dirty() const; 40 | void set_dirty(const bool value); 41 | 42 | int get_texture_flags() const; 43 | void set_texture_flags(const int flags); 44 | 45 | int get_max_atlas_size() const; 46 | void set_max_atlas_size(const int size); 47 | 48 | bool get_keep_original_atlases() const; 49 | void set_keep_original_atlases(const bool value); 50 | 51 | Color get_background_color() const; 52 | void set_background_color(const Color &color); 53 | 54 | int get_margin() const; 55 | void set_margin(const int margin); 56 | 57 | bool get_automatic_merge() const; 58 | void set_automatic_merge(const bool value); 59 | 60 | Ref get_packer() const; 61 | void set_packer(const Ref &packer); 62 | 63 | Vector get_textures(); 64 | void set_textures(const Vector &textures); 65 | 66 | Ref add_texture(const Ref &texture); 67 | Ref get_texture(const Ref &texture); 68 | Ref get_texture_index(const int index); 69 | Ref get_original_texture(const int index); 70 | bool contains_texture(const Ref &texture); 71 | 72 | bool unref_texture_index(const int index); 73 | bool unref_texture(const Ref &texture); 74 | void remove_texture_index(const int index); 75 | void remove_texture(const Ref &texture); 76 | 77 | int get_texture_count(); 78 | void clear(); 79 | 80 | Ref get_generated_texture(const int index); 81 | int get_generated_texture_count(); 82 | 83 | void merge(); 84 | 85 | #if VERSION_MAJOR >= 4 86 | GDVIRTUAL0(_texture_merged); 87 | GDVIRTUAL1(_texture_added, Ref); 88 | GDVIRTUAL0(_texture_removed); 89 | #endif 90 | 91 | TextureMerger(); 92 | ~TextureMerger(); 93 | 94 | protected: 95 | static void _bind_methods(); 96 | void _notification(int p_what); 97 | 98 | private: 99 | bool _automatic_merge; 100 | bool _dirty; 101 | 102 | Ref _packer; 103 | Vector > _textures; 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /rectpack2D/pack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "scene/resources/texture.h" 5 | 6 | /* of your interest: 7 | 8 | 1. rect_xywhf - structure representing your rectangle object 9 | members: 10 | int x, y, w, h; 11 | bool flipped; 12 | 13 | 2. bin - structure representing resultant bin object 14 | 3. bool pack(rect_xywhf* const * v, int n, int max_side, std::vector& bins) - actual packing function 15 | Arguments: 16 | input/output: v - pointer to array of pointers to your rectangles (const here means that the pointers will point to the same rectangles after the call) 17 | input: n - rectangles count 18 | 19 | input: max_side - maximum bins' side - algorithm works with square bins (in the end it may trim them to rectangular form). 20 | for the algorithm to finish faster, pass a reasonable value (unreasonable would be passing 1 000 000 000 for packing 4 50x50 rectangles). 21 | output: bins - vector to which the function will push_back() created bins, each of them containing vector to pointers of rectangles from "v" belonging to that particular bin. 22 | Every bin also keeps information about its width and height of course, none of the dimensions is bigger than max_side. 23 | 24 | returns true on success, false if one of the rectangles' dimension was bigger than max_side 25 | 26 | You want to your rectangles representing your textures/glyph objects with GL_MAX_TEXTURE_SIZE as max_side, 27 | then for each bin iterate through its rectangles, typecast each one to your own structure (or manually add userdata) and then memcpy its pixel contents (rotated by 90 degrees if "flipped" rect_xywhf's member is true) 28 | to the array representing your texture atlas to the place specified by the rectangle, then finally upload it with glTexImage2D. 29 | 30 | Algorithm doesn't create any new rectangles. 31 | You just pass an array of pointers - rectangles' x/y/w/h/flipped are modified in place. 32 | There is a vector of pointers for every resultant bin to let you know which ones belong to the particular bin. 33 | The algorithm may swap the w and h fields for the sake of better fitting, the flag "flipped" will be set to true whenever this occurs. 34 | 35 | For description how to tune the algorithm and how it actually works see the .cpp file. 36 | 37 | 38 | */ 39 | 40 | struct rect_ltrb; 41 | struct rect_xywh; 42 | 43 | struct rect_wh { 44 | rect_wh(const rect_ltrb &); 45 | rect_wh(const rect_xywh &); 46 | rect_wh(int w = 0, int h = 0); 47 | int w, h, area(), perimeter(), 48 | fits(const rect_wh &bigger, bool allowFlip) const; // 0 - no, 1 - yes, 2 - flipped, 3 - perfectly, 4 perfectly flipped 49 | }; 50 | 51 | // rectangle implementing left/top/right/bottom behaviour 52 | 53 | struct rect_ltrb { 54 | rect_ltrb(); 55 | rect_ltrb(int left, int top, int right, int bottom); 56 | int l, t, r, b, w() const, h() const, area() const, perimeter() const; 57 | void w(int), h(int); 58 | }; 59 | 60 | struct rect_xywh : public rect_wh { 61 | rect_xywh(); 62 | rect_xywh(const rect_ltrb &); 63 | rect_xywh(int x, int y, int width, int height); 64 | operator rect_ltrb(); 65 | 66 | int x, y, r() const, b() const; 67 | void r(int), b(int); 68 | }; 69 | 70 | struct rect_xywhf : public rect_xywh { 71 | rect_xywhf(const rect_ltrb &); 72 | rect_xywhf(int x, int y, int width, int height); 73 | rect_xywhf(); 74 | void flip(); 75 | bool flipped; 76 | 77 | int refcount; 78 | 79 | Ref atlas_texture; 80 | Ref original_texture; 81 | }; 82 | 83 | struct bin { 84 | rect_wh size; 85 | std::vector rects; 86 | }; 87 | 88 | bool pack(rect_xywhf *const *v, int n, int max_side, bool allowFlip, std::vector &bins); 89 | -------------------------------------------------------------------------------- /doc_classes/TexturePacker.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A packer utility that can merge textures. 5 | 6 | 7 | TexturePacker is a utility class that allows you to merge multiple textures into a single texture atlas. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Adds a texture to the packer and returns an [AtlasTexture] representing the added texture. 17 | 18 | 19 | 20 | 21 | 22 | Clears all textures from the packer. 23 | 24 | 25 | 26 | 27 | 28 | 29 | Returns the generated ImageTexture at the given index. 30 | 31 | 32 | 33 | 34 | 35 | Returns the number of generated textures in the packer. 36 | 37 | 38 | 39 | 40 | 41 | 42 | Returns the original [Texture2D] at the given index. 43 | 44 | 45 | 46 | 47 | 48 | 49 | Returns an [AtlasTexture] representing the given Texture2D. 50 | 51 | 52 | 53 | 54 | 55 | Returns the number of textures added to the packer. 56 | 57 | 58 | 59 | 60 | 61 | 62 | Returns an [AtlasTexture] representing the texture at the given index. 63 | 64 | 65 | 66 | 67 | 68 | Merges all added textures into a single texture atlas. 69 | 70 | 71 | 72 | 73 | 74 | 75 | Removes the specified texture from the packer. 76 | 77 | 78 | 79 | 80 | 81 | 82 | Removes the texture at the specified index from the packer. 83 | 84 | 85 | 86 | 87 | 88 | 89 | Unreferences the specified texture and returns true if successful, false otherwise. 90 | 91 | 92 | 93 | 94 | 95 | 96 | Unreferences the texture at the specified index and returns true if successful, false otherwise. 97 | 98 | 99 | 100 | 101 | 102 | The background color of the texture atlas. 103 | 104 | 105 | If true, the original atlases will be kept in memory after merging. 106 | 107 | 108 | The margin between textures in the atlas. 109 | 110 | 111 | The maximum size of the texture atlas. 112 | 113 | 114 | Texture flags applied to the generated atlas. 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /doc_classes/TextureLayerMerger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A utility class that can merge textures as layers together. 5 | 6 | 7 | TextureLayerMerger is a utility class that allows you to combine multiple [Texture2D] objects into a single [ImageTexture]. It provides methods for adding, modifying, and removing textures, as well as controlling their position, color, and cropping. Once all textures are set up, the `merge` method can be called to create the final combined texture. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Adds a new texture layer with the specified texture, color, position, and optional cropping rectangle. 20 | 21 | 22 | 23 | 24 | 25 | Removes all texture layers from the merger. 26 | 27 | 28 | 29 | 30 | 31 | 32 | Returns the color of the texture layer at the specified index. 33 | 34 | 35 | 36 | 37 | 38 | 39 | Returns the position of the texture layer at the specified index. 40 | 41 | 42 | 43 | 44 | 45 | 46 | Returns the cropping rectangle of the texture layer at the specified index. 47 | 48 | 49 | 50 | 51 | 52 | Returns the merged result as an [ImageTexture]. Call `merge` before using this method to get the updated result. 53 | 54 | 55 | 56 | 57 | 58 | 59 | Returns the [Texture2D] object of the texture layer at the specified index. 60 | 61 | 62 | 63 | 64 | 65 | Returns the total number of texture layers in the merger. 66 | 67 | 68 | 69 | 70 | 71 | Merges all texture layers into a single [ImageTexture]. Call `get_result_as_texture` after this method to get the merged result. 72 | 73 | 74 | 75 | 76 | 77 | 78 | Removes the texture layer at the specified index. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | Sets the color of the texture layer at the specified index. 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | Sets the position of the texture layer at the specified index. 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Sets the cropping rectangle of the texture layer at the specified index. 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Sets the [Texture2D] object of the texture layer at the specified index. 111 | 112 | 113 | 114 | 115 | 116 | The base color used as the background for the merged texture. 117 | 118 | 119 | The [Image] data of the merged texture. 120 | 121 | 122 | The height of the merged texture. 123 | 124 | 125 | [Texture] flags applied to the merged texture. 126 | 127 | 128 | The width of the merged texture. 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /doc_classes/TextureMerger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A node that can merge textures. 5 | 6 | 7 | TextureMerger is a utility node that allows you to combine multiple [Texture] objects into a single [AtlasTexture]. It provides methods for adding, modifying, and removing textures, as well as controlling their position and cropping. Once all textures are set up, the `merge` method can be called to create the final combined texture. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Called when a new texture is added to the merger. Can be overridden in extended classes to implement custom behavior. 17 | 18 | 19 | 20 | 21 | 22 | Called when the textures are merged. Can be overridden in extended classes to implement custom behavior. 23 | 24 | 25 | 26 | 27 | 28 | Called when a texture is removed from the merger. Can be overridden in derived classes to implement custom behavior. 29 | 30 | 31 | 32 | 33 | 34 | 35 | Adds a new texture to the merger and returns the corresponding [AtlasTexture]. 36 | 37 | 38 | 39 | 40 | 41 | Removes all textures from the merger. 42 | 43 | 44 | 45 | 46 | 47 | Returns whether the merger is marked as dirty, indicating that the merged texture needs to be updated. 48 | 49 | 50 | 51 | 52 | 53 | 54 | Returns the generated [ImageTexture] at the specified index after merging. 55 | 56 | 57 | 58 | 59 | 60 | Returns the total number of generated textures in the merger. 61 | 62 | 63 | 64 | 65 | 66 | 67 | Returns the original [Texture] object at the specified index. 68 | 69 | 70 | 71 | 72 | 73 | Returns the [TexturePacker] instance used by the merger. 74 | 75 | 76 | 77 | 78 | 79 | 80 | Returns the [AtlasTexture] corresponding to the given [Texture] object. 81 | 82 | 83 | 84 | 85 | 86 | Returns the total number of textures in the merger. 87 | 88 | 89 | 90 | 91 | 92 | 93 | Returns the [AtlasTexture] at the specified index. 94 | 95 | 96 | 97 | 98 | 99 | Merges all textures into a single [AtlasTexture]. Call `get_generated_texture` after this method to get the merged result. 100 | 101 | 102 | 103 | 104 | 105 | 106 | Removes the specified texture from the merger. 107 | 108 | 109 | 110 | 111 | 112 | 113 | Removes the texture at the specified index from the merger. 114 | 115 | 116 | 117 | 118 | 119 | 120 | Sets the dirty flag for the merger, indicating that the merged texture needs to be updated. 121 | 122 | 123 | 124 | 125 | 126 | 127 | Sets the [TexturePacker] instance to be used by the merger. 128 | 129 | 130 | 131 | 132 | 133 | 134 | Decreases the reference count of the specified texture and removes it from the merger if the reference count reaches zero. Returns true if the texture was removed. 135 | 136 | 137 | 138 | 139 | 140 | 141 | Decreases the reference count of the texture at the specified index and removes it from the merger if the reference count reaches zero. Returns true if the texture was removed. 142 | 143 | 144 | 145 | 146 | 147 | If true, textures will be automatically merged when added or removed. 148 | 149 | 150 | The background color of the generated [AtlasTexture]. 151 | 152 | 153 | If true, original atlases will be kept in memory after merging. 154 | 155 | 156 | The margin between textures in the generated [AtlasTexture]. 157 | 158 | 159 | The maximum size of the generated [AtlasTexture]. 160 | 161 | 162 | [Texture] flags to apply to the generated [AtlasTexture]. 163 | 164 | 165 | Array of textures to be merged. 166 | 167 | 168 | 169 | 170 | 171 | 172 | Emitted when a new texture is added to the merger. 173 | 174 | 175 | 176 | 177 | Emitted when the textures are merged. 178 | 179 | 180 | 181 | 182 | Emitted when a texture is removed from the merger. 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /rectpack2D/pack.cpp: -------------------------------------------------------------------------------- 1 | #include "pack.h" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | bool area(rect_xywhf *a, rect_xywhf *b) { 8 | return a->area() > b->area(); 9 | } 10 | 11 | bool perimeter(rect_xywhf *a, rect_xywhf *b) { 12 | return a->perimeter() > b->perimeter(); 13 | } 14 | 15 | bool max_side(rect_xywhf *a, rect_xywhf *b) { 16 | return std::max(a->w, a->h) > std::max(b->w, b->h); 17 | } 18 | 19 | bool max_width(rect_xywhf *a, rect_xywhf *b) { 20 | return a->w > b->w; 21 | } 22 | 23 | bool max_height(rect_xywhf *a, rect_xywhf *b) { 24 | return a->h > b->h; 25 | } 26 | 27 | // just add another comparing function name to cmpf to perform another packing attempt 28 | // more functions == slower but probably more efficient cases covered and hence less area wasted 29 | 30 | bool (*cmpf[])(rect_xywhf *, rect_xywhf *) = { 31 | area, 32 | perimeter, 33 | max_side, 34 | max_width, 35 | max_height 36 | }; 37 | 38 | // if you find the algorithm running too slow you may double this factor to increase speed but also decrease efficiency 39 | // 1 == most efficient, slowest 40 | // efficiency may be still satisfying at 64 or even 256 with nice speedup 41 | 42 | int discard_step = 128; 43 | 44 | /* 45 | 46 | For every sorting function, algorithm will perform packing attempts beginning with a bin with width and height equal to max_side, 47 | and decreasing its dimensions if it finds out that rectangles did actually fit, increasing otherwise. 48 | Although, it's doing that in sort of binary search manner, so for every comparing function it will perform at most log2(max_side) packing attempts looking for the smallest possible bin size. 49 | discard_step = 128 means that the algorithm will break of the searching loop if the rectangles fit but "it may be possible to fit them in a bin smaller by 128" 50 | the bigger the value, the sooner the algorithm will finish but the rectangles will be packed less tightly. 51 | use discard_step = 1 for maximum tightness. 52 | 53 | the algorithm was based on http://www.blackpawn.com/texts/lightmaps/default.html 54 | the algorithm reuses the node tree so it doesn't reallocate them between searching attempts 55 | 56 | */ 57 | 58 | /*************************************************************************** CHAOS BEGINS HERE */ 59 | 60 | struct node { 61 | struct pnode { 62 | node *pn = nullptr; 63 | bool fill = false; 64 | 65 | void set(int l, int t, int r, int b) { 66 | if (!pn) 67 | pn = new node(rect_ltrb(l, t, r, b)); 68 | else { 69 | (*pn).rc = rect_ltrb(l, t, r, b); 70 | (*pn).id = false; 71 | } 72 | fill = true; 73 | } 74 | }; 75 | 76 | pnode c[2]; 77 | rect_ltrb rc; 78 | bool id = false; 79 | node(rect_ltrb rect_rc = rect_ltrb()) : 80 | rc(rect_rc) {} 81 | 82 | void reset(const rect_wh &r) { 83 | id = false; 84 | rc = rect_ltrb(0, 0, r.w, r.h); 85 | delcheck(); 86 | } 87 | 88 | node *insert(rect_xywhf &img, bool allowFlip) { 89 | if (c[0].pn && c[0].fill) { 90 | if (auto newn = c[0].pn->insert(img, allowFlip)) return newn; 91 | return c[1].pn->insert(img, allowFlip); 92 | } 93 | 94 | if (id) return 0; 95 | int f = img.fits(rect_xywh(rc), allowFlip); 96 | 97 | switch (f) { 98 | case 0: return 0; 99 | case 1: img.flipped = false; break; 100 | case 2: img.flipped = true; break; 101 | case 3: 102 | id = true; 103 | img.flipped = false; 104 | return this; 105 | case 4: 106 | id = true; 107 | img.flipped = true; 108 | return this; 109 | } 110 | 111 | int iw = (img.flipped ? img.h : img.w), ih = (img.flipped ? img.w : img.h); 112 | 113 | if (rc.w() - iw > rc.h() - ih) { 114 | c[0].set(rc.l, rc.t, rc.l + iw, rc.b); 115 | c[1].set(rc.l + iw, rc.t, rc.r, rc.b); 116 | } else { 117 | c[0].set(rc.l, rc.t, rc.r, rc.t + ih); 118 | c[1].set(rc.l, rc.t + ih, rc.r, rc.b); 119 | } 120 | 121 | return c[0].pn->insert(img, allowFlip); 122 | } 123 | 124 | void delcheck() { 125 | if (c[0].pn) { 126 | c[0].fill = false; 127 | c[0].pn->delcheck(); 128 | } 129 | if (c[1].pn) { 130 | c[1].fill = false; 131 | c[1].pn->delcheck(); 132 | } 133 | } 134 | 135 | ~node() { 136 | if (c[0].pn) delete c[0].pn; 137 | if (c[1].pn) delete c[1].pn; 138 | } 139 | }; 140 | 141 | rect_wh _rect2D(rect_xywhf *const *v, int n, int max_s, bool allowFlip, vector &succ, vector &unsucc) { 142 | node root; 143 | 144 | const int funcs = (sizeof(cmpf) / sizeof(bool (*)(rect_xywhf *, rect_xywhf *))); 145 | 146 | rect_xywhf **order[funcs]; 147 | 148 | for (int f = 0; f < funcs; ++f) { 149 | order[f] = new rect_xywhf *[n]; 150 | std::memcpy(order[f], v, sizeof(rect_xywhf *) * n); 151 | sort(order[f], order[f] + n, cmpf[f]); 152 | } 153 | 154 | rect_wh min_bin = rect_wh(max_s, max_s); 155 | int min_func = -1, best_func = 0, best_area = 0, _area = 0, step, fit, i; 156 | 157 | bool fail = false; 158 | 159 | for (int f = 0; f < funcs; ++f) { 160 | v = order[f]; 161 | step = min_bin.w / 2; 162 | root.reset(min_bin); 163 | 164 | while (true) { 165 | if (root.rc.w() > min_bin.w) { 166 | if (min_func > -1) break; 167 | _area = 0; 168 | 169 | root.reset(min_bin); 170 | for (i = 0; i < n; ++i) 171 | if (root.insert(*v[i], allowFlip)) 172 | _area += v[i]->area(); 173 | 174 | fail = true; 175 | break; 176 | } 177 | 178 | fit = -1; 179 | 180 | for (i = 0; i < n; ++i) 181 | if (!root.insert(*v[i], allowFlip)) { 182 | fit = 1; 183 | break; 184 | } 185 | 186 | if (fit == -1 && step <= discard_step) 187 | break; 188 | 189 | root.reset(rect_wh(root.rc.w() + fit * step, root.rc.h() + fit * step)); 190 | 191 | step /= 2; 192 | if (!step) 193 | step = 1; 194 | } 195 | 196 | if (!fail && (min_bin.area() >= root.rc.area())) { 197 | min_bin = rect_wh(root.rc); 198 | min_func = f; 199 | } 200 | 201 | else if (fail && (_area > best_area)) { 202 | best_area = _area; 203 | best_func = f; 204 | } 205 | fail = false; 206 | } 207 | 208 | v = order[min_func == -1 ? best_func : min_func]; 209 | 210 | int clip_x = 0, clip_y = 0; 211 | 212 | root.reset(min_bin); 213 | 214 | for (i = 0; i < n; ++i) { 215 | if (auto ret = root.insert(*v[i], allowFlip)) { 216 | v[i]->x = ret->rc.l; 217 | v[i]->y = ret->rc.t; 218 | 219 | if (v[i]->flipped) { 220 | v[i]->flipped = false; 221 | v[i]->flip(); 222 | } 223 | 224 | clip_x = std::max(clip_x, ret->rc.r); 225 | clip_y = std::max(clip_y, ret->rc.b); 226 | 227 | succ.push_back(v[i]); 228 | } else { 229 | unsucc.push_back(v[i]); 230 | 231 | v[i]->flipped = false; 232 | } 233 | } 234 | 235 | for (int f = 0; f < funcs; ++f) 236 | delete[] order[f]; 237 | 238 | return rect_wh(clip_x, clip_y); 239 | } 240 | 241 | bool pack(rect_xywhf *const *v, int n, int max_s, bool allowFlip, vector &bins) { 242 | rect_wh _rect(max_s, max_s); 243 | 244 | for (int i = 0; i < n; ++i) 245 | if (!v[i]->fits(_rect, allowFlip)) return false; 246 | 247 | vector vec[2], *p[2] = { vec, vec + 1 }; 248 | vec[0].resize(n); 249 | vec[1].clear(); 250 | std::memcpy(&vec[0][0], v, sizeof(rect_xywhf *) * n); 251 | 252 | bin *b = 0; 253 | 254 | while (true) { 255 | bins.push_back(bin()); 256 | b = &bins[bins.size() - 1]; 257 | 258 | b->size = _rect2D(&((*p[0])[0]), static_cast(p[0]->size()), max_s, allowFlip, b->rects, *p[1]); 259 | p[0]->clear(); 260 | 261 | if (!p[1]->size()) break; 262 | 263 | std::swap(p[0], p[1]); 264 | } 265 | 266 | return true; 267 | } 268 | 269 | rect_wh::rect_wh(const rect_ltrb &rr) : 270 | w(rr.w()), 271 | h(rr.h()) {} 272 | rect_wh::rect_wh(const rect_xywh &rr) : 273 | w(rr.w), 274 | h(rr.h) {} 275 | rect_wh::rect_wh(int p_w, int p_h) : 276 | w(p_w), 277 | h(p_h) {} 278 | 279 | int rect_wh::fits(const rect_wh &p_r, bool p_allow_flip) const { 280 | if (w == p_r.w && h == p_r.h) return 3; 281 | if (p_allow_flip && h == p_r.w && w == p_r.h) return 4; 282 | if (w <= p_r.w && h <= p_r.h) return 1; 283 | if (p_allow_flip && h <= p_r.w && w <= p_r.h) return 2; 284 | return 0; 285 | } 286 | 287 | rect_ltrb::rect_ltrb() : 288 | l(0), 289 | t(0), 290 | r(0), 291 | b(0) {} 292 | rect_ltrb::rect_ltrb(int p_l, int p_t, int p_r, int p_b) : 293 | l(p_l), 294 | t(p_t), 295 | r(p_r), 296 | b(p_b) {} 297 | 298 | int rect_ltrb::w() const { 299 | return r - l; 300 | } 301 | 302 | int rect_ltrb::h() const { 303 | return b - t; 304 | } 305 | 306 | int rect_ltrb::area() const { 307 | return w() * h(); 308 | } 309 | 310 | int rect_ltrb::perimeter() const { 311 | return 2 * w() + 2 * h(); 312 | } 313 | 314 | void rect_ltrb::w(int ww) { 315 | r = l + ww; 316 | } 317 | 318 | void rect_ltrb::h(int hh) { 319 | b = t + hh; 320 | } 321 | 322 | rect_xywh::rect_xywh() : 323 | x(0), 324 | y(0) {} 325 | rect_xywh::rect_xywh(const rect_ltrb &p_rc) : 326 | x(p_rc.l), 327 | y(p_rc.t) { 328 | b(p_rc.b); 329 | r(p_rc.r); 330 | } 331 | rect_xywh::rect_xywh(int p_x, int p_y, int p_w, int p_h) : 332 | rect_wh(p_w, p_h), 333 | x(p_x), 334 | y(p_y) {} 335 | 336 | rect_xywh::operator rect_ltrb() { 337 | rect_ltrb rr(x, y, 0, 0); 338 | rr.w(w); 339 | rr.h(h); 340 | return rr; 341 | } 342 | 343 | int rect_xywh::r() const { 344 | return x + w; 345 | }; 346 | 347 | int rect_xywh::b() const { 348 | return y + h; 349 | } 350 | 351 | void rect_xywh::r(int p_right) { 352 | w = p_right - x; 353 | } 354 | 355 | void rect_xywh::b(int p_bottom) { 356 | h = p_bottom - y; 357 | } 358 | 359 | int rect_wh::area() { 360 | return w * h; 361 | } 362 | 363 | int rect_wh::perimeter() { 364 | return 2 * w + 2 * h; 365 | } 366 | 367 | rect_xywhf::rect_xywhf(const rect_ltrb &p_rr) : 368 | rect_xywh(p_rr), 369 | flipped(false) {} 370 | rect_xywhf::rect_xywhf(int p_x, int p_y, int p_width, int p_height) : 371 | rect_xywh(p_x, p_y, p_width, p_height), 372 | flipped(false) {} 373 | rect_xywhf::rect_xywhf() : 374 | flipped(false) {} 375 | 376 | void rect_xywhf::flip() { 377 | flipped = !flipped; 378 | std::swap(w, h); 379 | } 380 | -------------------------------------------------------------------------------- /texture_merger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "texture_merger.h" 24 | 25 | bool TextureMerger::get_dirty() const { 26 | return _dirty; 27 | } 28 | void TextureMerger::set_dirty(const bool value) { 29 | _dirty = value; 30 | 31 | if (!_automatic_merge || Engine::get_singleton()->is_editor_hint()) 32 | return; 33 | 34 | if (_dirty) 35 | set_process(true); 36 | } 37 | 38 | int TextureMerger::get_texture_flags() const { 39 | return _packer->get_texture_flags(); 40 | } 41 | void TextureMerger::set_texture_flags(const int flags) { 42 | _packer->set_texture_flags(flags); 43 | } 44 | 45 | int TextureMerger::get_max_atlas_size() const { 46 | return _packer->get_max_atlas_size(); 47 | } 48 | void TextureMerger::set_max_atlas_size(const int size) { 49 | _packer->set_max_atlas_size(size); 50 | } 51 | 52 | bool TextureMerger::get_keep_original_atlases() const { 53 | return _packer->get_keep_original_atlases(); 54 | } 55 | void TextureMerger::set_keep_original_atlases(const bool value) { 56 | _packer->set_keep_original_atlases(value); 57 | } 58 | 59 | Color TextureMerger::get_background_color() const { 60 | return _packer->get_background_color(); 61 | } 62 | void TextureMerger::set_background_color(const Color &color) { 63 | _packer->set_background_color(color); 64 | } 65 | 66 | int TextureMerger::get_margin() const { 67 | return _packer->get_margin(); 68 | } 69 | void TextureMerger::set_margin(const int margin) { 70 | _packer->set_margin(margin); 71 | } 72 | 73 | bool TextureMerger::get_automatic_merge() const { 74 | return _automatic_merge; 75 | } 76 | void TextureMerger::set_automatic_merge(const bool value) { 77 | _automatic_merge = value; 78 | } 79 | 80 | Ref TextureMerger::get_packer() const { 81 | return _packer; 82 | } 83 | void TextureMerger::set_packer(const Ref &packer) { 84 | _packer = packer; 85 | } 86 | 87 | Vector TextureMerger::get_textures() { 88 | Vector r; 89 | for (int i = 0; i < _textures.size(); i++) { 90 | #if VERSION_MAJOR < 4 91 | r.push_back(_textures[i].get_ref_ptr()); 92 | #else 93 | r.push_back(_textures[i]); 94 | #endif 95 | } 96 | return r; 97 | } 98 | void TextureMerger::set_textures(const Vector &textures) { 99 | bool texture_removed = false; 100 | for (int i = 0; i < _textures.size(); ++i) { 101 | if (_packer->unref_texture(_textures.get(i))) { 102 | texture_removed = true; 103 | } 104 | } 105 | 106 | _textures.clear(); 107 | _textures.resize(textures.size()); 108 | 109 | for (int i = 0; i < textures.size(); i++) { 110 | Ref texture = Ref(textures.get(i)); 111 | 112 | _textures.set(i, texture); 113 | } 114 | 115 | if (Engine::get_singleton()->is_editor_hint()) 116 | return; 117 | 118 | bool texture_added = false; 119 | for (int i = 0; i < _textures.size(); ++i) { 120 | Ref texture = _textures.get(i); 121 | 122 | if (texture.is_valid() && !_packer->contains_texture(texture)) { 123 | Ref tex = _packer->add_texture(texture); 124 | 125 | texture_added = true; 126 | } 127 | } 128 | 129 | if ((texture_added || texture_removed) && _automatic_merge) 130 | set_dirty(true); 131 | } 132 | 133 | Ref TextureMerger::add_texture(const Ref &texture) { 134 | ERR_FAIL_COND_V(!texture.is_valid(), Ref()); 135 | 136 | _textures.push_back(texture); 137 | 138 | bool contains = _packer->contains_texture(texture); 139 | 140 | Ref tex = _packer->add_texture(texture); 141 | 142 | if (!contains) { 143 | #if VERSION_MAJOR < 4 144 | if (has_method("_texture_added")) { 145 | call("_texture_added", tex); 146 | } 147 | #else 148 | GDVIRTUAL_CALL(_texture_added, tex); 149 | #endif 150 | 151 | emit_signal("texture_added", tex); 152 | 153 | set_dirty(true); 154 | } 155 | 156 | return tex; 157 | } 158 | 159 | Ref TextureMerger::get_original_texture(const int index) { 160 | return _packer->get_original_texture(index); 161 | } 162 | 163 | bool TextureMerger::contains_texture(const Ref &texture) { 164 | return _packer->contains_texture(texture); 165 | } 166 | 167 | Ref TextureMerger::get_texture(const Ref &texture) { 168 | return _packer->get_texture(texture); 169 | } 170 | 171 | Ref TextureMerger::get_texture_index(const int index) { 172 | return _packer->get_texture_index(index); 173 | } 174 | 175 | bool TextureMerger::unref_texture_index(const int index) { 176 | if (_packer->unref_texture_index(index)) { 177 | #if VERSION_MAJOR < 4 178 | if (has_method("_texture_removed")) { 179 | call("_texture_removed"); 180 | } 181 | #else 182 | GDVIRTUAL_CALL(_texture_removed); 183 | #endif 184 | 185 | emit_signal("texture_removed"); 186 | 187 | set_dirty(true); 188 | 189 | return true; 190 | } 191 | 192 | return false; 193 | } 194 | 195 | bool TextureMerger::unref_texture(const Ref &texture) { 196 | if (_packer->unref_texture(texture)) { 197 | #if VERSION_MAJOR < 4 198 | if (has_method("_texture_removed")) { 199 | call("_texture_removed"); 200 | } 201 | #else 202 | GDVIRTUAL_CALL(_texture_removed); 203 | #endif 204 | 205 | emit_signal("texture_removed"); 206 | 207 | set_dirty(true); 208 | 209 | return true; 210 | } 211 | 212 | return false; 213 | } 214 | 215 | void TextureMerger::remove_texture_index(const int index) { 216 | _packer->remove_texture_index(index); 217 | 218 | #if VERSION_MAJOR < 4 219 | if (has_method("_texture_removed")) { 220 | call("_texture_removed"); 221 | } 222 | #else 223 | GDVIRTUAL_CALL(_texture_removed); 224 | #endif 225 | 226 | emit_signal("texture_removed"); 227 | 228 | set_dirty(true); 229 | } 230 | 231 | void TextureMerger::remove_texture(const Ref &texture) { 232 | _packer->remove_texture(texture); 233 | 234 | #if VERSION_MAJOR < 4 235 | if (has_method("_texture_removed")) { 236 | call("_texture_removed"); 237 | } 238 | #else 239 | GDVIRTUAL_CALL(_texture_removed); 240 | #endif 241 | 242 | emit_signal("texture_removed"); 243 | 244 | set_dirty(true); 245 | } 246 | 247 | int TextureMerger::get_texture_count() { 248 | return _packer->get_texture_count(); 249 | } 250 | 251 | void TextureMerger::clear() { 252 | _packer->clear(); 253 | } 254 | 255 | Ref TextureMerger::get_generated_texture(const int index) { 256 | return _packer->get_generated_texture(index); 257 | } 258 | int TextureMerger::get_generated_texture_count() { 259 | return _packer->get_generated_texture_count(); 260 | } 261 | 262 | void TextureMerger::merge() { 263 | _packer->merge(); 264 | 265 | #if VERSION_MAJOR < 4 266 | if (has_method("_texture_merged")) { 267 | call("_texture_merged"); 268 | } 269 | #else 270 | GDVIRTUAL_CALL(_texture_merged); 271 | #endif 272 | 273 | emit_signal("texture_merged"); 274 | } 275 | 276 | TextureMerger::TextureMerger() { 277 | _automatic_merge = false; 278 | 279 | #if VERSION_MAJOR < 4 280 | _packer.instantiate(); 281 | #else 282 | _packer.instantiate(); 283 | #endif 284 | 285 | _packer->set_keep_original_atlases(false); 286 | } 287 | 288 | TextureMerger::~TextureMerger() { 289 | _packer.unref(); 290 | } 291 | 292 | void TextureMerger::_notification(int p_what) { 293 | switch (p_what) { 294 | case NOTIFICATION_PROCESS: { 295 | if (!_automatic_merge || Engine::get_singleton()->is_editor_hint()) 296 | return; 297 | 298 | set_process(false); 299 | 300 | if (!_dirty) 301 | return; 302 | 303 | _dirty = false; 304 | 305 | merge(); 306 | 307 | } break; 308 | } 309 | } 310 | 311 | void TextureMerger::_bind_methods() { 312 | ADD_SIGNAL(MethodInfo("texture_merged")); 313 | ADD_SIGNAL(MethodInfo("texture_added", PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "AtlasTexture"))); 314 | ADD_SIGNAL(MethodInfo("texture_removed")); 315 | 316 | #if VERSION_MAJOR < 4 317 | //BIND_VMETHOD(MethodInfo("_texture_merged")); 318 | //BIND_VMETHOD(MethodInfo("_texture_added", PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "AtlasTexture"))); 319 | //BIND_VMETHOD(MethodInfo("_texture_removed")); 320 | #else 321 | GDVIRTUAL_BIND(_texture_merged); 322 | GDVIRTUAL_BIND(_texture_added, "texture"); 323 | GDVIRTUAL_BIND(_texture_removed); 324 | #endif 325 | 326 | ClassDB::bind_method(D_METHOD("get_dirty"), &TextureMerger::get_dirty); 327 | ClassDB::bind_method(D_METHOD("set_dirty", "value"), &TextureMerger::set_dirty); 328 | 329 | ClassDB::bind_method(D_METHOD("get_texture_flags"), &TextureMerger::get_texture_flags); 330 | ClassDB::bind_method(D_METHOD("set_texture_flags", "flags"), &TextureMerger::set_texture_flags); 331 | ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_texture_flags", "get_texture_flags"); 332 | 333 | ClassDB::bind_method(D_METHOD("get_max_atlas_size"), &TextureMerger::get_max_atlas_size); 334 | ClassDB::bind_method(D_METHOD("set_max_atlas_size", "size"), &TextureMerger::set_max_atlas_size); 335 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_atlas_size"), "set_max_atlas_size", "get_max_atlas_size"); 336 | 337 | ClassDB::bind_method(D_METHOD("get_keep_original_atlases"), &TextureMerger::get_keep_original_atlases); 338 | ClassDB::bind_method(D_METHOD("set_keep_original_atlases", "value"), &TextureMerger::set_keep_original_atlases); 339 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_original_atlases"), "set_keep_original_atlases", "get_keep_original_atlases"); 340 | 341 | ClassDB::bind_method(D_METHOD("get_background_color"), &TextureMerger::get_background_color); 342 | ClassDB::bind_method(D_METHOD("set_background_color", "color"), &TextureMerger::set_background_color); 343 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_background_color", "get_background_color"); 344 | 345 | ClassDB::bind_method(D_METHOD("get_margin"), &TextureMerger::get_margin); 346 | ClassDB::bind_method(D_METHOD("set_margin", "size"), &TextureMerger::set_margin); 347 | ADD_PROPERTY(PropertyInfo(Variant::INT, "margin"), "set_margin", "get_margin"); 348 | 349 | ClassDB::bind_method(D_METHOD("get_automatic_merge"), &TextureMerger::get_automatic_merge); 350 | ClassDB::bind_method(D_METHOD("set_automatic_merge", "value"), &TextureMerger::set_automatic_merge); 351 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "automatic_merge"), "set_automatic_merge", "get_automatic_merge"); 352 | 353 | ClassDB::bind_method(D_METHOD("get_textures"), &TextureMerger::get_textures); 354 | ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TextureMerger::set_textures); 355 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "17/17:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures"); 356 | 357 | ClassDB::bind_method(D_METHOD("get_packer"), &TextureMerger::get_packer); 358 | ClassDB::bind_method(D_METHOD("set_packer", "packer"), &TextureMerger::set_packer); 359 | 360 | ClassDB::bind_method(D_METHOD("add_texture", "texture"), &TextureMerger::add_texture); 361 | ClassDB::bind_method(D_METHOD("get_texture", "texture"), &TextureMerger::get_texture); 362 | ClassDB::bind_method(D_METHOD("get_texture_index", "index"), &TextureMerger::get_texture_index); 363 | ClassDB::bind_method(D_METHOD("get_original_texture", "index"), &TextureMerger::get_original_texture); 364 | 365 | ClassDB::bind_method(D_METHOD("unref_texture_index", "index"), &TextureMerger::unref_texture_index); 366 | ClassDB::bind_method(D_METHOD("unref_texture", "texture"), &TextureMerger::unref_texture); 367 | ClassDB::bind_method(D_METHOD("remove_texture_index", "index"), &TextureMerger::remove_texture_index); 368 | ClassDB::bind_method(D_METHOD("remove_texture", "texture"), &TextureMerger::remove_texture); 369 | 370 | ClassDB::bind_method(D_METHOD("get_texture_count"), &TextureMerger::get_texture_count); 371 | ClassDB::bind_method(D_METHOD("clear"), &TextureMerger::clear); 372 | 373 | ClassDB::bind_method(D_METHOD("get_generated_texture", "index"), &TextureMerger::get_generated_texture); 374 | ClassDB::bind_method(D_METHOD("get_generated_texture_count"), &TextureMerger::get_generated_texture_count); 375 | 376 | ClassDB::bind_method(D_METHOD("merge"), &TextureMerger::merge); 377 | } 378 | -------------------------------------------------------------------------------- /layers/texture_layer_merger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "texture_layer_merger.h" 24 | 25 | #include "core/version.h" 26 | 27 | int TextureLayerMerger::get_width() const { 28 | return _width; 29 | } 30 | 31 | void TextureLayerMerger::set_width(const int p_value) { 32 | _width = p_value; 33 | } 34 | 35 | int TextureLayerMerger::get_height() const { 36 | return _height; 37 | } 38 | 39 | void TextureLayerMerger::set_height(const int p_value) { 40 | _height = p_value; 41 | } 42 | 43 | uint32_t TextureLayerMerger::get_texture_flags() const { 44 | return _texture_flags; 45 | } 46 | void TextureLayerMerger::set_texture_flags(uint32_t p_flags) { 47 | _texture_flags = p_flags; 48 | } 49 | 50 | Color TextureLayerMerger::get_base_color() const { 51 | return _base_color; 52 | } 53 | void TextureLayerMerger::set_base_color(const Color &p_color) { 54 | _base_color = p_color; 55 | } 56 | 57 | Ref TextureLayerMerger::get_data() const { 58 | return _image; 59 | } 60 | 61 | void TextureLayerMerger::set_data(const Ref &p_image) { 62 | ERR_FAIL_COND(p_image.is_null()); 63 | 64 | _image = p_image; 65 | } 66 | 67 | Ref TextureLayerMerger::get_result_as_texture() const { 68 | ERR_FAIL_COND_V(!_image.is_valid(), Ref()); 69 | 70 | Ref tex; 71 | #if VERSION_MAJOR < 4 72 | tex.instantiate(); 73 | #else 74 | tex.instantiate(); 75 | #endif 76 | tex->create_from_image(_image); 77 | 78 | return tex; 79 | } 80 | 81 | void TextureLayerMerger::add_texture(const Ref &p_texture, const Color &p_color, const Vector2 &p_position, Rect2 p_rect) { 82 | ERR_FAIL_COND(!p_texture.is_valid()); 83 | 84 | LayerMergerEntry entry; 85 | 86 | entry.texture = p_texture; 87 | entry.color = p_color; 88 | entry.position = p_position; 89 | 90 | if (p_rect.size.x <= 0) 91 | p_rect.size.x = p_texture->get_width(); 92 | 93 | if (p_rect.size.y <= 0) 94 | p_rect.size.y = p_texture->get_height(); 95 | 96 | entry.rect = p_rect; 97 | 98 | if (_width == 0 || _height == 0) { 99 | Ref at = p_texture; 100 | 101 | int w = 0; 102 | int h = 0; 103 | 104 | if (at.is_valid()) { 105 | w = at->get_region().size.x; 106 | h = at->get_region().size.y; 107 | } else { 108 | w = p_texture->get_width(); 109 | h = p_texture->get_height(); 110 | } 111 | 112 | if (_width == 0) 113 | _width = w; 114 | 115 | if (_height == 0) 116 | _height = h; 117 | } 118 | 119 | _entries.push_back(entry); 120 | } 121 | 122 | Ref TextureLayerMerger::get_texture(const int p_index) { 123 | ERR_FAIL_INDEX_V(p_index, _entries.size(), Ref()); 124 | 125 | return _entries.get(p_index).texture; 126 | } 127 | void TextureLayerMerger::set_texture(const int p_index, const Ref &p_texture) { 128 | ERR_FAIL_INDEX(p_index, _entries.size()); 129 | 130 | _entries.get(p_index).texture = p_texture; 131 | } 132 | 133 | Color TextureLayerMerger::get_color(const int p_index) { 134 | ERR_FAIL_INDEX_V(p_index, _entries.size(), Color()); 135 | 136 | return _entries.get(p_index).color; 137 | } 138 | void TextureLayerMerger::set_color(const int p_index, const Color &p_color) { 139 | ERR_FAIL_INDEX(p_index, _entries.size()); 140 | 141 | _entries.get(p_index).color = p_color; 142 | } 143 | 144 | Vector2 TextureLayerMerger::get_position(const int p_index) { 145 | ERR_FAIL_INDEX_V(p_index, _entries.size(), Vector2()); 146 | 147 | return _entries.get(p_index).position; 148 | } 149 | void TextureLayerMerger::set_position(const int p_index, const Vector2 &p_position) { 150 | ERR_FAIL_INDEX(p_index, _entries.size()); 151 | 152 | _entries.get(p_index).position = p_position; 153 | } 154 | 155 | Rect2 TextureLayerMerger::get_rect(const int p_index) { 156 | ERR_FAIL_INDEX_V(p_index, _entries.size(), Rect2()); 157 | 158 | return _entries.get(p_index).rect; 159 | } 160 | void TextureLayerMerger::set_rect(const int p_index, const Rect2 &p_rect) { 161 | ERR_FAIL_INDEX(p_index, _entries.size()); 162 | 163 | _entries.get(p_index).rect = p_rect; 164 | } 165 | 166 | void TextureLayerMerger::remove_texture(const int p_index) { 167 | ERR_FAIL_INDEX(p_index, _entries.size()); 168 | 169 | #if VERSION_MAJOR < 4 170 | return _entries.remove(p_index); 171 | #else 172 | return _entries.remove_at(p_index); 173 | #endif 174 | } 175 | 176 | int TextureLayerMerger::get_texture_count() { 177 | return _entries.size(); 178 | } 179 | 180 | void TextureLayerMerger::clear() { 181 | _entries.clear(); 182 | } 183 | 184 | void TextureLayerMerger::merge() { 185 | ERR_FAIL_COND(_width <= 0 || _height <= 0); 186 | 187 | if (!_image.is_valid()) { 188 | #if VERSION_MAJOR < 4 189 | _image.instantiate(); 190 | #else 191 | _image.instantiate(); 192 | #endif 193 | } 194 | 195 | Vector data; 196 | data.resize(_width * _height * 4); 197 | 198 | write_base_color_to_array(data); 199 | 200 | for (int i = 0; i < _entries.size(); ++i) { 201 | const LayerMergerEntry &e = _entries[i]; 202 | 203 | ERR_CONTINUE(!e.texture.is_valid()); 204 | 205 | int rx = e.rect.position.x + 0.1; 206 | int ry = e.rect.position.y + 0.1; 207 | int rw = e.rect.size.x + 0.1; 208 | int rh = e.rect.size.y + 0.1; 209 | 210 | int posx = e.position.x + 0.1; 211 | int posy = e.position.y + 0.1; 212 | 213 | int atlas_x = 0; 214 | int atlas_y = 0; 215 | 216 | if (posx > _width || posy > _height) 217 | continue; 218 | 219 | Ref altas_texture = e.texture; 220 | Ref input_image; 221 | 222 | if (altas_texture.is_valid()) { 223 | Ref atlas = altas_texture->get_atlas(); 224 | 225 | ERR_CONTINUE(!atlas.is_valid()); 226 | 227 | #if VERSION_MAJOR < 4 228 | input_image = atlas->get_data(); 229 | #else 230 | input_image = atlas->get_image(); 231 | #endif 232 | 233 | Rect2 region = altas_texture->get_region(); 234 | 235 | atlas_x = region.position.x + 0.1; 236 | atlas_y = region.position.y + 0.1; 237 | 238 | int atlas_w = region.size.x + 0.1; 239 | int atlas_h = region.size.y + 0.1; 240 | 241 | if (rw > atlas_w) 242 | rw = atlas_w; 243 | 244 | if (rh > atlas_h) 245 | rh = atlas_h; 246 | } else { 247 | #if VERSION_MAJOR < 4 248 | input_image = e.texture->get_data(); 249 | #else 250 | input_image = e.texture->get_image(); 251 | #endif 252 | } 253 | 254 | ERR_CONTINUE(!input_image.is_valid()); 255 | 256 | int iiw = input_image->get_width(); 257 | int iih = input_image->get_height(); 258 | Vector input_image_data = input_image->get_data(); 259 | 260 | const Color &blendcolor = e.color; 261 | 262 | float blend_arr[] = { 263 | blendcolor.r, 264 | blendcolor.g, 265 | blendcolor.b, 266 | blendcolor.a 267 | }; 268 | 269 | ERR_CONTINUE(iiw == 0 || iih == 0); 270 | ERR_CONTINUE(rx > iiw || ry > iih); 271 | 272 | //clamp width, and height if bigger (for ease of use) 273 | if (atlas_x + rx + rw >= iiw) 274 | rw -= (atlas_x + rx + rw) - iiw; 275 | 276 | if (atlas_y + ry + rh >= iih) 277 | rh -= (atlas_y + ry + rh) - iih; 278 | 279 | //Let's take position into account 280 | if (rx + rw + posx >= _width) 281 | rw -= (rx + rw + posx) - _width; 282 | 283 | if (ry + rh + posy >= _height) 284 | rh -= (ry + rh + posy) - _height; 285 | 286 | if (rw <= 0 || rh <= 0) 287 | continue; 288 | 289 | int elen = 0; 290 | 291 | if (input_image->get_format() == Image::FORMAT_RGBA8) { 292 | elen = 4; 293 | } else if (input_image->get_format() == Image::FORMAT_RGB8) { 294 | elen = 3; 295 | } 296 | 297 | ERR_CONTINUE_MSG(elen == 0, "Unsupported image format! Format: " + String::num(input_image->get_format())); 298 | 299 | for (int y = 0; y < rh; ++y) { 300 | int img_gen_index = (posx + ((y + posy) * _width)) * 4; 301 | int img_input_index = (rx + atlas_x + ((y + atlas_y + ry) * iiw)) * elen; 302 | 303 | for (int x = 0; x < rw; ++x) { 304 | float blend_alpha = blendcolor.a; 305 | 306 | if (elen == 4) { 307 | float orig_alpha = input_image_data.get(img_input_index + 3) / 255.0; 308 | 309 | blend_alpha -= 1.0 - orig_alpha; 310 | 311 | if (blend_alpha < 0) 312 | blend_alpha = 0; 313 | 314 | blend_alpha = orig_alpha; 315 | } 316 | 317 | for (int sp = 0; sp < elen; ++sp) { 318 | int main_index = img_gen_index + sp; 319 | 320 | int main_val = data.get(main_index); 321 | int input_val = input_image_data.get(img_input_index + sp); 322 | 323 | main_val = (input_val * blend_arr[sp]) * blend_alpha + (main_val * (1 - blend_alpha)); 324 | 325 | if (main_val > 255) 326 | main_val = 255; 327 | 328 | data.set(main_index, main_val); 329 | } 330 | 331 | img_gen_index += 4; 332 | img_input_index += elen; 333 | } 334 | } 335 | } 336 | _image = Image::create_from_data(_width, _height, true, Image::FORMAT_RGBA8, data); 337 | } 338 | 339 | void TextureLayerMerger::write_base_color_to_array(Vector &data) { 340 | int cr = _base_color.r * 255; 341 | int cg = _base_color.g * 255; 342 | int cb = _base_color.b * 255; 343 | int ca = _base_color.a * 255; 344 | 345 | for (int i = 0; i < data.size(); i += 4) { 346 | data.set(i, cr); 347 | data.set(i + 1, cg); 348 | data.set(i + 2, cb); 349 | data.set(i + 3, ca); 350 | } 351 | } 352 | 353 | void TextureLayerMerger::_bind_methods() { 354 | ClassDB::bind_method(D_METHOD("get_width"), &TextureLayerMerger::get_width); 355 | ClassDB::bind_method(D_METHOD("set_width", "image"), &TextureLayerMerger::set_width); 356 | ADD_PROPERTY(PropertyInfo(Variant::INT, "width"), "set_width", "get_width"); 357 | 358 | ClassDB::bind_method(D_METHOD("get_height"), &TextureLayerMerger::get_height); 359 | ClassDB::bind_method(D_METHOD("set_height", "image"), &TextureLayerMerger::set_height); 360 | ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); 361 | 362 | ClassDB::bind_method(D_METHOD("get_texture_flags"), &TextureLayerMerger::get_texture_flags); 363 | ClassDB::bind_method(D_METHOD("set_texture_flags", "image"), &TextureLayerMerger::set_texture_flags); 364 | ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_texture_flags", "get_texture_flags"); 365 | 366 | ClassDB::bind_method(D_METHOD("get_base_color"), &TextureLayerMerger::get_base_color); 367 | ClassDB::bind_method(D_METHOD("set_base_color", "color"), &TextureLayerMerger::set_base_color); 368 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "base_color"), "set_base_color", "get_base_color"); 369 | 370 | ClassDB::bind_method(D_METHOD("get_data"), &TextureLayerMerger::get_data); 371 | ClassDB::bind_method(D_METHOD("set_data", "image"), &TextureLayerMerger::set_data); 372 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_data", "get_data"); 373 | 374 | ClassDB::bind_method(D_METHOD("get_result_as_texture"), &TextureLayerMerger::get_result_as_texture); 375 | 376 | ClassDB::bind_method(D_METHOD("add_texture", "texture", "color", "position", "rect"), &TextureLayerMerger::add_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Vector2()), DEFVAL(Rect2())); 377 | 378 | ClassDB::bind_method(D_METHOD("get_texture", "index"), &TextureLayerMerger::get_texture); 379 | ClassDB::bind_method(D_METHOD("set_texture", "index", "texture"), &TextureLayerMerger::set_texture); 380 | 381 | ClassDB::bind_method(D_METHOD("get_color", "index"), &TextureLayerMerger::get_color); 382 | ClassDB::bind_method(D_METHOD("set_color", "index", "color"), &TextureLayerMerger::set_color); 383 | 384 | ClassDB::bind_method(D_METHOD("get_position", "index"), &TextureLayerMerger::get_position); 385 | ClassDB::bind_method(D_METHOD("set_position", "index", "position"), &TextureLayerMerger::set_position); 386 | 387 | ClassDB::bind_method(D_METHOD("get_rect", "index"), &TextureLayerMerger::get_rect); 388 | ClassDB::bind_method(D_METHOD("set_rect", "index", "rect"), &TextureLayerMerger::set_rect); 389 | 390 | ClassDB::bind_method(D_METHOD("remove_texture", "index"), &TextureLayerMerger::remove_texture); 391 | ClassDB::bind_method(D_METHOD("get_texture_count"), &TextureLayerMerger::get_texture_count); 392 | ClassDB::bind_method(D_METHOD("clear"), &TextureLayerMerger::clear); 393 | 394 | ClassDB::bind_method(D_METHOD("merge"), &TextureLayerMerger::merge); 395 | } 396 | 397 | TextureLayerMerger::TextureLayerMerger() { 398 | _width = 0; 399 | _height = 0; 400 | 401 | _texture_flags = 0; 402 | } 403 | 404 | TextureLayerMerger::~TextureLayerMerger() { 405 | _image.unref(); 406 | _entries.clear(); 407 | } 408 | -------------------------------------------------------------------------------- /texture_packer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019-2022 Péter Magyar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "texture_packer.h" 24 | 25 | int TexturePacker::get_texture_flags() const { 26 | return _texture_flags; 27 | } 28 | void TexturePacker::set_texture_flags(const int flags) { 29 | _texture_flags = flags; 30 | } 31 | 32 | int TexturePacker::get_max_atlas_size() const { 33 | return _max_atlas_size; 34 | } 35 | void TexturePacker::set_max_atlas_size(const int size) { 36 | _max_atlas_size = size; 37 | } 38 | 39 | bool TexturePacker::get_keep_original_atlases() const { 40 | return _keep_original_atlases; 41 | } 42 | void TexturePacker::set_keep_original_atlases(const bool value) { 43 | _keep_original_atlases = value; 44 | } 45 | 46 | Color TexturePacker::get_background_color() const { 47 | return _background_color; 48 | } 49 | void TexturePacker::set_background_color(const Color &color) { 50 | _background_color = color; 51 | } 52 | 53 | int TexturePacker::get_margin() const { 54 | return _margin; 55 | } 56 | void TexturePacker::set_margin(const int margin) { 57 | _margin = margin; 58 | } 59 | 60 | Ref TexturePacker::add_texture(const Ref &texture) { 61 | ERR_FAIL_COND_V(!texture.is_valid(), Ref()); 62 | 63 | Ref atlas_text = texture; 64 | 65 | if (atlas_text.is_valid()) { 66 | //we need to check differently this case 67 | for (int i = 0; i < _rects.size(); ++i) { 68 | rect_xywhf *r = _rects.get(i); 69 | 70 | Ref t; 71 | Ref at = texture; 72 | 73 | if (_keep_original_atlases && at.is_valid()) 74 | t = r->atlas_texture; 75 | else 76 | t = r->original_texture; 77 | 78 | if (t == texture) { 79 | ++(r->refcount); 80 | 81 | return r->atlas_texture; 82 | } 83 | } 84 | 85 | Ref tex; 86 | #if VERSION_MAJOR < 4 87 | tex.instantiate(); 88 | #else 89 | tex.instantiate(); 90 | #endif 91 | 92 | tex->set_atlas(atlas_text->get_atlas()); 93 | tex->set_region(atlas_text->get_region()); 94 | 95 | rect_xywhf *rect = memnew(rect_xywhf); 96 | 97 | rect->refcount = 1; 98 | rect->w = atlas_text->get_region().size.x + 2 * _margin; 99 | rect->h = atlas_text->get_region().size.y + 2 * _margin; 100 | 101 | _rects.push_back(rect); 102 | 103 | if (_keep_original_atlases) { 104 | rect->original_texture = tex; 105 | rect->atlas_texture = atlas_text; 106 | 107 | return atlas_text; 108 | } else { 109 | rect->original_texture = atlas_text; 110 | rect->atlas_texture = tex; 111 | 112 | return tex; 113 | } 114 | } 115 | 116 | for (int i = 0; i < _rects.size(); ++i) { 117 | rect_xywhf *r = _rects.get(i); 118 | 119 | if (r->original_texture == texture) { 120 | ++(r->refcount); 121 | 122 | return r->atlas_texture; 123 | } 124 | } 125 | 126 | Ref tex; 127 | #if VERSION_MAJOR < 4 128 | tex.instantiate(); 129 | #else 130 | tex.instantiate(); 131 | #endif 132 | 133 | //Temp setup, so the texture is usable even while the atlases are generating. 134 | tex->set_atlas(texture); 135 | tex->set_region(Rect2(0, 0, texture->get_width(), texture->get_height())); 136 | 137 | rect_xywhf *rect = memnew(rect_xywhf); 138 | 139 | rect->refcount = 1; 140 | rect->original_texture = texture; 141 | rect->atlas_texture = tex; 142 | 143 | rect->w = texture->get_width() + 2 * _margin; 144 | rect->h = texture->get_height() + 2 * _margin; 145 | 146 | _rects.push_back(rect); 147 | 148 | return tex; 149 | } 150 | 151 | Ref TexturePacker::get_texture(const Ref &texture) { 152 | for (int i = 0; i < _rects.size(); ++i) { 153 | rect_xywhf *r = _rects.get(i); 154 | 155 | Ref t; 156 | Ref at = texture; 157 | 158 | if (_keep_original_atlases && at.is_valid()) 159 | t = r->atlas_texture; 160 | else 161 | t = r->original_texture; 162 | 163 | if (t == texture) { 164 | return _rects.get(i)->atlas_texture; 165 | } 166 | } 167 | 168 | return Ref(); 169 | } 170 | 171 | Ref TexturePacker::get_texture_index(const int index) { 172 | ERR_FAIL_INDEX_V(index, _rects.size(), Ref()); 173 | 174 | return _rects.get(index)->atlas_texture; 175 | } 176 | 177 | Ref TexturePacker::get_original_texture(const int index) { 178 | ERR_FAIL_INDEX_V(index, _rects.size(), Ref()); 179 | 180 | return _rects.get(index)->original_texture; 181 | } 182 | 183 | bool TexturePacker::contains_texture(const Ref &texture) { 184 | for (int i = 0; i < _rects.size(); ++i) { 185 | rect_xywhf *r = _rects.get(i); 186 | 187 | Ref t; 188 | Ref at = texture; 189 | 190 | if (_keep_original_atlases && at.is_valid()) 191 | t = r->atlas_texture; 192 | else 193 | t = r->original_texture; 194 | 195 | if (t == texture) { 196 | return true; 197 | } 198 | } 199 | 200 | return false; 201 | } 202 | 203 | bool TexturePacker::unref_texture_index(const int index) { 204 | ERR_FAIL_INDEX_V(index, _rects.size(), false); 205 | 206 | rect_xywhf *r = _rects.get(index); 207 | 208 | int rc = --(r->refcount); 209 | 210 | if (rc <= 0) { 211 | #if VERSION_MAJOR < 4 212 | _rects.remove(index); 213 | #else 214 | _rects.remove_at(index); 215 | #endif 216 | 217 | r->original_texture.unref(); 218 | r->atlas_texture.unref(); 219 | 220 | memdelete(r); 221 | 222 | return true; 223 | } 224 | 225 | return false; 226 | } 227 | 228 | bool TexturePacker::unref_texture(const Ref &texture) { 229 | for (int i = 0; i < _rects.size(); ++i) { 230 | rect_xywhf *r = _rects.get(i); 231 | 232 | Ref t; 233 | Ref at = texture; 234 | 235 | if (_keep_original_atlases && at.is_valid()) 236 | t = r->atlas_texture; 237 | else 238 | t = r->original_texture; 239 | 240 | if (t == texture) { 241 | int rc = --(r->refcount); 242 | 243 | if (rc <= 0) { 244 | #if VERSION_MAJOR < 4 245 | _rects.remove(i); 246 | #else 247 | _rects.remove_at(i); 248 | #endif 249 | 250 | r->original_texture.unref(); 251 | r->atlas_texture.unref(); 252 | 253 | memdelete(r); 254 | 255 | return true; 256 | } 257 | 258 | return false; 259 | } 260 | } 261 | 262 | return false; 263 | } 264 | 265 | void TexturePacker::remove_texture_index(const int index) { 266 | ERR_FAIL_INDEX(index, _rects.size()); 267 | 268 | rect_xywhf *r = _rects.get(index); 269 | 270 | r->original_texture.unref(); 271 | r->atlas_texture.unref(); 272 | 273 | memdelete(r); 274 | } 275 | 276 | void TexturePacker::remove_texture(const Ref &texture) { 277 | for (int i = 0; i < _rects.size(); ++i) { 278 | rect_xywhf *r = _rects.get(i); 279 | 280 | Ref t; 281 | Ref at = texture; 282 | 283 | if (_keep_original_atlases && at.is_valid()) 284 | t = r->atlas_texture; 285 | else 286 | t = r->original_texture; 287 | 288 | if (t == texture) { 289 | #if VERSION_MAJOR < 4 290 | _rects.remove(i); 291 | #else 292 | _rects.remove_at(i); 293 | #endif 294 | 295 | r->original_texture.unref(); 296 | r->atlas_texture.unref(); 297 | 298 | memdelete(r); 299 | 300 | return; 301 | } 302 | } 303 | } 304 | 305 | int TexturePacker::get_texture_count() { 306 | return _rects.size(); 307 | } 308 | 309 | void TexturePacker::clear() { 310 | for (int i = 0; i < _rects.size(); ++i) { 311 | rect_xywhf *r = _rects.get(i); 312 | 313 | r->atlas_texture.unref(); 314 | r->original_texture.unref(); 315 | 316 | memdelete(r); 317 | } 318 | 319 | _rects.clear(); 320 | 321 | for (int i = 0; i < _generated_textures.size(); ++i) { 322 | _generated_textures.get(i).unref(); 323 | } 324 | 325 | _generated_textures.clear(); 326 | } 327 | 328 | Ref TexturePacker::get_generated_texture(const int index) { 329 | ERR_FAIL_INDEX_V(index, _generated_textures.size(), Ref()); 330 | 331 | return _generated_textures.get(index); 332 | } 333 | int TexturePacker::get_generated_texture_count() { 334 | return _generated_textures.size(); 335 | } 336 | 337 | void TexturePacker::merge() { 338 | std::vector bins; 339 | 340 | if (pack(_rects.ptr(), _rects.size(), _max_atlas_size, false, bins)) { 341 | _generated_textures.clear(); 342 | 343 | _generated_textures.resize(bins.size()); 344 | 345 | for (uint32_t i = 0; i < bins.size(); ++i) { 346 | bin b = bins[i]; 347 | 348 | PackedByteArray data; 349 | data.resize(b.size.w * b.size.h * 4); 350 | 351 | //Setup background color 352 | uint8_t cr = _background_color.r * 255.0; 353 | uint8_t cg = _background_color.g * 255.0; 354 | uint8_t cb = _background_color.b * 255.0; 355 | uint8_t ca = _background_color.a * 255.0; 356 | 357 | for (int j = 0; j < data.size(); j += 4) { 358 | data.set(j, cr); 359 | data.set(j + 1, cg); 360 | data.set(j + 2, cb); 361 | data.set(j + 3, ca); 362 | } 363 | 364 | //Process rects 365 | for (uint32_t j = 0; j < b.rects.size(); ++j) { 366 | rect_xywhf *r = b.rects[j]; 367 | 368 | Ref otext = r->original_texture; 369 | Ref aotext = otext; 370 | 371 | int rect_pos_x = 0; 372 | int rect_pos_y = 0; 373 | 374 | if (aotext.is_valid()) { 375 | otext = aotext->get_atlas(); 376 | 377 | Rect2 rect = aotext->get_region(); 378 | 379 | rect_pos_x = rect.position.x + 0.5; 380 | rect_pos_y = rect.position.y + 0.5; 381 | } 382 | 383 | ERR_CONTINUE(!otext.is_valid()); 384 | 385 | #if VERSION_MAJOR < 4 386 | Ref img = otext->get_data(); 387 | #else 388 | Ref img = otext->get_image(); 389 | #endif 390 | 391 | ERR_CONTINUE(!img.is_valid()); 392 | 393 | int img_width = img->get_width(); 394 | 395 | PackedByteArray image_data = img->get_data(); 396 | 397 | int input_format_offset = get_offset_for_format(img->get_format()); 398 | 399 | ERR_CONTINUE_MSG(input_format_offset == 0, "Format is not implemented, Skipping!"); 400 | 401 | int h_wo_margin = r->h - 2 * _margin; 402 | for (int y = 0; y < h_wo_margin; ++y) { 403 | int orig_img_indx = (rect_pos_y + y) * img_width * input_format_offset + rect_pos_x * input_format_offset; 404 | int start_indx = (r->y + y + _margin) * b.size.w * 4 + (r->x + _margin) * 4; 405 | 406 | int row_width = (r->w - 2 * _margin); 407 | for (int x = 0; x < row_width; ++x) { 408 | for (int sx = 0; sx < input_format_offset; ++sx) { 409 | data.set(start_indx + (x * 4) + sx, image_data[orig_img_indx + sx + (x * input_format_offset)]); 410 | } 411 | } 412 | } 413 | } 414 | 415 | Ref image = Image::create_from_data(b.size.w, b.size.h, false, Image::FORMAT_RGBA8, data); 416 | 417 | Ref texture = ImageTexture::create_from_image(image); 418 | 419 | _generated_textures.set(i, texture); 420 | 421 | for (uint32_t j = 0; j < b.rects.size(); ++j) { 422 | rect_xywhf *r = b.rects[j]; 423 | 424 | Ref at = r->atlas_texture; 425 | 426 | at->set_atlas(texture); 427 | at->set_region(Rect2(r->x + _margin, r->y + _margin, r->w - 2 * _margin, r->h - 2 * _margin)); 428 | } 429 | } 430 | } 431 | } 432 | 433 | int TexturePacker::get_offset_for_format(const Image::Format format) { 434 | switch (format) { 435 | case Image::FORMAT_RGB8: 436 | return 3; 437 | case Image::FORMAT_RGBA8: 438 | return 4; 439 | 440 | case Image::FORMAT_L8: 441 | case Image::FORMAT_LA8: 442 | case Image::FORMAT_R8: 443 | case Image::FORMAT_RG8: 444 | case Image::FORMAT_RGBA4444: 445 | case Image::FORMAT_RF: 446 | case Image::FORMAT_RGF: 447 | case Image::FORMAT_RGBF: 448 | case Image::FORMAT_RGBAF: 449 | case Image::FORMAT_RH: 450 | case Image::FORMAT_RGH: 451 | case Image::FORMAT_RGBH: 452 | case Image::FORMAT_RGBAH: 453 | case Image::FORMAT_RGBE9995: 454 | case Image::FORMAT_DXT1: 455 | case Image::FORMAT_DXT3: 456 | case Image::FORMAT_DXT5: 457 | case Image::FORMAT_RGTC_R: 458 | case Image::FORMAT_RGTC_RG: 459 | case Image::FORMAT_BPTC_RGBA: 460 | case Image::FORMAT_BPTC_RGBF: 461 | case Image::FORMAT_BPTC_RGBFU: 462 | case Image::FORMAT_ETC: 463 | case Image::FORMAT_ETC2_R11: 464 | case Image::FORMAT_ETC2_R11S: 465 | case Image::FORMAT_ETC2_RG11: 466 | case Image::FORMAT_ETC2_RG11S: 467 | case Image::FORMAT_ETC2_RGB8: 468 | case Image::FORMAT_ETC2_RGBA8: 469 | case Image::FORMAT_ETC2_RGB8A1: 470 | case Image::FORMAT_ASTC_4x4: 471 | case Image::FORMAT_ASTC_4x4_HDR: 472 | case Image::FORMAT_ASTC_8x8: 473 | case Image::FORMAT_ASTC_8x8_HDR: 474 | case Image::FORMAT_RGB565: 475 | case Image::FORMAT_ETC2_RA_AS_RG: 476 | case Image::FORMAT_DXT5_RA_AS_RG: 477 | case Image::FORMAT_MAX: 478 | return 0; 479 | } 480 | 481 | return 0; 482 | } 483 | 484 | TexturePacker::TexturePacker() { 485 | #if VERSION_MAJOR < 4 486 | _texture_flags = Texture2D::FLAG_MIPMAPS | Texture2D::FLAG_FILTER; 487 | #else 488 | _texture_flags = 0; 489 | #endif 490 | 491 | _max_atlas_size = 1024; 492 | _keep_original_atlases = false; 493 | _margin = 0; 494 | } 495 | 496 | TexturePacker::~TexturePacker() { 497 | clear(); 498 | } 499 | 500 | void TexturePacker::_bind_methods() { 501 | ClassDB::bind_method(D_METHOD("get_texture_flags"), &TexturePacker::get_texture_flags); 502 | ClassDB::bind_method(D_METHOD("set_texture_flags", "flags"), &TexturePacker::set_texture_flags); 503 | ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_texture_flags", "get_texture_flags"); 504 | 505 | ClassDB::bind_method(D_METHOD("get_max_atlas_size"), &TexturePacker::get_max_atlas_size); 506 | ClassDB::bind_method(D_METHOD("set_max_atlas_size", "size"), &TexturePacker::set_max_atlas_size); 507 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_atlas_size"), "set_max_atlas_size", "get_max_atlas_size"); 508 | 509 | ClassDB::bind_method(D_METHOD("get_keep_original_atlases"), &TexturePacker::get_keep_original_atlases); 510 | ClassDB::bind_method(D_METHOD("set_keep_original_atlases", "value"), &TexturePacker::set_keep_original_atlases); 511 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_original_atlases"), "set_keep_original_atlases", "get_keep_original_atlases"); 512 | 513 | ClassDB::bind_method(D_METHOD("get_background_color"), &TexturePacker::get_background_color); 514 | ClassDB::bind_method(D_METHOD("set_background_color", "color"), &TexturePacker::set_background_color); 515 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_background_color", "get_background_color"); 516 | 517 | ClassDB::bind_method(D_METHOD("get_margin"), &TexturePacker::get_margin); 518 | ClassDB::bind_method(D_METHOD("set_margin", "size"), &TexturePacker::set_margin); 519 | ADD_PROPERTY(PropertyInfo(Variant::INT, "margin"), "set_margin", "get_margin"); 520 | 521 | ClassDB::bind_method(D_METHOD("add_texture", "texture"), &TexturePacker::add_texture); 522 | 523 | ClassDB::bind_method(D_METHOD("get_texture", "texture"), &TexturePacker::get_texture); 524 | ClassDB::bind_method(D_METHOD("get_texture_index", "index"), &TexturePacker::get_texture_index); 525 | 526 | ClassDB::bind_method(D_METHOD("get_original_texture", "index"), &TexturePacker::get_original_texture); 527 | 528 | ClassDB::bind_method(D_METHOD("unref_texture_index", "index"), &TexturePacker::unref_texture_index); 529 | ClassDB::bind_method(D_METHOD("unref_texture", "texture"), &TexturePacker::unref_texture); 530 | ClassDB::bind_method(D_METHOD("remove_texture_index", "index"), &TexturePacker::remove_texture_index); 531 | ClassDB::bind_method(D_METHOD("remove_texture", "texture"), &TexturePacker::remove_texture); 532 | 533 | ClassDB::bind_method(D_METHOD("get_texture_count"), &TexturePacker::get_texture_count); 534 | ClassDB::bind_method(D_METHOD("clear"), &TexturePacker::clear); 535 | 536 | ClassDB::bind_method(D_METHOD("get_generated_texture", "index"), &TexturePacker::get_generated_texture); 537 | ClassDB::bind_method(D_METHOD("get_generated_texture_count"), &TexturePacker::get_generated_texture_count); 538 | 539 | ClassDB::bind_method(D_METHOD("merge"), &TexturePacker::merge); 540 | } 541 | --------------------------------------------------------------------------------