289 lines
13 KiB
GDScript
289 lines
13 KiB
GDScript
@tool
|
|
extends "_.gd"
|
|
|
|
const __aseprite_sheet_types_by_sprite_sheet_layout: PackedStringArray = \
|
|
[ "packed", "rows", "columns" ]
|
|
const __aseprite_animation_directions: PackedStringArray = \
|
|
[ "forward", "reverse", "pingpong", "pingpong_reverse" ]
|
|
|
|
var __os_command_setting: _Setting = _Setting.new(
|
|
"aseprite_or_libre_sprite_command", "", TYPE_STRING, PROPERTY_HINT_NONE,
|
|
"", true, func(v: String): return v.is_empty())
|
|
|
|
var __os_command_arguments_setting: _Setting = _Setting.new(
|
|
"aseprite_or_libre_sprite_command_arguments", PackedStringArray(), TYPE_PACKED_STRING_ARRAY, PROPERTY_HINT_NONE,
|
|
"", true, func(v: PackedStringArray): return false)
|
|
|
|
func _init(editor_file_system: EditorFileSystem) -> void:
|
|
var recognized_extensions: PackedStringArray = ["ase", "aseprite"]
|
|
super("Aseprite", recognized_extensions, [],
|
|
[__os_command_setting, __os_command_arguments_setting],
|
|
CustomImageFormatLoaderExtension.new(
|
|
recognized_extensions,
|
|
__os_command_setting,
|
|
__os_command_arguments_setting,
|
|
_Common.common_temporary_files_directory_path_setting))
|
|
|
|
func _export(res_source_file_path: String, options: Dictionary) -> ExportResult:
|
|
var result: ExportResult = ExportResult.new()
|
|
var err: Error
|
|
|
|
var os_command_result: _Setting.GettingValueResult = __os_command_setting.get_value()
|
|
if os_command_result.error:
|
|
result.fail(ERR_UNCONFIGURED, "Failed to get Aseprite Command to export spritesheet", os_command_result)
|
|
return result
|
|
|
|
var os_command_arguments_result: _Setting.GettingValueResult = __os_command_arguments_setting.get_value()
|
|
if os_command_arguments_result.error:
|
|
result.fail(ERR_UNCONFIGURED, "Failed to get Aseprite Command Arguments to export spritesheet", os_command_arguments_result)
|
|
return result
|
|
|
|
var temp_dir_path_result: _Setting.GettingValueResult = _Common.common_temporary_files_directory_path_setting.get_value()
|
|
if temp_dir_path_result.error:
|
|
result.fail(ERR_UNCONFIGURED, "Failed to get Temporary Files Directory Path to export spritesheet", temp_dir_path_result)
|
|
return result
|
|
var global_temp_dir_path: String = ProjectSettings.globalize_path(
|
|
temp_dir_path_result.value.strip_edges())
|
|
var unique_temp_dir_creation_result: _DirAccessExtensions.CreationResult = \
|
|
_DirAccessExtensions.create_directory_with_unique_name(global_temp_dir_path)
|
|
if unique_temp_dir_creation_result.error:
|
|
result.fail(ERR_QUERY_FAILED, "Failed to create unique temporary directory to export spritesheet", unique_temp_dir_creation_result)
|
|
return result
|
|
var unique_temp_dir_path: String = unique_temp_dir_creation_result.path
|
|
|
|
var global_source_file_path: String = ProjectSettings.globalize_path(res_source_file_path)
|
|
|
|
var global_png_path: String = unique_temp_dir_path.path_join("temp.png")
|
|
var global_json_path: String = unique_temp_dir_path.path_join("temp.json")
|
|
|
|
var command: String = os_command_result.value.strip_edges()
|
|
var arguments: PackedStringArray = \
|
|
os_command_arguments_result.value + \
|
|
PackedStringArray([
|
|
"--batch",
|
|
"--format", "json-array",
|
|
"--list-tags",
|
|
"--sheet", global_png_path,
|
|
"--data", global_json_path,
|
|
global_source_file_path])
|
|
|
|
var output: Array = []
|
|
var exit_code: int = OS.execute(command, arguments, output, true, false)
|
|
if exit_code:
|
|
for arg_index in arguments.size():
|
|
arguments[arg_index] = "\nArgument: " + arguments[arg_index]
|
|
result.fail(ERR_QUERY_FAILED, " ".join([
|
|
"An error occurred while executing the Aseprite command.",
|
|
"Process exited with code %s:\nCommand: %s%s"
|
|
]) % [exit_code, command, "".join(arguments)])
|
|
return result
|
|
var raw_atlas_image: Image = Image.load_from_file(global_png_path)
|
|
var json = JSON.new()
|
|
err = json.parse(FileAccess.get_file_as_string(global_json_path))
|
|
if err:
|
|
result.fail(ERR_INVALID_DATA, "Failed to parse sprite sheet json data with error %s \"%s\"" % [err, error_string(err)])
|
|
return result
|
|
var raw_sprite_sheet_data: Dictionary = json.data
|
|
|
|
var sprite_sheet_layout: _Common.SpriteSheetLayout = options[_Options.SPRITE_SHEET_LAYOUT]
|
|
var source_image_size: Vector2i = _Common.get_vector2i(
|
|
raw_sprite_sheet_data.frames[0].sourceSize, "w", "h")
|
|
|
|
var frames_images_by_indices: Dictionary
|
|
var tags_data: Array = raw_sprite_sheet_data.meta.frameTags
|
|
var frames_data: Array = raw_sprite_sheet_data.frames
|
|
var frames_count: int = frames_data.size()
|
|
if tags_data.is_empty():
|
|
var default_animation_name: String = options[_Options.DEFAULT_ANIMATION_NAME].strip_edges()
|
|
if default_animation_name.is_empty():
|
|
default_animation_name = "default"
|
|
tags_data.push_back({
|
|
name = default_animation_name,
|
|
from = 0,
|
|
to = frames_count - 1,
|
|
direction = __aseprite_animation_directions[options[_Options.DEFAULT_ANIMATION_DIRECTION]],
|
|
repeat = options[_Options.DEFAULT_ANIMATION_REPEAT_COUNT]
|
|
})
|
|
var animations_count: int = tags_data.size()
|
|
for tag_data in tags_data:
|
|
for frame_index in range(tag_data.from, tag_data.to + 1):
|
|
if frames_images_by_indices.has(frame_index):
|
|
continue
|
|
var frame_data: Dictionary = frames_data[frame_index]
|
|
frames_images_by_indices[frame_index] = raw_atlas_image.get_region(Rect2i(
|
|
_Common.get_vector2i(frame_data.frame, "x", "y"),
|
|
source_image_size))
|
|
var used_frames_indices: PackedInt32Array = PackedInt32Array(frames_images_by_indices.keys())
|
|
used_frames_indices.sort()
|
|
var used_frames_count: int = used_frames_indices.size()
|
|
var sprite_sheet_frames_indices_by_global_frame_indices: Dictionary
|
|
for sprite_sheet_frame_index in used_frames_indices.size():
|
|
sprite_sheet_frames_indices_by_global_frame_indices[
|
|
used_frames_indices[sprite_sheet_frame_index]] = \
|
|
sprite_sheet_frame_index
|
|
var used_frames_images: Array[Image]
|
|
used_frames_images.resize(used_frames_count)
|
|
for i in used_frames_count:
|
|
used_frames_images[i] = frames_images_by_indices[used_frames_indices[i]]
|
|
|
|
var sprite_sheet_builder: _SpriteSheetBuilderBase = _create_sprite_sheet_builder(options)
|
|
|
|
var sprite_sheet_building_result: _SpriteSheetBuilderBase.SpriteSheetBuildingResult = sprite_sheet_builder.build_sprite_sheet(used_frames_images)
|
|
if sprite_sheet_building_result.error:
|
|
result.fail(ERR_BUG, "Sprite sheet building failed", sprite_sheet_building_result)
|
|
return result
|
|
var sprite_sheet: _Common.SpriteSheetInfo = sprite_sheet_building_result.sprite_sheet
|
|
|
|
var animation_library: _Common.AnimationLibraryInfo = _Common.AnimationLibraryInfo.new()
|
|
var autoplay_animation_name: String = options[_Options.AUTOPLAY_ANIMATION_NAME].strip_edges()
|
|
|
|
var all_frames: Array[_Common.FrameInfo]
|
|
all_frames.resize(used_frames_count)
|
|
var unique_animations_names: PackedStringArray
|
|
for animation_index in animations_count:
|
|
var tag_data: Dictionary = tags_data[animation_index]
|
|
|
|
var animation_params_parsing_result: AnimationParamsParsingResult = _parse_animation_params(
|
|
tag_data.name.strip_edges(),
|
|
AnimationOptions.Direction | AnimationOptions.RepeatCount,
|
|
tag_data.from,
|
|
tag_data.to - tag_data.from + 1)
|
|
if animation_params_parsing_result.error:
|
|
result.fail(ERR_CANT_RESOLVE, "Failed to parse animation parameters",
|
|
animation_params_parsing_result)
|
|
return result
|
|
if unique_animations_names.has(animation_params_parsing_result.name):
|
|
result.fail(ERR_INVALID_DATA, "Duplicated animation name \"%s\" at index: %s" %
|
|
[animation_params_parsing_result.name, animation_index])
|
|
return result
|
|
unique_animations_names.push_back(animation_params_parsing_result.name)
|
|
var animation = _Common.AnimationInfo.new()
|
|
animation.name = animation_params_parsing_result.name
|
|
if animation.name.is_empty():
|
|
result.fail(ERR_INVALID_DATA, "A tag with empty name found")
|
|
return result
|
|
if animation.name == autoplay_animation_name:
|
|
animation_library.autoplay_index = animation_index
|
|
animation.direction = __aseprite_animation_directions.find(tag_data.direction)
|
|
if animation_params_parsing_result.direction >= 0:
|
|
animation.direction = animation_params_parsing_result.direction
|
|
animation.repeat_count = int(tag_data.get("repeat", "0"))
|
|
if animation_params_parsing_result.repeat_count >= 0:
|
|
animation.repeat_count = animation_params_parsing_result.repeat_count
|
|
for global_frame_index in range(tag_data.from, tag_data.to + 1):
|
|
var sprite_sheet_frame_index: int = \
|
|
sprite_sheet_frames_indices_by_global_frame_indices[global_frame_index]
|
|
var frame: _Common.FrameInfo = all_frames[sprite_sheet_frame_index]
|
|
if frame == null:
|
|
frame = _Common.FrameInfo.new()
|
|
frame.sprite = sprite_sheet.sprites[sprite_sheet_frame_index]
|
|
frame.duration = frames_data[global_frame_index].duration * 0.001
|
|
all_frames[sprite_sheet_frame_index] = frame
|
|
animation.frames.push_back(frame)
|
|
animation_library.animations.push_back(animation)
|
|
|
|
if not autoplay_animation_name.is_empty() and animation_library.autoplay_index < 0:
|
|
push_warning("Autoplay animation name not found: \"%s\". Continuing..." % [autoplay_animation_name])
|
|
|
|
if _DirAccessExtensions.remove_dir_recursive(unique_temp_dir_path).error:
|
|
push_warning(
|
|
"Failed to remove unique temporary directory: \"%s\"" %
|
|
[unique_temp_dir_path])
|
|
|
|
result.success(sprite_sheet_building_result.atlas_image, sprite_sheet, animation_library)
|
|
return result
|
|
|
|
class CustomImageFormatLoaderExtension:
|
|
extends ImageFormatLoaderExtension
|
|
|
|
var __recognized_extensions: PackedStringArray
|
|
var __os_command_setting: _Setting
|
|
var __os_command_arguments_setting: _Setting
|
|
var __common_temporary_files_directory_path_setting: _Setting
|
|
|
|
func _init(recognized_extensions: PackedStringArray,
|
|
os_command_setting: _Setting,
|
|
os_command_arguments_setting: _Setting,
|
|
common_temporary_files_directory_path_setting: _Setting
|
|
) -> void:
|
|
__recognized_extensions = recognized_extensions
|
|
__os_command_setting = os_command_setting
|
|
__os_command_arguments_setting = os_command_arguments_setting
|
|
__common_temporary_files_directory_path_setting = \
|
|
common_temporary_files_directory_path_setting
|
|
|
|
func _get_recognized_extensions() -> PackedStringArray:
|
|
return __recognized_extensions
|
|
|
|
func _load_image(image: Image, file_access: FileAccess, flags: int, scale: float) -> Error:
|
|
var global_source_file_path: String = file_access.get_path_absolute()
|
|
var err: Error
|
|
|
|
var os_command_result: _Setting.GettingValueResult = __os_command_setting.get_value()
|
|
if os_command_result.error:
|
|
push_error(os_command_result.error_description)
|
|
return os_command_result.error
|
|
|
|
var os_command_arguments_result: _Setting.GettingValueResult = __os_command_arguments_setting.get_value()
|
|
if os_command_arguments_result.error:
|
|
push_error(os_command_arguments_result.error_description)
|
|
return os_command_arguments_result.error
|
|
|
|
var temp_dir_path_result: _Setting.GettingValueResult = _Common.common_temporary_files_directory_path_setting.get_value()
|
|
if temp_dir_path_result.error:
|
|
push_error("Failed to get Temporary Files Directory Path to export spritesheet")
|
|
return temp_dir_path_result.error
|
|
var global_temp_dir_path: String = ProjectSettings.globalize_path(
|
|
temp_dir_path_result.value.strip_edges())
|
|
var unique_temp_dir_creation_result: _DirAccessExtensions.CreationResult = \
|
|
_DirAccessExtensions.create_directory_with_unique_name(global_temp_dir_path)
|
|
if unique_temp_dir_creation_result.error:
|
|
push_error("Failed to create unique temporary directory to export spritesheet")
|
|
return unique_temp_dir_creation_result.error
|
|
var unique_temp_dir_path: String = unique_temp_dir_creation_result.path
|
|
|
|
var global_png_path: String = unique_temp_dir_path.path_join("temp.png")
|
|
var global_json_path: String = unique_temp_dir_path.path_join("temp.json")
|
|
|
|
var command: String = os_command_result.value.strip_edges()
|
|
var arguments: PackedStringArray = \
|
|
os_command_arguments_result.value + \
|
|
PackedStringArray([
|
|
"--batch",
|
|
"--format", "json-array",
|
|
"--list-tags",
|
|
"--sheet", global_png_path,
|
|
"--data", global_json_path,
|
|
global_source_file_path,
|
|
])
|
|
|
|
var output: Array = []
|
|
var exit_code: int = OS.execute(command, arguments, output, true, false)
|
|
if exit_code:
|
|
for arg_index in arguments.size():
|
|
arguments[arg_index] = "\nArgument: " + arguments[arg_index]
|
|
push_error(" ".join([
|
|
"An error occurred while executing the Aseprite command.",
|
|
"Process exited with code %s:\nCommand: %s%s"
|
|
]) % [exit_code, command, "".join(arguments)])
|
|
return ERR_QUERY_FAILED
|
|
|
|
var raw_atlas_image: Image = Image.load_from_file(global_png_path)
|
|
var json = JSON.new()
|
|
err = json.parse(FileAccess.get_file_as_string(global_json_path))
|
|
if err:
|
|
push_error("Failed to parse sprite sheet json data with error %s \"%s\"" % [err, error_string(err)])
|
|
return ERR_INVALID_DATA
|
|
var raw_sprite_sheet_data: Dictionary = json.data
|
|
|
|
var source_image_size: Vector2i = _Common.get_vector2i(
|
|
raw_sprite_sheet_data.frames[0].sourceSize, "w", "h")
|
|
|
|
if _DirAccessExtensions.remove_dir_recursive(unique_temp_dir_path).error:
|
|
push_warning(
|
|
"Failed to remove unique temporary directory: \"%s\"" %
|
|
[unique_temp_dir_path])
|
|
|
|
image.copy_from(raw_atlas_image.get_region(Rect2i(Vector2i.ZERO, source_image_size)))
|
|
return OK
|