diff --git a/libs/gfx/include/psemek/gfx/obj_parser.hpp b/libs/gfx/include/psemek/gfx/obj_parser.hpp new file mode 100644 index 00000000..49d185ac --- /dev/null +++ b/libs/gfx/include/psemek/gfx/obj_parser.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace psemek::gfx +{ + + struct obj_data + { + struct vertex + { + geom::point position; + geom::vector texcoord; + geom::vector normal; + }; + + std::vector> triangles; + }; + + obj_data parse_obj(std::istream & is); + +} diff --git a/libs/gfx/source/obj_parser.cpp b/libs/gfx/source/obj_parser.cpp new file mode 100644 index 00000000..87b8a7f2 --- /dev/null +++ b/libs/gfx/source/obj_parser.cpp @@ -0,0 +1,99 @@ +#include +#include + +#include +#include +#include + +namespace psemek::gfx +{ + + obj_data parse_obj(std::istream & is) + { + std::vector> positions; + std::vector> texcoords; + std::vector> normals; + + obj_data result; + + std::string line; + std::size_t line_count = 0; + + auto fail = [&](auto const & ... args){ + throw std::runtime_error(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 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; + } + +}