22#ifndef EOS_READ_OBJ_HPP
23#define EOS_READ_OBJ_HPP
25#include "eos/core/Mesh.hpp"
26#include "eos/cpp17/optional.hpp"
47template <
class ContainerType>
48void tokenize(
const std::string& str, ContainerType& tokens,
const std::string& delimiters =
" ",
49 bool trim_empty =
false)
51 std::string::size_type pos, last_pos = 0;
52 const auto length = str.length();
54 using value_type =
typename ContainerType::value_type;
55 using size_type =
typename ContainerType::size_type;
57 while (last_pos < length + 1)
59 pos = str.find_first_of(delimiters, last_pos);
60 if (pos == std::string::npos)
65 if (pos != last_pos || !trim_empty)
66 tokens.push_back(value_type(str.data() + last_pos, (size_type)pos - last_pos));
91inline std::pair<Eigen::Vector3f, cpp17::optional<Eigen::Vector3f>> parse_vertex(
const std::string& line)
93 std::vector<std::string> tokens;
94 tokenize(line, tokens,
" ",
true);
95 if (tokens.size() != 3 && tokens.size() != 6)
97 throw std::runtime_error(
98 "Encountered a vertex ('v') line that does not consist of either 3 ('x y z') "
99 "or 6 ('x y z r g b') numbers.");
101 const Eigen::Vector3f vertex(std::stof(tokens[0]), std::stof(tokens[1]), std::stof(tokens[2]));
102 cpp17::optional<Eigen::Vector3f> vertex_color;
103 if (tokens.size() == 6)
105 vertex_color = Eigen::Vector3f(std::stof(tokens[3]), std::stof(tokens[4]), std::stof(tokens[5]));
107 return {vertex, vertex_color};
116inline Eigen::Vector2f parse_texcoords(
const std::string& line)
118 std::vector<std::string> tokens;
119 tokenize(line, tokens,
" ");
121 if (tokens.size() != 2)
123 if (tokens.size() == 3 && std::stof(tokens[2]) == 0.f)
128 throw std::runtime_error(
129 "Encountered a texture coordinates ('vt') line that does not consist of two "
130 "('u v') numbers. 'u v w' is only supported in the special case if w is 0.");
133 const Eigen::Vector2f texcoords(std::stof(tokens[0]), std::stof(tokens[1]));
142inline void parse_vertex_normal(
const std::string& line)
144 throw std::runtime_error(
"Parsing \"vn\" is not yet implemented.");
159inline auto parse_face(
const std::string& line)
165 vector<int> vertex_indices;
166 vector<int> texture_indices;
167 vector<int> normal_indices;
169 vector<string> tokens;
170 tokenize(line, tokens,
" ",
true);
171 if (tokens.size() != 3 && tokens.size() != 4)
174 throw std::runtime_error(
"Encountered a faces ('f') line that does not consist of three or four "
175 "blocks of numbers. We currently only support 3 blocks (triangle meshes) "
176 "and 4 blocks (quad meshes).");
179 for (
const auto& token : tokens)
181 vector<string> subtokens;
182 tokenize(token, subtokens,
"/",
false);
183 assert(subtokens.size() > 0 && subtokens.size() <= 3);
186 vertex_indices.push_back(std::stoi(subtokens[0]) - 1);
188 if (subtokens.size() == 2)
191 texture_indices.push_back(std::stoi(subtokens[1]) - 1);
194 if (subtokens.size() == 3)
197 if (!subtokens[1].empty())
200 texture_indices.push_back(std::stoi(subtokens[1]) - 1);
203 normal_indices.push_back(std::stoi(subtokens[2]) - 1);
208 return std::make_tuple(vertex_indices, texture_indices, normal_indices);
223 std::ifstream file(filename);
226 throw std::runtime_error(std::string(
"Could not open obj file: " + filename));
230 const auto starts_with = [](
const std::string& input,
const std::string& match) {
231 return input.size() >= match.size() && std::equal(match.begin(), match.end(), input.begin());
246 while (getline(file, line))
248 if (starts_with(line,
"#"))
253 if (starts_with(line,
"v "))
255 const auto vertex_data =
256 detail::parse_vertex(line.substr(2));
257 mesh.
vertices.push_back(vertex_data.first);
258 if (vertex_data.second)
260 mesh.
colors.push_back(vertex_data.second.value());
263 if (starts_with(line,
"vt "))
265 const auto texcoords = detail::parse_texcoords(line.substr(3));
268 if (starts_with(line,
"vn "))
274 if (starts_with(line,
"f "))
276 const auto face_data = detail::parse_face(line.substr(2));
277 if (std::get<0>(face_data).size() == 3)
280 {std::get<0>(face_data)[0], std::get<0>(face_data)[1], std::get<0>(face_data)[2]});
285 else if (std::get<0>(face_data).size() == 4)
289 {std::get<0>(face_data)[0], std::get<0>(face_data)[1], std::get<0>(face_data)[2]});
291 {std::get<0>(face_data)[0], std::get<0>(face_data)[2], std::get<0>(face_data)[3]});
Mesh read_obj(std::string filename)
Reads the given Wavefront .obj file into a Mesh.
Definition: read_obj.hpp:221
Namespace containing all of eos's 3D model fitting functionality.
This class represents a 3D mesh consisting of vertices, vertex colour information and texture coordin...
Definition: Mesh.hpp:45
std::vector< Eigen::Vector3f > colors
Colour information for each vertex. Expected to be in RGB order.
Definition: Mesh.hpp:47
std::vector< Eigen::Vector3f > vertices
3D vertex positions.
Definition: Mesh.hpp:46
std::vector< std::array< int, 3 > > tvi
Triangle vertex indices.
Definition: Mesh.hpp:50
std::vector< Eigen::Vector2f > texcoords
Texture coordinates.
Definition: Mesh.hpp:48