custom_resource_format_loaders.rst 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. .. _doc_custom_resource_format_loaders:
  2. Custom resource format loaders
  3. ==============================
  4. Introduction
  5. ------------
  6. ResourceFormatLoader is a factory interface for loading file assets.
  7. Resources are primary containers. When load is called on the same file
  8. path again, the previous loaded Resource will be referenced. Naturally,
  9. loaded resources must be stateless.
  10. This guide assumes the reader knows how to create C++ modules and Godot
  11. data types. If not, refer to this guide :ref:`doc_custom_modules_in_c++`.
  12. References
  13. ~~~~~~~~~~
  14. - :ref:`ResourceLoader<class_resourceloader>`
  15. - `core/io/resource_loader.cpp <https://github.com/godotengine/godot/blob/master/core/io/resource_loader.cpp#L258>`__
  16. What for?
  17. ---------
  18. - Adding new support for many file formats
  19. - Audio formats
  20. - Video formats
  21. - Machine learning models
  22. What not?
  23. ---------
  24. - Raster images
  25. ImageFormatLoader should be used to load images.
  26. References
  27. ~~~~~~~~~~
  28. - `core/io/image_loader.h <https://github.com/godotengine/godot/blob/master/core/io/image_loader.h>`__
  29. Creating a ResourceFormatLoader
  30. -------------------------------
  31. Each file format consist of a data container and a ``ResourceFormatLoader``.
  32. ResourceFormatLoaders are usually simple classes which return all the
  33. necessary metadata for supporting new extensions in Godot. The
  34. class must the return the format name and the extension string.
  35. In addition, ResourceFormatLoaders must convert file paths into
  36. resources with the ``load`` function. To load a resource, ``load`` must
  37. read and handle data serialization.
  38. .. code-block:: cpp
  39. /* resource_loader_json.h */
  40. #ifndef RESOURCE_LOADER_JSON_H
  41. #define RESOURCE_LOADER_JSON_H
  42. #include "core/io/resource_loader.h"
  43. class ResourceFormatLoaderJson : public ResourceFormatLoader {
  44. GDCLASS(ResourceFormatLoaderJson, ResourceFormatLoader)
  45. public:
  46. virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL);
  47. virtual void get_recognized_extensions(List<String> *p_extensions) const;
  48. virtual bool handles_type(const String &p_type) const;
  49. virtual String get_resource_type(const String &p_path) const;
  50. };
  51. #endif // RESOURCE_LOADER_JSON_H
  52. .. code-block:: cpp
  53. /* resource_loader_json.cpp */
  54. #include "resource_loader_json.h"
  55. #include "resource_json.h"
  56. RES ResourceFormatLoaderJson::load(const String &p_path, const String &p_original_path, Error *r_error) {
  57. Ref<JsonResource> json = memnew(JsonResource);
  58. if (r_error)
  59. *r_error = OK;
  60. Error err = json->load_file(p_path);
  61. return json;
  62. }
  63. void ResourceFormatLoaderJson::get_recognized_extensions(List<String> *p_extensions) const {
  64. if (!p_extensions->find("json"))
  65. p_extensions->push_back("json");
  66. }
  67. String ResourceFormatLoaderJson::get_resource_type(const String &p_path) const {
  68. return "Resource";
  69. }
  70. bool ResourceFormatLoaderJson::handles_type(const String &p_type) const {
  71. return (ClassDB::is_parent_class(p_type, "Resource"));
  72. }
  73. Creating a ResourceFormatSaver
  74. ------------------------------
  75. If you'd like to be able to edit and save a resource, you can implement a ResourceFormatSaver:
  76. .. code:: cpp
  77. /* resource_saver_json.h */
  78. #ifndef RESOURCE_SAVER_JSON_H
  79. #define RESOURCE_SAVER_JSON_H
  80. #include "core/io/resource_saver.h"
  81. class ResourceFormatSaverJson : public ResourceFormatSaver {
  82. GDCLASS(ResourceFormatSaverJson, ResourceFormatSaver)
  83. public:
  84. virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
  85. virtual bool recognize(const RES &p_resource) const;
  86. virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
  87. };
  88. #endif // RESOURCE_SAVER_JSON_H
  89. .. code:: cpp
  90. /* resource_saver_json.cpp */
  91. #include "resource_saver_json.h"
  92. #include "resource_json.h"
  93. #include "scene\resources\resource_format_text.h"
  94. Error ResourceFormatSaverJson::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
  95. Ref<JsonResource> json = memnew(JsonResource);
  96. Error error = json->save_file(p_path, p_resource);
  97. return error;
  98. }
  99. bool ResourceFormatSaverJson::recognize(const RES &p_resource) const {
  100. return Object::cast_to<JsonResource>(*p_resource) != NULL;
  101. }
  102. void ResourceFormatSaverJson::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
  103. if (Object::cast_to<JsonResource>(*p_resource))
  104. p_extensions->push_back("json");
  105. }
  106. Creating custom data types
  107. --------------------------
  108. Godot may not have a proper substitute within its :ref:`doc_core_types`
  109. or managed resources. Godot needs a new registered data type to
  110. understand additional binary formats such as machine learning models.
  111. Here is an example of how to create a custom datatype
  112. .. code-block:: cpp
  113. /* resource_json.h */
  114. #ifndef RESOURCE_JSON_H
  115. #define RESOURCE_JSON_H
  116. #include "core/io/json.h"
  117. #include "core/variant_parser.h"
  118. class JsonResource : public Resource {
  119. GDCLASS(JsonResource, Resource);
  120. protected:
  121. static void _bind_methods() {
  122. ClassDB::bind_method(D_METHOD("set_dict", "dict"), &JsonResource::set_dict);
  123. ClassDB::bind_method(D_METHOD("get_dict"), &JsonResource::get_dict);
  124. ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "content", PROPERTY_HINT_NONE, ""), "set_dict", "get_dict");
  125. }
  126. private:
  127. Dictionary content;
  128. public:
  129. Error load_file(const String &p_path);
  130. Error save_file(const String &p_path, const RES &p_resource);
  131. void set_dict(const Dictionary &p_dict);
  132. Dictionary get_dict();
  133. };
  134. #endif // RESOURCE_JSON_H
  135. .. code:: cpp
  136. /* resource_json.cpp */
  137. #include "resource_json.h"
  138. Error JsonResource::load_file(const String &p_path) {
  139. Error error;
  140. FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error);
  141. if (!error == OK) {
  142. if (file)
  143. file->close();
  144. return error;
  145. }
  146. String json_string = String("");
  147. while (!file->eof_reached())
  148. json_string += file->get_line();
  149. file->close();
  150. String error_string;
  151. int error_line;
  152. JSON json;
  153. Variant result;
  154. error = json.parse(json_string, result, error_string, error_line);
  155. if (!error == OK) {
  156. file->close();
  157. return error;
  158. }
  159. content = Dictionary(result);
  160. return OK;
  161. }
  162. Error JsonResource::save_file(const String &p_path, const RES &p_resource) {
  163. Error error;
  164. FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &error);
  165. if (!error == OK) {
  166. if (file)
  167. file->close();
  168. return error;
  169. }
  170. Ref<JsonResource> json_ref = p_resource.get_ref_ptr();
  171. JSON json;
  172. file->store_string(json.print(json_ref->get_dict(), " "));
  173. file->close();
  174. return Error::OK;
  175. }
  176. void JsonResource::set_dict(const Dictionary &p_dict) {
  177. content = p_dict;
  178. }
  179. Dictionary JsonResource::get_dict() {
  180. return content;
  181. }
  182. Considerations
  183. ~~~~~~~~~~~~~~
  184. Some libraries may not define certain common routines such as IO handling.
  185. Therefore, Godot call translations are required.
  186. For example, here is the code for translating ``FileAccess``
  187. calls into ``std::istream``.
  188. .. code-block:: cpp
  189. #include <istream>
  190. #include <streambuf>
  191. class GodotFileInStreamBuf : public std::streambuf {
  192. public:
  193. GodotFileInStreamBuf(FileAccess *fa) {
  194. _file = fa;
  195. }
  196. int underflow() {
  197. if (_file->eof_reached()) {
  198. return EOF;
  199. } else {
  200. size_t pos = _file->get_position();
  201. uint8_t ret = _file->get_8();
  202. _file->seek(pos); // required since get_8() advances the read head
  203. return ret;
  204. }
  205. }
  206. int uflow() {
  207. return _file->eof_reached() ? EOF : _file->get_8();
  208. }
  209. private:
  210. FileAccess *_file;
  211. };
  212. References
  213. ~~~~~~~~~~
  214. - `istream <http://www.cplusplus.com/reference/istream/istream/>`__
  215. - `streambuf <http://www.cplusplus.com/reference/streambuf/streambuf/?kw=streambuf>`__
  216. - `core/io/fileaccess.h <https://github.com/godotengine/godot/blob/master/core/os/file_access.h>`__
  217. Registering the new file format
  218. -------------------------------
  219. Godot registers ``ResourcesFormatLoader`` with a ``ResourceLoader``
  220. handler. The handler selects the proper loader automatically
  221. when ``load`` is called.
  222. .. code-block:: cpp
  223. /* register_types.h */
  224. void register_json_types();
  225. void unregister_json_types();
  226. .. code:: cpp
  227. /* register_types.cpp */
  228. #include "register_types.h"
  229. #include "core/class_db.h"
  230. #include "resource_loader_json.h"
  231. #include "resource_saver_json.h"
  232. #include "resource_json.h"
  233. static Ref<ResourceFormatLoaderJson> json_loader;
  234. static Ref<ResourceFormatSaverJson> json_saver;
  235. void register_json_types() {
  236. ClassDB::register_class<JsonResource>();
  237. json_loader.instance();
  238. ResourceLoader::add_resource_format_loader(json_loader);
  239. json_saver.instance();
  240. ResourceSaver::add_resource_format_saver(json_saver);
  241. }
  242. void unregister_json_types() {
  243. ResourceLoader::remove_resource_format_loader(json_loader);
  244. json_loader.unref();
  245. ResourceSaver::remove_resource_format_saver(json_saver);
  246. json_saver.unref();
  247. }
  248. References
  249. ~~~~~~~~~~
  250. - `core/io/resource_loader.cpp <https://github.com/godotengine/godot/blob/master/core/io/resource_loader.cpp#L280>`__
  251. Loading it on GDScript
  252. ----------------------
  253. .. code-block:: json
  254. /* example .json file */
  255. {
  256. "savefilename" : "demo.json",
  257. "demo": [
  258. "welcome",
  259. "to",
  260. "godot",
  261. "resource",
  262. "loaders"
  263. ]
  264. }
  265. ::
  266. extends Node
  267. onready var json_resource = load("res://demo.json")
  268. func _ready():
  269. print(json_resource.get_dict())