Godot’s import system automatically processes external assets and converts them into engine-optimized formats. The system supports various file types and allows you to create custom importers for specialized formats.
Asset Import Pipeline
When you add files to your project, Godot automatically:
Detects the file type based on extension
Selects an appropriate importer
Processes the file with import settings
Saves the imported resource to .godot/imported/
Creates a .import file to track settings
How Imports Work
# When you load a resource, Godot uses the imported version
var texture = load ( "res://icon.png" ) # Actually loads from .godot/imported/
# The original file remains untouched
# All modifications are stored in the .import file
ResourceImporter Base Class
All importers inherit from ResourceImporter:
extends ResourceImporter
# Check build dependencies for compilation profiles
func _get_build_dependencies ( path ):
var resource = load ( path )
var dependencies = PackedStringArray ()
if resource . has_advanced_features :
dependencies . push_back ( "module_advanced_enabled" )
return dependencies
Import Order
Importers run in a specific order to handle dependencies:
IMPORT_ORDER_DEFAULT (0) - Most importers
IMPORT_ORDER_SCENE (100) - Scene importers (run last)
Scenes import last because they may reference other imported resources like textures and models.
Creating Custom Import Plugins
Extend EditorImportPlugin to create custom importers:
@tool
extends EditorImportPlugin
func _get_importer_name ():
return "my.custom.importer"
func _get_visible_name ():
return "Custom Data"
func _get_recognized_extensions ():
return [ "mydata" , "dat" ]
func _get_save_extension ():
return "res"
func _get_resource_type ():
return "Resource"
func _get_preset_count ():
return 2
func _get_preset_name ( preset_index ):
match preset_index :
0 : return "Default"
1 : return "Optimized"
return "Unknown"
func _get_import_options ( path , preset_index ):
match preset_index :
0 : # Default
return [
{ "name" : "quality" , "default_value" : 1.0 },
{ "name" : "compress" , "default_value" : false }
]
1 : # Optimized
return [
{ "name" : "quality" , "default_value" : 0.8 },
{ "name" : "compress" , "default_value" : true }
]
return []
func _get_priority ():
return 1.0 # Higher priority than default importers
func _get_import_order ():
return ResourceImporter . IMPORT_ORDER_DEFAULT
func _import ( source_file , save_path , options , platform_variants , gen_files ):
var file = FileAccess . open ( source_file , FileAccess . READ )
if file == null :
return FileAccess . get_open_error ()
# Read and process file
var data = file . get_buffer ( file . get_length ())
file . close ()
# Create resource
var resource = MyCustomResource . new ()
resource . data = data
resource . quality = options [ "quality" ]
if options [ "compress" ]:
resource . compress_data ()
# Save imported resource
var filename = save_path + "." + _get_save_extension ()
return ResourceSaver . save ( resource , filename )
Return OK (or @GlobalScope.OK) for successful imports, other error codes for failures.
Register the Import Plugin
@tool
extends EditorPlugin
var import_plugin
func _enter_tree ():
import_plugin = preload ( "res://addons/my_importer/importer.gd" ). new ()
# Set first_priority to true to run before built-in importers
add_import_plugin ( import_plugin , false )
func _exit_tree ():
remove_import_plugin ( import_plugin )
Import Presets
Presets provide predefined configurations for different use cases:
func _get_preset_count ():
return 3
func _get_preset_name ( preset_index ):
match preset_index :
0 : return "Low Quality"
1 : return "Medium Quality"
2 : return "High Quality"
return ""
func _get_import_options ( path , preset_index ):
var base_options = [
{
"name" : "quality" ,
"default_value" : 0.5 ,
"property_hint" : PROPERTY_HINT_RANGE ,
"hint_string" : "0.0,1.0,0.1"
},
{
"name" : "format" ,
"default_value" : 0 ,
"property_hint" : PROPERTY_HINT_ENUM ,
"hint_string" : "Compressed,Uncompressed,Lossy"
}
]
match preset_index :
0 : # Low Quality
base_options [ 0 ][ "default_value" ] = 0.3
base_options [ 1 ][ "default_value" ] = 2 # Lossy
1 : # Medium Quality
base_options [ 0 ][ "default_value" ] = 0.7
base_options [ 1 ][ "default_value" ] = 0 # Compressed
2 : # High Quality
base_options [ 0 ][ "default_value" ] = 1.0
base_options [ 1 ][ "default_value" ] = 1 # Uncompressed
return base_options
Conditional Options
Show or hide options based on other settings:
func _get_option_visibility ( path , option_name , options ):
# Only show quality if format is "Lossy"
if option_name == "quality" :
return options . get ( "format" , 0 ) == 2
# Show all other options
return true
Importing 3D Models
For 3D formats like GLTF and FBX, use EditorSceneFormatImporter:
@tool
extends EditorSceneFormatImporter
func _get_extensions ():
return [ "custom3d" ]
func _get_import_options ( path ):
# Only add options when path matches this importer
if path . is_empty () or path . get_extension () == "custom3d" :
add_import_option ( "scale" , 1.0 )
add_import_option ( "generate_tangents" , true )
add_import_option_advanced (
TYPE_INT ,
"mesh_compression" ,
0 ,
PROPERTY_HINT_ENUM ,
"Disabled,Low,High"
)
func _get_option_visibility ( path , for_animation , option ):
# Hide mesh options when importing animations
if for_animation and option . begins_with ( "mesh_" ):
return false
return true
func _import_scene ( path , flags , options ):
var file = FileAccess . open ( path , FileAccess . READ )
if file == null :
return null
# Parse custom 3D format
var scene_data = _parse_custom_format ( file )
file . close ()
# Build scene tree
var root = Node3D . new ()
root . name = path . get_file (). get_basename ()
for mesh_data in scene_data . meshes :
var mesh_instance = MeshInstance3D . new ()
mesh_instance . mesh = _create_mesh ( mesh_data )
if options . get ( "generate_tangents" , true ):
mesh_instance . mesh = _generate_tangents ( mesh_instance . mesh )
root . add_child ( mesh_instance )
mesh_instance . owner = root
return root
Import Flags
Scene importers can check flags to determine what to import:
IMPORT_SCENE - Import as scene
IMPORT_ANIMATION - Import animations
IMPORT_FAIL_ON_MISSING_DEPENDENCIES - Fail if dependencies missing
IMPORT_GENERATE_TANGENT_ARRAYS - Generate tangent arrays
IMPORT_USE_NAMED_SKIN_BINDS - Use named skin bindings
IMPORT_DISCARD_MESHES_AND_MATERIALS - Import only structure
IMPORT_FORCE_DISABLE_MESH_COMPRESSION - Disable compression
func _import_scene ( path , flags , options ):
var import_meshes = flags & EditorSceneFormatImporter . IMPORT_SCENE
var import_animations = flags & EditorSceneFormatImporter . IMPORT_ANIMATION
if not import_meshes :
# Skip mesh data
pass
Importing Textures
For custom texture formats:
@tool
extends EditorImportPlugin
func _get_importer_name ():
return "custom.texture.importer"
func _get_visible_name ():
return "Custom Texture"
func _get_recognized_extensions ():
return [ "ctex" ]
func _get_save_extension ():
return "ctex"
func _get_resource_type ():
return "CompressedTexture2D"
func _get_import_options ( path , preset_index ):
return [
{ "name" : "mipmaps" , "default_value" : true },
{ "name" : "filter" , "default_value" : true },
{
"name" : "compression_mode" ,
"default_value" : 0 ,
"property_hint" : PROPERTY_HINT_ENUM ,
"hint_string" : "Lossless,Lossy,VRAM,Uncompressed"
},
{
"name" : "lossy_quality" ,
"default_value" : 0.7 ,
"property_hint" : PROPERTY_HINT_RANGE ,
"hint_string" : "0.0,1.0,0.01"
}
]
func _get_option_visibility ( path , option_name , options ):
if option_name == "lossy_quality" :
return options . get ( "compression_mode" , 0 ) == 1 # Only for Lossy
return true
func _import ( source_file , save_path , options , platform_variants , gen_files ):
# Load and decode custom texture format
var image = _load_custom_texture ( source_file )
if options [ "mipmaps" ]:
image . generate_mipmaps ()
# Create and save compressed texture
var texture = ImageTexture . create_from_image ( image )
# Save for different platforms if needed
if platform_variants . size () > 0 :
for tag in platform_variants :
var variant_path = save_path + "." + tag + "." + _get_save_extension ()
ResourceSaver . save ( texture , variant_path )
return ResourceSaver . save ( texture , save_path + "." + _get_save_extension ())
Importing Audio Files
@tool
extends EditorImportPlugin
func _get_importer_name ():
return "custom.audio.importer"
func _get_visible_name ():
return "Custom Audio"
func _get_recognized_extensions ():
return [ "caud" ]
func _get_save_extension ():
return "sample"
func _get_resource_type ():
return "AudioStreamWAV"
func _get_import_options ( path , preset_index ):
return [
{ "name" : "loop" , "default_value" : false },
{ "name" : "loop_offset" , "default_value" : 0.0 },
{
"name" : "format" ,
"default_value" : 0 ,
"property_hint" : PROPERTY_HINT_ENUM ,
"hint_string" : "8-bit,16-bit,IMA ADPCM,QOA"
}
]
func _import ( source_file , save_path , options , platform_variants , gen_files ):
var audio_data = _decode_custom_audio ( source_file )
var stream = AudioStreamWAV . new ()
stream . data = audio_data
stream . format = options [ "format" ]
stream . loop_mode = AudioStreamWAV . LOOP_FORWARD if options [ "loop" ] else AudioStreamWAV . LOOP_DISABLED
stream . loop_begin = int ( options [ "loop_offset" ] * stream . mix_rate )
return ResourceSaver . save ( stream , save_path + "." + _get_save_extension ())
Thread Safety
Indicate if your importer can run in parallel:
func _can_import_threaded ():
# Return true if your importer is thread-safe
return true
Only return true if your importer doesn’t use any shared state or makes thread-safe API calls.
Versioning Imports
Increment the format version when making breaking changes:
func _get_format_version ():
return 2 # Increment when format changes
# When version changes, all files will be re-imported
Appending External Resources
Import additional dependencies during the import process:
func _import ( source_file , save_path , options , platform_variants , gen_files ):
# Your main import logic
var main_resource = MyResource . new ()
# Import an embedded texture
var texture_path = "res://.godot/imported/embedded_texture.png"
var result = append_import_external_resource (
texture_path ,
{ "mipmaps" : true }, # Custom import options
"keep" # Use "keep" importer or specify custom one
)
if result == OK :
main_resource . texture = load ( texture_path )
gen_files . append ( texture_path ) # Track generated file
return ResourceSaver . save ( main_resource , save_path + "." + _get_save_extension ())
Common Import Patterns
Atlas/Sprite Sheet Importer
func _import ( source_file , save_path , options , platform_variants , gen_files ):
var image = Image . load_from_file ( source_file )
var tile_width = options [ "tile_width" ]
var tile_height = options [ "tile_height" ]
var atlas = SpriteFrames . new ()
var cols = image . get_width () / tile_width
var rows = image . get_height () / tile_height
for row in range ( rows ):
for col in range ( cols ):
var tile_image = Image . create ( tile_width , tile_height , false , image . get_format ())
tile_image . blit_rect ( image , Rect2i ( col * tile_width , row * tile_height , tile_width , tile_height ), Vector2i . ZERO )
var texture = ImageTexture . create_from_image ( tile_image )
atlas . add_frame ( "default" , texture )
return ResourceSaver . save ( atlas , save_path + ".res" )
CSV Data Importer
func _import ( source_file , save_path , options , platform_variants , gen_files ):
var file = FileAccess . open ( source_file , FileAccess . READ )
if file == null :
return FAILED
var data_resource = GameData . new ()
var delimiter = options . get ( "delimiter" , "," )
var skip_header = options . get ( "skip_first_row" , true )
if skip_header :
file . get_line () # Skip header
while not file . eof_reached ():
var line = file . get_line ()
if line . is_empty ():
continue
var fields = line . split ( delimiter )
data_resource . add_entry ( fields )
file . close ()
return ResourceSaver . save ( data_resource , save_path + "." + _get_save_extension ())
Best Practices
Always check file operations and return appropriate error codes. Provide helpful error messages.
Use appropriate import order
Set _get_import_order() correctly so resources import before scenes that use them.
Provide meaningful presets for common use cases (Low/Medium/High quality, etc.).
Make options discoverable
Use property hints to make options user-friendly with dropdowns, ranges, and clear labels.
Provide clear visible names and descriptions for your import options.
See Also
Editor Plugins Learn how to create and register editor plugins
Custom Nodes Create custom node types with tool scripts