Visual Computing Library
Loading...
Searching...
No Matches
load.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_LOAD_SAVE_OBJ_LOAD_H
24#define VCL_LOAD_SAVE_OBJ_LOAD_H
25
26#include "material.h"
27
28#include <vclib/algorithms/mesh/face_topology.h>
29#include <vclib/io/file_info.h>
30#include <vclib/io/read.h>
31#include <vclib/load_save/settings.h>
32#include <vclib/misc/logger.h>
33#include <vclib/space/complex/mesh_info.h>
34#include <vclib/space/core/texture.h>
35
36#include <algorithm>
37#include <map>
38
39namespace vcl {
40
41namespace detail {
42
43template<MeshConcept MeshType>
44using ObjNormalsMap = std::conditional_t<
45 HasPerVertexNormal<MeshType>,
46 std::map<uint, typename MeshType::VertexType::NormalType>,
47 std::map<uint, Point3d>>;
48
49template<MeshConcept MeshType>
50void loadObjMaterials(
51 std::map<std::string, ObjMaterial>& materialMap,
52 MeshType& mesh,
53 std::istream& stream,
54 MeshInfo& loadedInfo,
55 const LoadSettings& settings)
56{
57 std::string matName;
58 ObjMaterial mat;
59
60 do {
61 Tokenizer tokens = readAndTokenizeNextNonEmptyLineNoThrow(stream);
62 if (stream) {
63 // counter for texture images, used when mesh has no texture files
64 uint nt = 0;
65 Tokenizer::iterator token = tokens.begin();
66 std::string header = *token++;
67 if (header == "newmtl") {
68 if (!matName.empty())
69 materialMap[matName] = mat;
70 mat = ObjMaterial();
71 matName = *token;
72 }
73 if (header == "Ka") {
74 if (tokens.size() >= 4) {
75 if (*token != "spectral" && *token != "xyz") {
76 mat.Ka.x() = io::readFloat<float>(token);
77 mat.Ka.y() = io::readFloat<float>(token);
78 mat.Ka.z() = io::readFloat<float>(token);
79 }
80 }
81 }
82 if (header == "Kd") {
83 if (tokens.size() >= 4) {
84 if (*token != "spectral" && *token != "xyz") {
85 mat.Kd.x() = io::readFloat<float>(token);
86 mat.Kd.y() = io::readFloat<float>(token);
87 mat.Kd.z() = io::readFloat<float>(token);
88 mat.hasColor = true;
89 }
90 }
91 }
92 if (header == "Ks") {
93 if (tokens.size() >= 4) {
94 if (*token != "spectral" && *token != "xyz") {
95 mat.Ks.x() = io::readFloat<float>(token);
96 mat.Ks.y() = io::readFloat<float>(token);
97 mat.Ks.z() = io::readFloat<float>(token);
98 }
99 }
100 }
101 if (header == "d") {
102 if ((*token)[0] == '-')
103 token++;
104 mat.d = io::readFloat<float>(token);
105 }
106 if (header == "Tr") {
107 if ((*token)[0] == '-')
108 token++;
109 mat.d = 1 - io::readFloat<float>(token);
110 }
111 if (header == "Ns") {
112 mat.Ns = io::readFloat<float>(token);
113 }
114 if (header == "illum") {
115 mat.illum = io::readFloat<int>(token);
116 }
117 if (header == "map_Kd") {
118 // need to manage args
119 while ((*token)[0] == '-') {
120 if (*token == "-o" || *token == "-s" || *token == "-t") {
121 // ignore the argument and the three values
122 ++token;
123 ++token;
124 ++token;
125 ++token;
126 }
127 if (*token == "-mm") {
128 // ignore the argument and the two values
129 ++token;
130 ++token;
131 ++token;
132 }
133 if (*token == "-blendu" || *token == "-blendv" ||
134 *token == "-cc" || *token == "-clamp" ||
135 *token == "-texres") {
136 // ignore the argument and the value
137 ++token;
138 ++token;
139 }
140 }
141 mat.map_Kd = *token;
142 // replace backslashes with slashes - windows compatibility
143 std::ranges::replace(mat.map_Kd, '\\', '/');
144 mat.hasTexture = true;
145 if constexpr (HasTexturePaths<MeshType>) {
146 loadedInfo.setTextures();
147 mat.mapId = mesh.textureNumber();
148 mesh.pushTexturePath(mat.map_Kd);
149 }
150 else {
151 mat.mapId = nt++;
152 }
153 }
154 }
155 } while (stream);
156 if (!matName.empty())
157 materialMap[matName] = mat;
158}
159
160template<MeshConcept MeshType>
161void loadObjMaterials(
162 std::map<std::string, ObjMaterial>& materialMap,
163 MeshType& mesh,
164 const std::string& mtllib,
165 MeshInfo& loadedInfo,
166 const LoadSettings& settings)
167{
168 std::ifstream file = openInputFileStream(mtllib);
169 loadObjMaterials(materialMap, mesh, file, loadedInfo, settings);
170}
171
172template<MeshConcept MeshType>
173void readObjVertex(
174 MeshType& m,
175 Tokenizer::iterator& token,
176 MeshInfo& loadedInfo,
177 const Tokenizer& tokens,
178 const ObjMaterial& currentMaterial,
179 const LoadSettings& settings)
180{
181 // first, need to set that I'm loading vertices
182 if (m.vertexNumber() == 0) {
183 loadedInfo.setVertices();
184 loadedInfo.setVertexCoords();
185 }
186 uint vid = m.addVertex();
187 for (uint i = 0; i < 3; ++i) {
188 m.vertex(vid).coord()[i] = io::readDouble<double>(token);
189 }
190 if constexpr (HasPerVertexColor<MeshType>) {
191 if (vid == 0) {
192 // if the current material has a valid color, of the file stores the
193 // vertex color in the non-standard way (color values after the
194 // coordinates)
195 if (currentMaterial.hasColor || tokens.size() > 6) {
196 if (settings.enableOptionalComponents) {
197 enableIfPerVertexColorOptional(m);
198 loadedInfo.setVertexColors();
199 }
200 else {
201 if (isPerVertexColorAvailable(m))
202 loadedInfo.setVertexColors();
203 }
204 }
205 }
206 if (loadedInfo.hasVertexColors()) {
207 // the file has the nonstandard way to store vertex colors, after
208 // the coords...
209 if (tokens.size() > 6) {
210 m.vertex(vid).color().setRedF(io::readFloat<float>(token));
211 m.vertex(vid).color().setGreenF(io::readFloat<float>(token));
212 m.vertex(vid).color().setBlueF(io::readFloat<float>(token));
213 }
214 else if (currentMaterial.hasColor) {
215 m.vertex(vid).color() = currentMaterial.color();
216 }
217 }
218 }
219}
220
221template<MeshConcept MeshType>
222void readObjVertexNormal(
223 MeshType& m,
224 detail::ObjNormalsMap<MeshType>& mapNormalsCache,
225 uint vn,
226 Tokenizer::iterator& token,
227 MeshInfo& loadedInfo,
228 const LoadSettings& settings)
229{
230 using NormalType = MeshType::VertexType::NormalType;
231
232 // first, need to check if I can store normals in the mesh
233 if (vn == 0) {
234 if (settings.enableOptionalComponents) {
235 enableIfPerVertexNormalOptional(m);
236 loadedInfo.setVertexNormals();
237 }
238 else {
239 if (isPerVertexNormalAvailable(m))
240 loadedInfo.setVertexNormals();
241 }
242 }
243 if (loadedInfo.hasVertexNormals()) {
244 // read the normal
245 NormalType n;
246 for (uint i = 0; i < 3; ++i) {
247 n[i] = io::readDouble<typename NormalType::ScalarType>(token);
248 }
249 // I can store the normal in its vertex
250 if (m.vertexNumber() > vn) {
251 m.vertex(vn).normal() = n;
252 }
253 // read the normal and save it in the cache map, because we still don't
254 // have read the vertex corresponding to the current normal
255 else {
256 mapNormalsCache[vn] = n;
257 }
258 }
259}
260
261template<FaceMeshConcept MeshType>
262void readObjFace(
263 MeshType& m,
264 MeshInfo& loadedInfo,
265 const Tokenizer& tokens,
266 const std::vector<TexCoordIndexedd>& wedgeTexCoords,
267 const ObjMaterial& currentMaterial,
268 const LoadSettings& settings)
269{
270 using FaceType = MeshType::FaceType;
271
272 std::vector<uint> vids;
273 std::vector<uint> wids;
274
275 loadedInfo.updateMeshType(tokens.size() - 1);
276
277 // actual read - load vertex indices and texcoords indices, if present
278 Tokenizer::iterator token = tokens.begin();
279 ++token;
280 vids.resize(tokens.size() - 1);
281 wids.reserve(tokens.size() - 1);
282 for (uint i = 0; i < tokens.size() - 1; ++i) {
283 Tokenizer subt(*token, '/', false);
284 auto t = subt.begin();
285 vids[i] = io::readUInt<uint>(t) - 1;
286 if (subt.size() > 1) {
287 if (!t->empty()) {
288 wids.push_back(io::readUInt<uint>(t) - 1);
289 }
290 }
291 ++token;
292 }
293
294 // add the face
295 uint fid = m.addFace();
296 FaceType& f = m.face(fid);
297
298 // check if we need to split the face we read into triangles
299 bool splitFace = false;
300 // we have a polygonal mesh, no need to split
301 if constexpr (FaceType::VERTEX_NUMBER < 0) {
302 // need to resize to the right number of verts
303 f.resizeVertices(tokens.size() - 1);
304 }
305 else if (FaceType::VERTEX_NUMBER != tokens.size() - 1) {
306 // we have faces with static sizes (triangles), but we are loading faces
307 // with number of verts > 3. Need to split the face we are loading in n
308 // faces!
309 splitFace = true;
310 }
311
312 // create the face in the mesh, for now we manage only vertex indices
313 if (!splitFace) { // no need to split face case
314 for (uint i = 0; i < vids.size(); ++i) {
315 if (vids[i] >= m.vertexNumber()) {
316 throw MalformedFileException(
317 "Bad vertex index for face " + std::to_string(fid));
318 }
319 f.setVertex(i, vids[i]);
320 }
321 }
322 else { // split needed
323 addTriangleFacesFromPolygon(m, f, vids);
324 }
325
326 // color
327 if (HasPerFaceColor<MeshType>) {
328 // if the first face, we need to check if I can store colors
329 if (fid == 0) {
330 // if the current material has no color, we assume that the file has
331 // no face color
332 if (currentMaterial.hasColor) {
333 if (settings.enableOptionalComponents) {
335 loadedInfo.setFaceColors();
336 }
337 else {
339 loadedInfo.setFaceColors();
340 }
341 }
342 }
343 if (loadedInfo.hasFaceColors()) {
344 if (currentMaterial.hasColor) {
345 // in case the loaded polygon has been triangulated in the last
346 // n triangles of mesh
347 for (uint ff = fid; ff < m.faceNumber(); ++ff) {
348 m.face(ff).color() = currentMaterial.color();
349 }
350 }
351 }
352 }
353
354 // wedge coords
355 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
356 // first, need to check if I can store wedge texcoords in the mesh
357 if (fid == 0) {
358 // if the current face has the right number of wedge texcoords, we
359 // assume that we can load wedge texcoords
360 if (wids.size() == vids.size()) {
361 if (settings.enableOptionalComponents) {
363 loadedInfo.setFaceWedgeTexCoords();
364 }
365 else {
367 loadedInfo.setFaceWedgeTexCoords();
368 }
369 }
370 }
371 if (loadedInfo.hasFaceWedgeTexCoords()) {
372 if (wids.size() == vids.size()) {
373 if (!splitFace) { // there wasn't a triangulation of the face
374 // it is safe to assign each wedge texcoord to its position
375 // in the face
376 for (uint i = 0; i < wids.size(); ++i) {
377 if (wids[i] >= wedgeTexCoords.size()) {
378 throw MalformedFileException(
379 "Bad texcoord index for face " +
380 std::to_string(fid));
381 }
382 f.wedgeTexCoord(i) =
383 ((vcl::TexCoordd) wedgeTexCoords[wids[i]])
384 .cast<typename FaceType::WedgeTexCoordType::
385 ScalarType>();
386 if (currentMaterial.hasTexture) {
387 f.textureIndex() = currentMaterial.mapId;
388 }
389 }
390 }
391 else {
392 // take read texcoords and map them in the triangulated
393 // faces for each face of the triangulation of the polygon
394 for (uint ff = fid; ff < m.faceNumber(); ++ff) {
395 FaceType& f = m.face(ff);
396 // for each vertex of the face
397 for (uint i = 0; i < f.vertexNumber(); ++i) {
398 uint vid = m.index(f.vertex(i));
399 // find the position of the vertex in the vids array
400 auto it = std::find(vids.begin(), vids.end(), vid);
401 assert(it != vids.end());
402 uint pos = it - vids.begin();
403 // check that the texcoord id is valid
404 if (wids[pos] >= wedgeTexCoords.size()) {
405 throw MalformedFileException(
406 "Bad texcoord index for face " +
407 std::to_string(fid));
408 }
409 // set the wedge texcoord in the same position of
410 // the vertex
411 f.wedgeTexCoord(i) =
412 ((vcl::TexCoordd) wedgeTexCoords[wids[pos]])
413 .cast<typename FaceType::WedgeTexCoordType::
414 ScalarType>();
415 if (currentMaterial.hasTexture) {
416 f.textureIndex() = currentMaterial.mapId;
417 }
418 }
419 }
420 }
421 }
422 }
423 }
424}
425
426template<EdgeMeshConcept MeshType>
427void readObjEdge(
428 MeshType& m,
429 MeshInfo& loadedInfo,
430 const Tokenizer& tokens,
431 const ObjMaterial& currentMaterial,
432 const LoadSettings& settings)
433{
434 using EdgeType = MeshType::EdgeType;
435
436 // add the edge
437 uint eid = m.addEdge();
438 EdgeType& e = m.edge(eid);
439
440 // actual read - load vertex indices
441 Tokenizer::iterator token = tokens.begin();
442 ++token;
443 uint vid1 = io::readUInt<uint>(token) - 1;
444 uint vid2 = io::readUInt<uint>(token) - 1;
445 e.setVertices(vid1, vid2);
446
447 // color
448 if (HasPerEdgeColor<MeshType>) {
449 // if the first edge, we need to check if I can store colors
450 if (eid == 0) {
451 // if the current material has no color, we assume that the file has
452 // no edge color
453 if (currentMaterial.hasColor) {
454 if (settings.enableOptionalComponents) {
456 loadedInfo.setEdgeColors();
457 }
458 else {
460 loadedInfo.setEdgeColors();
461 }
462 }
463 }
464 if (loadedInfo.hasEdgeColors()) {
465 if (currentMaterial.hasColor) {
466 // set the current color to the edge
467 m.edge(eid).color() = currentMaterial.color();
468 }
469 }
470 }
471}
472
492template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
493void loadObj(
494 MeshType& m,
495 std::istream& inputObjStream,
496 const std::vector<std::istream*>& inputMtlStreams,
497 MeshInfo& loadedInfo,
498 const std::string& filename = "",
499 bool ignoreMtlLib = false,
500 LogType& log = nullLogger,
501 const LoadSettings& settings = LoadSettings())
502{
503 loadedInfo.clear();
504
505 // save normals if they can't be stored directly into vertices
506 detail::ObjNormalsMap<MeshType> mapNormalsCache;
507 uint vn = 0; // number of vertex normals read
508 // save array of texcoords, that are stored later (into wedges when loading
509 // faces or into vertices as a fallback)
510 std::vector<TexCoordIndexedd> texCoords;
511
512 // map of materials loaded
513 std::map<std::string, detail::ObjMaterial> materialMap;
514
515 // load materials from the material files, if any
516 for (auto* stream : inputMtlStreams) {
517 detail::loadObjMaterials(materialMap, m, *stream, loadedInfo, settings);
518 }
519
520 // the current material, set by 'usemtl'
521 detail::ObjMaterial currentMaterial;
522
523 if constexpr (HasTexturePaths<MeshType>) {
524 m.meshBasePath() = FileInfo::pathWithoutFileName(filename);
525 }
526
527 if constexpr (HasName<MeshType>) {
528 m.name() = FileInfo::fileNameWithoutExtension(filename);
529 }
530
531 inputObjStream.seekg(0, inputObjStream.end);
532 std::size_t fsize = inputObjStream.tellg();
533 inputObjStream.seekg(0, inputObjStream.beg);
534 log.startProgress("Loading OBJ file", fsize);
535
536 // cycle that reads line by line
537 do {
538 Tokenizer tokens =
539 readAndTokenizeNextNonEmptyLineNoThrow(inputObjStream);
540 if (inputObjStream) {
541 Tokenizer::iterator token = tokens.begin();
542 std::string header = *token++;
543 if (header == "mtllib" && !ignoreMtlLib) { // material file
544 // we load the material file if they are not ignored
545 std::string mtlfile =
546 FileInfo::pathWithoutFileName(filename) + *token;
547 try {
548 detail::loadObjMaterials(
549 materialMap, m, mtlfile, loadedInfo, settings);
550 }
551 catch (CannotOpenFileException) {
552 log.log(
553 "Cannot open material file " + mtlfile,
554 LogType::WARNING_LOG);
555 }
556 }
557 // use a new material - change currentMaterial
558 if (header == "usemtl") {
559 std::string matname = *token;
560 auto it = materialMap.find(matname);
561 if (it != materialMap.end()) {
562 currentMaterial = it->second;
563 }
564 else { // material not found - warning
565 log.log(
566 "Material " + matname + " not found.",
567 LogType::WARNING_LOG);
568 }
569 }
570 // read vertex (and for some non-standard obj files, also vertex
571 // color)
572 if (header == "v") {
573 detail::readObjVertex(
574 m, token, loadedInfo, tokens, currentMaterial, settings);
575 }
576 // read vertex normal (and save in vn how many normals we read)
577 if constexpr (HasPerVertexNormal<MeshType>) {
578 if (header == "vn") {
579 detail::readObjVertexNormal(
580 m, mapNormalsCache, vn, token, loadedInfo, settings);
581 vn++;
582 }
583 }
584 // read texcoords and save them in the vector of texcoords, we will
585 // store them in the mesh later
586 if constexpr (
587 HasPerVertexTexCoord<MeshType> ||
588 HasPerFaceWedgeTexCoords<MeshType>) {
589 if (header == "vt") {
590 // save the texcoord for later
591 TexCoordIndexedd tf;
592 for (uint i = 0; i < 2; ++i) {
593 tf[i] = io::readDouble<double>(token);
594 }
595 if (currentMaterial.hasTexture) {
596 tf.index() = currentMaterial.mapId;
597 }
598 texCoords.push_back(tf);
599 }
600 }
601 // read faces and manage:
602 // - color
603 // - eventual texcoords
604 // - possibility to split polygonal face into several triangles
605 if constexpr (HasFaces<MeshType>) {
606 if (header == "f") {
607 detail::readObjFace(
608 m,
609 loadedInfo,
610 tokens,
611 texCoords,
612 currentMaterial,
613 settings);
614 }
615 }
616 // read edges and manage their color
617 if constexpr (HasEdges<MeshType>) {
618 if (header == "l") {
619 detail::readObjEdge(
620 m, loadedInfo, tokens, currentMaterial, settings);
621 }
622 }
623 log.progress(inputObjStream.tellg());
624 }
625 } while (inputObjStream);
626
627 if constexpr (HasPerVertexNormal<MeshType>) {
628 // set all vertex normals that have not been stored in vertices
629 for (const auto& p : mapNormalsCache) {
630 if (p.first < m.vertexNumber()) {
631 m.vertex(p.first).normal() = p.second;
632 }
633 }
634 }
635 if constexpr (HasPerVertexTexCoord<MeshType>) {
636 using VertexType = MeshType::VertexType;
637 if (!loadedInfo.hasFaceWedgeTexCoords()) {
638 // we can set the loaded texCoords to vertices, also if they are not
639 // supported in obj
640 if (texCoords.size() == m.vertexNumber()) {
641 if (settings.enableOptionalComponents) {
642 enableIfPerVertexTexCoordOptional(m);
643 loadedInfo.setVertexTexCoords();
644 }
645 else {
646 if (isPerVertexTexCoordAvailable(m))
647 loadedInfo.setVertexTexCoords();
648 }
649 if (loadedInfo.hasVertexTexCoords()) {
650 uint i = 0;
651 for (VertexType& v : m.vertices()) {
652 v.texCoord() =
653 texCoords[i++]
654 .cast<typename VertexType::TexCoordType::
655 ScalarType>();
656 }
657 }
658 }
659 }
660 }
661
662 if constexpr (HasTextureImages<MeshType>) {
663 if (settings.loadTextureImages) {
664 for (Texture& texture : m.textures()) {
665 bool b =
666 texture.image().load(m.meshBasePath() + texture.path());
667 if (!b) {
668 log.log(
669 "Cannot load texture " + texture.path(),
670 LogType::WARNING_LOG);
671 }
672 }
673 }
674 }
675
676 log.endProgress();
677}
678
679} // namespace detail
680
709template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
711 MeshType& m,
712 std::istream& inputObjStream,
713 const std::vector<std::istream*>& inputMtlStreams,
715 LogType& log = nullLogger,
716 const LoadSettings& settings = LoadSettings())
717{
718 detail::loadObj(
719 m,
723 "",
724 true,
725 log,
726 settings);
727}
728
749template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
751 MeshType& m,
752 std::istream& inputObjStream,
753 const std::vector<std::istream*>& inputMtlStreams,
754 LogType& log = nullLogger,
755 const LoadSettings& settings = LoadSettings())
756{
758 detail::loadObj(
759 m,
763 "",
764 true,
765 log,
766 settings);
767}
768
797template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
798MeshType loadObj(
799 std::istream& inputObjStream,
800 const std::vector<std::istream*>& inputMtlStreams,
802 LogType& log = nullLogger,
803 const LoadSettings& settings = LoadSettings())
804{
805 MeshType m;
807 return m;
808}
809
833template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
834MeshType loadObj(
835 std::istream& inputObjStream,
836 const std::vector<std::istream*>& inputMtlStreams,
837 LogType& log = nullLogger,
838 const LoadSettings& settings = LoadSettings())
839{
840 MeshType m;
841 loadObj(m, inputObjStream, inputMtlStreams, log, settings);
842 return m;
843}
844
872template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
874 MeshType& m,
875 const std::string& filename,
877 LogType& log = nullLogger,
878 const LoadSettings& settings = LoadSettings())
879{
880 std::ifstream file = openInputFileStream(filename);
881
882 // some obj files do not declare the material file name with mtllib, but
883 // they assume that material file has the same name of the obj file.
884 // Therefore, we first load this file if it exists.
887 ".mtl";
888
889 std::ifstream f;
890 std::vector<std::istream*> mtlStreams;
891 try {
892 f = openInputFileStream(stdmtlfile);
893 mtlStreams.push_back(&f);
894 }
896 // nothing to do, this file was missing, but this was a fallback for
897 // some type of files...
898 }
899
900 detail::loadObj(
901 m, file, mtlStreams, loadedInfo, filename, false, log, settings);
902}
903
926template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
928 MeshType& m,
929 const std::string& filename,
930 LogType& log = nullLogger,
931 const LoadSettings& settings = LoadSettings())
932{
934 loadObj(m, filename, loadedInfo, log, settings);
935}
936
964template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
965MeshType loadObj(
966 const std::string& filename,
968 LogType& log = nullLogger,
969 const LoadSettings& settings = LoadSettings())
970{
971 MeshType m;
972 loadObj(m, filename, loadedInfo, log, settings);
973 return m;
974}
975
998template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
999MeshType loadObj(
1000 const std::string& filename,
1001 LogType& log = nullLogger,
1002 const LoadSettings& settings = LoadSettings())
1003{
1005 return loadObj<MeshType>(filename, loadedInfo, log, settings);
1006}
1007
1008} // namespace vcl
1009
1010#endif // VCL_LOAD_SAVE_OBJ_LOAD_H
Exception thrown when the file cannot be opened.
Definition io.h:58
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:217
static std::string pathWithoutFileName(const std::string &fullpath)
Get the path of a file.
Definition file_info.h:197
A simple class that allows to store which elements and their components have been imported/loaded or ...
Definition mesh_info.h:76
A class representing a line segment in n-dimensional space. The class is parameterized by a PointConc...
Definition segment.h:43
bool isPerEdgeColorAvailable(const MeshType &m)
Returns true if the Color component is available (enabled) in the Edge element of the input mesh m.
Definition edge_requirements.h:214
bool enableIfPerEdgeColorOptional(MeshType &m)
If the input mesh has a EdgeContainer, and the Edge Element has a Color Component,...
Definition edge_requirements.h:238
bool enableIfPerFaceWedgeTexCoordsOptional(MeshType &m)
If the input mesh has a FaceContainer, and the Face Element has a WedgeTexCoords Component,...
Definition face_requirements.h:597
bool isPerFaceWedgeTexCoordsAvailable(const MeshType &m)
Returns true if the WedgeTexCoords component is available (enabled) in the Face element of the input ...
Definition face_requirements.h:571
bool isPerFaceColorAvailable(const MeshType &m)
Returns true if the Color component is available (enabled) in the Face element of the input mesh m.
Definition face_requirements.h:214
bool enableIfPerFaceColorOptional(MeshType &m)
If the input mesh has a FaceContainer, and the Face Element has a Color Component,...
Definition face_requirements.h:238
void loadObj(MeshType &m, std::istream &inputObjStream, const std::vector< std::istream * > &inputMtlStreams, MeshInfo &loadedInfo, LogType &log=nullLogger, const LoadSettings &settings=LoadSettings())
Loads from the given input obj stream and puts the content into the mesh m.
Definition load.h:710
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:125
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:60
The LoadSettings structure contains the settings that can be used to load a mesh from a stream/file.
Definition settings.h:35