💿🐜 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.

443 lines
13 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 "render/material.hpp"
  22. #include "render/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_1d_property(resource_manager* resource_manager, render::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. render::material_property<const gl::texture_1d*>* property = material->add_property<const gl::texture_1d*>(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_1d* texture = resource_manager->load<gl::texture_1d>(filename);
  53. property->set_value(i++, texture);
  54. }
  55. }
  56. else
  57. {
  58. // Create property
  59. render::material_property<const gl::texture_1d*>* property = material->add_property<const gl::texture_1d*>(name);
  60. // Load texture
  61. std::string filename = json.get<std::string>();
  62. const gl::texture_1d* texture = resource_manager->load<gl::texture_1d>(filename);
  63. property->set_value(texture);
  64. }
  65. return true;
  66. }
  67. static bool load_texture_2d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json)
  68. {
  69. // If JSON element is an array
  70. if (json.is_array())
  71. {
  72. // Determine size of the array
  73. std::size_t array_size = json.size();
  74. // Create property
  75. render::material_property<const gl::texture_2d*>* property = material->add_property<const gl::texture_2d*>(name, array_size);
  76. // Load textures
  77. std::size_t i = 0;
  78. for (const auto& element: json)
  79. {
  80. std::string filename = element.get<std::string>();
  81. const gl::texture_2d* texture = resource_manager->load<gl::texture_2d>(filename);
  82. property->set_value(i++, texture);
  83. }
  84. }
  85. else
  86. {
  87. // Create property
  88. render::material_property<const gl::texture_2d*>* property = material->add_property<const gl::texture_2d*>(name);
  89. // Load texture
  90. std::string filename = json.get<std::string>();
  91. const gl::texture_2d* texture = resource_manager->load<gl::texture_2d>(filename);
  92. property->set_value(texture);
  93. }
  94. return true;
  95. }
  96. static bool load_texture_cube_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json)
  97. {
  98. return false;
  99. }
  100. template <typename T>
  101. static bool load_scalar_property(render::material* material, const std::string& name, const nlohmann::json& json)
  102. {
  103. // If JSON element is an array
  104. if (json.is_array())
  105. {
  106. // Determine size of the array
  107. std::size_t array_size = json.size();
  108. // Create property
  109. render::material_property<T>* property = material->add_property<T>(name, array_size);
  110. // Set property values
  111. std::size_t i = 0;
  112. for (const auto& element: json)
  113. property->set_value(i++, element.get<T>());
  114. }
  115. else
  116. {
  117. // Create property
  118. render::material_property<T>* property = material->add_property<T>(name);
  119. // Set property value
  120. property->set_value(json.get<T>());
  121. }
  122. return true;
  123. }
  124. template <typename T>
  125. static bool load_vector_property(render::material* material, const std::string& name, std::size_t vector_size, const nlohmann::json& json)
  126. {
  127. // If JSON element is an array of arrays
  128. if (json.is_array() && json.begin().value().is_array())
  129. {
  130. // Determine size of the array
  131. std::size_t array_size = json.size();
  132. // Create property
  133. render::material_property<T>* property = material->add_property<T>(name, array_size);
  134. // For each vector in the array
  135. std::size_t i = 0;
  136. for (const auto& vector_element: json)
  137. {
  138. // Read vector elements
  139. T value;
  140. std::size_t j = 0;
  141. for (const auto& value_element: vector_element)
  142. value[j++] = value_element.get<typename T::scalar_type>();
  143. // Set property values
  144. property->set_value(i++, value);
  145. }
  146. }
  147. else
  148. {
  149. // Create property
  150. render::material_property<T>* property = material->add_property<T>(name);
  151. // Read vector elements
  152. T value;
  153. std::size_t i = 0;
  154. for (const auto& value_element: json)
  155. value[i++] = value_element.get<typename T::scalar_type>();
  156. // Set property values
  157. property->set_value(value);
  158. }
  159. return true;
  160. }
  161. template <typename T>
  162. static bool load_matrix_property(render::material* material, const std::string& name, std::size_t column_count, std::size_t row_count, const nlohmann::json& json)
  163. {
  164. // If JSON element is an array of arrays of arrays
  165. if (json.is_array() && json.begin().value().is_array())
  166. {
  167. if (json.begin().value().begin().value().is_array())
  168. {
  169. // Determine size of the array
  170. std::size_t array_size = json.size();
  171. // Create property
  172. render::material_property<T>* property = material->add_property<T>(name, array_size);
  173. // For each matrix in the array
  174. std::size_t i = 0;
  175. for (const auto& matrix_element: json)
  176. {
  177. // Read vector elements
  178. T value;
  179. std::size_t j = 0;
  180. for (const auto& column_element: matrix_element)
  181. {
  182. std::size_t k = 0;
  183. for (const auto& row_element: column_element)
  184. {
  185. value[j][k] = row_element.get<typename T::element_type>();
  186. ++k;
  187. }
  188. ++j;
  189. }
  190. // Set property values
  191. property->set_value(i, value);
  192. ++i;
  193. }
  194. return true;
  195. }
  196. else
  197. {
  198. // Create property
  199. render::material_property<T>* property = material->add_property<T>(name);
  200. // Read matrix elements
  201. T value;
  202. std::size_t i = 0;
  203. for (const auto& column_element: json)
  204. {
  205. std::size_t j = 0;
  206. for (const auto& row_element: column_element)
  207. {
  208. value[i][j] = row_element.get<typename T::element_type>();
  209. ++j;
  210. }
  211. ++i;
  212. }
  213. // Set property values
  214. property->set_value(value);
  215. return true;
  216. }
  217. }
  218. return false;
  219. }
  220. template <>
  221. render::material* resource_loader<render::material>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
  222. {
  223. // Read file into buffer
  224. std::size_t size = static_cast<int>(PHYSFS_fileLength(file));
  225. std::string buffer;
  226. buffer.resize(size);
  227. PHYSFS_readBytes(file, &buffer[0], size);
  228. // Parse json from file buffer
  229. nlohmann::json json = nlohmann::json::parse(buffer, nullptr, true, true);
  230. // Allocate material
  231. render::material* material = new render::material();
  232. // Read shader filename
  233. std::string shader_filename;
  234. if (read_value(&shader_filename, json, "shader"))
  235. {
  236. // Load shader program
  237. gl::shader_program* program = resource_manager->load<gl::shader_program>(shader_filename);
  238. material->set_shader_program(program);
  239. }
  240. // Init material flags
  241. std::uint32_t flags = 0;
  242. // Read blend mode
  243. std::string blend_mode;
  244. read_value(&blend_mode, json, "blend_mode");
  245. if (blend_mode == "alpha_blend")
  246. flags |= MATERIAL_FLAG_TRANSLUCENT;
  247. else
  248. flags |= MATERIAL_FLAG_OPAQUE;
  249. // Read shadow mode
  250. std::string shadow_mode;
  251. read_value(&shadow_mode, json, "shadow_mode");
  252. if (shadow_mode == "none")
  253. flags |= MATERIAL_FLAG_NOT_SHADOW_CASTER;
  254. else
  255. flags |= MATERIAL_FLAG_SHADOW_CASTER;
  256. // Read cull mode
  257. std::string cull_mode;
  258. read_value(&cull_mode, json, "cull_mode");
  259. if (cull_mode == "none")
  260. flags |= MATERIAL_FLAG_FRONT_AND_BACK_FACES;
  261. else if (cull_mode == "front")
  262. flags |= MATERIAL_FLAG_BACK_FACES;
  263. else
  264. flags |= MATERIAL_FLAG_FRONT_FACES;
  265. // Read depth mode
  266. std::string depth_mode;
  267. read_value(&depth_mode, json, "depth_mode");
  268. if (depth_mode == "in_front")
  269. flags |= MATERIAL_FLAG_X_RAY;
  270. // Read decal mode
  271. std::string decal_mode;
  272. read_value(&decal_mode, json, "decal_mode");
  273. if (decal_mode == "decal")
  274. flags |= MATERIAL_FLAG_DECAL;
  275. else if (decal_mode == "surface")
  276. flags |= MATERIAL_FLAG_DECAL_SURFACE;
  277. // Set material flags
  278. material->set_flags(flags);
  279. // Read material properties
  280. if (auto properties_element = json.find("properties"); properties_element != json.end())
  281. {
  282. for (const auto& property_element: properties_element.value())
  283. {
  284. // Read property name
  285. std::string name;
  286. if (!read_value(&name, property_element, "name"))
  287. // Ignore nameless properties
  288. continue;
  289. // Read property type
  290. std::string type;
  291. if (!read_value(&type, property_element, "type"))
  292. // Ignore typeless properties
  293. continue;
  294. // Find value element
  295. auto value_element = property_element.find("value");
  296. if (value_element == property_element.end())
  297. // Ignore valueless properties
  298. continue;
  299. if (type == "texture_1d")
  300. {
  301. load_texture_1d_property(resource_manager, material, name, value_element.value());
  302. }
  303. else if (type == "texture_2d")
  304. {
  305. load_texture_2d_property(resource_manager, material, name, value_element.value());
  306. }
  307. else if (type == "texture_cube")
  308. {
  309. load_texture_cube_property(resource_manager, material, name, value_element.value());
  310. }
  311. // If property type is a matrix
  312. else if (type[type.size() - 2] == 'x' &&
  313. std::isdigit(type[type.size() - 3]) &&
  314. std::isdigit(type.back()))
  315. {
  316. std::size_t columns = std::stoul(type.substr(type.size() - 3, 1));
  317. std::size_t rows = std::stoul(type.substr(type.size() - 1, 1));
  318. if (type.find("float") != std::string::npos)
  319. {
  320. if (size == 2)
  321. load_matrix_property<float2x2>(material, name, columns, rows, value_element.value());
  322. else if (size == 3)
  323. load_matrix_property<float3x3>(material, name, columns, rows, value_element.value());
  324. else if (size == 4)
  325. load_matrix_property<float4x4>(material, name, columns, rows, value_element.value());
  326. }
  327. }
  328. // If property type is a vector
  329. else if (std::isdigit(type.back()))
  330. {
  331. std::size_t size = std::stoul(type.substr(type.size() - 1, 1));
  332. if (type.find("float") != std::string::npos)
  333. {
  334. if (size == 2)
  335. load_vector_property<float2>(material, name, size, value_element.value());
  336. else if (size == 3)
  337. load_vector_property<float3>(material, name, size, value_element.value());
  338. else if (size == 4)
  339. load_vector_property<float4>(material, name, size, value_element.value());
  340. }
  341. else if (type.find("uint") != std::string::npos)
  342. {
  343. if (size == 2)
  344. load_vector_property<uint2>(material, name, size, value_element.value());
  345. else if (size == 3)
  346. load_vector_property<uint3>(material, name, size, value_element.value());
  347. else if (size == 4)
  348. load_vector_property<uint4>(material, name, size, value_element.value());
  349. }
  350. else if (type.find("int") != std::string::npos)
  351. {
  352. if (size == 2)
  353. load_vector_property<int2>(material, name, size, value_element.value());
  354. else if (size == 3)
  355. load_vector_property<int3>(material, name, size, value_element.value());
  356. else if (size == 4)
  357. load_vector_property<int4>(material, name, size, value_element.value());
  358. }
  359. else if (type.find("bool") != std::string::npos)
  360. {
  361. if (size == 2)
  362. load_vector_property<bool2>(material, name, size, value_element.value());
  363. else if (size == 3)
  364. load_vector_property<bool3>(material, name, size, value_element.value());
  365. else if (size == 4)
  366. load_vector_property<bool4>(material, name, size, value_element.value());
  367. }
  368. }
  369. // If property type is a scalar
  370. else
  371. {
  372. if (type.find("float") != std::string::npos)
  373. load_scalar_property<float>(material, name, value_element.value());
  374. else if (type.find("uint") != std::string::npos)
  375. load_scalar_property<unsigned int>(material, name, value_element.value());
  376. else if (type.find("int") != std::string::npos)
  377. load_scalar_property<int>(material, name, value_element.value());
  378. else if (type.find("bool") != std::string::npos)
  379. load_scalar_property<bool>(material, name, value_element.value());
  380. }
  381. }
  382. }
  383. // Update material tweens
  384. material->update_tweens();
  385. return material;
  386. }