Scene Files vs Direct GLB

TODO: collapasable, copyable, download, etc

I'm learning Godot and a sample project, I see they import .glb files into models folder and then make scene files for each of those and then put instances of the scene files into the main scene. Why do they do that, instead of just putting the .glb files directly into the main scene?In Godot, the practice of importing .glb files into a models folder, creating scene files for each model, and then instancing these scene files in the main scene is considered good practice for several reasons:

  1. Separation of Concerns: By creating separate scene files for each model, you maintain a clean separation between the raw 3D model data and the game logic or scene composition. This makes it easier to manage and modify the game components individually.

  2. Reusability: Scene files allow for easy reuse of models across different parts of the game. You can instance the same scene multiple times without duplicating the .glb file, which saves memory and keeps the project organized.

  3. Modularity: Each scene file can encapsulate not just the 3D model but also its behaviors, animations, and any additional nodes or scripts that might be necessary. This modularity makes it easier to manage complex objects.

  4. Easier Updates: If you need to update a model, you can simply re-import the .glb file and the changes will automatically propagate to all instances of the scene file. This reduces the risk of inconsistencies and simplifies the update process.

  5. Performance: Using instances of scene files can improve performance. Godot's instancing system is optimized for handling multiple copies of the same object, which can reduce the overall computational load compared to having many unique objects.

  6. Hierarchy and Organization: Having a clear hierarchy and organization within the project files helps maintain a clean workflow, especially in larger projects. It makes it easier to find, debug, and manage assets and scenes.

  7. Scene-Specific Customizations: Scene files can have scene-specific customizations like scripts, animations, and additional nodes that are specific to that scene. This level of customization is more difficult to achieve when directly using .glb files in the main scene.

Overall, this approach aligns with the principles of good software engineering, promoting modularity, reusability, maintainability, and performance optimization. If I have thousands of .glb files, can I automatically create one scene per?Yes, you can automatically create a scene for each .glb file using a script in Godot. Here's a step-by-step guide on how to do it:

  1. Create a New Script: You can create a new script that will handle the creation of scene files from your .glb files.

  2. Script to Create Scenes: Below is an example script that automates the creation of scene files for each .glb file in a specified directory.

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    if not Directory.new().dir_exists(output_dir):
        Directory.new().make_dir(output_dir)
    
    var dir = Directory.new()
    if dir.open(glb_dir) == OK:
        dir.list_dir_begin()
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.extension() == "glb":
                create_scene_from_glb(glb_dir + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = File.new()
        if file.open(scene_path, File.WRITE) == OK:
            file.store_var(packed_scene)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)
  1. Run the Script: To execute the script, go to the Godot editor and open the Script Editor. Attach the script to an EditorScript node or simply run it as an EditorScript. Make sure to adjust the glb_dir and output_dir paths to match the directories in your project.

This script will iterate through all .glb files in the specified directory, create a scene for each file, and save it in the specified output directory. Each scene will be named after the corresponding .glb file.

Note: This script assumes the use of Godot 3.x. If you are using Godot 4.x, some adjustments might be necessary due to API changes.What are the most common folders to have in a Godot project. Please name and describe them in a listIn a Godot project, it is common to organize your assets and resources into specific folders to maintain a clean and manageable structure. Here are the most common folders and their descriptions:

  1. Scenes:

    • Description: Contains all the scene files (.tscn or .scn) of your project.
    • Example: res://scenes/
    • Usage: This is where you organize different scenes of your game, such as levels, menus, and UI elements.
  2. Scripts:

    • Description: Holds all the script files (.gd, .cs, etc.).
    • Example: res://scripts/
    • Usage: Contains the game logic and functionality scripts.
  3. Models:

    • Description: Stores 3D model files (.glb, .obj, .dae, etc.).
    • Example: res://models/
    • Usage: Keeps all 3D assets used in the project.
  4. Textures:

    • Description: Contains texture and image files (.png, .jpg, .tga, etc.).
    • Example: res://textures/
    • Usage: Stores images used for textures, sprites, and UI elements.
  5. Materials:

    • Description: Holds material files (.tres, .material).
    • Example: res://materials/
    • Usage: Contains materials applied to 3D models and other objects.
  6. Audio:

    • Description: Contains audio files (.wav, .mp3, .ogg).
    • Example: res://audio/
    • Usage: Stores sound effects, music tracks, and other audio assets.
  7. UI:

    • Description: Stores user interface elements and resources.
    • Example: res://ui/
    • Usage: Contains UI scenes, scripts, textures, and other related assets.
  8. Animations:

    • Description: Holds animation resources and files (.anim, .tres).
    • Example: res://animations/
    • Usage: Stores animations for characters, objects, and UI elements.
  9. Shaders:

    • Description: Contains shader files (.gdshader, .shader).
    • Example: res://shaders/
    • Usage: Stores custom shader code for various graphical effects.
  10. Fonts:

    • Description: Holds font files (.ttf, .fnt).
    • Example: res://fonts/
    • Usage: Contains fonts used in the UI and other text elements.
  11. Plugins:

    • Description: Stores custom plugins and addons.
    • Example: res://plugins/
    • Usage: Contains plugins that extend the functionality of the Godot editor or runtime.
  12. Assets:

    • Description: A general folder for miscellaneous assets not fitting other categories.
    • Example: res://assets/
    • Usage: Contains various assets like icons, particle systems, etc.
  13. Resources:

    • Description: Holds various resource files (.tres, .res).
    • Example: res://resources/
    • Usage: Stores reusable resources like configurations, constants, and other data.
  14. Libraries:

    • Description: Contains third-party libraries and modules.
    • Example: res://libraries/
    • Usage: Stores external code libraries that are used in the project.

By organizing your project with these folders, you can maintain a clear structure that makes it easier to manage assets, navigate the project, and collaborate with others.ine 8:Identifier "Directory" not declared in the current scope. Did you mean to use "DirAccess"? Line 9:Identifier "Directory" not declared in the current scope. Did you mean to use "DirAccess"? Line 11:Identifier "Directory" not declared in the current scope. Did you mean to use "DirAccess"? Line 38:Identifier "File" not declared in the current scope. Line 39:Identifier "File" not declared in the current scope.It looks like the script needs to be updated to use the correct Godot 4.x API. In Godot 4.x, the Directory class has been replaced by DirAccess, and the File class remains the same but with some slight differences in usage. Here is the corrected script for Godot 4.x:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    var dir = DirAccess.open(output_dir)
    if not dir:
        DirAccess.make_dir_recursive(output_dir)
    
    dir = DirAccess.open(glb_dir)
    if dir:
        dir.list_dir_begin()
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.extension() == "glb":
                create_scene_from_glb(glb_dir + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = FileAccess.open(scene_path, FileAccess.WRITE)
        if file:
            file.store_var(packed_scene)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

Key changes include:

  1. Replacing Directory with DirAccess.
  2. Using DirAccess.open instead of Directory.new().
  3. Checking if the directory exists with DirAccess.open.
  4. Using FileAccess instead of File.

This script should work in Godot 4.x, creating scene files from .glb files automatically. Make sure to adjust the paths as needed for your specific project.Line 10:Cannot call non-static function "make_dir_recursive()" on the class "DirAccess" directly. Make an instance instead.To address the error and correctly create directories in Godot 4.x, you need to use an instance of DirAccess. Here's the revised script:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    var dir = DirAccess.open(output_dir)
    if not dir:
        var output_dir_access = DirAccess.create_for_path(output_dir)
        output_dir_access.make_dir_recursive(output_dir)
    
    dir = DirAccess.open(glb_dir)
    if dir:
        dir.list_dir_begin()
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.extension() == "glb":
                create_scene_from_glb(glb_dir + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = FileAccess.open(scene_path, FileAccess.WRITE)
        if file:
            file.store_var(packed_scene)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

Key changes include:

  1. Using DirAccess.create_for_path to create an instance for directory creation.
  2. Calling make_dir_recursive on the output_dir_access instance.

This should resolve the issue and allow the script to correctly create the necessary directories and scene files.Line 10:Static function "create_for_path()" not found in base "GDScriptNativeClass".It looks like there's been a misunderstanding regarding the Godot 4.x API. Let's correct the approach to creating directories. Here's the updated script:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    var dir = DirAccess.open(output_dir)
    if not dir:
        dir = DirAccess.create(DirAccess.Access.RESOURCES)
        if not dir:
            print("Failed to create DirAccess instance.")
            return
        dir.make_dir_recursive(output_dir)
    
    dir = DirAccess.open(glb_dir)
    if dir:
        dir.list_dir_begin()
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.extension() == "glb":
                create_scene_from_glb(glb_dir + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = FileAccess.open(scene_path, FileAccess.WRITE)
        if file:
            file.store_var(packed_scene)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

Key changes include:

  1. Using DirAccess.create with the appropriate access mode (DirAccess.Access.RESOURCES).
  2. Ensuring DirAccess is correctly instantiated before calling make_dir_recursive.

This should correctly handle the creation of directories and files in Godot 4.x.Line 10:Cannot find member "Access" in base "DirAccess". Line 10:Static function "create()" not found in base "GDScriptNativeClass".It seems there were some misunderstandings regarding the Godot 4.x API changes. Let's simplify and correct the script for creating directories and handling files. Here’s the updated script:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    if not DirAccess.exists(output_dir):
        var dir = DirAccess.open("res://")
        if dir:
            dir.make_dir_recursive(output_dir)

    var dir = DirAccess.open(glb_dir)
    if dir:
        dir.list_dir_begin()
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.extension() == "glb":
                create_scene_from_glb(glb_dir + "/" + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + "/" + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = FileAccess.open(scene_path, FileAccess.WRITE)
        if file:
            file.store_var(packed_scene)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

Key changes:

  1. Using DirAccess.exists() to check if a directory exists.
  2. Using DirAccess.open("res://") to get the root directory access and create directories recursively.
  3. Corrected path concatenation by adding slashes appropriately.

This should now work correctly in Godot 4.x for creating scene files from .glb files and ensuring directories are created as needed.Line 8:Static function "exists()" not found in base "GDScriptNativeClass".It looks like I made a mistake regarding the DirAccess API. There isn't a static exists method in DirAccess. Instead, you can use the instance method file_exists to check for the existence of the directory by looking for a known file or by creating a helper function to check the directory. Here's the corrected script:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    if not directory_exists(output_dir):
        var dir = DirAccess.open("res://")
        if dir:
            dir.make_dir_recursive(output_dir)

    dir = DirAccess.open(glb_dir)
    if dir:
        dir.list_dir_begin()
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.extension() == "glb":
                create_scene_from_glb(glb_dir + "/" + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
    else:
        print("Failed to open directory: ", glb_dir)

func directory_exists(path: String) -> bool:
    var dir = DirAccess.open(path)
    if dir:
        var err = dir.list_dir_begin(true, true)  # Skip hidden files and special files
        if err == OK:
            dir.list_dir_end()
            return true
    return false

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + "/" + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = FileAccess.open(scene_path, FileAccess.WRITE)
        if file:
            file.store_var(packed_scene)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

This script includes a helper function directory_exists to check if a directory exists by attempting to open it and listing its contents. If the directory exists, it lists the contents and returns true; otherwise, it returns false. The main function then uses this helper function to check for the existence of the output_dir and creates it if necessary.Can you read all of the feedback and errors and output a working Godot 4.x script?Certainly! Let's consolidate all the feedback and errors to produce a fully working Godot 4.x script. The script will ensure the necessary directories exist, iterate through .glb files in a given directory, create a scene for each file, and save these scenes. Here’s the corrected script:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    if not DirAccess.open("res://").dir_exists(output_dir):
        var dir = DirAccess.open("res://")
        if dir:
            dir.make_dir_recursive(output_dir)
            dir.close()

    # Process each GLB file in the models directory
    var dir = DirAccess.open(glb_dir)
    if dir:
        dir.list_dir_begin(skip_navigational=false, skip_hidden=true)
        var file_name = dir.get_next()
        while file_name != "":
            if file_name.ends_with(".glb"):
                create_scene_from_glb(glb_dir + "/" + file_name, output_dir)
            file_name = dir.get_next()
        dir.list_dir_end()
        dir.close()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + "/" + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        var file = FileAccess.open(scene_path, FileAccess.WRITE)
        if file:
            packed_scene.save_to_path(scene_path)
            file.close()
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

Key Changes and Corrections:

  1. Directory Checking and Creation: Uses DirAccess.open() to check if directories exist and to create them if they do not.
  2. GLB File Processing: Correctly uses list_dir_begin() and checks files using ends_with(".glb") to ensure only GLB files are processed.
  3. Scene Saving: Uses PackedScene.save_to_path() for saving the scene, ensuring that the path handling and file saving are correctly handled.

This script should work as expected in Godot 4.x, handling the creation of scenes from GLB models efficiently. Make sure to test it in your Godot environment to verify it integrates well with your project setup.Line 17:Assignment is not allowed inside an expression. Line 17:Assignment is not allowed inside an expression.It seems there was a mistake in the condition for opening and creating directories. Let’s fix that and simplify the directory handling with correct usage patterns. Here’s a corrected and cleaner version of the script:

extends EditorScript

func _run():
    var glb_dir = "res://models/"  # Directory containing the .glb files
    var output_dir = "res://scenes/"  # Directory where the scenes will be saved

    # Ensure the output directory exists
    var output_dir_access = DirAccess.open("res://")
    if output_dir_access and not output_dir_access.dir_exists(output_dir):
        output_dir_access.make_dir_recursive(output_dir)
    output_dir_access.close()

    # Process each GLB file in the models directory
    var model_dir_access = DirAccess.open(glb_dir)
    if model_dir_access:
        model_dir_access.list_dir_begin(skip_navigational=false, skip_hidden=true)
        var file_name = model_dir_access.get_next()
        while file_name != "":
            if file_name.ends_with(".glb"):
                create_scene_from_glb(glb_dir + "/" + file_name, output_dir)
            file_name = model_dir_access.get_next()
        model_dir_access.list_dir_end()
        model_dir_access.close()
    else:
        print("Failed to open directory: ", glb_dir)

func create_scene_from_glb(glb_file_path, output_dir):
    var gltf = GLTFDocument.new()
    var err = gltf.open(glb_file_path)
    if err != OK:
        print("Failed to open .glb file: ", glb_file_path)
        return
    
    var imported_scene = gltf.import_scene()
    if imported_scene:
        var scene_name = glb_file_path.get_file().get_basename() + ".tscn"
        var scene_path = output_dir + "/" + scene_name
        
        var packed_scene = PackedScene.new()
        packed_scene.pack(imported_scene)
        
        err = ResourceSaver.save(scene_path, packed_scene)
        if err == OK:
            print("Created scene: ", scene_path)
        else:
            print("Failed to save scene: ", scene_path)
    else:
        print("Failed to import scene from .glb file: ", glb_file_path)

Key Adjustments:

  1. Directory Management: Utilizes DirAccess.open("res://") to obtain directory access for the resource root and checks if the output directory exists using dir_exists. It also correctly closes the DirAccess instance after use.
  2. File Handling in Directories: Processes files within the models directory by iterating through them only if the directory access is successful. It checks for files ending with ".glb" to process them accordingly.
  3. Scene Saving: Uses ResourceSaver.save() to save the packed scenes directly, which is the proper method to save scenes and resources in Godot.

This version should now be functional without syntactic errors and appropriately manage directory and file operations in Godot 4.x.

Scene Files vs Direct GLB created at Tue Jan 20 1970 21:58:44 GMT+0000 (Coordinated Universal Time)

Related Chats

MVVM: Prefix with `$` 0.371

Shared ViewModel Across Views 0.355

New chat 0.348

Create 3D Models Prog. Swiftly 0.338

Rust Project Repository Structure 0.335

Giraffe 3D Model Tutorial 0.332

Immersive View Phases 0.313

SwiftUI RealityKit Integration 0.306

Provider vs useAtom: when? 0.304