Automatically set "keep_custom_tracks" on imported animations

Godot Version

4.5 Stable

Question

Hi, I’m trying to figure out if there’s any way to have all imported animations setup with the “save to file” and, most importantly, “keep custom tracks” option on, without having to turn it on manually on every asset.
My project uses a lot of custom tracks so I pretty much need it on every animation I import, and if I forget and reimport a new version of the animation I can lose all my custom tracks.

The closest I got so far to a solution is to use a post-import script (see doc)
That way I can get the AnimationPlayer and the Animations in it using this script:

@tool # Needed so it runs in editor.
extends EditorScenePostImport

# This sample changes all node names.
# Called right after the scene is imported and gets the root node.
func _post_import(scene):
	# Change all node names to "modified_[oldnodename]"
	iterate(scene)
	return scene # Remember to return the imported scene

# Recursive function that is called on every node
# (for demonstration purposes; EditorScenePostImport only requires a `_post_import(scene)` function).
func iterate(node):
	if node != null:
		#print_rich("Post-import: [b]%s[/b] -> [b]%s[/b]" % [node.name, "modified_" + node.name])
		#node.name = "modified_" + node.name
		if node is AnimationPlayer:
			for anim in (node as AnimationPlayer).get_animation_list():
				var animation = (node as AnimationPlayer).get_animation(anim)
				print("THIS IS THE ANIMATION: ", animation)

		for child in node.get_children():
			iterate(child)

Unfortunately that’s as far as I could get, because “keep custom tracks” does not seem to be an attribute of the animation itself.

Any idea how to do this?

Here’s a solution for that

1 Like

Thanks, that seems to be pretty much what I’m looking for!
But how do I load a EditorScenePostImportPlugin?
I checked the documentation but I don’t see it explained anywhere and loading it as a regular Editor Plugin doesn’t seem to work.

EDIT:
Never mind I figured it out, you have to load it through a Editor Plugin like it’s explained here

Ok so I got it to work the way I wanted, this was the solution:

First, create an EditorPlugin, this is necessary because you can’t load the EditorScenePostImportPlugin directly.
The plugin code looks like this:

@tool
extends EditorPlugin

var import_plugin

func _enter_tree():
	import_plugin = preload("import_post_process.gd").new()
	add_scene_post_import_plugin(import_plugin)

func _exit_tree():
	remove_scene_post_import_plugin(import_plugin)
	import_plugin = null

Note that you need to use add_scene_post_import_plugin() and remove_scene_post_import_plugin() to be able to use a EditorScenePostImportPlugin.
The “import_post_process.gd” file has to be in the same folder of the plugin and contains this code:

@tool
extends EditorScenePostImportPlugin

func _pre_process(scene: Node) -> void:
	var original_out  = get_option_value("_subresources")
	var subresources = {}
	iterate(scene, subresources)

	var animations = subresources.get("animations", [])
	if not animations.is_empty():
		var new_out = {} 
		new_out["animations"] = {}
		for animation in animations:
			new_out["animations"][animation] = {
				"save_to_file/enabled": true,
				"save_to_file/path": original_out["animations"][animation]["save_to_file/path"],
				"save_to_file/keep_custom_tracks": true}
		original_out = new_out

func iterate(node:Node, subresources:Dictionary) -> void:
	if node == null:
		return

	if node is AnimationPlayer:
		if not subresources.has("animations"):
			subresources["animations"] = []
		var animations = subresources["animations"]
		animations.append_array(node.get_animation_list())

	for child in node.get_children():
		iterate(child, subresources)

Which is a modified version of the solution posted aboce, so big thanks to @mrcdk !