Browse Source

Added custom format loader guide (#1193)

hungrymonkey 7 years ago
parent
commit
3c747d963b
2 changed files with 272 additions and 0 deletions
  1. 271 0
      development/cpp/custom_resource_format_loaders.rst
  2. 1 0
      development/cpp/index.rst

+ 271 - 0
development/cpp/custom_resource_format_loaders.rst

@@ -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())

+ 1 - 0
development/cpp/index.rst

@@ -12,5 +12,6 @@ Engine development
    object_class
    object_class
    inheritance_class_tree
    inheritance_class_tree
    custom_modules_in_cpp
    custom_modules_in_cpp
+   custom_resource_format_loaders
    custom_audiostreams
    custom_audiostreams
    creating_android_modules
    creating_android_modules