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

367 lines
9.9 KiB

  1. /*
  2. * Copyright (C) 2020 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 "resources/resource-loader.hpp"
  20. #include "resources/resource-manager.hpp"
  21. #include "renderer/model.hpp"
  22. #include "renderer/vertex-attributes.hpp"
  23. #include "rasterizer/vertex-attribute-type.hpp"
  24. #include "rasterizer/drawing-mode.hpp"
  25. #include "utility/fundamental-types.hpp"
  26. #include <sstream>
  27. #include <stdexcept>
  28. #include <limits>
  29. #include <physfs.h>
  30. struct material_group
  31. {
  32. std::string name;
  33. material* material;
  34. std::size_t start_index;
  35. std::size_t index_count;
  36. };
  37. static const float3 barycentric_coords[3] =
  38. {
  39. float3{1, 0, 0},
  40. float3{0, 1, 0},
  41. float3{0, 0, 1}
  42. };
  43. template <>
  44. model* resource_loader<model>::load(resource_manager* resource_manager, PHYSFS_File* file)
  45. {
  46. std::string line;
  47. std::vector<float3> positions;
  48. std::vector<float2> uvs;
  49. std::vector<float3> normals;
  50. std::vector<float3> tangents;
  51. std::vector<float3> bitangents;
  52. std::vector<std::vector<std::size_t>> faces;
  53. std::vector<material_group> material_groups;
  54. material_group* current_material_group = nullptr;
  55. aabb<float> bounds =
  56. {
  57. {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()},
  58. {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()}
  59. };
  60. while (!PHYSFS_eof(file))
  61. {
  62. // Read line
  63. physfs_getline(file, line);
  64. // Tokenize line
  65. std::vector<std::string> tokens;
  66. std::string token;
  67. std::istringstream linestream(line);
  68. while (linestream >> token)
  69. tokens.push_back(token);
  70. // Skip empty lines and comments
  71. if (tokens.empty() || tokens[0][0] == '#')
  72. continue;
  73. if (tokens[0] == "v")
  74. {
  75. if (tokens.size() != 4)
  76. {
  77. std::stringstream stream;
  78. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  79. throw std::runtime_error(stream.str());
  80. }
  81. float3 position;
  82. std::stringstream(tokens[1]) >> position[0];
  83. std::stringstream(tokens[2]) >> position[1];
  84. std::stringstream(tokens[3]) >> position[2];
  85. positions.push_back(position);
  86. // Add position to bounds
  87. for (int i = 0; i < 3; ++i)
  88. {
  89. bounds.min_point[i] = std::min<float>(bounds.min_point[i], position[i]);
  90. bounds.max_point[i] = std::max<float>(bounds.max_point[i], position[i]);
  91. }
  92. }
  93. else if (tokens[0] == "vt")
  94. {
  95. if (tokens.size() != 3)
  96. {
  97. std::stringstream stream;
  98. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  99. throw std::runtime_error(stream.str());
  100. }
  101. float2 uv;
  102. std::stringstream(tokens[1]) >> uv[0];
  103. std::stringstream(tokens[2]) >> uv[1];
  104. uvs.push_back(uv);
  105. }
  106. else if (tokens[0] == "vn")
  107. {
  108. if (tokens.size() != 4)
  109. {
  110. std::stringstream stream;
  111. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  112. throw std::runtime_error(stream.str());
  113. }
  114. float3 normal;
  115. std::stringstream(tokens[1]) >> normal[0];
  116. std::stringstream(tokens[2]) >> normal[1];
  117. std::stringstream(tokens[3]) >> normal[2];
  118. normals.push_back(normal);
  119. }
  120. else if (tokens[0] == "f")
  121. {
  122. if (tokens.size() != 4)
  123. {
  124. std::stringstream stream;
  125. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  126. throw std::runtime_error(stream.str());
  127. }
  128. std::vector<std::size_t> face;
  129. for (std::size_t i = 0; i < 3; ++i)
  130. {
  131. std::stringstream ss(tokens[i + 1]);
  132. while (ss.good())
  133. {
  134. std::string substring;
  135. std::getline(ss, substring, '/');
  136. if (!substring.empty())
  137. {
  138. std::size_t index = std::stoul(substring) - 1;
  139. face.push_back(index);
  140. }
  141. }
  142. }
  143. faces.push_back(face);
  144. }
  145. else if (tokens[0] == "usemtl")
  146. {
  147. if (tokens.size() != 2)
  148. {
  149. std::stringstream stream;
  150. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  151. throw std::runtime_error(stream.str());
  152. }
  153. if (current_material_group)
  154. {
  155. current_material_group->index_count = faces.size() * 3 - current_material_group->start_index;
  156. }
  157. material_groups.push_back(material_group());
  158. current_material_group = &material_groups.back();
  159. current_material_group->name = tokens[1];
  160. current_material_group->start_index = faces.size() * 3;
  161. current_material_group->index_count = 0;
  162. }
  163. }
  164. if (current_material_group)
  165. {
  166. current_material_group->index_count = faces.size() * 3 - current_material_group->start_index;
  167. }
  168. // Load material group materials
  169. for (material_group& material_group: material_groups)
  170. {
  171. material_group.material = resource_manager->load<material>(material_group.name + ".mtl");
  172. }
  173. bool has_uvs = (!uvs.empty());
  174. bool has_normals = (!normals.empty());
  175. bool has_tangents = (has_uvs && has_normals);
  176. bool has_barycentric = false;
  177. has_barycentric = true;
  178. // Calculate faceted tangents and bitangents
  179. if (has_tangents)
  180. {
  181. tangents.resize(positions.size());
  182. bitangents.resize(positions.size());
  183. for (std::size_t i = 0; i < positions.size(); ++i)
  184. {
  185. tangents[i] = {0.0f, 0.0f, 0.0f};
  186. bitangents[i] = {0.0f, 0.0f, 0.0f};
  187. }
  188. for (std::size_t i = 0; i < faces.size(); ++i)
  189. {
  190. const std::vector<std::size_t>& face = faces[i];
  191. std::size_t ia = face[0];
  192. std::size_t ib = face[3];
  193. std::size_t ic = face[6];
  194. const float3& a = positions[ia];
  195. const float3& b = positions[ib];
  196. const float3& c = positions[ic];
  197. const float2& uva = uvs[face[1]];
  198. const float2& uvb = uvs[face[4]];
  199. const float2& uvc = uvs[face[7]];
  200. float3 ba = b - a;
  201. float3 ca = c - a;
  202. float2 uvba = uvb - uva;
  203. float2 uvca = uvc - uva;
  204. float f = 1.0f / (uvba.x * uvca.y - uvca.x * uvba.y);
  205. float3 tangent = (ba * uvca.y - ca * uvba.y) * f;
  206. float3 bitangent = (ba * -uvca.x + ca * uvba.x) * f;
  207. tangents[ia] += tangent;
  208. tangents[ib] += tangent;
  209. tangents[ic] += tangent;
  210. bitangents[ia] += bitangent;
  211. bitangents[ib] += bitangent;
  212. bitangents[ic] += bitangent;
  213. }
  214. }
  215. std::size_t vertex_size = 3;
  216. if (has_uvs)
  217. vertex_size += 2;
  218. if (has_normals)
  219. vertex_size += 3;
  220. if (has_tangents)
  221. vertex_size += 6;
  222. if (has_barycentric)
  223. vertex_size += 3;
  224. std::size_t vertex_stride = sizeof(float) * vertex_size;
  225. // Generate vertex buffer
  226. float* vertex_data = new float[vertex_size * faces.size() * 3];
  227. float* v = &vertex_data[0];
  228. for (std::size_t i = 0; i < faces.size(); ++i)
  229. {
  230. const std::vector<std::size_t>& face = faces[i];
  231. std::size_t k = 0;
  232. for (std::size_t j = 0; j < 3; ++j)
  233. {
  234. const float3& position = positions[face[k++]];
  235. *(v++) = position.x;
  236. *(v++) = position.y;
  237. *(v++) = position.z;
  238. if (has_uvs)
  239. {
  240. const float2& uv = uvs[face[k++]];
  241. *(v++) = uv.x;
  242. *(v++) = uv.y;
  243. }
  244. if (has_normals)
  245. {
  246. const float3& normal = normals[face[k++]];
  247. *(v++) = normal.x;
  248. *(v++) = normal.y;
  249. *(v++) = normal.z;
  250. }
  251. if (has_tangents)
  252. {
  253. const float3& n = normals[face[k - 1]];
  254. const float3& t = tangents[face[k - 3]];
  255. const float3& b = bitangents[face[k - 3]];
  256. // Gram-Schmidt orthogonalize tangent and bitangent
  257. float3 tangent = math::normalize(t - n * dot(n, t));
  258. tangent = (math::dot(math::cross(n, t), b) < 0.0f) ? -tangent : tangent;
  259. float3 bitangent = math::cross(n, tangent);
  260. *(v++) = tangent.x;
  261. *(v++) = tangent.y;
  262. *(v++) = tangent.z;
  263. *(v++) = bitangent.x;
  264. *(v++) = bitangent.y;
  265. *(v++) = bitangent.z;
  266. }
  267. if (has_barycentric)
  268. {
  269. *(v++) = barycentric_coords[j].x;
  270. *(v++) = barycentric_coords[j].y;
  271. *(v++) = barycentric_coords[j].z;
  272. }
  273. }
  274. }
  275. // Allocate a model
  276. model* model = new ::model();
  277. model->set_bounds(bounds);
  278. vertex_buffer* vbo = model->get_vertex_buffer();
  279. vertex_array* vao = model->get_vertex_array();
  280. vbo->resize(sizeof(float) * vertex_size * faces.size() * 3, vertex_data);
  281. std::size_t offset = 0;
  282. vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0);
  283. offset += 3;
  284. if (has_uvs)
  285. {
  286. vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *vbo, 2, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  287. offset += 2;
  288. }
  289. if (has_normals)
  290. {
  291. vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  292. offset += 3;
  293. }
  294. if (has_tangents)
  295. {
  296. vao->bind_attribute(VERTEX_TANGENT_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  297. offset += 3;
  298. vao->bind_attribute(VERTEX_BITANGENT_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  299. offset += 3;
  300. }
  301. if (has_barycentric)
  302. {
  303. vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  304. offset += 3;
  305. }
  306. // Add model groups for each material
  307. for (const material_group& material_group: material_groups)
  308. {
  309. model_group* model_group = model->add_group(material_group.name);
  310. model_group->set_material(material_group.material);
  311. model_group->set_drawing_mode(drawing_mode::triangles);
  312. model_group->set_start_index(material_group.start_index);
  313. model_group->set_index_count(material_group.index_count);
  314. }
  315. // Deallocate vertex data
  316. delete[] vertex_data;
  317. return model;
  318. }