init
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
@tool
|
||||
extends "../base_sprite_resource_creator.gd"
|
||||
|
||||
var _DEFAULT_ANIMATION_LIBRARY = "" # GLOBAL
|
||||
|
||||
func create_animations(target_node: Node, player: AnimationPlayer, aseprite_files: Dictionary, options: Dictionary):
|
||||
var result = _import(target_node, player, aseprite_files, options)
|
||||
|
||||
if result != result_code.SUCCESS:
|
||||
printerr(result_code.get_error_message(result))
|
||||
|
||||
|
||||
func _import(target_node: Node, player: AnimationPlayer, aseprite_files: Dictionary, options: Dictionary):
|
||||
var source_file = aseprite_files.data_file
|
||||
var sprite_sheet = aseprite_files.sprite_sheet
|
||||
var data = _aseprite_file_exporter.load_json_content(source_file)
|
||||
|
||||
if not data.is_ok:
|
||||
return data.code
|
||||
|
||||
var content = data.content
|
||||
|
||||
var context = {}
|
||||
|
||||
if target_node is CanvasItem:
|
||||
target_node.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
else:
|
||||
target_node.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
|
||||
_setup_texture(target_node, sprite_sheet, content, context, options.slice != "")
|
||||
var result = _configure_animations(target_node, player, content, context, options)
|
||||
if result != result_code.SUCCESS:
|
||||
return result
|
||||
|
||||
return _cleanup_animations(target_node, player, content, options)
|
||||
|
||||
|
||||
func _load_texture(sprite_sheet: String) -> Texture2D:
|
||||
var texture = ResourceLoader.load(sprite_sheet, 'Image', ResourceLoader.CACHE_MODE_IGNORE)
|
||||
texture.take_over_path(sprite_sheet)
|
||||
return texture
|
||||
|
||||
|
||||
func _configure_animations(target_node: Node, player: AnimationPlayer, content: Dictionary, context: Dictionary, options: Dictionary):
|
||||
var frames = _aseprite.get_content_frames(content)
|
||||
var slice_rect = null
|
||||
if options.slice != "":
|
||||
options["slice_rect"] = _aseprite.get_slice_rect(content, options.slice)
|
||||
|
||||
if not player.has_animation_library(_DEFAULT_ANIMATION_LIBRARY):
|
||||
player.add_animation_library(_DEFAULT_ANIMATION_LIBRARY, AnimationLibrary.new())
|
||||
|
||||
if content.meta.has("frameTags") and content.meta.frameTags.size() > 0:
|
||||
var result = result_code.SUCCESS
|
||||
for tag in content.meta.frameTags:
|
||||
var selected_frames = frames.slice(tag.from, tag.to + 1)
|
||||
result = _add_animation_frames(target_node, player, tag.name, selected_frames, context, options, tag.direction, int(tag.get("repeat", -1)))
|
||||
if result != result_code.SUCCESS:
|
||||
break
|
||||
return result
|
||||
else:
|
||||
return _add_animation_frames(target_node, player, "default", frames, context, options)
|
||||
|
||||
|
||||
func _add_animation_frames(target_node: Node, player: AnimationPlayer, anim_name: String, frames: Array, context: Dictionary, options: Dictionary, direction = 'forward', repeat = -1):
|
||||
var animation_name = anim_name
|
||||
var library_name = _DEFAULT_ANIMATION_LIBRARY
|
||||
var is_loopable = _config.is_default_animation_loop_enabled()
|
||||
var slice_rect = options.get("slice_rect")
|
||||
var is_importing_slice: bool = slice_rect != null
|
||||
|
||||
var anim_tokens := anim_name.split("/")
|
||||
|
||||
if anim_tokens.size() > 2:
|
||||
push_error("Invalid animation name: %s" % animation_name)
|
||||
return
|
||||
elif anim_tokens.size() == 2:
|
||||
library_name = anim_tokens[0]
|
||||
animation_name = anim_tokens[1]
|
||||
|
||||
if not _validate_animation_name(animation_name):
|
||||
push_error("Invalid animation name: %s" % animation_name)
|
||||
return
|
||||
|
||||
# Create library if doesn't exist
|
||||
if library_name != _DEFAULT_ANIMATION_LIBRARY and not player.has_animation_library(library_name):
|
||||
player.add_animation_library(library_name, AnimationLibrary.new())
|
||||
|
||||
# Check loop
|
||||
if animation_name.begins_with(_config.get_animation_loop_exception_prefix()):
|
||||
animation_name = animation_name.substr(_config.get_animation_loop_exception_prefix().length())
|
||||
is_loopable = not is_loopable
|
||||
|
||||
# Add library
|
||||
if not player.get_animation_library(library_name).has_animation(animation_name):
|
||||
player.get_animation_library(library_name).add_animation(animation_name, Animation.new())
|
||||
|
||||
var full_name = (
|
||||
animation_name if library_name == "" else "%s/%s" % [library_name, animation_name]
|
||||
)
|
||||
|
||||
var animation = player.get_animation(full_name)
|
||||
_cleanup_tracks(target_node, player, animation)
|
||||
_create_meta_tracks(target_node, player, animation)
|
||||
|
||||
var frame_track = _get_property_track_path(player, target_node, _get_frame_property(is_importing_slice))
|
||||
var frame_track_index = _create_track(target_node, animation, frame_track)
|
||||
|
||||
if direction == "reverse" or direction == "pingpong_reverse":
|
||||
frames.reverse()
|
||||
|
||||
var animation_length = 0
|
||||
|
||||
var repetition = 1
|
||||
|
||||
if repeat != -1:
|
||||
is_loopable = false
|
||||
repetition = repeat
|
||||
|
||||
for i in range(repetition):
|
||||
for frame in frames:
|
||||
var frame_key = _get_frame_key(target_node, frame, context, slice_rect)
|
||||
animation.track_insert_key(frame_track_index, animation_length, frame_key)
|
||||
animation_length += frame.duration / 1000
|
||||
|
||||
# Godot 4 has an Animation.LOOP_PINGPONG mode, however it does not
|
||||
# behave like in Aseprite, so I'm keeping the custom implementation
|
||||
if direction.begins_with("pingpong"):
|
||||
var working_frames = frames.duplicate()
|
||||
working_frames.remove_at(working_frames.size() - 1)
|
||||
if is_loopable or (repetition > 1 and i < repetition - 1):
|
||||
working_frames.remove_at(0)
|
||||
working_frames.reverse()
|
||||
|
||||
for frame in working_frames:
|
||||
var frame_key = _get_frame_key(target_node, frame, context, slice_rect)
|
||||
animation.track_insert_key(frame_track_index, animation_length, frame_key)
|
||||
animation_length += frame.duration / 1000
|
||||
|
||||
# if keep_anim_length is enabled only adjust length if
|
||||
# - there aren't other tracks besides metas and frame
|
||||
# - the current animation is shorter than new one
|
||||
if not options.keep_anim_length or (animation.get_track_count() == (_get_meta_prop_names().size() + 1) or animation.length < animation_length):
|
||||
animation.length = animation_length
|
||||
|
||||
animation.loop_mode = Animation.LOOP_LINEAR if is_loopable else Animation.LOOP_NONE
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
const _INVALID_TOKENS := ["/", ":", ",", "["]
|
||||
|
||||
|
||||
func _validate_animation_name(name: String) -> bool:
|
||||
return not _INVALID_TOKENS.any(func(token: String): return token in name)
|
||||
|
||||
|
||||
func _create_track(target_node: Node, animation: Animation, track: String):
|
||||
var track_index = animation.find_track(track, Animation.TYPE_VALUE)
|
||||
|
||||
if track_index != -1:
|
||||
animation.remove_track(track_index)
|
||||
|
||||
track_index = animation.add_track(Animation.TYPE_VALUE)
|
||||
animation.track_set_path(track_index, track)
|
||||
animation.track_set_interpolation_loop_wrap(track_index, false)
|
||||
animation.value_track_set_update_mode(track_index, Animation.UPDATE_DISCRETE)
|
||||
|
||||
return track_index
|
||||
|
||||
|
||||
func _get_property_track_path(player: AnimationPlayer, target_node: Node, prop: String) -> String:
|
||||
var node_path = player.get_node(player.root_node).get_path_to(target_node)
|
||||
return "%s:%s" % [node_path, prop]
|
||||
|
||||
|
||||
func _cleanup_animations(target_node: Node, player: AnimationPlayer, content: Dictionary, options: Dictionary):
|
||||
if not (content.meta.has("frameTags") and content.meta.frameTags.size() > 0):
|
||||
return result_code.SUCCESS
|
||||
|
||||
_remove_unused_animations(content, player)
|
||||
|
||||
if options.get("cleanup_hide_unused_nodes", false):
|
||||
_hide_unused_nodes(target_node, player, content)
|
||||
|
||||
return result_code.SUCCESS
|
||||
|
||||
|
||||
func _remove_unused_animations(content: Dictionary, player: AnimationPlayer):
|
||||
pass # FIXME it's not removing unused animations anymore. Sample impl bellow
|
||||
# var tags = ["RESET"]
|
||||
# for t in content.meta.frameTags:
|
||||
# var a = t.name
|
||||
# if a.begins_with(_config.get_animation_loop_exception_prefix()):
|
||||
# a = a.substr(_config.get_animation_loop_exception_prefix().length())
|
||||
# tags.push_back(a)
|
||||
|
||||
# var track = _get_frame_track_path(player, sprite)
|
||||
# for a in player.get_animation_list():
|
||||
# if tags.has(a):
|
||||
# continue
|
||||
#
|
||||
# var animation = player.get_animation(a)
|
||||
# if animation.get_track_count() != 1:
|
||||
# var t = animation.find_track(track)
|
||||
# if t != -1:
|
||||
# animation.remove_track(t)
|
||||
# continue
|
||||
#
|
||||
# if animation.find_track(track) != -1:
|
||||
# player.remove_animation(a)
|
||||
|
||||
|
||||
func _hide_unused_nodes(target_node: Node, player: AnimationPlayer, content: Dictionary):
|
||||
var root_node := player.get_node(player.root_node)
|
||||
var all_animations := player.get_animation_list()
|
||||
var all_sprite_nodes := []
|
||||
var animation_sprites := {}
|
||||
|
||||
for a in all_animations:
|
||||
var animation := player.get_animation(a)
|
||||
var sprite_nodes := []
|
||||
|
||||
for track_idx in animation.get_track_count():
|
||||
var raw_path := animation.track_get_path(track_idx)
|
||||
|
||||
if raw_path.get_subname(0) == "visible":
|
||||
continue
|
||||
|
||||
var path := _remove_properties_from_path(raw_path)
|
||||
var sprite_node := root_node.get_node(path)
|
||||
|
||||
if !(sprite_node is Sprite2D || sprite_node is Sprite3D):
|
||||
continue
|
||||
|
||||
if sprite_nodes.has(sprite_node):
|
||||
continue
|
||||
sprite_nodes.append(sprite_node)
|
||||
|
||||
animation_sprites[animation] = sprite_nodes
|
||||
for sn in sprite_nodes:
|
||||
if all_sprite_nodes.has(sn):
|
||||
continue
|
||||
all_sprite_nodes.append(sn)
|
||||
|
||||
for animation in animation_sprites:
|
||||
var sprite_nodes : Array = animation_sprites[animation]
|
||||
for node in all_sprite_nodes:
|
||||
if sprite_nodes.has(node):
|
||||
continue
|
||||
var visible_track = _get_property_track_path(player, node, "visible")
|
||||
if animation.find_track(visible_track, Animation.TYPE_VALUE) != -1:
|
||||
continue
|
||||
var visible_track_index = _create_track(node, animation, visible_track)
|
||||
animation.track_insert_key(visible_track_index, 0, false)
|
||||
|
||||
|
||||
func list_layers(file: String, only_visibles = false) -> Array:
|
||||
return _aseprite.list_layers(file, only_visibles)
|
||||
|
||||
|
||||
func list_slices(file: String) -> Array:
|
||||
return _aseprite.list_slices(file)
|
||||
|
||||
|
||||
func _remove_properties_from_path(path: NodePath) -> NodePath:
|
||||
var string_path := path as String
|
||||
if !(":" in string_path):
|
||||
return string_path as NodePath
|
||||
|
||||
var property_path := path.get_concatenated_subnames() as String
|
||||
string_path = string_path.substr(0, string_path.length() - property_path.length() - 1)
|
||||
|
||||
return string_path as NodePath
|
||||
|
||||
|
||||
func _create_meta_tracks(target_node: Node, player: AnimationPlayer, animation: Animation):
|
||||
for prop in _get_meta_prop_names():
|
||||
var track = _get_property_track_path(player, target_node, prop)
|
||||
var track_index = _create_track(target_node, animation, track)
|
||||
animation.track_insert_key(track_index, 0, true if prop == "visible" else target_node.get(prop))
|
||||
|
||||
|
||||
func _cleanup_tracks(target_node: Node, player: AnimationPlayer, animation: Animation):
|
||||
for track_key in ["texture", "hframes", "vframes", "region_rect", "frame"]:
|
||||
var track = _get_property_track_path(player, target_node, track_key)
|
||||
var track_index = animation.find_track(track, Animation.TYPE_VALUE)
|
||||
if track_index != -1:
|
||||
animation.remove_track(track_index)
|
||||
|
||||
|
||||
func _setup_texture(target_node: Node, sprite_sheet: String, content: Dictionary, context: Dictionary, is_importing_slice: bool):
|
||||
push_error("_setup_texture not implemented!")
|
||||
|
||||
|
||||
func _get_frame_property(is_importing_slice: bool) -> String:
|
||||
push_error("_get_frame_property not implemented!")
|
||||
return ""
|
||||
|
||||
|
||||
func _get_frame_key(target_node: Node, frame: Dictionary, context: Dictionary, slice_info: Variant):
|
||||
push_error("_get_frame_key not implemented!")
|
||||
|
||||
|
||||
func _get_meta_prop_names():
|
||||
push_error("_get_meta_prop_names not implemented!")
|
||||
@@ -0,0 +1,48 @@
|
||||
extends "animation_creator.gd"
|
||||
|
||||
func _get_meta_prop_names():
|
||||
return [ "visible" ]
|
||||
|
||||
|
||||
func _setup_texture(sprite: Node, sprite_sheet: String, content: Dictionary, context: Dictionary, is_importing_slice: bool):
|
||||
var texture = _load_texture(sprite_sheet)
|
||||
sprite.texture = texture
|
||||
|
||||
if content.frames.is_empty():
|
||||
return
|
||||
|
||||
if is_importing_slice:
|
||||
sprite.region_enabled = true
|
||||
sprite.hframes = 1
|
||||
sprite.vframes = 1
|
||||
sprite.frame = 0
|
||||
else:
|
||||
sprite.region_enabled = false
|
||||
sprite.hframes = content.meta.size.w / content.frames[0].sourceSize.w
|
||||
sprite.vframes = content.meta.size.h / content.frames[0].sourceSize.h
|
||||
|
||||
|
||||
func _get_frame_property(is_importing_slice: bool) -> String:
|
||||
return "frame" if not is_importing_slice else "region_rect"
|
||||
|
||||
|
||||
func _get_frame_key(sprite: Node, frame: Dictionary, context: Dictionary, slice_info: Variant):
|
||||
if slice_info != null:
|
||||
return _create_slice_rect(frame, slice_info)
|
||||
return _calculate_frame_index(sprite,frame)
|
||||
|
||||
|
||||
func _calculate_frame_index(sprite: Node, frame: Dictionary) -> int:
|
||||
var column = floor(frame.frame.x * sprite.hframes / sprite.texture.get_width())
|
||||
var row = floor(frame.frame.y * sprite.vframes / sprite.texture.get_height())
|
||||
return (row * sprite.hframes) + column
|
||||
|
||||
|
||||
func _create_slice_rect(frame_data: Dictionary, slice_rect: Rect2) -> Rect2:
|
||||
var frame = frame_data.frame
|
||||
return Rect2(
|
||||
frame.x + slice_rect.position.x,
|
||||
frame.y + slice_rect.position.y,
|
||||
slice_rect.size.x,
|
||||
slice_rect.size.y
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
extends "animation_creator.gd"
|
||||
|
||||
|
||||
func _get_meta_prop_names():
|
||||
return [ "visible" ]
|
||||
|
||||
|
||||
func _setup_texture(target_node: Node, sprite_sheet: String, content: Dictionary, context: Dictionary, _is_importing_slice: bool):
|
||||
context["base_texture"] = _load_texture(sprite_sheet)
|
||||
|
||||
|
||||
func _get_frame_property(_is_importing_slice: bool) -> String:
|
||||
return "texture"
|
||||
|
||||
|
||||
func _get_frame_key(target_node: Node, frame: Dictionary, context: Dictionary, slice_info: Variant):
|
||||
return _get_atlas_texture(context["base_texture"], frame, slice_info)
|
||||
|
||||
|
||||
func _get_atlas_texture(base_texture: Texture2D, frame_data: Dictionary, slice_info: Variant) -> AtlasTexture:
|
||||
var tex = AtlasTexture.new()
|
||||
tex.atlas = base_texture
|
||||
tex.region = Rect2(Vector2(frame_data.frame.x, frame_data.frame.y), Vector2(frame_data.frame.w, frame_data.frame.h))
|
||||
tex.filter_clip = true
|
||||
|
||||
if slice_info != null:
|
||||
tex.region.position.x += slice_info.position.x
|
||||
tex.region.position.y += slice_info.position.y
|
||||
tex.region.size.x = slice_info.size.x
|
||||
tex.region.size.y = slice_info.size.y
|
||||
|
||||
return tex
|
||||
@@ -0,0 +1,16 @@
|
||||
@tool
|
||||
extends RefCounted
|
||||
|
||||
var result_code = preload("../config/result_codes.gd")
|
||||
var _aseprite = preload("../aseprite/aseprite.gd").new()
|
||||
var _aseprite_file_exporter = preload("../aseprite/file_exporter.gd").new()
|
||||
|
||||
var _config
|
||||
|
||||
##
|
||||
## Load initial dependencies
|
||||
##
|
||||
func init(config):
|
||||
_config = config
|
||||
_aseprite.init(config)
|
||||
_aseprite_file_exporter.init(config)
|
||||
@@ -0,0 +1,242 @@
|
||||
@tool
|
||||
extends "../base_sprite_resource_creator.gd"
|
||||
|
||||
enum {
|
||||
FILE_EXPORT_MODE,
|
||||
LAYERS_EXPORT_MODE
|
||||
}
|
||||
|
||||
###
|
||||
### Create SpriteFrames from aseprite files and insert
|
||||
### them to the animated_sprite node
|
||||
###
|
||||
func create_animations(animated_sprite: Node, aseprite_files: Dictionary, options: Dictionary) -> void:
|
||||
var sprite_frames_result = _create_sprite_frames(aseprite_files, options)
|
||||
if not sprite_frames_result.is_ok:
|
||||
printerr(result_code.get_error_message(sprite_frames_result.code))
|
||||
return
|
||||
|
||||
animated_sprite.frames = sprite_frames_result.content
|
||||
|
||||
if animated_sprite is CanvasItem:
|
||||
animated_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
else:
|
||||
animated_sprite.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
|
||||
|
||||
func create_and_save_resources(source_files: Array) -> int:
|
||||
var resources = create_resources(source_files)
|
||||
if resources.is_ok:
|
||||
return _save_resources(resources.content)
|
||||
|
||||
return resources.code
|
||||
|
||||
|
||||
func create_resources(source_files: Array, options: Dictionary = {}) -> Dictionary:
|
||||
var resources = []
|
||||
|
||||
for o in source_files:
|
||||
if o.is_empty():
|
||||
return result_code.error(result_code.ERR_ASEPRITE_EXPORT_FAILED)
|
||||
|
||||
var resource = _create_sprite_frames(o, options)
|
||||
|
||||
if not resource.is_ok:
|
||||
return resource
|
||||
|
||||
resources.push_back({
|
||||
"data_file": o.data_file,
|
||||
"resource": resource.content,
|
||||
})
|
||||
|
||||
return result_code.result(resources)
|
||||
|
||||
|
||||
func _create_sprite_frames(data: Dictionary, options: Dictionary) -> Dictionary:
|
||||
var aseprite_resources = _load_aseprite_resources(data)
|
||||
if not aseprite_resources.is_ok:
|
||||
return aseprite_resources
|
||||
|
||||
return result_code.result(
|
||||
_create_sprite_frames_with_animations(
|
||||
aseprite_resources.content.metadata,
|
||||
aseprite_resources.content.texture,
|
||||
options,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _load_aseprite_resources(aseprite_data: Dictionary):
|
||||
var content_result = _aseprite_file_exporter.load_json_content(aseprite_data.data_file)
|
||||
|
||||
if not content_result.is_ok:
|
||||
return content_result
|
||||
|
||||
var texture = _load_texture(aseprite_data.sprite_sheet)
|
||||
|
||||
return result_code.result({
|
||||
"metadata": content_result.content,
|
||||
"texture": texture
|
||||
})
|
||||
|
||||
|
||||
func _save_resources(resources: Array) -> int:
|
||||
for resource in resources:
|
||||
var code = _save_resource(resource.resource, resource.data_file)
|
||||
if code != OK:
|
||||
return code
|
||||
return OK
|
||||
|
||||
|
||||
func _save_resource(resource, source_path: String) -> int:
|
||||
var save_path = "%s.%s" % [source_path.get_basename(), "res"]
|
||||
var code = ResourceSaver.save(resource, save_path, ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS)
|
||||
resource.take_over_path(save_path)
|
||||
return code
|
||||
|
||||
|
||||
func _create_sprite_frames_with_animations(content: Dictionary, texture, options: Dictionary) -> SpriteFrames:
|
||||
var frame_cache = {}
|
||||
var frames = _aseprite.get_content_frames(content)
|
||||
var sprite_frames := SpriteFrames.new()
|
||||
sprite_frames.remove_animation("default")
|
||||
|
||||
var frame_rect: Variant = null
|
||||
|
||||
# currently, aseprite does not work with the --slice option, so we need to manually
|
||||
# do it. https://github.com/aseprite/aseprite/issues/2469
|
||||
if options.get("slice", "") != "":
|
||||
frame_rect = _aseprite.get_slice_rect(content, options.slice)
|
||||
|
||||
if content.meta.has("frameTags") and content.meta.frameTags.size() > 0:
|
||||
for tag in content.meta.frameTags:
|
||||
var selected_frames = frames.slice(tag.from, tag.to + 1)
|
||||
_add_animation_frames(sprite_frames, tag.name, selected_frames, texture, frame_rect, tag.direction, int(tag.get("repeat", -1)), frame_cache)
|
||||
else:
|
||||
_add_animation_frames(sprite_frames, "default", frames, texture, frame_rect)
|
||||
|
||||
return sprite_frames
|
||||
|
||||
|
||||
func _add_animation_frames(
|
||||
sprite_frames: SpriteFrames,
|
||||
anim_name: String,
|
||||
frames: Array,
|
||||
texture,
|
||||
frame_rect: Variant,
|
||||
direction = 'forward',
|
||||
repeat = -1,
|
||||
frame_cache = {}
|
||||
):
|
||||
var animation_name := anim_name
|
||||
var is_loopable = _config.is_default_animation_loop_enabled()
|
||||
|
||||
var loop_prefix = _config.get_animation_loop_exception_prefix()
|
||||
if animation_name.begins_with(loop_prefix):
|
||||
animation_name = anim_name.trim_prefix(loop_prefix)
|
||||
is_loopable = not is_loopable
|
||||
|
||||
sprite_frames.add_animation(animation_name)
|
||||
|
||||
var min_duration = _get_min_duration(frames)
|
||||
var fps = _calculate_fps(min_duration)
|
||||
|
||||
if direction == "reverse" or direction == "pingpong_reverse":
|
||||
frames.reverse()
|
||||
|
||||
var repetition = 1
|
||||
|
||||
if repeat != -1:
|
||||
is_loopable = false
|
||||
repetition = repeat
|
||||
|
||||
for i in range(repetition):
|
||||
for frame in frames:
|
||||
_add_to_sprite_frames(sprite_frames, animation_name, texture, frame, min_duration, frame_cache, frame_rect)
|
||||
|
||||
if direction.begins_with("pingpong"):
|
||||
var working_frames = frames.duplicate()
|
||||
working_frames.remove_at(working_frames.size() - 1)
|
||||
if is_loopable or (repetition > 1 and i < repetition - 1):
|
||||
working_frames.remove_at(0)
|
||||
working_frames.reverse()
|
||||
|
||||
for frame in working_frames:
|
||||
_add_to_sprite_frames(sprite_frames, animation_name, texture, frame, min_duration, frame_cache, frame_rect)
|
||||
|
||||
sprite_frames.set_animation_loop(animation_name, is_loopable)
|
||||
sprite_frames.set_animation_speed(animation_name, fps)
|
||||
|
||||
|
||||
func _calculate_fps(min_duration: int) -> float:
|
||||
return ceil(1000.0 / min_duration)
|
||||
|
||||
|
||||
func _get_min_duration(frames) -> int:
|
||||
var min_duration = 100000
|
||||
for frame in frames:
|
||||
if frame.duration < min_duration:
|
||||
min_duration = frame.duration
|
||||
return min_duration
|
||||
|
||||
|
||||
func _load_texture(path) -> CompressedTexture2D:
|
||||
return ResourceLoader.load(path, "CompressedTexture2D", ResourceLoader.CACHE_MODE_REPLACE)
|
||||
|
||||
|
||||
func _add_to_sprite_frames(
|
||||
sprite_frames,
|
||||
animation_name: String,
|
||||
texture,
|
||||
frame: Dictionary,
|
||||
min_duration: int,
|
||||
frame_cache: Dictionary,
|
||||
frame_rect: Variant,
|
||||
):
|
||||
var atlas : AtlasTexture = _create_atlastexture_from_frame(texture, frame, sprite_frames, frame_cache, frame_rect)
|
||||
var duration = frame.duration / min_duration
|
||||
sprite_frames.add_frame(animation_name, atlas, duration)
|
||||
|
||||
|
||||
func _create_atlastexture_from_frame(
|
||||
image,
|
||||
frame_data,
|
||||
sprite_frames: SpriteFrames,
|
||||
frame_cache: Dictionary,
|
||||
frame_rect: Variant,
|
||||
) -> AtlasTexture:
|
||||
var frame = frame_data.frame
|
||||
var region := Rect2(frame.x, frame.y, frame.w, frame.h)
|
||||
|
||||
# this is to manually set the slice
|
||||
if frame_rect != null:
|
||||
region.position.x += frame_rect.position.x
|
||||
region.position.y += frame_rect.position.y
|
||||
region.size.x = frame_rect.size.x
|
||||
region.size.y = frame_rect.size.y
|
||||
|
||||
var key := "%s_%s_%s_%s" % [frame.x, frame.y, frame.w, frame.h]
|
||||
var texture = frame_cache.get(key)
|
||||
|
||||
if texture != null and texture.atlas == image:
|
||||
return texture
|
||||
|
||||
var atlas_texture := AtlasTexture.new()
|
||||
atlas_texture.atlas = image
|
||||
atlas_texture.region = region
|
||||
|
||||
frame_cache[key] = atlas_texture
|
||||
|
||||
return atlas_texture
|
||||
|
||||
|
||||
func list_layers(file: String, only_visibles = false) -> Array:
|
||||
return _aseprite.list_layers(file, only_visibles)
|
||||
|
||||
|
||||
func list_slices(file: String) -> Array:
|
||||
return _aseprite.list_slices(file)
|
||||
|
||||
|
||||
func _get_file_basename(file_path: String) -> String:
|
||||
return file_path.get_file().trim_suffix('.%s' % file_path.get_extension())
|
||||
@@ -0,0 +1,26 @@
|
||||
@tool
|
||||
extends "../base_sprite_resource_creator.gd"
|
||||
|
||||
func load_texture(target_node: Node, aseprite_files: Dictionary, options: Dictionary) -> void:
|
||||
var source_file = aseprite_files.data_file
|
||||
var sprite_sheet = aseprite_files.sprite_sheet
|
||||
var data = _aseprite_file_exporter.load_json_content(source_file)
|
||||
var texture = ResourceLoader.load(sprite_sheet)
|
||||
|
||||
if not data.is_ok:
|
||||
printerr("Failed to load aseprite source %s" % source_file)
|
||||
return
|
||||
|
||||
if options.slice == "":
|
||||
target_node.texture = texture
|
||||
else:
|
||||
var region = _aseprite.get_slice_rect(data.content, options.slice)
|
||||
var atlas_texture := AtlasTexture.new()
|
||||
atlas_texture.atlas = texture
|
||||
atlas_texture.region = region
|
||||
target_node.texture = atlas_texture
|
||||
|
||||
if target_node is CanvasItem:
|
||||
target_node.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
else:
|
||||
target_node.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
Reference in New Issue
Block a user