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

407 lines
12 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 <typename T>
  133. static bool load_matrix_property(material* material, const std::string& name, std::size_t column_count, std::size_t row_count, const nlohmann::json& json)
  134. {
  135. // If JSON element is an array of arrays of arrays
  136. if (json.is_array() && json.begin().value().is_array())
  137. {
  138. if (json.begin().value().begin().value().is_array())
  139. {
  140. // Determine size of the array
  141. std::size_t array_size = json.size();
  142. // Create property
  143. material_property<T>* property = material->add_property<T>(name, array_size);
  144. // For each matrix in the array
  145. std::size_t i = 0;
  146. for (const auto& matrix_element: json)
  147. {
  148. // Read vector elements
  149. T value;
  150. std::size_t j = 0;
  151. for (const auto& column_element: matrix_element)
  152. {
  153. std::size_t k = 0;
  154. for (const auto& row_element: column_element)
  155. {
  156. value[j][k] = row_element.get<typename T::element_type>();
  157. ++k;
  158. }
  159. ++j;
  160. }
  161. // Set property values
  162. property->set_value(i, value);
  163. ++i;
  164. }
  165. return true;
  166. }
  167. else
  168. {
  169. // Create property
  170. material_property<T>* property = material->add_property<T>(name);
  171. // Read matrix elements
  172. T value;
  173. std::size_t i = 0;
  174. for (const auto& column_element: json)
  175. {
  176. std::size_t j = 0;
  177. for (const auto& row_element: column_element)
  178. {
  179. value[i][j] = row_element.get<typename T::element_type>();
  180. ++j;
  181. }
  182. ++i;
  183. }
  184. // Set property values
  185. property->set_value(value);
  186. return true;
  187. }
  188. }
  189. return false;
  190. }
  191. template <>
  192. material* resource_loader<material>::load(resource_manager* resource_manager, PHYSFS_File* file)
  193. {
  194. // Read file into buffer
  195. std::size_t size = static_cast<int>(PHYSFS_fileLength(file));
  196. std::string buffer;
  197. buffer.resize(size);
  198. PHYSFS_readBytes(file, &buffer[0], size);
  199. // Parse json from file buffer
  200. nlohmann::json json = nlohmann::json::parse(buffer);
  201. // Allocate material
  202. material* material = new ::material();
  203. // Read shader filename
  204. std::string shader_filename;
  205. if (read_value(&shader_filename, json, "shader"))
  206. {
  207. // Load shader program
  208. gl::shader_program* program = resource_manager->load<gl::shader_program>(shader_filename);
  209. material->set_shader_program(program);
  210. }
  211. // Init material flags
  212. std::uint32_t flags = 0;
  213. // Read blend mode
  214. std::string blend_mode;
  215. read_value(&blend_mode, json, "blend_mode");
  216. if (blend_mode == "alpha_blend")
  217. flags |= MATERIAL_FLAG_TRANSLUCENT;
  218. else
  219. flags |= MATERIAL_FLAG_OPAQUE;
  220. // Read shadow mode
  221. std::string shadow_mode;
  222. read_value(&shadow_mode, json, "shadow_mode");
  223. if (shadow_mode == "none")
  224. flags |= MATERIAL_FLAG_NOT_SHADOW_CASTER;
  225. else
  226. flags |= MATERIAL_FLAG_SHADOW_CASTER;
  227. // Read cull mode
  228. std::string cull_mode;
  229. read_value(&cull_mode, json, "cull_mode");
  230. if (cull_mode == "none")
  231. flags |= MATERIAL_FLAG_FRONT_AND_BACK_FACES;
  232. else if (cull_mode == "front")
  233. flags |= MATERIAL_FLAG_BACK_FACES;
  234. else
  235. flags |= MATERIAL_FLAG_FRONT_FACES;
  236. // Read depth mode
  237. std::string depth_mode;
  238. read_value(&depth_mode, json, "depth_mode");
  239. if (depth_mode == "in_front")
  240. flags |= MATERIAL_FLAG_X_RAY;
  241. // Read decal mode
  242. std::string decal_mode;
  243. read_value(&decal_mode, json, "decal_mode");
  244. if (decal_mode == "decal")
  245. flags |= MATERIAL_FLAG_DECAL;
  246. else if (decal_mode == "surface")
  247. flags |= MATERIAL_FLAG_DECAL_SURFACE;
  248. // Set material flags
  249. material->set_flags(flags);
  250. // Read material properties
  251. if (auto properties_element = json.find("properties"); properties_element != json.end())
  252. {
  253. for (const auto& property_element: properties_element.value())
  254. {
  255. // Read property name
  256. std::string name;
  257. if (!read_value(&name, property_element, "name"))
  258. // Ignore nameless properties
  259. continue;
  260. // Read property type
  261. std::string type;
  262. if (!read_value(&type, property_element, "type"))
  263. // Ignore typeless properties
  264. continue;
  265. // Find value element
  266. auto value_element = property_element.find("value");
  267. if (value_element == property_element.end())
  268. // Ignore valueless properties
  269. continue;
  270. // If property type is a 2D texture
  271. if (type == "texture_2d")
  272. {
  273. load_texture_2d_property(resource_manager, material, name, value_element.value());
  274. }
  275. // If property type is a cubic texture
  276. else if (type == "texture_cube")
  277. {
  278. load_texture_cube_property(resource_manager, material, name, value_element.value());
  279. }
  280. // If property type is a matrix
  281. else if (type[type.size() - 2] == 'x' &&
  282. std::isdigit(type[type.size() - 3]) &&
  283. std::isdigit(type.back()))
  284. {
  285. std::size_t columns = std::stoul(type.substr(type.size() - 3, 1));
  286. std::size_t rows = std::stoul(type.substr(type.size() - 1, 1));
  287. if (type.find("float") != std::string::npos)
  288. {
  289. if (size == 2)
  290. load_matrix_property<float2x2>(material, name, columns, rows, value_element.value());
  291. else if (size == 3)
  292. load_matrix_property<float3x3>(material, name, columns, rows, value_element.value());
  293. else if (size == 4)
  294. load_matrix_property<float4x4>(material, name, columns, rows, value_element.value());
  295. }
  296. }
  297. // If property type is a vector
  298. else if (std::isdigit(type.back()))
  299. {
  300. std::size_t size = std::stoul(type.substr(type.size() - 1, 1));
  301. if (type.find("float") != std::string::npos)
  302. {
  303. if (size == 2)
  304. load_vector_property<float2>(material, name, size, value_element.value());
  305. else if (size == 3)
  306. load_vector_property<float3>(material, name, size, value_element.value());
  307. else if (size == 4)
  308. load_vector_property<float4>(material, name, size, value_element.value());
  309. }
  310. else if (type.find("uint") != std::string::npos)
  311. {
  312. if (size == 2)
  313. load_vector_property<uint2>(material, name, size, value_element.value());
  314. else if (size == 3)
  315. load_vector_property<uint3>(material, name, size, value_element.value());
  316. else if (size == 4)
  317. load_vector_property<uint4>(material, name, size, value_element.value());
  318. }
  319. else if (type.find("int") != std::string::npos)
  320. {
  321. if (size == 2)
  322. load_vector_property<int2>(material, name, size, value_element.value());
  323. else if (size == 3)
  324. load_vector_property<int3>(material, name, size, value_element.value());
  325. else if (size == 4)
  326. load_vector_property<int4>(material, name, size, value_element.value());
  327. }
  328. else if (type.find("bool") != std::string::npos)
  329. {
  330. if (size == 2)
  331. load_vector_property<bool2>(material, name, size, value_element.value());
  332. else if (size == 3)
  333. load_vector_property<bool3>(material, name, size, value_element.value());
  334. else if (size == 4)
  335. load_vector_property<bool4>(material, name, size, value_element.value());
  336. }
  337. }
  338. // If property type is a scalar
  339. else
  340. {
  341. if (type.find("float") != std::string::npos)
  342. load_scalar_property<float>(material, name, value_element.value());
  343. else if (type.find("uint") != std::string::npos)
  344. load_scalar_property<unsigned int>(material, name, value_element.value());
  345. else if (type.find("int") != std::string::npos)
  346. load_scalar_property<int>(material, name, value_element.value());
  347. else if (type.find("bool") != std::string::npos)
  348. load_scalar_property<bool>(material, name, value_element.value());
  349. }
  350. }
  351. }
  352. // Update material tweens
  353. material->update_tweens();
  354. return material;
  355. }