Custom resource format loaders

    This guide assumes the reader knows how to create C++ modules and Godot data types. If not, refer to this guide .

    What for?

    • Adding new support for many file formats
    • Audio formats
    • Video formats
    • Machine learning models
    • Raster images

    ImageFormatLoader should be used to load images.

    Creating a ResourceFormatLoader

    Each file format consist of a data container and a .

    In addition, ResourceFormatLoaders must convert file paths into resources with the load function. To load a resource, load must read and handle data serialization.

    1. /* resource_loader_json.cpp */
    2. #include "resource_loader_json.h"
    3. #include "resource_json.h"
    4. RES ResourceFormatLoaderJson::load(const String &p_path, const String &p_original_path, Error *r_error) {
    5. Ref<JsonResource> json = memnew(JsonResource);
    6. if (r_error) {
    7. *r_error = OK;
    8. }
    9. Error err = json->load_file(p_path);
    10. return json;
    11. }
    12. void ResourceFormatLoaderJson::get_recognized_extensions(List<String> *r_extensions) const {
    13. if (!r_extensions->find("json")) {
    14. r_extensions->push_back("json");
    15. }
    16. }
    17. String ResourceFormatLoaderJson::get_resource_type(const String &p_path) const {
    18. return "Resource";
    19. }
    20. bool ResourceFormatLoaderJson::handles_type(const String &p_type) const {
    21. return ClassDB::is_parent_class(p_type, "Resource");
    22. }

    If you’d like to be able to edit and save a resource, you can implement a ResourceFormatSaver:

    1. /* resource_saver_json.h */
    2. #ifndef RESOURCE_SAVER_JSON_H
    3. #define RESOURCE_SAVER_JSON_H
    4. #include "core/io/resource_saver.h"
    5. class ResourceFormatSaverJson : public ResourceFormatSaver {
    6. GDCLASS(ResourceFormatSaverJson, ResourceFormatSaver);
    7. public:
    8. virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
    9. virtual bool recognize(const RES &p_resource) const;
    10. virtual void get_recognized_extensions(const RES &p_resource, List<String> *r_extensions) const;
    11. };
    12. #endif // RESOURCE_SAVER_JSON_H

    Creating custom data types

    Godot may not have a proper substitute within its or managed resources. Godot needs a new registered data type to understand additional binary formats such as machine learning models.

    1. #ifndef RESOURCE_JSON_H
    2. #define RESOURCE_JSON_H
    3. #include "core/io/json.h"
    4. #include "core/variant_parser.h"
    5. class JsonResource : public Resource {
    6. GDCLASS(JsonResource, Resource);
    7. protected:
    8. static void _bind_methods() {
    9. ClassDB::bind_method(D_METHOD("get_dict"), &JsonResource::get_dict);
    10. ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "content"), "set_dict", "get_dict");
    11. }
    12. private:
    13. Dictionary content;
    14. public:
    15. Error load_file(const String &p_path);
    16. Error save_file(const String &p_path, const RES &p_resource);
    17. void set_dict(const Dictionary &p_dict);
    18. Dictionary get_dict();
    19. };
    20. #endif // RESOURCE_JSON_H
    1. /* resource_json.cpp */
    2. #include "resource_json.h"
    3. Error JsonResource::load_file(const String &p_path) {
    4. Error error;
    5. FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error);
    6. if (error != OK) {
    7. if (file) {
    8. file->close();
    9. }
    10. return error;
    11. }
    12. String json_string = String("");
    13. while (!file->eof_reached()) {
    14. json_string += file->get_line();
    15. }
    16. file->close();
    17. String error_string;
    18. int error_line;
    19. JSON json;
    20. Variant result;
    21. error = json.parse(json_string, result, error_string, error_line);
    22. if (error != OK) {
    23. file->close();
    24. return error;
    25. }
    26. content = Dictionary(result);
    27. return OK;
    28. }
    29. Error JsonResource::save_file(const String &p_path, const RES &p_resource) {
    30. FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &error);
    31. if (error != OK) {
    32. if (file) {
    33. file->close();
    34. }
    35. return error;
    36. }
    37. Ref<JsonResource> json_ref = p_resource.get_ref_ptr();
    38. JSON json;
    39. file->store_string(json.print(json_ref->get_dict(), " "));
    40. file->close();
    41. return OK;
    42. }
    43. void JsonResource::set_dict(const Dictionary &p_dict) {
    44. content = p_dict;
    45. }
    46. Dictionary JsonResource::get_dict() {
    47. return content;
    48. }

    Some libraries may not define certain common routines such as IO handling. Therefore, Godot call translations are required.

    For example, here is the code for translating FileAccess calls into std::istream.

    Godot registers ResourcesFormatLoader with a ResourceLoader handler. The handler selects the proper loader automatically when load is called.

    1. /* register_types.h */
    2. void register_json_types();
    3. void unregister_json_types();
    1. /* register_types.cpp */
    2. #include "register_types.h"
    3. #include "core/class_db.h"
    4. #include "resource_loader_json.h"
    5. #include "resource_saver_json.h"
    6. #include "resource_json.h"
    7. static Ref<ResourceFormatLoaderJson> json_loader;
    8. static Ref<ResourceFormatSaverJson> json_saver;
    9. void register_json_types() {
    10. ClassDB::register_class<JsonResource>();
    11. json_loader.instance();
    12. ResourceLoader::add_resource_format_loader(json_loader);
    13. json_saver.instance();
    14. ResourceSaver::add_resource_format_saver(json_saver);
    15. }
    16. void unregister_json_types() {
    17. ResourceLoader::remove_resource_format_loader(json_loader);
    18. json_loader.unref();
    19. ResourceSaver::remove_resource_format_saver(json_saver);
    20. json_saver.unref();
    21. }

    Loading it on GDScript

    Then attach the following script to any node:

    1. extends Node
    2. onready var json_resource = load("res://demo.json")