💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

310 lines
9.2 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "resource-loader.hpp"
  20. #include "resource-manager.hpp"
  21. #include "renderer/material.hpp"
  22. #include "renderer/material-flags.hpp"
  23. #include <nlohmann/json.hpp>
  24. #include <physfs.h>
  25. #include <utility>
  26. #include <type_traits>
  27. #include <string>
  28. template <typename T>
  29. static bool read_value(T* value, const nlohmann::json& json, const std::string& name)
  30. {
  31. if (auto element = json.find(name); element != json.end())
  32. {
  33. *value = element.value().get<T>();
  34. return true;
  35. }
  36. return false;
  37. }
  38. static bool load_texture_2d_property(resource_manager* resource_manager, material* material, const std::string& name, const nlohmann::json& json)
  39. {
  40. // If JSON element is an array
  41. if (json.is_array())
  42. {
  43. // Determine size of the array
  44. std::size_t array_size = json.size();
  45. // Create property
  46. material_property<const gl::texture_2d*>* property = material->add_property<const gl::texture_2d*>(name, array_size);
  47. // Load textures
  48. std::size_t i = 0;
  49. for (const auto& element: json)
  50. {
  51. std::string filename = element.get<std::string>();
  52. const gl::texture_2d* texture = resource_manager->load<gl::texture_2d>(filename);
  53. property->set_value(i++, texture);
  54. }
  55. }
  56. else
  57. {
  58. // Create property
  59. material_property<const gl::texture_2d*>* property = material->add_property<const gl::texture_2d*>(name);
  60. // Load texture
  61. std::string filename = json.get<std::string>();
  62. const gl::texture_2d* texture = resource_manager->load<gl::texture_2d>(filename);
  63. property->set_value(texture);
  64. }
  65. return true;
  66. }
  67. static bool load_texture_cube_property(resource_manager* resource_manager, material* material, const std::string& name, const nlohmann::json& json)
  68. {
  69. return false;
  70. }
  71. template <typename T>
  72. static bool load_scalar_property(material* material, const std::string& name, const nlohmann::json& json)
  73. {
  74. // If JSON element is an array
  75. if (json.is_array())
  76. {
  77. // Determine size of the array
  78. std::size_t array_size = json.size();
  79. // Create property
  80. material_property<T>* property = material->add_property<T>(name, array_size);
  81. // Set property values
  82. std::size_t i = 0;
  83. for (const auto& element: json)
  84. property->set_value(i++, element.get<T>());
  85. }
  86. else
  87. {
  88. // Create property
  89. material_property<T>* property = material->add_property<T>(name);
  90. // Set property value
  91. property->set_value(json.get<T>());
  92. }
  93. return true;
  94. }
  95. template <typename T>
  96. static bool load_vector_property(material* material, const std::string& name, std::size_t vector_size, const nlohmann::json& json)
  97. {
  98. // If JSON element is an array of arrays
  99. if (json.is_array() && json.begin().value().is_array())
  100. {
  101. // Determine size of the array
  102. std::size_t array_size = json.size();
  103. // Create property
  104. material_property<T>* property = material->add_property<T>(name, array_size);
  105. // For each vector in the array
  106. std::size_t i = 0;
  107. for (const auto& vector_element: json)
  108. {
  109. // Read vector elements
  110. T value;
  111. std::size_t j = 0;
  112. for (const auto& value_element: vector_element)
  113. value[j++] = value_element.get<typename T::scalar_type>();
  114. // Set property values
  115. property->set_value(i++, value);
  116. }
  117. }
  118. else
  119. {
  120. // Create property
  121. material_property<T>* property = material->add_property<T>(name);
  122. // Read vector elements
  123. T value;
  124. std::size_t i = 0;
  125. for (const auto& value_element: json)
  126. value[i++] = value_element.get<typename T::scalar_type>();
  127. // Set property values
  128. property->set_value(value);
  129. }
  130. return true;
  131. }
  132. template <>
  133. material* resource_loader<material>::load(resource_manager* resource_manager, PHYSFS_File* file)
  134. {
  135. // Read file into buffer
  136. std::size_t size = static_cast<int>(PHYSFS_fileLength(file));
  137. std::string buffer;
  138. buffer.resize(size);
  139. PHYSFS_readBytes(file, &buffer[0], size);
  140. // Parse json from file buffer
  141. nlohmann::json json = nlohmann::json::parse(buffer);
  142. // Allocate material
  143. material* material = new ::material();
  144. // Read shader filename
  145. std::string shader_filename;
  146. if (read_value(&shader_filename, json, "shader"))
  147. {
  148. // Load shader program
  149. gl::shader_program* program = resource_manager->load<gl::shader_program>(shader_filename);
  150. material->set_shader_program(program);
  151. }
  152. // Init material flags
  153. std::uint32_t flags = 0;
  154. // Read blend mode
  155. std::string blend_mode;
  156. read_value(&blend_mode, json, "blend_mode");
  157. if (blend_mode == "alpha_blend")
  158. flags |= MATERIAL_FLAG_TRANSLUCENT;
  159. else
  160. flags |= MATERIAL_FLAG_OPAQUE;
  161. // Read shadow mode
  162. std::string shadow_mode;
  163. read_value(&shadow_mode, json, "shadow_mode");
  164. if (shadow_mode == "none")
  165. flags |= MATERIAL_FLAG_NOT_SHADOW_CASTER;
  166. else
  167. flags |= MATERIAL_FLAG_SHADOW_CASTER;
  168. // Read cull mode
  169. std::string cull_mode;
  170. read_value(&cull_mode, json, "cull_mode");
  171. if (cull_mode == "none")
  172. flags |= MATERIAL_FLAG_FRONT_AND_BACK_FACES;
  173. else if (cull_mode == "front")
  174. flags |= MATERIAL_FLAG_BACK_FACES;
  175. else
  176. flags |= MATERIAL_FLAG_FRONT_FACES;
  177. // Set material flags
  178. material->set_flags(flags);
  179. // Read material properties
  180. if (auto properties_element = json.find("properties"); properties_element != json.end())
  181. {
  182. for (const auto& property_element: properties_element.value())
  183. {
  184. // Read property name
  185. std::string name;
  186. if (!read_value(&name, property_element, "name"))
  187. // Ignore nameless properties
  188. continue;
  189. // Read property type
  190. std::string type;
  191. if (!read_value(&type, property_element, "type"))
  192. // Ignore typeless properties
  193. continue;
  194. // Find value element
  195. auto value_element = property_element.find("value");
  196. if (value_element == property_element.end())
  197. // Ignore valueless properties
  198. continue;
  199. // If property type is a 2D texture
  200. if (type == "texture_2d")
  201. {
  202. load_texture_2d_property(resource_manager, material, name, value_element.value());
  203. }
  204. // If property type is a cubic texture
  205. else if (type == "texture_cube")
  206. {
  207. load_texture_cube_property(resource_manager, material, name, value_element.value());
  208. }
  209. // If property type is a matrix
  210. else if (type[type.size() - 2] == 'x' &&
  211. std::isdigit(type[type.size() - 3]) &&
  212. std::isdigit(type.back()))
  213. {
  214. std::size_t columns = std::stoul(type.substr(type.size() - 3, 1));
  215. std::size_t rows = std::stoul(type.substr(type.size() - 1, 1));
  216. //load_matrix_property<float>(material, name, columns, rows, value_element.value());
  217. }
  218. // If property type is a vector
  219. else if (std::isdigit(type.back()))
  220. {
  221. std::size_t size = std::stoul(type.substr(type.size() - 1, 1));
  222. if (type.find("float") != std::string::npos)
  223. {
  224. if (size == 2)
  225. load_vector_property<float2>(material, name, size, value_element.value());
  226. else if (size == 3)
  227. load_vector_property<float3>(material, name, size, value_element.value());
  228. else if (size == 4)
  229. load_vector_property<float4>(material, name, size, value_element.value());
  230. }
  231. else if (type.find("uint") != std::string::npos)
  232. {
  233. if (size == 2)
  234. load_vector_property<uint2>(material, name, size, value_element.value());
  235. else if (size == 3)
  236. load_vector_property<uint3>(material, name, size, value_element.value());
  237. else if (size == 4)
  238. load_vector_property<uint4>(material, name, size, value_element.value());
  239. }
  240. else if (type.find("int") != std::string::npos)
  241. {
  242. if (size == 2)
  243. load_vector_property<int2>(material, name, size, value_element.value());
  244. else if (size == 3)
  245. load_vector_property<int3>(material, name, size, value_element.value());
  246. else if (size == 4)
  247. load_vector_property<int4>(material, name, size, value_element.value());
  248. }
  249. else if (type.find("bool") != std::string::npos)
  250. {
  251. if (size == 2)
  252. load_vector_property<bool2>(material, name, size, value_element.value());
  253. else if (size == 3)
  254. load_vector_property<bool3>(material, name, size, value_element.value());
  255. else if (size == 4)
  256. load_vector_property<bool4>(material, name, size, value_element.value());
  257. }
  258. }
  259. // If property type is a scalar
  260. else
  261. {
  262. if (type.find("float") != std::string::npos)
  263. load_scalar_property<float>(material, name, value_element.value());
  264. else if (type.find("uint") != std::string::npos)
  265. load_scalar_property<unsigned int>(material, name, value_element.value());
  266. else if (type.find("int") != std::string::npos)
  267. load_scalar_property<int>(material, name, value_element.value());
  268. else if (type.find("bool") != std::string::npos)
  269. load_scalar_property<bool>(material, name, value_element.value());
  270. }
  271. }
  272. }
  273. return material;
  274. }