|
@@ -0,0 +1,271 @@
|
|
|
|
+.. _custom_resource_format_loaders:
|
|
|
|
+
|
|
|
|
+Custom Resource Format Loaders
|
|
|
|
+==============================
|
|
|
|
+
|
|
|
|
+Introduction
|
|
|
|
+------------
|
|
|
|
+
|
|
|
|
+ResourceFormatLoader is a factory interface for loading file assets.
|
|
|
|
+Resources are primary containers. When load is called on the same file
|
|
|
|
+path again, the previous loaded Resource will be referenced. Naturally,
|
|
|
|
+loaded resources must be stateless.
|
|
|
|
+
|
|
|
|
+This guide assumes the reader knows how to create C++ modules and godot
|
|
|
|
+data types. If not, refer to this guide :ref:`doc_custom_modules_in_c++`.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+References:
|
|
|
|
+~~~~~~~~~~~
|
|
|
|
+
|
|
|
|
+- :ref:`ResourceLoader<class_resourceloader>`
|
|
|
|
+- `core/io/resource_loader.cpp <https://github.com/godotengine/godot/blob/master/core/io/resource_loader.cpp#L258>`__
|
|
|
|
+
|
|
|
|
+What for?
|
|
|
|
+---------
|
|
|
|
+
|
|
|
|
+- Adding new support for many file formats
|
|
|
|
+- Audio formats
|
|
|
|
+- Video formats
|
|
|
|
+- Machine learning models
|
|
|
|
+
|
|
|
|
+What not?
|
|
|
|
+---------
|
|
|
|
+
|
|
|
|
+- Raster images
|
|
|
|
+
|
|
|
|
+ImageFormatLoader should be used to load images.
|
|
|
|
+
|
|
|
|
+References:
|
|
|
|
+~~~~~~~~~~~
|
|
|
|
+
|
|
|
|
+- `core/io/image_loader.h <https://github.com/godotengine/godot/blob/master/core/io/image_loader.h>`__
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Creating a ResourceFormatLoader
|
|
|
|
+-------------------------------
|
|
|
|
+
|
|
|
|
+Each file format consist of a data container and a ``ResourceFormatLoader``.
|
|
|
|
+
|
|
|
|
+ResourceFormatLoaders are usually simple classes which return all the
|
|
|
|
+necessary metadata for supporting new extentions in Godot. The
|
|
|
|
+class must the return the format name and the extension string.
|
|
|
|
+
|
|
|
|
+In addition, ResourceFormatLoaders must convert file paths into
|
|
|
|
+resources with the ``load`` function. To load a resource, ``load`` must
|
|
|
|
+read and handle data serialization.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+.. code:: cpp
|
|
|
|
+
|
|
|
|
+ #ifndef MY_JSON_LOADER_H
|
|
|
|
+ #define MY_JSON_LOADER_H
|
|
|
|
+
|
|
|
|
+ #include "io/resource_loader.h"
|
|
|
|
+
|
|
|
|
+ class ResourceFormatLoaderMyJson : public ResourceFormatLoader {
|
|
|
|
+ public:
|
|
|
|
+ virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL);
|
|
|
|
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
|
|
|
+ virtual bool handles_type(const String &p_type) const;
|
|
|
|
+ virtual String get_resource_type(const String &p_path) const;
|
|
|
|
+
|
|
|
|
+ ResourceFormatLoaderMyJson();
|
|
|
|
+ virtual ~ResourceFormatLoaderMyJson() {}
|
|
|
|
+ };
|
|
|
|
+ #endif // MY_JSON_LOADER_H
|
|
|
|
+
|
|
|
|
+.. code:: cpp
|
|
|
|
+
|
|
|
|
+ #include "my_json_loader.h"
|
|
|
|
+ #include "my_json.h"
|
|
|
|
+
|
|
|
|
+ ResourceFormatLoaderMyJson::ResourceFormatLoaderMyJson() {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ RES ResourceFormatLoaderMyJson::load(const String &p_path, const String &p_original_path, Error *r_error) {
|
|
|
|
+ MyJson *my = memnew(MyJson);
|
|
|
|
+ if (r_error)
|
|
|
|
+ *r_error = OK;
|
|
|
|
+ Error err = my->set_file(p_path);
|
|
|
|
+ return Ref<MyJson>(my);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void ResourceFormatLoaderMyJson::get_recognized_extensions(List<String> *p_extensions) const {
|
|
|
|
+ p_extensions->push_back("mjson");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String ResourceFormatLoaderMyJson::get_resource_type(const String &p_path) const {
|
|
|
|
+
|
|
|
|
+ if (p_path.get_extension().to_lower() == "mjson")
|
|
|
|
+ return "MyJson";
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool ResourceFormatLoaderMyJson::handles_type(const String &p_type) const {
|
|
|
|
+ return (p_type == "MyJson");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Creating Custom Data Types
|
|
|
|
+--------------------------
|
|
|
|
+
|
|
|
|
+Godot may not have a proper substitute within its :ref:`doc_core_types`
|
|
|
|
+or managed resources. Godot needs a new registered data type to
|
|
|
|
+understand additional binary formats such as machine learning models.
|
|
|
|
+
|
|
|
|
+Here is an example of how to create a custom datatype
|
|
|
|
+
|
|
|
|
+.. code:: cpp
|
|
|
|
+
|
|
|
|
+ #ifndef MY_JSON_H
|
|
|
|
+ #define MY_JSON_H
|
|
|
|
+
|
|
|
|
+ #include "core/variant.h"
|
|
|
|
+ #include "reference.h"
|
|
|
|
+ #include "variant_parser.h"
|
|
|
|
+ #include "io/json.h"
|
|
|
|
+ #include "dictionary.h"
|
|
|
|
+
|
|
|
|
+ class MyJson : public Resource{
|
|
|
|
+ GDCLASS(MyJson, Resource);
|
|
|
|
+
|
|
|
|
+ protected:
|
|
|
|
+ static void _bind_methods() {
|
|
|
|
+ ClassDB::bind_method(D_METHOD("toString"), &MyJson::toString);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private:
|
|
|
|
+ Dictionary dict;
|
|
|
|
+ public:
|
|
|
|
+ Error set_file(const String &p_path){
|
|
|
|
+ Error error_file;
|
|
|
|
+ FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error_file);
|
|
|
|
+
|
|
|
|
+ String buf = String("");
|
|
|
|
+ while(!file->eof_reached()){
|
|
|
|
+ buf += file->get_line();
|
|
|
|
+ }
|
|
|
|
+ String err_string;
|
|
|
|
+ int err_line;
|
|
|
|
+ JSON cmd;
|
|
|
|
+ Variant ret;
|
|
|
|
+ Error err = cmd.parse( buf, ret, err_string, err_line);
|
|
|
|
+ dict = Dictionary(ret);
|
|
|
|
+ file -> close();
|
|
|
|
+ return OK;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String toString() const {
|
|
|
|
+ return String(*this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ operator String() const {
|
|
|
|
+ JSON a;
|
|
|
|
+ return a.print(dict);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ MyJson() {};
|
|
|
|
+ ~MyJson() {};
|
|
|
|
+ };
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
+Considerations
|
|
|
|
+~~~~~~~~~~~~~~
|
|
|
|
+
|
|
|
|
+Some libraries may not define certain common routines such as i/o handling.
|
|
|
|
+Therefore, Godot call translations are required.
|
|
|
|
+
|
|
|
|
+For example, here is the code for translating ``FileAccess``
|
|
|
|
+calls into ``std::istream``.
|
|
|
|
+
|
|
|
|
+.. code:: cpp
|
|
|
|
+
|
|
|
|
+ #include <istream>
|
|
|
|
+ #include <streambuf>
|
|
|
|
+
|
|
|
|
+ class GodotFileInStreamBuf : public std::streambuf{
|
|
|
|
+ public:
|
|
|
|
+ GodotFileInStreamBuf(FileAccess * fa) {
|
|
|
|
+ _file = fa;
|
|
|
|
+ }
|
|
|
|
+ int underflow() {
|
|
|
|
+ if (_file->eof_reached()) {
|
|
|
|
+ return EOF;
|
|
|
|
+ } else {
|
|
|
|
+ size_t pos = _file->get_position();
|
|
|
|
+ uint8_t ret = _file->get_8();
|
|
|
|
+ _file->seek(pos); //required since get_8() advances the read head
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ int uflow() {
|
|
|
|
+ return _file->eof_reached() ? EOF : _file -> get_8();
|
|
|
|
+ }
|
|
|
|
+ private:
|
|
|
|
+ FileAccess * _file;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+References:
|
|
|
|
+~~~~~~~~~~~
|
|
|
|
+- `istream <http://www.cplusplus.com/reference/istream/istream/>`__
|
|
|
|
+- `streambuf <http://www.cplusplus.com/reference/streambuf/streambuf/?kw=streambuf>`__
|
|
|
|
+- `core/io/fileaccess.h <https://github.com/godotengine/godot/blob/master/core/os/file_access.h>`__
|
|
|
|
+
|
|
|
|
+Registering the New File Format
|
|
|
|
+-------------------------------
|
|
|
|
+
|
|
|
|
+Godot registers ``ResourcesFormatLoader`` with a ``ResourceLoader``
|
|
|
|
+handler. The handler selects the proper loader automatically
|
|
|
|
+when ``load`` is called.
|
|
|
|
+
|
|
|
|
+.. code:: cpp
|
|
|
|
+
|
|
|
|
+ /* register_types.cpp */
|
|
|
|
+ #include "register_types.h"
|
|
|
|
+ #include "class_db.h"
|
|
|
|
+
|
|
|
|
+ #include "my_json_loader.h"
|
|
|
|
+ #include "my_json.h"
|
|
|
|
+
|
|
|
|
+ static ResourceFormatLoaderMyJson *my_json_loader = NULL;
|
|
|
|
+ void register_my_json_types() {
|
|
|
|
+ my_json_loader = memnew(ResourceFormatLoaderMyJson);
|
|
|
|
+ ResourceLoader::add_resource_format_loader(my_json_loader);
|
|
|
|
+ ClassDB::register_class<MyJson>();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void unregister_my_json_types() {
|
|
|
|
+ memdelete(my_json_loader);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+References:
|
|
|
|
+~~~~~~~~~~~
|
|
|
|
+- `core/io/resource_loader.cpp <https://github.com/godotengine/godot/blob/master/core/io/resource_loader.cpp#L280>`__
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Loading it on GDScript
|
|
|
|
+----------------------
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+.. code::
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ "savefilename" : "demo.mjson",
|
|
|
|
+ "demo": [
|
|
|
|
+ "welcome",
|
|
|
|
+ "to",
|
|
|
|
+ "godot",
|
|
|
|
+ "resource",
|
|
|
|
+ "loaders"
|
|
|
|
+ ]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+.. code::
|
|
|
|
+
|
|
|
|
+ extends Node
|
|
|
|
+
|
|
|
|
+ func _ready():
|
|
|
|
+ var myjson = load("res://demo.mjson")
|
|
|
|
+ print( myjson.toString())
|