19 #include <nori/mesh.h>
20 #include <nori/timer.h>
21 #include <filesystem/resolver.h>
22 #include <unordered_map>
33 typedef std::unordered_map<OBJVertex, uint32_t, OBJVertexHash> VertexMap;
35 filesystem::path filename =
36 getFileResolver()->resolve(propList.
getString(
"filename"));
38 std::ifstream is(filename.str());
40 throw NoriException(
"Unable to open OBJ file \"%s\"!", filename);
43 cout <<
"Loading \"" << filename <<
"\" .. ";
47 std::vector<Vector3f> positions;
48 std::vector<Vector2f> texcoords;
49 std::vector<Vector3f> normals;
50 std::vector<uint32_t> indices;
51 std::vector<OBJVertex> vertices;
55 while (std::getline(is, line_str)) {
56 std::istringstream line(line_str);
63 line >> p.x() >> p.y() >> p.z();
66 positions.push_back(p);
67 }
else if (prefix ==
"vt") {
69 line >> tc.x() >> tc.y();
70 texcoords.push_back(tc);
71 }
else if (prefix ==
"vn") {
73 line >> n.x() >> n.y() >> n.z();
74 normals.push_back((trafo * n).normalized());
75 }
else if (prefix ==
"f") {
76 std::string v1, v2, v3, v4;
77 line >> v1 >> v2 >> v3 >> v4;
93 for (
int i=0; i<nVertices; ++i) {
95 VertexMap::const_iterator it = vertexMap.find(v);
96 if (it == vertexMap.end()) {
97 vertexMap[v] = (uint32_t) vertices.size();
98 indices.push_back((uint32_t) vertices.size());
99 vertices.push_back(v);
101 indices.push_back(it->second);
107 m_F.resize(3, indices.size()/3);
108 memcpy(
m_F.data(), indices.data(),
sizeof(uint32_t)*indices.size());
110 m_V.resize(3, vertices.size());
111 for (uint32_t i=0; i<vertices.size(); ++i)
112 m_V.col(i) = positions.at(vertices[i].p-1);
114 if (!normals.empty()) {
115 m_N.resize(3, vertices.size());
116 for (uint32_t i=0; i<vertices.size(); ++i)
117 m_N.col(i) = normals.at(vertices[i].n-1);
120 if (!texcoords.empty()) {
121 m_UV.resize(2, vertices.size());
122 for (uint32_t i=0; i<vertices.size(); ++i)
123 m_UV.col(i) = texcoords.at(vertices[i].uv-1);
126 size_t meshSize =
m_F.size() *
sizeof(uint32_t) +
127 sizeof(
float) * (
m_V.size() +
m_N.size() +
m_UV.size());
131 throw NoriException(
"OBJ file \"%s\" contains no data! Make sure you have Git LFS installed", filename);
135 cout <<
"done. (V=" <<
m_V.cols() <<
", F=" <<
m_F.cols() <<
", took "
137 << memString(meshSize)
144 uint32_t p = (uint32_t) -1;
145 uint32_t n = (uint32_t) -1;
146 uint32_t uv = (uint32_t) -1;
150 inline OBJVertex(
const std::string &
string) {
151 std::vector<std::string> tokens = tokenize(
string,
"/",
true);
153 if (tokens.size() < 1 || tokens.size() > 3)
156 p = toUInt(tokens[0]);
158 if (tokens.size() >= 2 && !tokens[1].empty())
159 uv = toUInt(tokens[1]);
161 if (tokens.size() >= 3 && !tokens[2].empty())
162 n = toUInt(tokens[2]);
165 inline bool operator==(
const OBJVertex &v)
const {
166 return v.p == p && v.n == n && v.uv == uv;
172 std::size_t operator()(
const OBJVertex& v)
const {
173 size_t hash = std::hash<uint32_t>()(v.p);
174 hash = hash * 37 + std::hash<uint32_t>()(v.uv);
175 hash = hash * 37 + std::hash<uint32_t>()(v.n);
MatrixXf m_UV
Vertex texture coordinates.
MatrixXf m_N
Vertex normals.
std::string m_name
Identifying name.
MatrixXf m_V
Vertex positions.
Simple exception class, which stores a human-readable error description.
This is an associative container used to supply the constructors of NoriObject subclasses with parame...
Transform getTransform(const std::string &name) const
Get a transform property, and throw an exception if it does not exist.
std::string getString(const std::string &name) const
Get a string property, and throw an exception if it does not exist.
BoundingBox3f m_bbox
Bounding box of the mesh.
Simple timer with millisecond precision.
std::string elapsedString(bool precise=false) const
Like elapsed(), but return a human-readable string.
Loader for Wavefront OBJ triangle meshes.
3-dimensional surface normal representation
void expandBy(const PointType &p)
Expand the bounding box to contain another point.
Hash function for OBJVertex.
Vertex indices used by the OBJ format.