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

457 lines
13 KiB

  1. /*
  2. * Copyright (C) 2023 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::element_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::element_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 == "opaque")
  246. {
  247. material->set_blend_mode(render::blend_mode::opaque);
  248. }
  249. else if (blend_mode == "masked")
  250. {
  251. material->set_blend_mode(render::blend_mode::masked);
  252. }
  253. else if (blend_mode == "translucent")
  254. {
  255. material->set_blend_mode(render::blend_mode::translucent);
  256. }
  257. // Read opacity threshold
  258. float opacity_threshold = 0.5f;
  259. if (read_value(&opacity_threshold, json, "opacity_threshold"))
  260. {
  261. material->set_opacity_threshold(opacity_threshold);
  262. }
  263. // Read two sided
  264. bool two_sided = false;
  265. read_value(&two_sided, json, "two_sided");
  266. material->set_two_sided(two_sided);
  267. // Read shadow mode
  268. std::string shadow_mode;
  269. read_value(&shadow_mode, json, "shadow_mode");
  270. if (shadow_mode == "opaque")
  271. {
  272. material->set_shadow_mode(render::shadow_mode::opaque);
  273. }
  274. else if (shadow_mode == "none")
  275. {
  276. material->set_shadow_mode(render::shadow_mode::none);
  277. }
  278. // Read depth mode
  279. std::string depth_mode;
  280. read_value(&depth_mode, json, "depth_mode");
  281. if (depth_mode == "in_front")
  282. flags |= MATERIAL_FLAG_X_RAY;
  283. // Read decal mode
  284. std::string decal_mode;
  285. read_value(&decal_mode, json, "decal_mode");
  286. if (decal_mode == "decal")
  287. flags |= MATERIAL_FLAG_DECAL;
  288. else if (decal_mode == "surface")
  289. flags |= MATERIAL_FLAG_DECAL_SURFACE;
  290. // Set material flags
  291. material->set_flags(flags);
  292. // Read material properties
  293. if (auto properties_element = json.find("properties"); properties_element != json.end())
  294. {
  295. for (const auto& property_element: properties_element.value())
  296. {
  297. // Read property name
  298. std::string name;
  299. if (!read_value(&name, property_element, "name"))
  300. // Ignore nameless properties
  301. continue;
  302. // Read property type
  303. std::string type;
  304. if (!read_value(&type, property_element, "type"))
  305. // Ignore typeless properties
  306. continue;
  307. // Find value element
  308. auto value_element = property_element.find("value");
  309. if (value_element == property_element.end())
  310. // Ignore valueless properties
  311. continue;
  312. if (type == "texture_1d")
  313. {
  314. load_texture_1d_property(resource_manager, material, name, value_element.value());
  315. }
  316. else if (type == "texture_2d")
  317. {
  318. load_texture_2d_property(resource_manager, material, name, value_element.value());
  319. }
  320. else if (type == "texture_cube")
  321. {
  322. load_texture_cube_property(resource_manager, material, name, value_element.value());
  323. }
  324. // If property type is a matrix
  325. else if (type[type.size() - 2] == 'x' &&
  326. std::isdigit(type[type.size() - 3]) &&
  327. std::isdigit(type.back()))
  328. {
  329. std::size_t columns = std::stoul(type.substr(type.size() - 3, 1));
  330. std::size_t rows = std::stoul(type.substr(type.size() - 1, 1));
  331. if (type.find("float") != std::string::npos)
  332. {
  333. if (size == 2)
  334. load_matrix_property<float2x2>(material, name, columns, rows, value_element.value());
  335. else if (size == 3)
  336. load_matrix_property<float3x3>(material, name, columns, rows, value_element.value());
  337. else if (size == 4)
  338. load_matrix_property<float4x4>(material, name, columns, rows, value_element.value());
  339. }
  340. }
  341. // If property type is a vector
  342. else if (std::isdigit(type.back()))
  343. {
  344. std::size_t size = std::stoul(type.substr(type.size() - 1, 1));
  345. if (type.find("float") != std::string::npos)
  346. {
  347. if (size == 2)
  348. load_vector_property<float2>(material, name, size, value_element.value());
  349. else if (size == 3)
  350. load_vector_property<float3>(material, name, size, value_element.value());
  351. else if (size == 4)
  352. load_vector_property<float4>(material, name, size, value_element.value());
  353. }
  354. else if (type.find("uint") != std::string::npos)
  355. {
  356. if (size == 2)
  357. load_vector_property<uint2>(material, name, size, value_element.value());
  358. else if (size == 3)
  359. load_vector_property<uint3>(material, name, size, value_element.value());
  360. else if (size == 4)
  361. load_vector_property<uint4>(material, name, size, value_element.value());
  362. }
  363. else if (type.find("int") != std::string::npos)
  364. {
  365. if (size == 2)
  366. load_vector_property<int2>(material, name, size, value_element.value());
  367. else if (size == 3)
  368. load_vector_property<int3>(material, name, size, value_element.value());
  369. else if (size == 4)
  370. load_vector_property<int4>(material, name, size, value_element.value());
  371. }
  372. else if (type.find("bool") != std::string::npos)
  373. {
  374. if (size == 2)
  375. load_vector_property<bool2>(material, name, size, value_element.value());
  376. else if (size == 3)
  377. load_vector_property<bool3>(material, name, size, value_element.value());
  378. else if (size == 4)
  379. load_vector_property<bool4>(material, name, size, value_element.value());
  380. }
  381. }
  382. // If property type is a scalar
  383. else
  384. {
  385. if (type.find("float") != std::string::npos)
  386. load_scalar_property<float>(material, name, value_element.value());
  387. else if (type.find("uint") != std::string::npos)
  388. load_scalar_property<unsigned int>(material, name, value_element.value());
  389. else if (type.find("int") != std::string::npos)
  390. load_scalar_property<int>(material, name, value_element.value());
  391. else if (type.find("bool") != std::string::npos)
  392. load_scalar_property<bool>(material, name, value_element.value());
  393. }
  394. }
  395. }
  396. // Update material tweens
  397. material->update_tweens();
  398. return material;
  399. }