VOX
A little voxel engine
Loading...
Searching...
No Matches
ObjLoader.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "define.hpp"
4
5#include <vulkan/vulkan.h>
6
7#define GLM_ENABLE_EXPERIMENTAL
8#include <glm/glm.hpp>
9#include <glm/gtx/hash.hpp>
10
11#include <iostream>
12#include <fstream>
13#include <sstream>
14#include <vector>
15#include <cstring>
16#include <regex>
17#include <unordered_map>
18#include <stdexcept>
19
21{
22 glm::vec3 pos;
23 glm::vec3 normal;
24 glm::vec2 tex_coord;
25
26 static VkVertexInputBindingDescription getBindingDescription()
27 {
28 VkVertexInputBindingDescription bindingDescription{};
29 bindingDescription.binding = 0;
30 bindingDescription.stride = sizeof(ObjVertex);
31 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
32
33 return bindingDescription;
34 }
35
36 static std::vector<VkVertexInputAttributeDescription> getAttributeDescriptions()
37 {
38 std::vector<VkVertexInputAttributeDescription> attributeDescriptions(3);
39
40 attributeDescriptions[0].binding = 0;
41 attributeDescriptions[0].location = 0;
42 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
43 attributeDescriptions[0].offset = offsetof(ObjVertex, pos);
44
45 attributeDescriptions[1].binding = 0;
46 attributeDescriptions[1].location = 1;
47 attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
48 attributeDescriptions[1].offset = offsetof(ObjVertex, normal);
49
50 attributeDescriptions[2].binding = 0;
51 attributeDescriptions[2].location = 2;
52 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
53 attributeDescriptions[2].offset = offsetof(ObjVertex, tex_coord);
54
55 return attributeDescriptions;
56 }
57
58 bool operator==(const ObjVertex& other) const
59 {
60 return pos == other.pos && normal == other.normal && tex_coord == other.tex_coord;
61 }
62};
63
64namespace std
65{
66 template<> struct hash<ObjVertex>
67 {
68 size_t operator()(const ObjVertex & vertex) const
69 {
70 return ((hash<glm::vec3>()(vertex.pos) ^
71 (hash<glm::vec3>()(vertex.normal) << 1)) >> 1) ^
72 (hash<glm::vec2>()(vertex.tex_coord) << 1);
73 }
74 };
75}
76
77struct Face
78{
79 uint32_t vertexIndex[3] = {0};
80 uint32_t texCoordIndex[3] = {0};
81 uint32_t normalIndex[3] = {0};
82
83 void log() const
84 {
85 std::cout << "f";
86 for (size_t i = 0; i < 3; i++)
87 {
88 std::cout << " " << vertexIndex[i] << "/" << texCoordIndex[i] << "/" << normalIndex[i];
89 }
90 std::cout << std::endl;
91 }
92};
93
95{
96
97public:
98
99 ObjLoader(const std::string & m_path)
100 {
101 this->m_path = m_path;
102
103 this->readFile();
104 this->removeComments();
105 this->parse();
106 this->populateVerticesAndIndices();
107 }
108
109 const std::vector<ObjVertex> & vertices() const
110 {
111 return this->m_vertices;
112 }
113
114 const std::vector<uint32_t> & indices() const
115 {
116 return this->m_indices;
117 }
118
119private:
120
121 std::string m_path;
122 std::vector<std::string> m_lines;
123
124 std::vector<glm::vec3> m_vertexPos;
125 std::vector<glm::vec2> m_texCoords;
126 std::vector<glm::vec3> m_normals;
127
128 std::vector<Face> m_faces;
129
130 bool m_hasTexCoords = false;
131 bool m_hasNormals = false;
132
133 std::vector<ObjVertex> m_vertices;
134 std::vector<uint32_t> m_indices;
135
136
137 void readFile()
138 {
139 std::ifstream file(this->m_path);
140 if (!file.is_open())
141 {
142 throw std::runtime_error("Failed to open file '" + this->m_path + "'");
143 }
144
145 std::string line;
146 while (std::getline(file, line))
147 {
148 this->m_lines.push_back(line);
149 }
150 }
151
152 void removeComments()
153 {
154 for (size_t i = 0; i < this->m_lines.size(); i++)
155 {
156 std::string line = this->m_lines[i];
157 size_t commentPos = line.find('#');
158 if (commentPos != std::string::npos)
159 {
160 line = line.substr(0, commentPos);
161 }
162 this->m_lines[i] = line;
163 }
164 }
165
166 void parse()
167 {
168
169 std::regex faceRegex_v(R"(^f(\s\d+){3,}$)");
170 std::regex faceRegex_v_vt(R"(^f(\s\d+\/\d+){3,}$)");
171 std::regex faceRegex_v_vn(R"(^f(\s\d+\/\/\d+){3,}$)");
172 std::regex faceRegex_v_vt_vn(R"(^f(\s\d+\/\d+\/\d+){3,}$)");
173
174 for (uint32_t i = 0; i < this->m_lines.size(); i++)
175 {
176
177 /* Skip empty m_lines or m_lines with only spaces */
178 if (std::all_of(this->m_lines[i].begin(), this->m_lines[i].end(), isspace))
179 {
180 continue;
181 }
182
183 std::string line = this->m_lines[i];
184 std::stringstream ss(line);
185 ss.exceptions(std::ios_base::failbit | std::ios_base::badbit);
186
187 try
188 {
189
190 if (memcmp(line.c_str(), "v ", 2) == 0)
191 {
192 glm::vec3 vertex;
193 ss.ignore(2);
194 ss >> vertex[0] >> vertex[1] >> vertex[2];
195 this->m_vertexPos.push_back(vertex);
196 }
197 else if (memcmp(line.c_str(), "vt ", 3) == 0)
198 {
199 glm::vec2 texCoord;
200 ss.ignore(3);
201 ss >> texCoord[0] >> texCoord[1];
202 this->m_texCoords.push_back(texCoord);
203 this->m_hasTexCoords = true;
204 }
205 else if (memcmp(line.c_str(), "vn ", 3) == 0)
206 {
207 glm::vec3 normal;
208 ss.ignore(3);
209 ss >> normal[0] >> normal[1] >> normal[2];
210 this->m_normals.push_back(normal);
211 this->m_hasNormals = true;
212 }
213 else if (memcmp(line.c_str(), "f ", 2) == 0)
214 {
215 std::vector<Face> tmp_faces;
216
217 /* Check which face regex matches */
218 bool v = false, v_vt = false, v_vn = false, v_vt_vn = false;
219 if (std::regex_match(line, faceRegex_v))
220 v = true;
221 else if (std::regex_match(line, faceRegex_v_vt) && m_hasTexCoords)
222 v_vt = true;
223 else if (std::regex_match(line, faceRegex_v_vn) && m_hasNormals)
224 v_vn = true;
225 else if (std::regex_match(line, faceRegex_v_vt_vn) && m_hasTexCoords && m_hasNormals)
226 v_vt_vn = true;
227 else
228 throw std::string("Parsing syntax error: Invalid face format");
229
230 ss.ignore(2);
231
232 /* Read each vertex of the face */
233 size_t j = 0;
234 while (ss.rdstate() == std::ios_base::goodbit)
235 {
236 Face face;
237 char c;
238 if (v)
239 ss >> face.vertexIndex[0];
240 else if (v_vt)
241 ss >> face.vertexIndex[0] >> c >> face.texCoordIndex[0];
242 else if (v_vn)
243 ss >> face.vertexIndex[0] >> c >> c >> face.normalIndex[0];
244 else if (v_vt_vn)
245 ss >> face.vertexIndex[0] >> c >> face.texCoordIndex[0] >> c >> face.normalIndex[0];
246
247 if (this->checkIndices(face) == false)
248 {
249 throw std::string("Parsing value error");
250 }
251
252 tmp_faces.push_back(face);
253 j++;
254 }
255
256 /* Construct a face for each triangle (e.i. 3 vertices) */
257 for (j = 1; j + 1 < tmp_faces.size(); j++)
258 {
259 Face face;
260 face.vertexIndex[0] = tmp_faces[0].vertexIndex[0];
261 face.vertexIndex[1] = tmp_faces[j].vertexIndex[0];
262 face.vertexIndex[2] = tmp_faces[j + 1].vertexIndex[0];
263 if (v_vt || v_vt_vn)
264 {
265 face.texCoordIndex[0] = tmp_faces[0].texCoordIndex[0];
266 face.texCoordIndex[1] = tmp_faces[j].texCoordIndex[0];
267 face.texCoordIndex[2] = tmp_faces[j + 1].texCoordIndex[0];
268 }
269 if (v_vn || v_vt_vn)
270 {
271 face.normalIndex[0] = tmp_faces[0].normalIndex[0];
272 face.normalIndex[1] = tmp_faces[j].normalIndex[0];
273 face.normalIndex[2] = tmp_faces[j + 1].normalIndex[0];
274 }
275 this->m_faces.push_back(face);
276 }
277 }
278 else if (
279 memcmp(line.c_str(), "mtllib ", 7) != 0
280 && memcmp(line.c_str(), "usemtl ", 7) != 0
281 && memcmp(line.c_str(), "s ", 2) != 0
282 && memcmp(line.c_str(), "g ", 2) != 0
283 && memcmp(line.c_str(), "o ", 2) != 0
284 )
285 {
286 throw std::string("Parsing syntax error: Unknown line type");
287 }
288
289 } catch (std::string& e)
290 {
291 throw std::runtime_error(this->m_path + ": line " + std::to_string(i + 1) + ": " + e);
292 }
293 }
294 }
295
296 bool checkIndices(const Face & face)
297 {
298 if (face.vertexIndex[0] > this->m_vertexPos.size() || face.vertexIndex[0] <= 0)
299 {
300 return false;
301 }
302 if (this->m_hasTexCoords && (face.texCoordIndex[0] > this->m_texCoords.size() || face.texCoordIndex[0] <= 0))
303 {
304 return false;
305 }
306 if (this->m_hasNormals && (face.normalIndex[0] > this->m_normals.size() || face.normalIndex[0] <= 0))
307 {
308 return false;
309 }
310 return true;
311 }
312
313 void populateVerticesAndIndices()
314 {
315 std::unordered_map<ObjVertex, uint32_t> uniqueVertices{};
316
317 for (const auto& face : this->m_faces)
318 {
319 glm::vec2 arbitraryTexCoords[3] =
320 {
321 glm::vec2(0.0f, 0.0f),
322 glm::vec2(1.0f, 0.0f),
323 glm::vec2(0.0f, 1.0f)
324 };
325
326 for (size_t i = 0; i < 3; i++)
327 {
328 ObjVertex vertex{};
329
330 vertex.pos = this->m_vertexPos[face.vertexIndex[i] - 1];
331
332 if (this->m_hasTexCoords)
333 {
334 vertex.tex_coord = this->m_texCoords[face.texCoordIndex[i] - 1];
335 }
336 else
337 {
338 vertex.tex_coord = arbitraryTexCoords[i];
339 }
340
341 if (this->m_hasNormals)
342 {
343 vertex.normal = this->m_normals[face.normalIndex[i] - 1];
344 }
345
346 if (uniqueVertices.count(vertex) == 0)
347 {
348 uniqueVertices[vertex] = static_cast<uint32_t>(m_vertices.size());
349 m_vertices.push_back(vertex);
350 }
351
352 m_indices.push_back(uniqueVertices[vertex]);
353 }
354 }
355 }
356
357};
Definition: ObjLoader.hpp:95
ObjLoader(const std::string &m_path)
Definition: ObjLoader.hpp:99
const std::vector< uint32_t > & indices() const
Definition: ObjLoader.hpp:114
const std::vector< ObjVertex > & vertices() const
Definition: ObjLoader.hpp:109
Definition: ObjLoader.hpp:65
Definition: ObjLoader.hpp:78
uint32_t normalIndex[3]
Definition: ObjLoader.hpp:81
uint32_t vertexIndex[3]
Definition: ObjLoader.hpp:79
void log() const
Definition: ObjLoader.hpp:83
uint32_t texCoordIndex[3]
Definition: ObjLoader.hpp:80
Definition: ObjLoader.hpp:21
bool operator==(const ObjVertex &other) const
Definition: ObjLoader.hpp:58
static VkVertexInputBindingDescription getBindingDescription()
Definition: ObjLoader.hpp:26
static std::vector< VkVertexInputAttributeDescription > getAttributeDescriptions()
Definition: ObjLoader.hpp:36
glm::vec3 normal
Definition: ObjLoader.hpp:23
glm::vec2 tex_coord
Definition: ObjLoader.hpp:24
glm::vec3 pos
Definition: ObjLoader.hpp:22
size_t operator()(const ObjVertex &vertex) const
Definition: ObjLoader.hpp:68