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

291 lines
7.7 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<std::vector<std::size_t>> faces;
  51. std::vector<material_group> material_groups;
  52. material_group* current_material_group = nullptr;
  53. aabb<float> bounds =
  54. {
  55. {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()},
  56. {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()}
  57. };
  58. while (!PHYSFS_eof(file))
  59. {
  60. // Read line
  61. physfs_getline(file, line);
  62. // Tokenize line
  63. std::vector<std::string> tokens;
  64. std::string token;
  65. std::istringstream linestream(line);
  66. while (linestream >> token)
  67. tokens.push_back(token);
  68. // Skip empty lines and comments
  69. if (tokens.empty() || tokens[0][0] == '#')
  70. continue;
  71. if (tokens[0] == "v")
  72. {
  73. if (tokens.size() != 4)
  74. {
  75. std::stringstream stream;
  76. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  77. throw std::runtime_error(stream.str());
  78. }
  79. float3 position;
  80. std::stringstream(tokens[1]) >> position[0];
  81. std::stringstream(tokens[2]) >> position[1];
  82. std::stringstream(tokens[3]) >> position[2];
  83. positions.push_back(position);
  84. // Add position to bounds
  85. for (int i = 0; i < 3; ++i)
  86. {
  87. bounds.min_point[i] = std::min<float>(bounds.min_point[i], position[i]);
  88. bounds.max_point[i] = std::max<float>(bounds.max_point[i], position[i]);
  89. }
  90. }
  91. else if (tokens[0] == "vt")
  92. {
  93. if (tokens.size() != 3)
  94. {
  95. std::stringstream stream;
  96. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  97. throw std::runtime_error(stream.str());
  98. }
  99. float2 uv;
  100. std::stringstream(tokens[1]) >> uv[0];
  101. std::stringstream(tokens[2]) >> uv[1];
  102. uvs.push_back(uv);
  103. }
  104. else if (tokens[0] == "vn")
  105. {
  106. if (tokens.size() != 4)
  107. {
  108. std::stringstream stream;
  109. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  110. throw std::runtime_error(stream.str());
  111. }
  112. float3 normal;
  113. std::stringstream(tokens[1]) >> normal[0];
  114. std::stringstream(tokens[2]) >> normal[1];
  115. std::stringstream(tokens[3]) >> normal[2];
  116. normals.push_back(normal);
  117. }
  118. else if (tokens[0] == "f")
  119. {
  120. if (tokens.size() != 4)
  121. {
  122. std::stringstream stream;
  123. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  124. throw std::runtime_error(stream.str());
  125. }
  126. std::vector<std::size_t> face;
  127. for (std::size_t i = 0; i < 3; ++i)
  128. {
  129. std::stringstream ss(tokens[i + 1]);
  130. while (ss.good())
  131. {
  132. std::string substring;
  133. std::getline(ss, substring, '/');
  134. if (!substring.empty())
  135. {
  136. std::size_t index = std::stoul(substring) - 1;
  137. face.push_back(index);
  138. }
  139. }
  140. }
  141. faces.push_back(face);
  142. }
  143. else if (tokens[0] == "usemtl")
  144. {
  145. if (tokens.size() != 2)
  146. {
  147. std::stringstream stream;
  148. stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
  149. throw std::runtime_error(stream.str());
  150. }
  151. if (current_material_group)
  152. {
  153. current_material_group->index_count = faces.size() * 3 - current_material_group->start_index;
  154. }
  155. material_groups.push_back(material_group());
  156. current_material_group = &material_groups.back();
  157. current_material_group->name = tokens[1];
  158. current_material_group->start_index = faces.size() * 3;
  159. current_material_group->index_count = 0;
  160. }
  161. }
  162. if (current_material_group)
  163. {
  164. current_material_group->index_count = faces.size() * 3 - current_material_group->start_index;
  165. }
  166. // Load material group materials
  167. for (material_group& material_group: material_groups)
  168. {
  169. material_group.material = resource_manager->load<material>(material_group.name + ".mtl");
  170. }
  171. bool has_uvs = (!uvs.empty());
  172. bool has_normals = (!normals.empty());
  173. bool has_barycentric = true;
  174. std::size_t vertex_size = 3;
  175. if (has_uvs)
  176. vertex_size += 2;
  177. if (has_normals)
  178. vertex_size += 3;
  179. if (has_barycentric)
  180. vertex_size += 3;
  181. std::size_t vertex_stride = sizeof(float) * vertex_size;
  182. // Generate vertex buffer
  183. float* vertex_data = new float[vertex_size * faces.size() * 3];
  184. float* v = &vertex_data[0];
  185. for (std::size_t i = 0; i < faces.size(); ++i)
  186. {
  187. const std::vector<std::size_t>& face = faces[i];
  188. std::size_t k = 0;
  189. for (std::size_t j = 0; j < 3; ++j)
  190. {
  191. const float3& position = positions[face[k++]];
  192. *(v++) = position[0];
  193. *(v++) = position[1];
  194. *(v++) = position[2];
  195. if (has_uvs)
  196. {
  197. const float2& uv = uvs[face[k++]];
  198. *(v++) = uv[0];
  199. *(v++) = uv[1];
  200. }
  201. if (has_normals)
  202. {
  203. const float3& normal = normals[face[k++]];
  204. *(v++) = normal[0];
  205. *(v++) = normal[1];
  206. *(v++) = normal[2];
  207. }
  208. if (has_barycentric)
  209. {
  210. *(v++) = barycentric_coords[j][0];
  211. *(v++) = barycentric_coords[j][1];
  212. *(v++) = barycentric_coords[j][2];
  213. }
  214. }
  215. }
  216. // Allocate a model
  217. model* model = new ::model();
  218. model->set_bounds(bounds);
  219. vertex_buffer* vbo = model->get_vertex_buffer();
  220. vertex_array* vao = model->get_vertex_array();
  221. vbo->resize(sizeof(float) * vertex_size * faces.size() * 3, vertex_data);
  222. std::size_t offset = 0;
  223. vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0);
  224. offset += 3;
  225. if (has_uvs)
  226. {
  227. vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *vbo, 2, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  228. offset += 2;
  229. }
  230. if (has_normals)
  231. {
  232. vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  233. offset += 3;
  234. }
  235. if (has_barycentric)
  236. {
  237. vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
  238. offset += 3;
  239. }
  240. // Add model groups for each material
  241. for (const material_group& material_group: material_groups)
  242. {
  243. model_group* model_group = model->add_group(material_group.name);
  244. model_group->set_material(material_group.material);
  245. model_group->set_drawing_mode(drawing_mode::triangles);
  246. model_group->set_start_index(material_group.start_index);
  247. model_group->set_index_count(material_group.index_count);
  248. }
  249. // Deallocate vertex data
  250. delete[] vertex_data;
  251. return model;
  252. }