├── .gitignore ├── LICENSE ├── README.md ├── README.ru.md └── addons └── nklbdev.aseprite_importers ├── aseprite_image_format_loader_extension.gd ├── common.gd ├── editor_import_plugins ├── _animation_importer_base.gd ├── _importer_base.gd ├── animated_sprite_2d.gd ├── animated_sprite_3d.gd ├── sprite_2d.gd ├── sprite_3d.gd ├── sprite_frames.gd └── texture_rect.gd ├── editor_plugin.gd ├── icon.png ├── icon.png.import └── plugin.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !/LICENSE 3 | !/README.*.md 4 | !/addons/ 5 | /addons/* 6 | !/addons/nklbdev.aseprite_importers/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 nklbdev 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godot 4 Aseprite Importers 2 | 3 | [![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) 4 | [![ru](https://img.shields.io/badge/lang-ru-green.svg)](README.ru.md) 5 | 6 | This is a plugin for the [Godot](https://godotengine.org/) 4.x game engine that adds several import plugins for the [Aseprite](https://www.aseprite.org/) 1.3+ graphics pixel art editor files. 7 | 8 | ## I highly recommend that you use the [Importality](https://github.com/nklbdev/godot-4-importality) plugin instead of this plugin. It is a rethinking and continuation of the initiatives I made in this repository. 9 | Please read [the documentation](https://github.com/nklbdev/godot-4-importality/wiki/exporting-data-from-aseprite-(en)) for importing files from Aseprite. 10 | And I highly recommend [using RAM Drive](https://github.com/nklbdev/godot-4-importality/wiki/about-temporary-files-and-ram_drives-(en)) for intermediate temporary files during the import process. 11 | 12 | https://user-images.githubusercontent.com/7024016/236665418-fe8036b9-7de5-4608-a247-b35a7f97891b.mp4 13 | 14 | ## 💽 Installation 15 | 16 | Simply download it from [Godot Asset Library](https://godotengine.org/asset-library/asset/1880). 17 | 18 | Alternatively download or clone this repository and copy the contents of the `addons` folder to your own project's `addons` folder. 19 | 20 | Then: 21 | 22 | - Go to Project Settings 23 | - Switch to the Plugins tab 24 | - Enable the plugin 25 | - Switch to the General tab 26 | - Turn on Advanced Settings toggle 27 | - Scroll down the settings tree and select Aseprite Importers section 28 | - Specify the path to Aseprite executable file 29 | 30 | ## 👷‍♀️ How to use 31 | 32 | After installing the plugin, the project will support `.ase`- and `.aseprite`-files. 33 | 1. Place the Aseprite graphics files in the project 34 | 2. Select one or more Aseprite graphics files in the project file system tree 35 | 3. Select one of the import plugins in the import panel 36 | 4. Set import preferences 37 | 5. Optionally set a customized settings configuration for the selected import plugin as default 38 | 6. Click the `Reimport` button 39 | 40 | ## 🛠 Import settings 41 | 42 | - **`Spritesheet`** - *spritesheet import settings group* 43 | - **`Embed Image` (not implemented yet)** - *include the resulting image in a resource or place it next to the source file* 44 | - **`Layout`** - *spritesheet layout type* 45 | - **`Packed`** *all the sprites trimmed and compactly arranged in the spritesheet* 46 | - **`By Rows`** (with the **`Fixed Columns Count`** parameter) - *all the sprites have similar size and layed out by rows with fixed length* 47 | - **`By Columns`** (with the **`Fixed Columns Count`** parameter) - *all the sprites have similar size and layed out by columns with fixed height* 48 | - **`Border Type`** - *the type of the border around each sprite* 49 | - **`None`** - *does not create border around sprites* 50 | - **`Transparent`** - *creates a 1 pixel wide transparent border around each sprite* 51 | - **`Extruded`** - *creates a 1 pixel wide border, duplicating the colors of adjacent sprite pixels, around each sprite* 52 | - **`Trim`** - (only for grid-based layouts) - *reduces all sprite cells equally so that the animation fits into the new cell size* 53 | - **`Ignore Empty`** - *does not include sprites in the spritesheet image, on which all pixels are transparent* 54 | - **`Merge Duplicates`** - *merges the same sprites into the same areas on the spritesheet image* 55 | - **`Animation`** - *animation settings group* 56 | - **`Default`** - *settings for the default animation (if there are no tags available in the Aseprite graphics file)* 57 | - **`Name`** - *the name for the default animation* 58 | - **`Direction`** - *default animation direction* 59 | - **`Forward`** - *animation plays from the first to the last frame* 60 | - **`Reverse`** - *animation plays from the first to the last frame* 61 | - **`Ping-pong`** - *the animation plays from the first to the last frame, and then back to the first, without duplicating the last frame* 62 | - **`Ping-pong reverse`** - *the animation plays from the last to the first frame, and then back to the last, without duplicating the first frame* 63 | - **`Repeat Count`** - *number of repetitions. Edge frames are not duplicated when animation changes direction* 64 | - **`Autoplay`** - *the name of the animation that will be marked as starting automatically* 65 | - **`Strategy...` (only for animations based on `AnimationPlayer`)** - *set of node properties that the AnimationPlayer will use to animate* 66 | - **`Layers` (not implemented yet)** - *Aseprite layers settings group* 67 | - **`Include Reg Ex` (not implemented yet)** - *Regular expression for white list of included layers* 68 | - **`Exclude Reg Ex` (not implemented yet)** - *Regular expression for blacklist of included layers* 69 | - **`Tags` (not implemented yet)** - *Aseprite tags settings group* 70 | - **`Include Reg Ex` (not implemented yet)** - *Regular expression for white list of included tags* 71 | - **`Exclude Reg Ex` (not implemented yet)** - *Regular expression for blacklist of included tags* 72 | 73 | 74 | ## 🧱 Types of Imported Resources 75 | 76 | ### 🖼️ `Texture` 77 | 78 | You can import your `*.aseprite` or `*.ase` files as regular textures from image files. Unfortunately you can not select layers or frames to render. It renders all visible layers from first animation frame. 79 | 80 | ### 🎞 `SpriteFrames`-based animations 81 | 82 | - **`SpriteFrames`** - *creates a `SpriteFrames` resource for further use in animated sprites* 83 | - **`AnimatedSprite2D`** - *creates ready-to-use animated sprite for 2D scenes* 84 | - **`AnimatedSprite3D`** - *creates ready-to-use animated sprite for 3D scenes* 85 | 86 | ### 📽 `AnimationPlayer`-based animations 87 | 88 | Creates a `PackedScene` resources with an `AnimationPlayer` child node that animates it's owner. You can see `AnimationPlayer` node in the parent node if you check the `Editable Children` box in the context menu. 89 | 90 | - **`Sprite2D`** - *creates a `PackedScene` resource with `Sprite2D` node and child `AnimationPlayer` node* 91 | - **`Sprite3D`** - *creates a `PackedScene` resource with `Sprite3D` node and child `AnimationPlayer` node* 92 | - **`TextureRect`** - *creates a `PackedScene` resource with `TextureRect` node and child `AnimationPlayer` node* 93 | 94 | #### Animation strategies with `AnimationPlayer`: 95 | 96 | ##### For grid-based spritesheet layout: 97 | - **`Animate sprite's region`** - *animates the `region` property of the sprite* 98 | - **`Animate sprite's frame index`** - *animates the `frame` property of the sprite* 99 | - **`Animate sprite's frame coords`** - *animates the `frame_coords` property of the sprite* 100 | - **`Animate single atlas texture's region`** - *animates the `region` property of the atlas texture of the sprite* 101 | - **`Animate multiple atlas texture instances`** - *instantiates an `AtlasTexture` per unique frame and animates the `texture` property of the sprite* 102 | 103 | ##### For packed spritesheet layout: 104 | - **`Animate sprite's region and offset`** - *animates the `region` and `offset` properties of the sprite* 105 | - **`Animate single atlas texture's region and margin`** - *animates the `region` and `margin` properties of the `AtlasTexture` in the `texture` property of the sprite* 106 | - **`Animate multiple atlas texture instances`** - *instantiates an `AtlasTexture` per unique frame and animates the `texture` property of the sprite* 107 | 108 | ## 🤖 In plans for the future: 109 | 110 | ### Handle import error messages in the console 111 | 112 | Some errors may appear in the console during the import process. Most of them are internal bugs in the Godot engine version 4.x, while it has not yet been fixed all the shortcomings. 113 | 114 | If there will be error messages related directly to the import script - please create a ticket with their description and reproduction algorithm. 115 | 116 | ### Add import settings 117 | 118 | - Checkbox to embed image in resource **`Spritesheet/Embed Image: bool`** 119 | - Regular expressions for **`Layers/Include`**, **`Layers/Exclude`**, **`Tags/Exclude`** and **`Tags/Exclude`** 120 | - Import regular texture resources (**`TileSetAtlasSource`**, **`ImageTexture`**, **`CompressedTexture`**, **`PortableCompressedTexture`** and **`AtlasTexture`**) 121 | - Regular sprites (`Sprite2D` and `Sprite3D` without animation) 122 | - Import resource type **`TileSetAtlasSource`** 123 | - Import resource type **`NinePatchRect`** 124 | - And something else, if there are interesting proposals from you))) 125 | -------------------------------------------------------------------------------- /README.ru.md: -------------------------------------------------------------------------------- 1 | # Godot 4 Aseprite Importers 2 | 3 | [![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) 4 | [![ru](https://img.shields.io/badge/lang-ru-green.svg)](README.ru.md) 5 | 6 | Это плагин для игрового движка [Godot](https://godotengine.org/) 4.x, добавляющий несколько плагинов импорта для файлов графического пиксел-арт редактора [Aseprite](https://www.aseprite.org/) 1.3+. 7 | 8 | Этот плагин в настоящее время ещё работоспособен, но попробуйте вместо него [Importality](https://github.com/nklbdev/godot-4-importality) - более полный м функциональный набор импортеров графики и анимации самых разных форматов! 9 | 10 | https://user-images.githubusercontent.com/7024016/236665418-fe8036b9-7de5-4608-a247-b35a7f97891b.mp4 11 | 12 | ## 💽 Установка 13 | 14 | Просто скачайте этот плагин из [Библиотеки Ассетов Godot](https://godotengine.org/asset-library/asset/1880) 15 | 16 | Другой способ - скачайте или клонируйте этот репозиторий и скопируйте содержимое его папки `addons` в папку `addons` вашего проекта. 17 | 18 | После этого: 19 | 20 | - Откройте Настройки проекта (Project Settings) 21 | - Переключитесь на вкладку Плагины (Plugins) 22 | - Включите плагин, установив флажок напротив его названия 23 | - Переключитесь на вкладку Основное (General) 24 | - Включите переключатель "Расширенные настройки" (Advanced Settings) 25 | - Промотайте вниз дерево настроек и выберите секцию Aseprite Importers 26 | - Укажите в настройках этой секции путь к исполняемому файлу Aseprite 27 | 28 | ## 👷‍♀️ Как использовать 29 | 30 | После установки плагина в проекте появится поддержка `.ase`- и `.aseprite`-файлов. 31 | 1. Поместите Aseprite-файлы в проект 32 | 2. Выберите один или несколько Aseprite-файлов в дереве файловой системы проекта 33 | 3. В панели импорта выберите один из плагинов импорта 34 | 4. Установите настройки импорта 35 | 5. Опционально установите настроеннуюю конфигурацию настроек по умолчанию для выбранного плагина импорта 36 | 6. Нажмите кнопку `Reimport` 37 | 38 | ## 🛠 Настройки импорта 39 | 40 | - **`Spritesheet`** - *настройки листа спрайтов* 41 | - **`Embed Image` (пока не реализовано)** - *включить полученное изображение в ресурс или разместить рядом с исходным файлом* 42 | - **`Layout`** - *вариант раскладки листа спрайтов* 43 | - **`Packed`** - упакованный - *все спрайты обрезаны и максимально плотно прилегают друг к другу* 44 | - **`By Rows`** (с параметром **`Fixed Columns Count`**) - по строкам - *все спрайты имеют одинаковый размер и располагаются по строкам фиксированной ширины* 45 | - **`By Columns`** (с параметром **`Fixed Columns Count`**) - по колонкам - *все спрайты имеют одинаковый размер и располагаются по колонкам фиксированной высоты* 46 | - **`Border Type`** - тип рамки вокруг каждого спрайта 47 | - **`None`** - *спрайты в листе примыкают друг к другу без зазоров* 48 | - **`Transparent`** - *вокруг каждого спрайта создается прозрачная рамка шириной в 1 пиксел* 49 | - **`Extruded`** - *вокруг каждого спрайта создается рамка шириной в 1 пиксел, дублирующая цвета прилежащих пикселов спрайта* 50 | - **`Trim`** - (only for grid-based layouts) - *одинаково уменьшает все ячейки спрайтов так, чтобы анимация была вписана в новый размер ячейки* 51 | - **`Ignore Empty`** - *не включает в лист спрайты, на которых все пикселы прозрачные* 52 | - **`Merge Duplicates`** - *объединяет одинаковые спрайты в одни и те же участки на листе* 53 | - **`Animation`** - *настройки анимации* 54 | - **`Default`** - *анимация по умолчанию* 55 | - **`Name`** - *имя анимации, которым будет названа анимация, если в исходном файле нет ни одного тэга* 56 | - **`Direction`** - *направление анимации, созданной по умолчанию* 57 | - **`Forward`** - *анимация проигрывается от первого до последнего кадров* 58 | - **`Reverse`** - *анимация проигрывается от последнего до первого кадров* 59 | - **`Ping-pong`** - *анимация проигрывается от первого до последнего кадров, а потом обратно до первого, без дублирования последнего кадра* 60 | - **`Ping-pong reverse`** - *анимация проигрывается от последнего до первого кадров, а потом обратно до последнего, без дублирования первого кадра* 61 | - **`Repeat Count`** - *количество повторений для анимации, созданной по умолчанию (0 - для бесконечного повторения). Крайние кадры не дублируются, когда анимация меняет направление.* 62 | - **`Autoplay`** - *имя анимации, которая будет помечена, как запускающаяся автоматически* 63 | - **`Strategy...` (только для анимаций, основанных на `AnimationPlayer`)** - *набор свойств узла, которые будет использовать `AnimationPlayer` для анимирования* 64 | - **`Layers` (пока не реализовано)** - *настройки слоев Aseprite* 65 | - **`Include Reg Ex` (пока не реализовано)** - *регулярное выражение для белого списка включаемых слоев* 66 | - **`Exclude Reg Ex` (пока не реализовано)** - *регулярное выражение для черного списка включаемых слоев* 67 | - **`Tags` (пока не реализовано)** - *настройки тэгов Aseprite* 68 | - **`Include Reg Ex` (пока не реализовано)** - *регулярное выражение для белого списка включаемых тэгов* 69 | - **`Exclude Reg Ex` (пока не реализовано)** - *регулярное выражение для черного списка включаемых тэгов* 70 | 71 | ## 🧱 Типы импортируемых ресурсов 72 | 73 | ### 🖼️ Текстура (ресурс `Texture`) 74 | 75 | Вы можете импортировать ваши `*.aseprite` или `*.ase` файлы как обычные текстуры из файлов изображений. К несчастью, нет возможности выбирать слои и кадры анимации. В этом режиме текстура создается из всех видимых слоев на самом первом кадре анимации. 76 | 77 | ### 🎞 Анимации, основанные на ресурсе `SpriteFrames` 78 | 79 | - **`SpriteFrames`** - *создает ресурс `SpriteFrames` для дальнейшего его использования в анимированных спрайтах* 80 | - **`AnimatedSprite2D`** - *готовый анимированный спрайт для плоских сцен* 81 | - **`AnimatedSprite3D`** - *готовый анимированный спрайт для объемных сцен* 82 | 83 | ### 📽 Анимации, основанные на узле `AnimationPlayer` 84 | 85 | Создает ресурс `PackedScene` с дочерним узлом `AnimationPlayer`, с помощью которого происходит анимация. Вы можете увидеть этот узел в родительской сцене, если установите в контекстном меню флажок `Editable Children` 86 | 87 | - **`Sprite2D`** - *создает ресурс `PackedScene` с узлом `Sprite2D` и дочерним узлом `AnimationPlayer`* 88 | - **`Sprite3D`** - *создает ресурс `PackedScene` с узлом `Sprite3D` и дочерним узлом `AnimationPlayer`* 89 | - **`TextureRect`** - *создает ресурс `PackedScene` с узлом `TextureRect` и дочерним узлом `AnimationPlayer`* 90 | 91 | #### Стратегии анимации с помощью `AnimationPlayer`: 92 | 93 | ##### При раскладке спрайтов, основанной на сетке: 94 | 95 | - **`Animate sprite's region`** - *анимирует свойство спрайта `region`* 96 | - **`Animate sprite's frame index`** - *анимирует свойство спрайта `frame`* 97 | - **`Animate sprite's frame coords`** - *анимирует свойство спрайта `frame_coords`* 98 | - **`Animate single atlas texture's region`** - *анимирует свойство атлас-текстуры спрайта `region`* 99 | - **`Animate multiple atlas texture instances`** - *создает по экземпляру атлас-текстуры на каждый уникальный фрейм и анимирует свойство спрайта `texture`, подставляя их в него одну за одной* 100 | 101 | ##### При упакованной раскладке тайлов: 102 | 103 | - **`Animate sprite's region and offset`** - *анимирует свойства спрайта `region` и `offset`* 104 | - **`Animate single atlas texture's region and margin`** - *анимирует свойства атлас-текстуры спрайта `region` и `margin`* 105 | - **`Animate multiple atlas texture instances`** - *создает по экземпляру атлас-текстуры на каждый уникальный фрейм и анимирует свойство спрайта `texture`, подставляя их в него одну за одной* 106 | 107 | ## 🤖 Планы на будущее: 108 | 109 | ### Подавить возникающие при импорте сообщения об ошибках в консоли 110 | 111 | Во время выполнения импорта могут появляться некоторые ошибки в консоли. Большинство из них - внутренние ошибки движка Godot версии 4.x, пока в нем еще не исправлены все недочёты. 112 | 113 | Если там будут возникать сообщения об ошибках, связанные непосредственно с работой скрипта импорта - пожалуйста, создайте тикет с их описанием и алгоритмом воспроизведения. 114 | 115 | ### Добавить настройки импорта 116 | 117 | #### Флажок для включения изображения в ресурс `Spritesheet/Embed Image: bool` 118 | 119 | Пока что возможности движка Godot 4 не позволяют сохранить `CompressedTexture` в папку импортированных файлов. Этот ресурс создается автоматически только для изображений, поддерживаемых движком. По этому чтобы получить `CompressedTexture` нужно создать файл изображения в дереве ресурсов проекта и указать движку импортировать его. 120 | 121 | Если же встраивать изображение в ресурс формата `.res` (бинарный), то оно будет запаковано не самым оптимальным образом, и будет занимать в несколько раз больше места на диске. 122 | 123 | Если использовать формат ресурса `.tres` (текстовый), то встроенное изображение будет кодироваться в `Base64`, и займет еще в несколько раз больше места. 124 | 125 | #### Полный список планируемых функций: 126 | 127 | - Опция включения полученного изображения листа тайлов в ресурс: **`Spritesheet/Embed Image: bool`** 128 | - Регулярные выражения для включения/исключения слоёв и тэгов: **`Layers/Include`**, **`Layers/Exclude`**, **`Tags/Exclude`** and **`Tags/Exclude`** 129 | - Импорт обычных ресурсов текстур: (**`TileSetAtlasSource`**, **`ImageTexture`**, **`CompressedTexture`**, **`PortableCompressedTexture`**, и **`AtlasTexture`**) 130 | - Импорт обычных спрайтов (`Sprite2D` и `Sprite3D` без анимации) 131 | - Импорт ресурса **`TileSetAtlasSource`** 132 | - Импорт ресурса **`NinePatchRect`** 133 | - И еще чего-нибудь, если будут интересные предложения от вас))) 134 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/aseprite_image_format_loader_extension.gd: -------------------------------------------------------------------------------- 1 | extends ImageFormatLoaderExtension 2 | 3 | const Common = preload("common.gd") 4 | 5 | const __recognized_extensions: PackedStringArray = ["ase", "aseprite"] 6 | 7 | func _get_recognized_extensions() -> PackedStringArray: 8 | return __recognized_extensions 9 | 10 | #func _load_image(image: Image, file_access: FileAccess, flags: int, scale: float) -> Error: 11 | # type hints is removed because different releases of Godot have different signature of this method 12 | func _load_image(image, file_access, flags, scale): 13 | flags = flags as ImageFormatLoader.LoaderFlags 14 | 15 | var source_file_path: String = ProjectSettings.globalize_path(file_access.get_path_absolute()) 16 | var global_png_path: String = source_file_path + ".png" 17 | var aseprite_executable_path: String = ProjectSettings.get_setting(Common.ASEPRITE_EXECUTABLE_PATH_SETTING_NAME) 18 | 19 | var command_line_params: PackedStringArray = PackedStringArray([ 20 | "--batch", 21 | source_file_path, 22 | "--frame-range", "0,0", 23 | "--save-as", 24 | global_png_path 25 | ]) 26 | 27 | var output: Array = [] 28 | var err: Error = OS.execute( 29 | ProjectSettings.get_setting(Common.ASEPRITE_EXECUTABLE_PATH_SETTING_NAME), 30 | command_line_params, output, true) 31 | if err: 32 | push_error("There was an error while executing aseprite command: %s" % error_string(err)) 33 | return err 34 | 35 | image.load_png_from_buffer(FileAccess.get_file_as_bytes(global_png_path)) 36 | DirAccess.remove_absolute(global_png_path) 37 | 38 | return OK 39 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/common.gd: -------------------------------------------------------------------------------- 1 | extends Object 2 | 3 | const PLUGIN_BUNDLE_NAME: StringName = "aseprite_importers" 4 | const ASEPRITE_EXECUTABLE_PATH_SETTING_NAME: StringName = PLUGIN_BUNDLE_NAME + "/aseprite_executable_path" 5 | 6 | enum CompressMode { 7 | LOSSLESS = 0, 8 | LOSSY = 1, 9 | VRAM_COMPRESSED = 2, 10 | VRAM_UNCOMPRESSED = 3, 11 | BASIS_UNIVERSAL = 4, 12 | } 13 | const COMPRESS_MODES_NAMES: PackedStringArray = [ 14 | "Lossless", 15 | "Lossy", 16 | "VRAM Compressed", 17 | "VRAM Uncompressed", 18 | "Basis Universal", 19 | ] 20 | 21 | # ONLY FOR VRAM_COMPRESSED 22 | enum HdrCompression { 23 | DISABLED = 0, 24 | OPAQUE_ONLY = 1, 25 | ALWAYS = 2, 26 | } 27 | const HDR_COMPRESSION_NAMES: PackedStringArray = [ 28 | "Disabled", 29 | "Opaque Only", 30 | "Always", 31 | ] 32 | 33 | # EXCEPT LOSSLESS 34 | enum NormalMap { 35 | DETECT = 0, 36 | ENABLE = 1, 37 | DISABLED = 2, 38 | } 39 | const NORMAL_MAP_NAMES: PackedStringArray = [ 40 | "Detect", 41 | "Enable", 42 | "Disabled", 43 | ] 44 | 45 | enum ChannelPack { 46 | SRGB_FRIENDLY = 0, 47 | OPTIMIZED = 1, 48 | } 49 | const CHANNEL_PACK_NAMES: PackedStringArray = [ 50 | "sRGB Friendly", 51 | "Optimized", 52 | ] 53 | 54 | enum Roughness { 55 | DETECT = 0, 56 | DISABLED = 1, 57 | RED = 2, 58 | GREEN = 3, 59 | BLUE = 4, 60 | ALPHA = 5, 61 | GRAY = 6, 62 | } 63 | const ROUGHNESS_NAMES: PackedStringArray = [ 64 | "Detect", 65 | "Disabled", 66 | "Red", 67 | "Green", 68 | "Blue", 69 | "Alpha", 70 | "Gray", 71 | ] 72 | 73 | enum CompressMode3D { 74 | DISABLED = 0, 75 | VRAM_COMPRESSED = 1, 76 | BASIS_UNIVERSAL = 2, 77 | } 78 | const COMPRESS_MODE_3D_NAMES: PackedStringArray = [ 79 | "Disabled", 80 | "VRAM Compressed", 81 | "Basis Universal", 82 | ] 83 | 84 | const EMPTY_CALLABLE: Callable = Callable() 85 | 86 | static func create_option( 87 | name: String, 88 | default_value: Variant, 89 | property_hint: PropertyHint = PROPERTY_HINT_NONE, 90 | hint_string: String = "", 91 | usage: PropertyUsageFlags = PROPERTY_USAGE_NONE, 92 | get_is_visible: Callable = EMPTY_CALLABLE 93 | ) -> Dictionary: 94 | var option_data: Dictionary = { 95 | name = name, 96 | default_value = default_value, 97 | } 98 | if hint_string: option_data["hint_string"] = hint_string 99 | if property_hint: option_data["property_hint"] = property_hint 100 | if usage: option_data["usage"] = usage 101 | if get_is_visible != EMPTY_CALLABLE: option_data["get_is_visible"] = get_is_visible 102 | return option_data 103 | 104 | enum BorderType { 105 | None = 0, 106 | Transparent = 1, 107 | Extruded = 2, 108 | } 109 | const SPRITESHEET_BORDER_TYPES: PackedStringArray = [ 110 | "None", 111 | "Transparent", 112 | "Extruded", 113 | ] 114 | 115 | enum AnimationDirection { 116 | FORWARD = 0, 117 | REVERSE = 1, 118 | PING_PONG = 2, 119 | PING_PONG_REVERSE = 3, 120 | } 121 | const ASEPRITE_OUTPUT_ANIMATION_DIRECTIONS: PackedStringArray = [ 122 | "forward", "reverse", "pingpong", "pingpong_reverse" ] 123 | const PRESET_OPTIONS_ANIMATION_DIRECTIONS: PackedStringArray = [ 124 | "Forward", "Reverse", "Ping-pong", "Ping-pong reverse" ] 125 | 126 | enum SpritesheetLayout { 127 | PACKED = 0, 128 | BY_ROWS = 1, 129 | BY_COLUMNS = 2, 130 | } 131 | const SPRITESHEET_LAYOUTS: PackedStringArray = ["Packed", "By rows", "By columns"] 132 | 133 | const OPTION_SPRITESHEET_BORDER_TYPE: String = "spritesheet/border_type" 134 | const OPTION_SPRITESHEET_TRIM: String = "spritesheet/trim" 135 | const OPTION_SPRITESHEET_IGNORE_EMPTY: String = "spritesheet/ignore_empty" 136 | const OPTION_SPRITESHEET_MERGE_DUPLICATES: String = "spritesheet/merge_duplicates" 137 | const OPTION_SPRITESHEET_LAYOUT: String = "spritesheet/layout" 138 | const OPTION_ANIMATION_DEFAULT_NAME: String = "animation/default/name" 139 | const OPTION_ANIMATION_DEFAULT_DIRECTION: String = "animation/default/direction" 140 | const OPTION_ANIMATION_DEFAULT_REPEAT_COUNT: String = "animation/default/repeat_count" 141 | const OPTION_ANIMATION_AUTOPLAY_NAME: String = "animation/autoplay" 142 | const OPTION_ANIMATION_STRATEGY: String = "animation/strategy" 143 | const OPTION_LAYERS_INCLUDE_REG_EX: String = "layers/include_reg_ex" 144 | const OPTION_LAYERS_EXCLUDE_REG_EX: String = "layers/exclude_reg_ex" 145 | const OPTION_TAGS_INCLUDE_REG_EX: String = "tags/include_reg_ex" 146 | const OPTION_TAGS_EXCLUDE_REG_EX: String = "tags/exclude_reg_ex" 147 | const SPRITESHEET_FIXED_ROWS_COUNT: String = "spritesheet/fixed_rows_count" 148 | const SPRITESHEET_FIXED_COLUMNS_COUNT: String = "spritesheet/fixed_columns_count" 149 | 150 | 151 | 152 | class ParsedAnimationOptions: 153 | var border_type: BorderType 154 | var trim: bool 155 | var ignore_empty: bool 156 | var merge_duplicates: bool 157 | var spritesheet_layout: SpritesheetLayout 158 | var spritesheet_fixed_rows_count: int 159 | var spritesheet_fixed_columns_count: int 160 | var default_animation_name: String 161 | var default_animation_direction: AnimationDirection 162 | var default_animation_repeat_count: int 163 | var animation_autoplay_name: String 164 | func _init(options: Dictionary) -> void: 165 | border_type = options[OPTION_SPRITESHEET_BORDER_TYPE] 166 | trim = options[OPTION_SPRITESHEET_TRIM] 167 | ignore_empty = options[OPTION_SPRITESHEET_IGNORE_EMPTY] 168 | merge_duplicates = options[OPTION_SPRITESHEET_MERGE_DUPLICATES] 169 | spritesheet_layout = options[OPTION_SPRITESHEET_LAYOUT] 170 | spritesheet_fixed_rows_count = options[SPRITESHEET_FIXED_ROWS_COUNT] 171 | spritesheet_fixed_columns_count = options[SPRITESHEET_FIXED_COLUMNS_COUNT] 172 | default_animation_name = options[OPTION_ANIMATION_DEFAULT_NAME].strip_edges().strip_escapes() 173 | if default_animation_name.is_empty(): default_animation_name = "default" 174 | default_animation_direction = options[OPTION_ANIMATION_DEFAULT_DIRECTION] 175 | default_animation_repeat_count = options[OPTION_ANIMATION_DEFAULT_REPEAT_COUNT] 176 | animation_autoplay_name = options[OPTION_ANIMATION_AUTOPLAY_NAME].strip_edges().strip_escapes() 177 | 178 | static func create_common_animation_options() -> Array[Dictionary]: 179 | return [ 180 | create_option(OPTION_SPRITESHEET_LAYOUT, SpritesheetLayout.PACKED, PROPERTY_HINT_ENUM, ",".join(SPRITESHEET_LAYOUTS), PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED ), 181 | create_option(SPRITESHEET_FIXED_ROWS_COUNT, 1, PROPERTY_HINT_RANGE, "1,32,1,or_greater", PROPERTY_USAGE_EDITOR, 182 | func(options): return options[OPTION_SPRITESHEET_LAYOUT] == SpritesheetLayout.BY_COLUMNS), 183 | create_option(SPRITESHEET_FIXED_COLUMNS_COUNT, 1, PROPERTY_HINT_RANGE, "1,32,1,or_greater", PROPERTY_USAGE_EDITOR, 184 | func(options): return options[OPTION_SPRITESHEET_LAYOUT] == SpritesheetLayout.BY_ROWS), 185 | create_option(OPTION_SPRITESHEET_BORDER_TYPE, BorderType.None, PROPERTY_HINT_ENUM, ",".join(SPRITESHEET_BORDER_TYPES), PROPERTY_USAGE_EDITOR), 186 | create_option(OPTION_SPRITESHEET_TRIM, false, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR, 187 | func(options): return options[OPTION_SPRITESHEET_LAYOUT] != SpritesheetLayout.PACKED), 188 | create_option(OPTION_SPRITESHEET_IGNORE_EMPTY, false, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), 189 | create_option(OPTION_SPRITESHEET_MERGE_DUPLICATES, false, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), 190 | create_option(OPTION_ANIMATION_DEFAULT_NAME, "default", PROPERTY_HINT_PLACEHOLDER_TEXT, "default", PROPERTY_USAGE_EDITOR), 191 | create_option(OPTION_ANIMATION_DEFAULT_DIRECTION, AnimationDirection.FORWARD, PROPERTY_HINT_ENUM, ",".join(PRESET_OPTIONS_ANIMATION_DIRECTIONS), PROPERTY_USAGE_EDITOR), 192 | create_option(OPTION_ANIMATION_DEFAULT_REPEAT_COUNT, 0, PROPERTY_HINT_RANGE, "0,32,1,or_greater", PROPERTY_USAGE_EDITOR), 193 | create_option(OPTION_ANIMATION_AUTOPLAY_NAME, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), 194 | ] 195 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/_animation_importer_base.gd: -------------------------------------------------------------------------------- 1 | extends "_importer_base.gd" 2 | 3 | # Base class for all nested animation import plugins 4 | 5 | func set_preset(name: StringName, options: Array[Dictionary]) -> void: 6 | var preset: Array[Dictionary] = [] 7 | preset.append_array(_parent_plugin.common_options) 8 | preset.append_array(options) 9 | __option_visibility_checkers.clear() 10 | for option in preset: 11 | var option_visibility_checker: Callable = option.get("get_is_visible", Common.EMPTY_CALLABLE) 12 | if option_visibility_checker != Common.EMPTY_CALLABLE: 13 | __option_visibility_checkers[option.name] = option_visibility_checker 14 | _presets[name] = preset 15 | 16 | func _init(parent_plugin: EditorPlugin) -> void: 17 | super(parent_plugin) 18 | 19 | class ExportResult: 20 | var error: Error 21 | var error_message: String 22 | var raw_output: String 23 | var parsed_json: JSON 24 | var texture: Texture2D 25 | var spritesheet_metadata: SpritesheetMetadata 26 | 27 | class FrameData: 28 | var region_rect: Rect2i 29 | var region_rect_offset: Vector2i 30 | var duration_ms: int 31 | 32 | class AnimationTag: 33 | var name: String 34 | var frames: Array[FrameData] 35 | var duration_ms: int 36 | var looped: bool 37 | 38 | class SpritesheetMetadata: 39 | var source_size: Vector2i 40 | var spritesheet_size: Vector2i 41 | var animation_tags: Array[AnimationTag] 42 | 43 | class TrackFrame: 44 | var duration_ms: int 45 | var value: Variant 46 | func _init(duration_ms: int, value: Variant) -> void: 47 | self.duration_ms = duration_ms 48 | self.value = value 49 | 50 | const __sheet_types_by_spritesheet_layout: Dictionary = { 51 | Common.SpritesheetLayout.PACKED: "packed", 52 | Common.SpritesheetLayout.BY_ROWS: "rows", 53 | Common.SpritesheetLayout.BY_COLUMNS: "columns", 54 | } 55 | 56 | func _export_texture(source_file: String, options: Common.ParsedAnimationOptions, image_options: Dictionary, gen_files: Array[String]) -> ExportResult: 57 | var export_result = ExportResult.new() 58 | var spritesheet_metadata = SpritesheetMetadata.new() 59 | var png_path: String = source_file.get_basename() + ".png" 60 | var data_path: String = source_file.get_basename() + ".json" 61 | var global_png_path: String = ProjectSettings.globalize_path(png_path) 62 | var global_data_path: String = ProjectSettings.globalize_path(data_path) 63 | var is_png_file_present = FileAccess.file_exists(png_path) 64 | 65 | var aseprite_executable_path: String = ProjectSettings.get_setting(Common.ASEPRITE_EXECUTABLE_PATH_SETTING_NAME) 66 | 67 | var variable_options: Array 68 | if options.spritesheet_layout == Common.SpritesheetLayout.BY_ROWS: 69 | variable_options += ["--sheet-columns", str(options.spritesheet_fixed_columns_count)] 70 | if options.spritesheet_layout == Common.SpritesheetLayout.BY_COLUMNS: 71 | variable_options += ["--sheet-rows", str(options.spritesheet_fixed_rows_count)] 72 | match options.border_type: 73 | Common.BorderType.Transparent: variable_options += ["--inner-padding", "1"] 74 | Common.BorderType.Extruded: variable_options += ["--extrude"] 75 | Common.BorderType.None: pass 76 | _: push_error("unexpected border type") 77 | if options.ignore_empty: variable_options += ["--ignore-empty"] 78 | if options.merge_duplicates: variable_options += ["--merge-duplicates"] 79 | if options.trim: variable_options += ["--trim" if options.spritesheet_layout == Common.SpritesheetLayout.PACKED else "--trim-sprite"] 80 | 81 | var command_line_params: PackedStringArray = PackedStringArray([ 82 | "--batch", 83 | "--filename-format", "{tag}{tagframe}", 84 | "--format", "json-array", 85 | "--list-tags", 86 | "--trim" if options.spritesheet_layout == Common.SpritesheetLayout.PACKED else "--trim-sprite" if options.trim else "", 87 | "--sheet-type", __sheet_types_by_spritesheet_layout[options.spritesheet_layout], 88 | ] + variable_options + [ 89 | "--sheet", global_png_path, 90 | "--data", global_data_path, 91 | ProjectSettings.globalize_path(source_file) 92 | ]) 93 | 94 | var err: Error = OS.execute( 95 | ProjectSettings.get_setting(Common.ASEPRITE_EXECUTABLE_PATH_SETTING_NAME), 96 | command_line_params) 97 | if err: 98 | export_result.error = err 99 | export_result.error_message = "There was an error while executing aseprite command: %s" % error_string(err) 100 | return export_result 101 | var json = JSON.new() 102 | var data: String = FileAccess.get_file_as_string(global_data_path) 103 | json.parse(data) 104 | DirAccess.remove_absolute(global_data_path) 105 | 106 | var sourceSizeData = json.data.frames[0].sourceSize 107 | spritesheet_metadata.source_size = Vector2i(sourceSizeData.w, sourceSizeData.h) 108 | spritesheet_metadata.spritesheet_size = Vector2i(json.data.meta.size.w, json.data.meta.size.h) 109 | var frames_data: Array[FrameData] 110 | for frame_data in json.data.frames: 111 | var fd: FrameData = FrameData.new() 112 | fd.region_rect = Rect2i( 113 | frame_data.frame.x, frame_data.frame.y, 114 | frame_data.frame.w, frame_data.frame.h) 115 | fd.region_rect_offset = Vector2i( 116 | frame_data.spriteSourceSize.x, frame_data.spriteSourceSize.y) 117 | if options.border_type == Common.BorderType.Transparent: 118 | fd.region_rect = fd.region_rect.grow(-1) 119 | fd.region_rect_offset += Vector2i.ONE 120 | fd.duration_ms = frame_data.duration 121 | frames_data.append(fd) 122 | 123 | var tags_data: Array = json.data.meta.frameTags 124 | var unique_names: Array[String] = [] 125 | if tags_data.is_empty(): 126 | var default_animation_tag = AnimationTag.new() 127 | default_animation_tag.name = options.default_animation_name 128 | if options.default_animation_repeat_count > 0: 129 | for cycle_index in options.default_animation_repeat_count: 130 | default_animation_tag.frames.append_array(frames_data) 131 | else: 132 | default_animation_tag.frames = frames_data 133 | default_animation_tag.looped = true 134 | spritesheet_metadata.animation_tags.append(default_animation_tag) 135 | else: 136 | for tag_data in tags_data: 137 | var animation_tag = AnimationTag.new() 138 | animation_tag.name = tag_data.name.strip_edges().strip_escapes() 139 | if animation_tag.name.is_empty(): 140 | push_error("Found empty tag name") 141 | return null 142 | if unique_names.has(animation_tag.name): 143 | push_error("Found duplicated tag name") 144 | return null 145 | unique_names.append(animation_tag.name) 146 | 147 | var animation_direction = Common.ASEPRITE_OUTPUT_ANIMATION_DIRECTIONS.find(tag_data.direction) 148 | var animation_frames: Array = frames_data.slice(tag_data.from, tag_data.to + 1) 149 | # Apply animation direction 150 | if animation_direction & Common.AnimationDirection.REVERSE > 0: 151 | animation_frames.reverse() 152 | if animation_direction & Common.AnimationDirection.PING_PONG > 0: 153 | if animation_frames.size() > 2: 154 | animation_frames += animation_frames.slice(-2, 0, -1) 155 | 156 | var repeat_count: int = int(tag_data.get("repeat", "0")) 157 | if repeat_count > 0: 158 | for cycle_index in repeat_count: 159 | animation_tag.frames.append_array(animation_frames) 160 | else: 161 | animation_tag.frames.append_array(animation_frames) 162 | animation_tag.looped = true 163 | spritesheet_metadata.animation_tags.append(animation_tag) 164 | 165 | var image = Image.load_from_file(global_png_path) 166 | image.save_png(global_png_path) 167 | image = null 168 | 169 | if is_png_file_present: 170 | # This is for reload the image if it was changed by import processing 171 | _parent_plugin.get_editor_interface().get_resource_filesystem().call_deferred("scan_sources") 172 | else: 173 | # This function does not import the file. But its call is needed 174 | # so that the call to the "append" function passes without errors 175 | _parent_plugin.get_editor_interface().get_resource_filesystem().update_file(png_path) 176 | append_import_external_resource(png_path, image_options, "texture") 177 | 178 | # This is a working way to reuse a previously imported resource. Don't change it! 179 | var texture: Texture2D = ResourceLoader.load(png_path, "Texture2D", ResourceLoader.CACHE_MODE_REPLACE) as Texture2D 180 | 181 | 182 | export_result.texture = texture 183 | export_result.raw_output = data 184 | export_result.parsed_json = json 185 | export_result.spritesheet_metadata = spritesheet_metadata 186 | return export_result 187 | 188 | static func _create_animation_player( 189 | spritesheet_metadata: SpritesheetMetadata, 190 | track_value_getters_by_property_path: Dictionary, 191 | animation_autoplay_name: String = "" 192 | ) -> AnimationPlayer: 193 | var animation_player: AnimationPlayer = AnimationPlayer.new() 194 | animation_player.name = "AnimationPlayer" 195 | var animation_library: AnimationLibrary = AnimationLibrary.new() 196 | 197 | for animation_tag in spritesheet_metadata.animation_tags: 198 | var animation: Animation = Animation.new() 199 | for property_path in track_value_getters_by_property_path.keys(): 200 | __create_track(animation, property_path, 201 | animation_tag, track_value_getters_by_property_path[property_path]) 202 | 203 | animation.length = animation_tag.frames.reduce( 204 | func (accum: int, frame_data: FrameData): 205 | return accum + frame_data.duration_ms, 0) * 0.001 206 | 207 | animation.loop_mode = Animation.LOOP_LINEAR if animation_tag.looped else Animation.LOOP_NONE 208 | animation_library.add_animation(animation_tag.name, animation) 209 | animation_player.add_animation_library("", animation_library) 210 | 211 | if not animation_autoplay_name.is_empty(): 212 | if animation_player.has_animation(animation_autoplay_name): 213 | animation_player.autoplay = animation_autoplay_name 214 | else: 215 | push_warning("Not found animation to set autoplay with name \"%s\"" % 216 | animation_autoplay_name) 217 | 218 | return animation_player 219 | 220 | static func __create_track( 221 | animation: Animation, 222 | property_path: NodePath, 223 | animation_tag: AnimationTag, 224 | track_value_getter: Callable # func(fd: FrameData) -> Variant for each fd in animation_tag.frames 225 | ) -> int: 226 | var track_index = animation.add_track(Animation.TYPE_VALUE) 227 | animation.track_set_path(track_index, property_path) 228 | animation.value_track_set_update_mode(track_index, Animation.UPDATE_DISCRETE) 229 | animation.track_set_interpolation_loop_wrap(track_index, false) 230 | animation.track_set_interpolation_type(track_index, Animation.INTERPOLATION_NEAREST) 231 | var track_frames = animation_tag.frames.map( 232 | func (frame_data: FrameData): 233 | return TrackFrame.new( 234 | frame_data.duration_ms, 235 | track_value_getter.call(frame_data))) 236 | 237 | var transition: float = 1 238 | var track_length_ms: int = 0 239 | var previous_track_frame: TrackFrame = null 240 | for track_frame in track_frames: 241 | if previous_track_frame == null or track_frame.value != previous_track_frame.value: 242 | animation.track_insert_key(track_index, 243 | track_length_ms * 0.001, track_frame.value, transition) 244 | previous_track_frame = track_frame 245 | track_length_ms += track_frame.duration_ms 246 | 247 | return track_index 248 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/_importer_base.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorImportPlugin 3 | 4 | # Base class for all nested import plugins 5 | 6 | const Common = preload("../common.gd") 7 | 8 | var _parent_plugin: EditorPlugin 9 | 10 | var _import_order: int = 0 11 | var _importer_name: String = "" 12 | var _priority: float = 1 13 | var _recognized_extensions: PackedStringArray 14 | var _resource_type: StringName 15 | var _save_extension: String 16 | var _visible_name: String 17 | var _presets: Dictionary 18 | var __option_visibility_checkers: Dictionary 19 | 20 | func _init(parent_plugin: EditorPlugin) -> void: 21 | _parent_plugin = parent_plugin 22 | 23 | func _get_import_options(path: String, preset_index: int) -> Array[Dictionary]: 24 | return _presets.values()[preset_index] as Array[Dictionary] 25 | 26 | func _get_option_visibility(path: String, option_name: StringName, options: Dictionary) -> bool: 27 | var option_visibility_checker: Callable = __option_visibility_checkers.get(option_name, Common.EMPTY_CALLABLE) 28 | if option_visibility_checker: 29 | if option_visibility_checker == Common.EMPTY_CALLABLE: 30 | return true 31 | else: 32 | return option_visibility_checker.call(options) 33 | else: 34 | return true 35 | 36 | func _get_import_order() -> int: 37 | return _import_order 38 | 39 | func _get_importer_name() -> String: 40 | return _importer_name 41 | 42 | func _get_preset_count() -> int: 43 | return _presets.size() 44 | 45 | func _get_preset_name(preset_index: int) -> String: 46 | return _presets.keys()[preset_index] 47 | 48 | func _get_priority() -> float: 49 | return _priority 50 | 51 | func _get_recognized_extensions() -> PackedStringArray: 52 | return _recognized_extensions 53 | 54 | func _get_resource_type() -> String: 55 | return _resource_type 56 | 57 | func _get_save_extension() -> String: 58 | return _save_extension 59 | 60 | func _get_visible_name() -> String: 61 | return _visible_name 62 | 63 | func _import(source_file: String, save_path: String, options: Dictionary, 64 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 65 | return ERR_UNCONFIGURED 66 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/animated_sprite_2d.gd: -------------------------------------------------------------------------------- 1 | extends "_animation_importer_base.gd" 2 | 3 | const SpriteFramesImporter = preload("sprite_frames.gd") 4 | 5 | func _init(parent_plugin: EditorPlugin) -> void: 6 | super(parent_plugin) 7 | _import_order = 0 8 | _importer_name = "Aseprite AnimatedSprite2D Import" 9 | _priority = 1 10 | _recognized_extensions = ["ase", "aseprite"] 11 | _resource_type = "PackedScene" 12 | _save_extension = "scn" 13 | _visible_name = "AnimatedSprite2D" 14 | 15 | set_preset("Animation", []) 16 | 17 | 18 | func _import(source_file: String, save_path: String, options: Dictionary, 19 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 20 | var status: Error = OK 21 | var parsed_options = Common.ParsedAnimationOptions.new(options) 22 | var export_result: ExportResult = _export_texture(source_file, parsed_options, options, gen_files) 23 | if export_result.error: 24 | push_error("There was an error during exporting texture: %s with message: %s" % 25 | [error_string(export_result.error), export_result.error_message]) 26 | return export_result.error 27 | 28 | var packed_scene: PackedScene 29 | var animated_sprite: AnimatedSprite2D 30 | var sprite_frames: SpriteFrames 31 | 32 | if ResourceLoader.exists(source_file): 33 | # This is a working way to reuse a previously imported resource. Don't change it! 34 | packed_scene = ResourceLoader.load(source_file, "PackedScene", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene 35 | 36 | if packed_scene and packed_scene.can_instantiate(): 37 | animated_sprite = packed_scene.instantiate() as AnimatedSprite2D 38 | 39 | if animated_sprite: 40 | sprite_frames = animated_sprite.sprite_frames 41 | 42 | if not sprite_frames: 43 | sprite_frames = SpriteFrames.new() 44 | 45 | if not animated_sprite: 46 | animated_sprite = AnimatedSprite2D.new() 47 | animated_sprite.name = source_file.get_file().get_basename() 48 | 49 | animated_sprite.sprite_frames = sprite_frames 50 | 51 | if not packed_scene: 52 | packed_scene = PackedScene.new() 53 | 54 | 55 | status = SpriteFramesImporter.update_sprite_frames(export_result, sprite_frames) 56 | if status: push_error("Cannot update SpriteFrames", status); return status 57 | 58 | if not parsed_options.animation_autoplay_name.is_empty(): 59 | if sprite_frames.has_animation(parsed_options.animation_autoplay_name): 60 | animated_sprite.autoplay = parsed_options.animation_autoplay_name 61 | else: 62 | push_warning("Not found animation to set autoplay with name \"%s\"" % 63 | parsed_options.animation_autoplay_name) 64 | 65 | packed_scene.pack(animated_sprite) 66 | 67 | status = ResourceSaver.save(packed_scene, save_path + "." + _get_save_extension(), ResourceSaver.FLAG_COMPRESS) 68 | if status: push_error("Can't save imported resource.", status); return status 69 | return status 70 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/animated_sprite_3d.gd: -------------------------------------------------------------------------------- 1 | extends "_animation_importer_base.gd" 2 | 3 | const SpriteFramesImporter = preload("sprite_frames.gd") 4 | 5 | func _init(parent_plugin: EditorPlugin) -> void: 6 | super(parent_plugin) 7 | _import_order = 0 8 | _importer_name = "Aseprite AnimatedSprite3D Import" 9 | _priority = 1 10 | _recognized_extensions = ["ase", "aseprite"] 11 | _resource_type = "PackedScene" 12 | _save_extension = "scn" 13 | _visible_name = "AnimatedSprite3D" 14 | 15 | set_preset("Animation", []) 16 | 17 | 18 | func _import(source_file: String, save_path: String, options: Dictionary, 19 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 20 | var status: Error = OK 21 | var parsed_options = Common.ParsedAnimationOptions.new(options) 22 | var export_result: ExportResult = _export_texture(source_file, parsed_options, options, gen_files) 23 | if export_result.error: 24 | push_error("There was an error during exporting texture: %s with message: %s" % 25 | [error_string(export_result.error), export_result.error_message]) 26 | return export_result.error 27 | 28 | var packed_scene: PackedScene 29 | var animated_sprite: AnimatedSprite3D 30 | var sprite_frames: SpriteFrames 31 | 32 | if ResourceLoader.exists(source_file): 33 | # This is a working way to reuse a previously imported resource. Don't change it! 34 | packed_scene = ResourceLoader.load(source_file, "PackedScene", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene 35 | 36 | if packed_scene and packed_scene.can_instantiate(): 37 | animated_sprite = packed_scene.instantiate() as AnimatedSprite3D 38 | 39 | if animated_sprite: 40 | sprite_frames = animated_sprite.sprite_frames 41 | 42 | if not sprite_frames: 43 | sprite_frames = SpriteFrames.new() 44 | 45 | if not animated_sprite: 46 | animated_sprite = AnimatedSprite3D.new() 47 | animated_sprite.name = source_file.get_file().get_basename() 48 | 49 | animated_sprite.sprite_frames = sprite_frames 50 | 51 | if not packed_scene: 52 | packed_scene = PackedScene.new() 53 | 54 | 55 | status = SpriteFramesImporter.update_sprite_frames(export_result, sprite_frames) 56 | if status: push_error("Cannot update SpriteFrames", status); return status 57 | 58 | if not parsed_options.animation_autoplay_name.is_empty(): 59 | if sprite_frames.has_animation(parsed_options.animation_autoplay_name): 60 | animated_sprite.autoplay = parsed_options.animation_autoplay_name 61 | else: 62 | push_warning("Not found animation to set autoplay with name \"%s\"" % 63 | parsed_options.animation_autoplay_name) 64 | 65 | packed_scene.pack(animated_sprite) 66 | 67 | status = ResourceSaver.save(packed_scene, save_path + "." + _get_save_extension(), ResourceSaver.FLAG_COMPRESS) 68 | if status: push_error("Can't save imported resource.", status); return status 69 | 70 | return status 71 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/sprite_2d.gd: -------------------------------------------------------------------------------- 1 | extends "_animation_importer_base.gd" 2 | 3 | const OPTION_SPRITE2D_CENTERED: String = "sprite2d/centered" 4 | 5 | 6 | const OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY: String = "animation/strategy_(packed_spritesheet)" 7 | 8 | const PACKED_SPRITESHEET_ANIMATION_STRATEGIES: PackedStringArray = [ 9 | "Animate sprite's region and offset", 10 | "Animate single atlas texture's region and margin", 11 | "Animate multiple atlas texture instances", 12 | ] 13 | 14 | enum PackedSpritesheetAnimationStrategy 15 | { 16 | SpriteRegionAndOffset = 0, 17 | TextureRegionAndMargin = 1, 18 | TextureInstances = 2, 19 | } 20 | 21 | const OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY: String = "animation/strategy_(grid-based_spritesheet)" 22 | 23 | const GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES: PackedStringArray = [ 24 | "Animate sprite's region", 25 | "Animate sprite's frame index", 26 | "Animate sprite's frame coords", 27 | "Animate single atlas texture's region", 28 | "Animate multiple atlas texture instances", 29 | ] 30 | 31 | enum GridBasedSpritesheetAnimationStrategy 32 | { 33 | SpriteRegion = 0, 34 | SpriteFrameIndex = 1, 35 | SpriteFrameCoords = 2, 36 | TextureRegion = 3, 37 | TextureInstances = 4, 38 | } 39 | 40 | class Sprite2DParsedAnimationOptions: 41 | extends Common.ParsedAnimationOptions 42 | var centered: bool 43 | var packed_animation_strategy: PackedSpritesheetAnimationStrategy 44 | var grid_based_animation_strategy: GridBasedSpritesheetAnimationStrategy 45 | func _init(options: Dictionary) -> void: 46 | packed_animation_strategy = PACKED_SPRITESHEET_ANIMATION_STRATEGIES \ 47 | .find(options[OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY]) 48 | grid_based_animation_strategy = GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES \ 49 | .find(options[OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY]) 50 | super(options) 51 | 52 | 53 | func _init(parent_plugin: EditorPlugin) -> void: 54 | super(parent_plugin) 55 | _import_order = 0 56 | _importer_name = "Aseprite Sprite2D Import" 57 | _priority = 1 58 | _recognized_extensions = ["ase", "aseprite"] 59 | _resource_type = "PackedScene" 60 | _save_extension = "scn" 61 | _visible_name = "Sprite2D (with AnimationPlayer)" 62 | 63 | set_preset("Animation", [ 64 | Common.create_option(OPTION_SPRITE2D_CENTERED, true, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), 65 | Common.create_option( 66 | OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY, 67 | PackedSpritesheetAnimationStrategy.SpriteRegionAndOffset, 68 | PROPERTY_HINT_ENUM, ",".join(PACKED_SPRITESHEET_ANIMATION_STRATEGIES), 69 | PROPERTY_USAGE_EDITOR, 70 | func (options:Dictionary) -> bool: 71 | return options[Common.OPTION_SPRITESHEET_LAYOUT] == Common.SPRITESHEET_LAYOUTS[Common.SpritesheetLayout.PACKED]), 72 | Common.create_option( 73 | OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY, 74 | GridBasedSpritesheetAnimationStrategy.SpriteRegion, 75 | PROPERTY_HINT_ENUM, ",".join(GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES), 76 | PROPERTY_USAGE_EDITOR, 77 | func (options:Dictionary) -> bool: 78 | return options[Common.OPTION_SPRITESHEET_LAYOUT] != Common.SPRITESHEET_LAYOUTS[Common.SpritesheetLayout.PACKED]), 79 | ]) 80 | 81 | func _import(source_file: String, save_path: String, options: Dictionary, 82 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 83 | var status: Error = OK 84 | var parsed_options: Sprite2DParsedAnimationOptions = Sprite2DParsedAnimationOptions.new(options) 85 | var export_result: ExportResult = _export_texture(source_file, parsed_options, options, gen_files) 86 | if export_result.error: 87 | push_error("There was an error during exporting texture: %s with message: %s" % 88 | [error_string(export_result.error), export_result.error_message]) 89 | return export_result.error 90 | 91 | var centered = options[OPTION_SPRITE2D_CENTERED] 92 | 93 | var sprite: Sprite2D = Sprite2D.new() 94 | sprite.name = source_file.get_file().get_basename() 95 | sprite.texture = export_result.texture 96 | sprite.centered = centered 97 | 98 | var ssmd: SpritesheetMetadata = export_result.spritesheet_metadata 99 | var autoplay: String = parsed_options.animation_autoplay_name 100 | var animation_player: AnimationPlayer 101 | match parsed_options.spritesheet_layout: 102 | Common.SpritesheetLayout.PACKED: 103 | match parsed_options.packed_animation_strategy: 104 | 105 | PackedSpritesheetAnimationStrategy.SpriteRegionAndOffset: 106 | sprite.region_enabled = true 107 | animation_player = _create_animation_player(ssmd, { 108 | ".:offset": func (frame_data: FrameData) -> Vector2: 109 | return frame_data.region_rect_offset + \ 110 | ((frame_data.region_rect.size - ssmd.source_size) if centered else Vector2i.ZERO) / 2, 111 | ".:region_rect" : func (frame_data: FrameData) -> Rect2i: 112 | return frame_data.region_rect }, 113 | autoplay) 114 | 115 | PackedSpritesheetAnimationStrategy.TextureRegionAndMargin: 116 | var atlas_texture: AtlasTexture = AtlasTexture.new() 117 | atlas_texture.filter_clip = true 118 | atlas_texture.resource_local_to_scene = true 119 | atlas_texture.atlas = sprite.texture 120 | sprite.texture = atlas_texture 121 | animation_player = _create_animation_player(ssmd, { 122 | ".:texture:margin": func (frame_data: FrameData) -> Rect2: 123 | return Rect2(frame_data.region_rect_offset, ssmd.source_size - frame_data.region_rect.size), 124 | ".:texture:region" : func (frame_data: FrameData) -> Rect2i: 125 | return frame_data.region_rect }, 126 | parsed_options.animation_autoplay_name) 127 | 128 | PackedSpritesheetAnimationStrategy.TextureInstances: 129 | var texture_cache: Array[AtlasTexture] 130 | animation_player = _create_animation_player(ssmd, { 131 | ".:texture": func (frame_data: FrameData) -> Texture2D: 132 | var margin = Rect2(frame_data.region_rect_offset, ssmd.source_size - frame_data.region_rect.size) 133 | var region = Rect2(frame_data.region_rect) 134 | var cached_result = texture_cache.filter(func (t: AtlasTexture) -> bool: return t.margin == margin and t.region == region) 135 | var texture: AtlasTexture 136 | if not cached_result.is_empty(): 137 | return cached_result.front() 138 | texture = AtlasTexture.new() 139 | texture.atlas = export_result.texture 140 | texture.filter_clip = true 141 | texture.margin = margin 142 | texture.region = region 143 | texture_cache.append(texture) 144 | return texture}, 145 | autoplay) 146 | 147 | Common.SpritesheetLayout.BY_ROWS, Common.SpritesheetLayout.BY_COLUMNS: 148 | match parsed_options.grid_based_animation_strategy: 149 | 150 | GridBasedSpritesheetAnimationStrategy.SpriteRegion: 151 | sprite.region_enabled = true 152 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 153 | sprite.offset = random_frame_data.region_rect_offset + \ 154 | ((random_frame_data.region_rect.size - ssmd.source_size) if centered else Vector2i.ZERO) / 2 155 | animation_player = _create_animation_player(ssmd, { 156 | ".:region_rect" : func (frame_data: FrameData) -> Rect2i: 157 | return frame_data.region_rect }, 158 | autoplay) 159 | 160 | GridBasedSpritesheetAnimationStrategy.SpriteFrameIndex: 161 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 162 | var grid_cell_size: Vector2i = random_frame_data.region_rect.size 163 | if parsed_options.border_type == Common.BorderType.Extruded: 164 | grid_cell_size += Vector2i.ONE * 2 165 | match parsed_options.spritesheet_layout: 166 | Common.SpritesheetLayout.BY_ROWS: 167 | sprite.hframes = parsed_options.spritesheet_fixed_columns_count 168 | sprite.vframes = ssmd.spritesheet_size.y / grid_cell_size.y 169 | Common.SpritesheetLayout.BY_COLUMNS: 170 | sprite.hframes = ssmd.spritesheet_size.x / grid_cell_size.x 171 | sprite.vframes = parsed_options.spritesheet_fixed_rows_count 172 | animation_player = _create_animation_player(ssmd, { 173 | ".:frame": func (frame_data: FrameData) -> int: 174 | var frame_coords: Vector2i = frame_data.region_rect.position / grid_cell_size 175 | match parsed_options.spritesheet_layout: 176 | Common.SpritesheetLayout.BY_ROWS: 177 | return parsed_options.spritesheet_fixed_columns_count * frame_coords.y + frame_coords.x 178 | Common.SpritesheetLayout.BY_COLUMNS: 179 | return parsed_options.spritesheet_fixed_rows_count * frame_coords.x + frame_coords.y 180 | push_error("Unexpected spritesheet layout type") 181 | return 0 }, 182 | autoplay) 183 | 184 | GridBasedSpritesheetAnimationStrategy.SpriteFrameCoords: 185 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 186 | var grid_cell_size: Vector2i = random_frame_data.region_rect.size 187 | if parsed_options.border_type == Common.BorderType.Extruded: 188 | grid_cell_size += Vector2i.ONE * 2 189 | match parsed_options.spritesheet_layout: 190 | Common.SpritesheetLayout.BY_ROWS: 191 | sprite.hframes = parsed_options.spritesheet_fixed_columns_count 192 | sprite.vframes = ssmd.spritesheet_size.y / grid_cell_size.y 193 | Common.SpritesheetLayout.BY_COLUMNS: 194 | sprite.hframes = ssmd.spritesheet_size.x / grid_cell_size.x 195 | sprite.vframes = parsed_options.spritesheet_fixed_rows_count 196 | animation_player = _create_animation_player(ssmd, { 197 | ".:frame_coords": func (frame_data: FrameData) -> Vector2i: 198 | return frame_data.region_rect.position / grid_cell_size }, 199 | autoplay) 200 | 201 | GridBasedSpritesheetAnimationStrategy.TextureRegion: 202 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 203 | var atlas_texture = AtlasTexture.new() 204 | atlas_texture.atlas = export_result.texture 205 | atlas_texture.filter_clip = true 206 | atlas_texture.resource_local_to_scene = true 207 | atlas_texture.margin = Rect2(random_frame_data.region_rect_offset, ssmd.source_size - random_frame_data.region_rect.size) 208 | sprite.texture = atlas_texture 209 | animation_player = _create_animation_player(ssmd, { 210 | ".:texture:region" : func (frame_data: FrameData) -> Rect2i: 211 | return frame_data.region_rect }, 212 | autoplay) 213 | 214 | GridBasedSpritesheetAnimationStrategy.TextureInstances: 215 | var texture_cache: Array[AtlasTexture] 216 | animation_player = _create_animation_player(ssmd, { 217 | ".:texture": func (frame_data: FrameData) -> Texture2D: 218 | var region = Rect2(frame_data.region_rect) 219 | var cached_result = texture_cache.filter(func (t: AtlasTexture) -> bool: return t.region == region) 220 | var texture: AtlasTexture 221 | if not cached_result.is_empty(): 222 | return cached_result.front() 223 | texture = AtlasTexture.new() 224 | texture.atlas = export_result.texture 225 | texture.filter_clip = true 226 | texture.region = region 227 | texture_cache.append(texture) 228 | return texture}, 229 | autoplay) 230 | 231 | 232 | sprite.add_child(animation_player) 233 | animation_player.owner = sprite 234 | 235 | var packed_scene: PackedScene 236 | if ResourceLoader.exists(source_file): 237 | # This is a working way to reuse a previously imported resource. Don't change it! 238 | packed_scene = ResourceLoader.load(source_file, "PackedScene", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene 239 | if not packed_scene: 240 | packed_scene = PackedScene.new() 241 | 242 | packed_scene.pack(sprite) 243 | 244 | status = ResourceSaver.save( 245 | packed_scene, 246 | save_path + "." + _get_save_extension(), 247 | ResourceSaver.FLAG_COMPRESS) 248 | if status: push_error("Can't save imported resource.", status) 249 | 250 | return status 251 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/sprite_3d.gd: -------------------------------------------------------------------------------- 1 | extends "_animation_importer_base.gd" 2 | 3 | const OPTION_SPRITE3D_CENTERED: String = "sprite3d/centered" 4 | 5 | const OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY: String = "animation/strategy_(packed_spritesheet)" 6 | 7 | const PACKED_SPRITESHEET_ANIMATION_STRATEGIES: PackedStringArray = [ 8 | "Animate sprite's region and offset", 9 | "Animate single atlas texture's region and margin", 10 | "Animate multiple atlas texture instances", 11 | ] 12 | 13 | enum PackedSpritesheetAnimationStrategy 14 | { 15 | SpriteRegionAndOffset = 0, 16 | TextureRegionAndMargin = 1, 17 | TextureInstances = 2, 18 | } 19 | 20 | const OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY: String = "animation/strategy_(grid-based_spritesheet)" 21 | 22 | const GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES: PackedStringArray = [ 23 | "Animate sprite's region", 24 | "Animate sprite's frame index", 25 | "Animate sprite's frame coords", 26 | "Animate single atlas texture's region", 27 | "Animate multiple atlas texture instances", 28 | ] 29 | 30 | enum GridBasedSpritesheetAnimationStrategy 31 | { 32 | SpriteRegion = 0, 33 | SpriteFrameIndex = 1, 34 | SpriteFrameCoords = 2, 35 | TextureRegion = 3, 36 | TextureInstances = 4, 37 | } 38 | 39 | class Sprite3DParsedAnimationOptions: 40 | extends Common.ParsedAnimationOptions 41 | var centered: bool 42 | var packed_animation_strategy: PackedSpritesheetAnimationStrategy 43 | var grid_based_animation_strategy: GridBasedSpritesheetAnimationStrategy 44 | func _init(options: Dictionary) -> void: 45 | packed_animation_strategy = PACKED_SPRITESHEET_ANIMATION_STRATEGIES \ 46 | .find(options[OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY]) 47 | grid_based_animation_strategy = GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES \ 48 | .find(options[OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY]) 49 | super(options) 50 | 51 | 52 | func _init(parent_plugin: EditorPlugin) -> void: 53 | super(parent_plugin) 54 | _import_order = 0 55 | _importer_name = "Aseprite Sprite3D Import" 56 | _priority = 1 57 | _recognized_extensions = ["ase", "aseprite"] 58 | _resource_type = "PackedScene" 59 | _save_extension = "scn" 60 | _visible_name = "Sprite3D (with AnimationPlayer)" 61 | 62 | set_preset("Animation", [ 63 | Common.create_option(OPTION_SPRITE3D_CENTERED, true, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), 64 | Common.create_option( 65 | OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY, 66 | PackedSpritesheetAnimationStrategy.SpriteRegionAndOffset, 67 | PROPERTY_HINT_ENUM, ",".join(PACKED_SPRITESHEET_ANIMATION_STRATEGIES), 68 | PROPERTY_USAGE_EDITOR, 69 | func (options:Dictionary) -> bool: 70 | return options[Common.OPTION_SPRITESHEET_LAYOUT] == Common.SPRITESHEET_LAYOUTS[Common.SpritesheetLayout.PACKED]), 71 | Common.create_option( 72 | OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY, 73 | GridBasedSpritesheetAnimationStrategy.SpriteRegion, 74 | PROPERTY_HINT_ENUM, ",".join(GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES), 75 | PROPERTY_USAGE_EDITOR, 76 | func (options:Dictionary) -> bool: 77 | return options[Common.OPTION_SPRITESHEET_LAYOUT] != Common.SPRITESHEET_LAYOUTS[Common.SpritesheetLayout.PACKED]), 78 | ]) 79 | 80 | func _import(source_file: String, save_path: String, options: Dictionary, 81 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 82 | var status: Error = OK 83 | var parsed_options: Sprite3DParsedAnimationOptions = Sprite3DParsedAnimationOptions.new(options) 84 | var export_result: ExportResult = _export_texture(source_file, parsed_options, options, gen_files) 85 | if export_result.error: 86 | push_error("There was an error during exporting texture: %s with message: %s" % 87 | [error_string(export_result.error), export_result.error_message]) 88 | return export_result.error 89 | 90 | var centered = options[OPTION_SPRITE3D_CENTERED] 91 | 92 | var sprite: Sprite3D = Sprite3D.new() 93 | sprite.name = source_file.get_file().get_basename() 94 | sprite.texture = export_result.texture 95 | sprite.centered = centered 96 | 97 | var ssmd: SpritesheetMetadata = export_result.spritesheet_metadata 98 | var autoplay: String = parsed_options.animation_autoplay_name 99 | var animation_player: AnimationPlayer 100 | match parsed_options.spritesheet_layout: 101 | Common.SpritesheetLayout.PACKED: 102 | match parsed_options.packed_animation_strategy: 103 | 104 | PackedSpritesheetAnimationStrategy.SpriteRegionAndOffset: 105 | sprite.region_enabled = true 106 | animation_player = _create_animation_player(ssmd, { 107 | ".:offset": func (frame_data: FrameData) -> Vector2: 108 | return Vector2( # spatial sprite offset (the Y-axis is Up-directed) 109 | frame_data.region_rect_offset.x, 110 | ssmd.source_size.y - 111 | frame_data.region_rect_offset.y - 112 | frame_data.region_rect.size.y) + \ 113 | # add center correction 114 | ((frame_data.region_rect.size - ssmd.source_size) * 0.5 115 | if centered else Vector2.ZERO), 116 | ".:region_rect" : func (frame_data: FrameData) -> Rect2i: 117 | return frame_data.region_rect }, 118 | autoplay) 119 | 120 | PackedSpritesheetAnimationStrategy.TextureRegionAndMargin: 121 | var atlas_texture: AtlasTexture = AtlasTexture.new() 122 | atlas_texture.filter_clip = true 123 | atlas_texture.resource_local_to_scene = true 124 | atlas_texture.atlas = sprite.texture 125 | sprite.texture = atlas_texture 126 | animation_player = _create_animation_player(ssmd, { 127 | ".:texture:margin": func (frame_data: FrameData) -> Rect2: 128 | return Rect2(frame_data.region_rect_offset, ssmd.source_size - frame_data.region_rect.size), 129 | ".:texture:region" : func (frame_data: FrameData) -> Rect2i: 130 | return frame_data.region_rect }, 131 | parsed_options.animation_autoplay_name) 132 | 133 | PackedSpritesheetAnimationStrategy.TextureInstances: 134 | var texture_cache: Array[AtlasTexture] 135 | animation_player = _create_animation_player(ssmd, { 136 | ".:texture": func (frame_data: FrameData) -> Texture2D: 137 | var margin = Rect2(frame_data.region_rect_offset, ssmd.source_size - frame_data.region_rect.size) 138 | var region = Rect2(frame_data.region_rect) 139 | var cached_result = texture_cache.filter(func (t: AtlasTexture) -> bool: return t.margin == margin and t.region == region) 140 | var texture: AtlasTexture 141 | if not cached_result.is_empty(): 142 | return cached_result.front() 143 | texture = AtlasTexture.new() 144 | texture.atlas = export_result.texture 145 | texture.filter_clip = true 146 | texture.margin = margin 147 | texture.region = region 148 | texture_cache.append(texture) 149 | return texture}, 150 | autoplay) 151 | 152 | Common.SpritesheetLayout.BY_ROWS, Common.SpritesheetLayout.BY_COLUMNS: 153 | match parsed_options.grid_based_animation_strategy: 154 | 155 | GridBasedSpritesheetAnimationStrategy.SpriteRegion: 156 | sprite.region_enabled = true 157 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 158 | sprite.offset = random_frame_data.region_rect_offset + \ 159 | ((random_frame_data.region_rect.size - ssmd.source_size) if centered else Vector2i.ZERO) / 2 160 | animation_player = _create_animation_player(ssmd, { 161 | ".:region_rect" : func (frame_data: FrameData) -> Rect2i: 162 | return frame_data.region_rect }, 163 | autoplay) 164 | 165 | GridBasedSpritesheetAnimationStrategy.SpriteFrameIndex: 166 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 167 | var grid_cell_size: Vector2i = random_frame_data.region_rect.size 168 | if parsed_options.border_type == Common.BorderType.Extruded: 169 | grid_cell_size += Vector2i.ONE * 2 170 | match parsed_options.spritesheet_layout: 171 | Common.SpritesheetLayout.BY_ROWS: 172 | sprite.hframes = parsed_options.spritesheet_fixed_columns_count 173 | sprite.vframes = ssmd.spritesheet_size.y / grid_cell_size.y 174 | Common.SpritesheetLayout.BY_COLUMNS: 175 | sprite.hframes = ssmd.spritesheet_size.x / grid_cell_size.x 176 | sprite.vframes = parsed_options.spritesheet_fixed_rows_count 177 | animation_player = _create_animation_player(ssmd, { 178 | ".:frame": func (frame_data: FrameData) -> int: 179 | var frame_coords: Vector2i = frame_data.region_rect.position / grid_cell_size 180 | match parsed_options.spritesheet_layout: 181 | Common.SpritesheetLayout.BY_ROWS: 182 | return parsed_options.spritesheet_fixed_columns_count * frame_coords.y + frame_coords.x 183 | Common.SpritesheetLayout.BY_COLUMNS: 184 | return parsed_options.spritesheet_fixed_rows_count * frame_coords.x + frame_coords.y 185 | push_error("Unexpected spritesheet layout type") 186 | return 0 }, 187 | autoplay) 188 | 189 | GridBasedSpritesheetAnimationStrategy.SpriteFrameCoords: 190 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 191 | var grid_cell_size: Vector2i = random_frame_data.region_rect.size 192 | if parsed_options.border_type == Common.BorderType.Extruded: 193 | grid_cell_size += Vector2i.ONE * 2 194 | match parsed_options.spritesheet_layout: 195 | Common.SpritesheetLayout.BY_ROWS: 196 | sprite.hframes = parsed_options.spritesheet_fixed_columns_count 197 | sprite.vframes = ssmd.spritesheet_size.y / grid_cell_size.y 198 | Common.SpritesheetLayout.BY_COLUMNS: 199 | sprite.hframes = ssmd.spritesheet_size.x / grid_cell_size.x 200 | sprite.vframes = parsed_options.spritesheet_fixed_rows_count 201 | animation_player = _create_animation_player(ssmd, { 202 | ".:frame_coords": func (frame_data: FrameData) -> Vector2i: 203 | return frame_data.region_rect.position / grid_cell_size }, 204 | autoplay) 205 | 206 | GridBasedSpritesheetAnimationStrategy.TextureRegion: 207 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 208 | var atlas_texture = AtlasTexture.new() 209 | atlas_texture.atlas = export_result.texture 210 | atlas_texture.filter_clip = true 211 | atlas_texture.resource_local_to_scene = true 212 | atlas_texture.margin = Rect2(random_frame_data.region_rect_offset, ssmd.source_size - random_frame_data.region_rect.size) 213 | sprite.texture = atlas_texture 214 | animation_player = _create_animation_player(ssmd, { 215 | ".:texture:region" : func (frame_data: FrameData) -> Rect2i: 216 | return frame_data.region_rect }, 217 | autoplay) 218 | 219 | GridBasedSpritesheetAnimationStrategy.TextureInstances: 220 | var texture_cache: Array[AtlasTexture] 221 | animation_player = _create_animation_player(ssmd, { 222 | ".:texture": func (frame_data: FrameData) -> Texture2D: 223 | var region = Rect2(frame_data.region_rect) 224 | var cached_result = texture_cache.filter(func (t: AtlasTexture) -> bool: return t.region == region) 225 | var texture: AtlasTexture 226 | if not cached_result.is_empty(): 227 | return cached_result.front() 228 | texture = AtlasTexture.new() 229 | texture.atlas = export_result.texture 230 | texture.filter_clip = true 231 | texture.region = region 232 | texture_cache.append(texture) 233 | return texture}, 234 | autoplay) 235 | 236 | sprite.add_child(animation_player) 237 | animation_player.owner = sprite 238 | 239 | var packed_scene: PackedScene 240 | if ResourceLoader.exists(source_file): 241 | # This is a working way to reuse a previously imported resource. Don't change it! 242 | packed_scene = ResourceLoader.load(source_file, "PackedScene", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene 243 | if not packed_scene: 244 | packed_scene = PackedScene.new() 245 | 246 | packed_scene.pack(sprite) 247 | 248 | status = ResourceSaver.save( 249 | packed_scene, 250 | save_path + "." + _get_save_extension(), 251 | ResourceSaver.FLAG_COMPRESS) 252 | if status: push_error("Can't save imported resource.", status) 253 | 254 | return status 255 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/sprite_frames.gd: -------------------------------------------------------------------------------- 1 | extends "_animation_importer_base.gd" 2 | 3 | func _init(parent_plugin: EditorPlugin) -> void: 4 | super(parent_plugin) 5 | _import_order = 0 6 | _importer_name = "Aseprite SpriteFrames Import" 7 | _priority = 1 8 | _recognized_extensions = ["ase", "aseprite"] 9 | _resource_type = "SpriteFrames" 10 | _save_extension = "res" 11 | _visible_name = "SpriteFrames" 12 | 13 | set_preset("Animation", []) 14 | 15 | func _import(source_file: String, save_path: String, options: Dictionary, 16 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 17 | var status: Error = OK 18 | var parsed_options = Common.ParsedAnimationOptions.new(options) 19 | var export_result: ExportResult = _export_texture(source_file, parsed_options, options, gen_files) 20 | if export_result.error: 21 | push_error("There was an error during exporting texture: %s with message: %s" % 22 | [error_string(export_result.error), export_result.error_message]) 23 | return export_result.error 24 | 25 | var sprite_frames: SpriteFrames 26 | if ResourceLoader.exists(source_file): 27 | # This is a working way to reuse a previously imported resource. Don't change it! 28 | sprite_frames = ResourceLoader.load(source_file, "SpriteFrames", ResourceLoader.CACHE_MODE_REPLACE) as SpriteFrames 29 | if not sprite_frames: 30 | sprite_frames = SpriteFrames.new() 31 | 32 | status = update_sprite_frames(export_result, sprite_frames) 33 | if status: 34 | push_error("Cannot update SpriteFrames", status) 35 | return status 36 | 37 | status = ResourceSaver.save( 38 | sprite_frames, 39 | save_path + "." + _get_save_extension(), 40 | ResourceSaver.FLAG_COMPRESS) 41 | if status: 42 | push_error("Can't save imported resource.", status) 43 | return status 44 | 45 | static func update_sprite_frames(export_result: ExportResult, sprite_frames: SpriteFrames, animation_autoplay_name: String = "") -> Error: 46 | var spritesheet_metadata: SpritesheetMetadata = export_result.spritesheet_metadata 47 | var exported_animation_names: Array = export_result.spritesheet_metadata.animation_tags.map( 48 | func (at: AnimationTag) -> String: return at.name) 49 | var actual_animation_names: PackedStringArray = sprite_frames.get_animation_names() 50 | for name in actual_animation_names: 51 | if exported_animation_names.has(name): 52 | sprite_frames.clear(name) 53 | else: 54 | sprite_frames.remove_animation(name) 55 | var atlas_textures: Dictionary = {} 56 | for animation_tag in spritesheet_metadata.animation_tags: 57 | if not sprite_frames.has_animation(animation_tag.name): 58 | sprite_frames.add_animation(animation_tag.name) 59 | sprite_frames.set_animation_loop(animation_tag.name, animation_tag.looped) 60 | sprite_frames.set_animation_speed(animation_tag.name, 1) 61 | for frame_data in animation_tag.frames: 62 | var atlas_texture = atlas_textures.get(frame_data.region_rect) 63 | if atlas_texture == null: 64 | atlas_texture = AtlasTexture.new() 65 | atlas_texture.atlas = export_result.texture 66 | atlas_texture.region = frame_data.region_rect 67 | atlas_texture.margin = Rect2(frame_data.region_rect_offset, spritesheet_metadata.source_size - frame_data.region_rect.size) 68 | atlas_textures[frame_data.region_rect] = atlas_texture 69 | 70 | sprite_frames.add_frame(animation_tag.name, atlas_texture, frame_data.duration_ms * 0.001) 71 | return OK 72 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_import_plugins/texture_rect.gd: -------------------------------------------------------------------------------- 1 | extends "_animation_importer_base.gd" 2 | 3 | 4 | const OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY: String = "animation/strategy_(packed_spritesheet)" 5 | 6 | const PACKED_SPRITESHEET_ANIMATION_STRATEGIES: PackedStringArray = [ 7 | "Animate single atlas texture's region and margin", 8 | "Animate multiple atlas texture instances", 9 | ] 10 | 11 | enum PackedSpritesheetAnimationStrategy 12 | { 13 | TextureRegionAndMargin = 0, 14 | TextureInstances = 1 15 | } 16 | 17 | 18 | const OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY: String = "animation/strategy_(grid-based_spritesheet)" 19 | 20 | const GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES: PackedStringArray = [ 21 | "Animate single atlas texture's region", 22 | "Animate multiple atlas texture instances", 23 | ] 24 | 25 | enum GridBasedSpritesheetAnimationStrategy 26 | { 27 | TextureRegion = 0, 28 | TextureInstances = 1 29 | } 30 | 31 | class TextureRectParsedAnimationOptions: 32 | extends Common.ParsedAnimationOptions 33 | var centered: bool 34 | var packed_animation_strategy: PackedSpritesheetAnimationStrategy 35 | var grid_based_animation_strategy: GridBasedSpritesheetAnimationStrategy 36 | func _init(options: Dictionary) -> void: 37 | packed_animation_strategy = PACKED_SPRITESHEET_ANIMATION_STRATEGIES \ 38 | .find(options[OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY]) 39 | grid_based_animation_strategy = GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES \ 40 | .find(options[OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY]) 41 | super(options) 42 | 43 | 44 | 45 | func _init(parent_plugin: EditorPlugin) -> void: 46 | super(parent_plugin) 47 | _import_order = 0 48 | _importer_name = "Aseprite TextureRect Import" 49 | _priority = 1 50 | _recognized_extensions = ["ase", "aseprite"] 51 | _resource_type = "PackedScene" 52 | _save_extension = "scn" 53 | _visible_name = "TextureRect (with AnimationPlayer)" 54 | 55 | set_preset("Animation", [ 56 | Common.create_option( 57 | OPTION_PACKED_SPRITESHEET_ANIMATION_STRATEGY, 58 | PackedSpritesheetAnimationStrategy.TextureRegionAndMargin, 59 | PROPERTY_HINT_ENUM, ",".join(PACKED_SPRITESHEET_ANIMATION_STRATEGIES), 60 | PROPERTY_USAGE_EDITOR, 61 | func (options:Dictionary) -> bool: 62 | return options[Common.OPTION_SPRITESHEET_LAYOUT] == Common.SpritesheetLayout.PACKED), 63 | Common.create_option( 64 | OPTION_GRID_BASED_SPRITESHEET_ANIMATION_STRATEGY, 65 | GridBasedSpritesheetAnimationStrategy.TextureRegion, 66 | PROPERTY_HINT_ENUM, ",".join(GRID_BASED_SPRITESHEET_ANIMATION_STRATEGIES), 67 | PROPERTY_USAGE_EDITOR, 68 | func (options:Dictionary) -> bool: 69 | return options[Common.OPTION_SPRITESHEET_LAYOUT] != Common.SpritesheetLayout.PACKED), 70 | ]) 71 | 72 | 73 | func _import(source_file: String, save_path: String, options: Dictionary, 74 | platform_variants: Array[String], gen_files: Array[String]) -> Error: 75 | var status: Error = OK 76 | var parsed_options: TextureRectParsedAnimationOptions = TextureRectParsedAnimationOptions.new(options) 77 | var export_result: ExportResult = _export_texture(source_file, parsed_options, options, gen_files) 78 | if export_result.error: 79 | push_error("There was an error during exporting texture: %s with message: %s" % 80 | [error_string(export_result.error), export_result.error_message]) 81 | return export_result.error 82 | 83 | var frame_size: Vector2i = export_result.spritesheet_metadata.source_size 84 | 85 | var atlas_texture: AtlasTexture = AtlasTexture.new() 86 | atlas_texture.atlas = export_result.texture 87 | atlas_texture.filter_clip = true 88 | atlas_texture.resource_local_to_scene = true 89 | # for TextureRect, AtlasTexture must have region with area 90 | # we gave it frame size and negative position to avoid to show any visible pixel of the texture 91 | atlas_texture.region = Rect2(-frame_size - Vector2i.ONE, frame_size) 92 | 93 | var texture_rect: TextureRect = TextureRect.new() 94 | texture_rect.name = source_file.get_file().get_basename() 95 | texture_rect.texture = atlas_texture 96 | texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE 97 | texture_rect.size = frame_size 98 | 99 | 100 | 101 | 102 | 103 | var ssmd: SpritesheetMetadata = export_result.spritesheet_metadata 104 | var autoplay: String = parsed_options.animation_autoplay_name 105 | var animation_player: AnimationPlayer 106 | match parsed_options.spritesheet_layout: 107 | Common.SpritesheetLayout.PACKED: 108 | match parsed_options.packed_animation_strategy: 109 | 110 | PackedSpritesheetAnimationStrategy.TextureRegionAndMargin: 111 | animation_player = _create_animation_player(ssmd, { 112 | ".:texture:margin": func (frame_data: FrameData) -> Rect2: 113 | return Rect2(frame_data.region_rect_offset, ssmd.source_size - frame_data.region_rect.size), 114 | ".:texture:region" : func (frame_data: FrameData) -> Rect2i: 115 | return frame_data.region_rect }, 116 | parsed_options.animation_autoplay_name) 117 | 118 | PackedSpritesheetAnimationStrategy.TextureInstances: 119 | var texture_cache: Array[AtlasTexture] 120 | animation_player = _create_animation_player(ssmd, { 121 | ".:texture": func (frame_data: FrameData) -> Texture2D: 122 | var margin = Rect2(frame_data.region_rect_offset, ssmd.source_size - frame_data.region_rect.size) 123 | var region = Rect2(frame_data.region_rect) 124 | var cached_result = texture_cache.filter(func (t: AtlasTexture) -> bool: return t.margin == margin and t.region == region) 125 | var texture: AtlasTexture 126 | if not cached_result.is_empty(): 127 | return cached_result.front() 128 | texture = AtlasTexture.new() 129 | texture.atlas = export_result.texture 130 | texture.filter_clip = true 131 | texture.margin = margin 132 | texture.region = region 133 | texture_cache.append(texture) 134 | return texture}, 135 | autoplay) 136 | 137 | Common.SpritesheetLayout.BY_ROWS, Common.SpritesheetLayout.BY_COLUMNS: 138 | match parsed_options.grid_based_animation_strategy: 139 | 140 | GridBasedSpritesheetAnimationStrategy.TextureRegion: 141 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 142 | atlas_texture.margin = Rect2(random_frame_data.region_rect_offset, random_frame_data.region_rect_offset * 2) 143 | animation_player = _create_animation_player(ssmd, { 144 | ".:texture:region" : func (frame_data: FrameData) -> Rect2i: 145 | return frame_data.region_rect }, 146 | autoplay) 147 | 148 | GridBasedSpritesheetAnimationStrategy.TextureInstances: 149 | var random_frame_data: FrameData = ssmd.animation_tags[0].frames[0] 150 | var common_atlas_texture_margin: Rect2 = Rect2( 151 | random_frame_data.region_rect_offset, 152 | ssmd.source_size - random_frame_data.region_rect.size) 153 | var texture_cache: Array[AtlasTexture] 154 | animation_player = _create_animation_player(ssmd, { 155 | ".:texture": func (frame_data: FrameData) -> Texture2D: 156 | var region = Rect2(frame_data.region_rect) 157 | var cached_result = texture_cache.filter(func (t: AtlasTexture) -> bool: return t.region == region) 158 | var texture: AtlasTexture 159 | if not cached_result.is_empty(): 160 | return cached_result.front() 161 | texture = AtlasTexture.new() 162 | texture.atlas = export_result.texture 163 | texture.filter_clip = true 164 | texture.region = region 165 | texture.margin = common_atlas_texture_margin 166 | texture_cache.append(texture) 167 | return texture}, 168 | autoplay) 169 | 170 | texture_rect.add_child(animation_player) 171 | animation_player.owner = texture_rect 172 | 173 | var packed_scene: PackedScene 174 | if ResourceLoader.exists(source_file): 175 | # This is a working way to reuse a previously imported resource. Don't change it! 176 | packed_scene = ResourceLoader.load(source_file, "PackedScene", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene 177 | if not packed_scene: 178 | packed_scene = PackedScene.new() 179 | 180 | packed_scene.pack(texture_rect) 181 | 182 | status = ResourceSaver.save( 183 | packed_scene, 184 | save_path + "." + _get_save_extension(), 185 | ResourceSaver.FLAG_COMPRESS | ResourceSaver.FLAG_BUNDLE_RESOURCES) 186 | if status: push_error("Can't save imported resource.", status) 187 | 188 | return status 189 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/editor_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const PLUGIN_SCRIPTS: Array[GDScript] = [ 5 | preload("editor_import_plugins/animated_sprite_2d.gd"), 6 | preload("editor_import_plugins/animated_sprite_3d.gd"), 7 | preload("editor_import_plugins/sprite_2d.gd"), 8 | preload("editor_import_plugins/sprite_3d.gd"), 9 | preload("editor_import_plugins/sprite_frames.gd"), 10 | preload("editor_import_plugins/texture_rect.gd"), 11 | ] 12 | 13 | const Common = preload("common.gd") 14 | const ImportPlugin = preload("editor_import_plugins/_animation_importer_base.gd") 15 | const AsepriteImageFormatLoaderExtension = preload("aseprite_image_format_loader_extension.gd") 16 | 17 | var __import_plugins: Array[ImportPlugin] 18 | var __aseprite_image_format_loader_extension: AsepriteImageFormatLoaderExtension 19 | 20 | var common_options: Array[Dictionary] = Common.create_common_animation_options() 21 | 22 | func _enter_tree() -> void: 23 | __register_project_setting( 24 | Common.ASEPRITE_EXECUTABLE_PATH_SETTING_NAME, "", 25 | TYPE_STRING, PROPERTY_HINT_GLOBAL_FILE, "*.exe") 26 | 27 | for plugin_script in PLUGIN_SCRIPTS: 28 | var import_plugin = plugin_script.new(self) 29 | add_import_plugin(import_plugin) 30 | __import_plugins.append(import_plugin) 31 | 32 | __aseprite_image_format_loader_extension = AsepriteImageFormatLoaderExtension.new() 33 | __aseprite_image_format_loader_extension.add_format_loader() 34 | 35 | func _exit_tree() -> void: 36 | for import_plugin in __import_plugins: 37 | remove_import_plugin(import_plugin) 38 | __import_plugins.clear() 39 | 40 | __aseprite_image_format_loader_extension.remove_format_loader() 41 | __aseprite_image_format_loader_extension = null 42 | 43 | func __register_project_setting(name: StringName, initial_value, type: int, hint: int, hint_string: String = "") -> void: 44 | if not ProjectSettings.has_setting(name): 45 | ProjectSettings.set_setting(name, initial_value) 46 | ProjectSettings.set_initial_value(name, initial_value) 47 | var property_info: Dictionary = { name = name, type = type, hint = hint } 48 | if hint_string: property_info.hint_string = hint_string 49 | ProjectSettings.add_property_info(property_info) 50 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nklbdev/godot-4-aseprite-importers/3ed41a8b8cbc5e7ce7a3e39c42c463e9d86f4ff8/addons/nklbdev.aseprite_importers/icon.png -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bbww6j66bavar" 6 | path="res://.godot/imported/icon.png-fcba8a3700fb5af89c995132142de0f4.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/nklbdev.aseprite_importers/icon.png" 14 | dest_files=["res://.godot/imported/icon.png-fcba8a3700fb5af89c995132142de0f4.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/nklbdev.aseprite_importers/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Aseprite Importers" 4 | description="A bundle of plug-ins for importing Aesprite files into different types of Godot resources" 5 | author="Nikolay Lebedev aka nklbdev" 6 | version="1.0.0" 7 | script="editor_plugin.gd" 8 | --------------------------------------------------------------------------------