psemek/libs/gfx/source/obj_parser.cpp

100 lines
2 KiB
C++

#include <psemek/gfx/obj_parser.hpp>
#include <psemek/util/to_string.hpp>
#include <psemek/util/exception.hpp>
#include <string>
#include <sstream>
#include <stdexcept>
namespace psemek::gfx
{
obj_data parse_obj(std::istream & is)
{
std::vector<math::point<float, 3>> positions;
std::vector<math::vector<float, 2>> texcoords;
std::vector<math::vector<float, 3>> normals;
obj_data result;
std::string line;
std::size_t line_count = 0;
auto fail = [&](auto const & ... args){
throw util::exception(util::to_string("Error parsing OBJ data, line ", line_count, ": ", args...));
};
while (std::getline(is >> std::ws, line))
{
++line_count;
if (line.empty()) continue;
if (line[0] == '#') continue;
std::istringstream ls(std::move(line));
std::string tag;
ls >> tag;
if (tag == "v")
{
auto & p = positions.emplace_back();
ls >> p[0] >> p[1] >> p[2];
}
else if (tag == "vt")
{
auto & t = texcoords.emplace_back();
ls >> t[0] >> t[1];
}
else if (tag == "vn")
{
auto & n = normals.emplace_back();
ls >> n[0] >> n[1] >> n[2];
}
else if (tag == "f")
{
std::vector<obj_data::vertex> vertices;
while (ls)
{
std::size_t ip, it, in;
ls >> ip;
if (ls.eof()) break;
if (!ls || ls.get() != '/')
fail("expected '/'");
ls >> it;
if (!ls || ls.get() != '/')
fail("expected '/'");
ls >> in;
--ip;
--it;
--in;
if (ip >= positions.size())
fail("bad position index (", ip, ")");
if (it >= texcoords.size())
fail("bad texcoord index (", ip, ")");
if (in >= normals.size())
fail("bad normal index (", ip, ")");
auto & v = vertices.emplace_back();
v.position = positions[ip];
v.texcoord = texcoords[it];
v.normal = normals[in];
}
for (std::size_t i = 1; i + 1 < vertices.size(); ++i)
{
result.triangles.push_back({{vertices[0], vertices[i], vertices[i + 1]}});
}
}
}
return result;
}
}