Ways to trace usage of built-in Resources

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By mneg

Under some occasions - maybe duplicating Nodes - Resources like Shaders “get built-in” and within the .tscn files start a life of their own without me noticing it. So my root .tscn file now has 50 MB due to some built-in texture images used by some built-in Shader used by some […]. Of course I can use e.g. “grep -nri” to roll through my project files, but is there a more “intended” way to trace or view built-in Resources and their usage?

EDIT: A dream tool would be a menu entry “Scene → View built-in resources” leading to a window listing all built-in resources and which Node is using it with some actions and wizards for extraction.

Just in case someone is interested, this is the answer GPT-4 gave me:

When resources get “built-in” into a scene file (.tscn), they become embedded into the scene and are no longer external dependencies. While this behavior can be useful for bundling resources in some cases, it can lead to unnecessarily large files and harder-to-maintain projects, as you have experienced.

Unfortunately, as of my knowledge cutoff in September 2021, Godot does not provide a built-in feature to easily trace or view built-in resources and their usage within the editor. However, you can take the following steps to address this issue and prevent it from happening in the future:

  • Manually inspect your scene files: Open your scene files in a text editor, and search for [sub_resource entries, which indicate built-in resources. If you find any, you can extract the resource and save it as an external file, then replace the reference in the scene file with a reference to the external file.

  • Use version control: If you’re not already using version control (e.g., Git), consider setting it up for your project. Version control can help you track changes to your files and detect when resources become built-in. When reviewing your commits, you can spot unintentional changes and revert them as needed.

  • Be mindful of duplicating nodes: When duplicating nodes in the editor, be aware of the resources used by the node and their duplication settings. To prevent resources from being embedded, ensure the resource’s “Local to Scene” property is unchecked in the Inspector.

  • Use ResourceLoader and ResourceSaver: When dealing with resources programmatically, use the ResourceLoader and ResourceSaver classes to load and save resources. This way, you can better manage how resources are stored and referenced in your project.

  • Optimize your project before exporting: When preparing your project for deployment, use the Godot export settings to optimize resource usage. In the export settings, you can enable options like “Compress Textures” and “Remove Unused Resources” to reduce the size of your exported project.

While these steps can help you manage built-in resources and prevent them from bloating your project files, it would be beneficial for future Godot versions to provide a more streamlined way to trace and view built-in resources within the editor.

mneg | 2023-04-12 15:20

:bust_in_silhouette: Reply From: mneg

I found out that some font files and textures got built-in and provided their users with the corresponding external resources. Now I am back to 92 kB for my root .tscn file. Crazy that a simple font got blown up to 50 MB. In order to trace the usage of built-in resources in my scene, I wrote and used the following script. It would not be too much work to build an editor tool around this.

func find_builtin_resources(object: Object, path: String = "/" + object.name):
	var found = {"resources": {}, "objects": {}}
	var node := object as Node

	# Iterate over the properties of the object
	for property in object.get_property_list():
		if property.type == TYPE_OBJECT:
			var obj = object.get(property.name)
			if !obj: continue
			var id: String = path + "/" + property.name
			if obj is Resource:
				# check if the resource is built-in
				if obj.resource_path.contains("::"):
					var resourceID: String = obj.resource_path
					found.resources[resourceID] = id
					found.objects[object] = obj

				var ret = find_builtin_resources(obj, id)

	# Recursively search Nodes's children
	if node: for child in object.get_children():
		var ret = find_builtin_resources(child, path + "/" + child.name)
	return found

Edit: I found this feature proposal, the according tool will be integrated in 4.1. Maybe it can do the job.