Visual Computing Library  devel
Loading...
Searching...
No Matches
load.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2026 *
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_IO_MESH_OBJ_LOAD_H
24#define VCL_IO_MESH_OBJ_LOAD_H
25
26#include "material.h"
27
28#include <vclib/io/file_info.h>
29#include <vclib/io/image/load.h>
30#include <vclib/io/mesh/settings.h>
31#include <vclib/io/read.h>
32
33#include <vclib/algorithms/mesh.h>
34#include <vclib/space/complex.h>
35#include <vclib/space/core.h>
36
37#include <algorithm>
38#include <map>
39
40namespace vcl {
41
42namespace detail {
43
44inline void loadObjMaterials(
45 std::map<std::string, ObjMaterial>& materialMap,
46 std::istream& stream)
47{
48 std::string matName;
49 ObjMaterial mat;
50
51 auto manageMaterialArgsFn = [&](auto& token) {
52 while ((*token)[0] == '-') {
53 if (*token == "-o" || *token == "-s" || *token == "-t") {
54 // ignore the argument and the three values
55 ++token;
56 ++token;
57 ++token;
58 ++token;
59 }
60 if (*token == "-mm") {
61 // ignore the argument and the two values
62 ++token;
63 ++token;
64 ++token;
65 }
66 if (*token == "-blendu" || *token == "-blendv" || *token == "-cc" ||
67 *token == "-clamp" || *token == "-texres") {
68 // ignore the argument and the value
69 ++token;
70 ++token;
71 }
72 }
73 };
74
75 do {
76 Tokenizer tokens = readAndTokenizeNextNonEmptyLineNoThrow(stream);
77 if (stream) {
78 // counter for texture images, used when mesh has no texture files
79 uint nt = 0;
80 Tokenizer::iterator token = tokens.begin();
81 std::string header = *token++;
82 if (header == "newmtl") {
83 if (!matName.empty())
84 materialMap[matName] = mat;
85 mat = ObjMaterial();
86 matName = *token;
87 mat.matName = matName;
88 }
89 if (header == "Ka") {
90 if (tokens.size() >= 4) {
91 if (*token != "spectral" && *token != "xyz") {
92 mat.Ka.x() = io::readFloat<float>(token);
93 mat.Ka.y() = io::readFloat<float>(token);
94 mat.Ka.z() = io::readFloat<float>(token);
95 }
96 }
97 }
98 if (header == "Kd") {
99 if (tokens.size() >= 4) {
100 if (*token != "spectral" && *token != "xyz") {
101 mat.Kd.x() = io::readFloat<float>(token);
102 mat.Kd.y() = io::readFloat<float>(token);
103 mat.Kd.z() = io::readFloat<float>(token);
104 }
105 }
106 }
107 if (header == "Ks") {
108 if (tokens.size() >= 4) {
109 if (*token != "spectral" && *token != "xyz") {
110 mat.Ks.x() = io::readFloat<float>(token);
111 mat.Ks.y() = io::readFloat<float>(token);
112 mat.Ks.z() = io::readFloat<float>(token);
113 }
114 }
115 }
116 if (header == "Ke") {
117 if (tokens.size() >= 4) {
118 if (*token != "spectral" && *token != "xyz") {
119 mat.Ke.x() = io::readFloat<float>(token);
120 mat.Ke.y() = io::readFloat<float>(token);
121 mat.Ke.z() = io::readFloat<float>(token);
122 }
123 }
124 }
125 if (header == "d") {
126 if ((*token)[0] == '-')
127 token++;
128 mat.d = io::readFloat<float>(token);
129 }
130 if (header == "Tr") {
131 if ((*token)[0] == '-')
132 token++;
133 mat.d = 1 - io::readFloat<float>(token);
134 }
135 if (header == "Ns") {
136 mat.Ns = io::readFloat<float>(token);
137 }
138 if (header == "illum") {
139 mat.illum = io::readFloat<int>(token);
140 }
141 if (header == "map_Kd") {
142 // need to manage args
143 manageMaterialArgsFn(token);
144 mat.map_Kd = *token;
145 // replace backslashes with slashes - windows compatibility
146 std::ranges::replace(mat.map_Kd, '\\', '/');
147 }
148 if (header == "map_Ke") {
149 // need to manage args
150 manageMaterialArgsFn(token);
151 mat.map_Ke = *token;
152 // replace backslashes with slashes - windows compatibility
153 std::ranges::replace(mat.map_Ke, '\\', '/');
154 }
155 if (header == "map_bump" || header == "bump") {
156 // need to manage args
157 manageMaterialArgsFn(token);
158 mat.map_bump = *token;
159 // replace backslashes with slashes - windows compatibility
160 std::ranges::replace(mat.map_bump, '\\', '/');
161 }
162 }
163 } while (stream);
164 if (!matName.empty())
165 materialMap[matName] = mat;
166}
167
168template<MeshConcept MeshType>
169void loadObjMaterials(
170 std::map<std::string, ObjMaterial>& materialMap,
171 MeshType& mesh,
172 std::istream& stream,
173 MeshInfo& loadedInfo,
174 const LoadSettings& settings)
175{
176 loadObjMaterials(materialMap, stream);
177
178 for (auto& [matName, mat] : materialMap) {
179 if constexpr (HasMaterials<MeshType>) {
180 loadedInfo.setMaterials();
181 Material m = mat.toMaterial();
182 mat.matId = mesh.materialCount();
183 mesh.pushMaterial(m);
184 }
185 }
186}
187
188template<MeshConcept MeshType>
189void loadObjMaterials(
190 std::map<std::string, ObjMaterial>& materialMap,
191 MeshType& mesh,
192 const std::string& mtllib,
193 MeshInfo& loadedInfo,
194 const LoadSettings& settings)
195{
196 std::ifstream file = openInputFileStream(mtllib);
197 loadObjMaterials(materialMap, mesh, file, loadedInfo, settings);
198}
199
200template<MeshConcept MeshType>
201void readObjVertex(
202 MeshType& m,
203 Tokenizer::iterator& token,
204 MeshInfo& loadedInfo,
205 const Tokenizer& tokens,
206 const LoadSettings& settings)
207{
208 uint vid = m.addVertex();
209 for (uint i = 0; i < 3; ++i) {
210 m.vertex(vid).position()[i] = io::readDouble<double>(token);
211 }
212 if constexpr (HasPerVertexColor<MeshType>) {
213 if (vid == 0) {
214 // if the file stores the vertex color in the non-standard way
215 // (color values after the positions)
216 if (tokens.size() > 6) {
217 if (settings.enableOptionalComponents) {
218 enableIfPerVertexColorOptional(m);
219 loadedInfo.setPerVertexColor();
220 }
221 else {
222 if (isPerVertexColorAvailable(m))
223 loadedInfo.setPerVertexColor();
224 }
225 }
226 }
227 if (loadedInfo.hasPerVertexColor()) {
228 // the file has the nonstandard way to store vertex colors, after
229 // the positions...
230 if (tokens.size() > 6) {
231 m.vertex(vid).color().setRedF(io::readFloat<float>(token));
232 m.vertex(vid).color().setGreenF(io::readFloat<float>(token));
233 m.vertex(vid).color().setBlueF(io::readFloat<float>(token));
234 }
235 }
236 }
237}
238
239template<FaceMeshConcept MeshType>
240void readObjFace(
241 MeshType& m,
242 MeshInfo& loadedInfo,
243 const Tokenizer& tokens,
244 const std::vector<Point3d>& normals,
245 const std::vector<TexCoordd>& wedgeTexCoords,
246 const ObjMaterial& currentMaterial,
247 const LoadSettings& settings)
248{
249 using FaceType = MeshType::FaceType;
250
251 std::vector<uint> vids;
252 std::vector<uint> wids;
253 std::vector<uint> nids;
254
255 loadedInfo.updateMeshType(tokens.size() - 1);
256
257 // actual read - load vertex indices and texcoords indices, if present
258 Tokenizer::iterator token = tokens.begin();
259 ++token;
260 vids.resize(tokens.size() - 1);
261 wids.reserve(tokens.size() - 1);
262 nids.reserve(tokens.size() - 1);
263 for (uint i = 0; i < tokens.size() - 1; ++i) {
264 Tokenizer subt(*token, '/', false);
265 auto t = subt.begin();
266 vids[i] = io::readUInt<uint>(t) - 1;
267 if (subt.size() > 1) {
268 if (!t->empty()) {
269 wids.push_back(io::readUInt<uint>(t) - 1);
270 }
271 if (subt.size() > 2) {
272 if (!t->empty()) {
273 nids.push_back(io::readUInt<uint>(t) - 1);
274 }
275 }
276 }
277 ++token;
278 }
279
280 // add the face
281 uint fid = m.addFace();
282 FaceType& f = m.face(fid);
283
284 // check if we need to split the face we read into triangles
285 bool splitFace = false;
286 // we have a polygonal mesh, no need to split
287 if constexpr (FaceType::VERTEX_COUNT < 0) {
288 // need to resize to the right number of verts
289 f.resizeVertices(tokens.size() - 1);
290 }
291 else if (FaceType::VERTEX_COUNT != tokens.size() - 1) {
292 // we have faces with static sizes (triangles), but we are loading faces
293 // with number of verts > 3. Need to split the face we are loading in n
294 // faces!
295 splitFace = true;
296 }
297
298 // create the face in the mesh, for now we manage only vertex indices
299 if (!splitFace) { // no need to split face case
300 for (uint i = 0; i < vids.size(); ++i) {
301 if (vids[i] >= m.vertexCount()) {
302 throw MalformedFileException(
303 "Bad vertex index for face " + std::to_string(fid));
304 }
305 f.setVertex(i, vids[i]);
306 }
307 }
308 else { // split needed
309 addTriangleFacesFromPolygon(m, f, vids);
310 }
311
312 // material
313 if constexpr (HasPerFaceMaterialIndex<MeshType>) {
314 if (fid == 0 && currentMaterial.matId != UINT_NULL) {
315 if (settings.enableOptionalComponents) {
317 loadedInfo.setPerFaceMaterialIndex();
318 }
319 else {
321 loadedInfo.setPerFaceMaterialIndex();
322 }
323 }
324 }
325 if (loadedInfo.hasPerFaceMaterialIndex()) {
326 if (!splitFace) {
327 f.materialIndex() = currentMaterial.matId;
328 }
329 else {
330 for (uint ff = fid; ff < m.faceCount(); ++ff) {
331 FaceType& f = m.face(ff);
332 f.materialIndex() = currentMaterial.matId;
333 }
334 }
335 }
336 }
337
338 // vertex normals
339 if constexpr (HasPerVertexNormal<MeshType>) {
340 using NormalType = typename MeshType::VertexType::NormalType;
341 using NST = typename NormalType::ScalarType;
342 // first, need to check if I can store per-vertex normals in the mesh
343 if (fid == 0) {
344 // if the current face has the right number of normals, we assume
345 // that we can load per-vertex normals
346 if (nids.size() == vids.size()) {
347 if (settings.enableOptionalComponents) {
348 enableIfPerVertexNormalOptional(m);
349 loadedInfo.setPerVertexNormal();
350 }
351 else {
352 if (isPerVertexNormalAvailable(m)) {
353 loadedInfo.setPerVertexNormal();
354 }
355 }
356 }
357 }
358 if (loadedInfo.hasPerVertexNormal()) {
359 if (nids.size() == vids.size()) {
360 for (uint i = 0; i < nids.size(); ++i) {
361 if (nids[i] >= normals.size()) {
362 throw MalformedFileException(
363 "Bad normal index for face " + std::to_string(fid));
364 }
365 m.vertex(vids[i]).normal() = normals[nids[i]].cast<NST>();
366 }
367 }
368 }
369 }
370
371 // wedge texcoords
372 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
373 using WedgeTCType = MeshType::FaceType::WedgeTexCoordType;
374 using WTCST = typename WedgeTCType::ScalarType;
375 // first, need to check if I can store wedge texcoords in the mesh
376 if (fid == 0) {
377 // if the current face has the right number of wedge texcoords, we
378 // assume that we can load wedge texcoords
379 if (wids.size() == vids.size()) {
380 if (settings.enableOptionalComponents) {
382 loadedInfo.setPerFaceWedgeTexCoords();
383 }
384 else {
386 loadedInfo.setPerFaceWedgeTexCoords();
387 }
388 }
389 }
390 }
391 if (loadedInfo.hasPerFaceWedgeTexCoords()) {
392 if (wids.size() == vids.size()) {
393 if (!splitFace) { // there wasn't a triangulation of the face
394 // it is safe to assign each wedge texcoord to its position
395 // in the face
396 for (uint i = 0; i < wids.size(); ++i) {
397 if (wids[i] >= wedgeTexCoords.size()) {
398 throw MalformedFileException(
399 "Bad texcoord index for face " +
400 std::to_string(fid));
401 }
402 f.wedgeTexCoord(i) =
403 wedgeTexCoords[wids[i]].cast<WTCST>();
404 }
405 }
406 else {
407 // take read texcoords and map them in the triangulated
408 // faces for each face of the triangulation of the polygon
409 for (uint ff = fid; ff < m.faceCount(); ++ff) {
410 FaceType& f = m.face(ff);
411 // for each vertex of the face
412 for (uint i = 0; i < f.vertexCount(); ++i) {
413 uint vid = m.index(f.vertex(i));
414 // find the position of the vertex in the vids array
415 auto it = std::find(vids.begin(), vids.end(), vid);
416 assert(it != vids.end());
417 uint pos = it - vids.begin();
418 // check that the texcoord id is valid
419 if (wids[pos] >= wedgeTexCoords.size()) {
420 throw MalformedFileException(
421 "Bad texcoord index for face " +
422 std::to_string(fid));
423 }
424 // set the wedge texcoord in the same position of
425 // the vertex
426 f.wedgeTexCoord(i) =
427 wedgeTexCoords[wids[pos]]
428 .cast<typename FaceType::WedgeTexCoordType::
429 ScalarType>();
430 }
431 }
432 }
433 }
434 }
435 }
436}
437
438template<EdgeMeshConcept MeshType>
439void readObjEdge(
440 MeshType& m,
441 MeshInfo& loadedInfo,
442 const Tokenizer& tokens,
443 const ObjMaterial& currentMaterial,
444 const LoadSettings& settings)
445{
446 using EdgeType = MeshType::EdgeType;
447
448 // add the edge
449 uint eid = m.addEdge();
450 EdgeType& e = m.edge(eid);
451
452 // actual read - load vertex indices
453 Tokenizer::iterator token = tokens.begin();
454 ++token;
455 uint vid1 = io::readUInt<uint>(token) - 1;
456 uint vid2 = io::readUInt<uint>(token) - 1;
457 e.setVertices(vid1, vid2);
458}
459
479template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
480void loadObj(
481 MeshType& m,
482 std::istream& inputObjStream,
483 const std::vector<std::istream*>& inputMtlStreams,
484 MeshInfo& loadedInfo,
485 const std::string& filename = "",
486 bool ignoreMtlLib = false,
487 const LoadSettings& settings = LoadSettings(),
488 LogType& log = nullLogger)
489{
490 loadedInfo.clear();
491
492 // save array of normals, that are stored later
493 std::vector<Point3d> normals;
494
495 // save array of texcoords, that are stored later (into wedges when loading
496 // faces or into vertices as a fallback)
497 std::vector<TexCoordd> texCoords;
498
499 // map of materials loaded
500 std::map<std::string, detail::ObjMaterial> materialMap;
501
502 // load materials from the material files, if any
503 for (auto* stream : inputMtlStreams) {
504 detail::loadObjMaterials(materialMap, m, *stream, loadedInfo, settings);
505 }
506
507 // the current material, set by 'usemtl'
508 detail::ObjMaterial currentMaterial;
509
510 if constexpr (HasMaterials<MeshType>) {
511 m.meshBasePath() = FileInfo::pathWithoutFileName(filename);
512 }
513
514 if constexpr (HasName<MeshType>) {
515 m.name() = FileInfo::fileNameWithoutExtension(filename);
516 }
517
518 inputObjStream.seekg(0, inputObjStream.end);
519 std::size_t fsize = inputObjStream.tellg();
520 inputObjStream.seekg(0, inputObjStream.beg);
521 log.startProgress("Loading OBJ file", fsize);
522
523 // cycle that reads line by line
524 do {
525 Tokenizer tokens =
526 readAndTokenizeNextNonEmptyLineNoThrow(inputObjStream);
527 if (inputObjStream) {
528 Tokenizer::iterator token = tokens.begin();
529 std::string header = *token++;
530 if (header == "mtllib" && !ignoreMtlLib) { // material file
531 // we load the material file if they are not ignored
532 std::string mtlfile =
533 FileInfo::pathWithoutFileName(filename) + *token;
534 try {
535 detail::loadObjMaterials(
536 materialMap, m, mtlfile, loadedInfo, settings);
537 }
538 catch (CannotOpenFileException) {
539 log.log(
540 "Cannot open material file " + mtlfile,
541 LogType::WARNING_LOG);
542 }
543 }
544 // use a new material - change currentMaterial
545 if (header == "usemtl") {
546 std::string matname = *token;
547 auto it = materialMap.find(matname);
548 if (it != materialMap.end()) {
549 currentMaterial = it->second;
550 }
551 else { // material not found - warning
552 log.log(
553 "Material " + matname + " not found.",
554 LogType::WARNING_LOG);
555 }
556 }
557 // read vertex (and for some non-standard obj files, also vertex
558 // color)
559 if (header == "v") {
560 loadedInfo.setVertices();
561 loadedInfo.setPerVertexPosition();
562 detail::readObjVertex(m, token, loadedInfo, tokens, settings);
563 }
564 // read normals and save them in the vector of normasl, we will
565 // store them in the mesh later
566 if (header == "vn") {
567 if constexpr (HasPerVertexNormal<MeshType>) {
568 Point3d n;
569 for (uint i = 0; i < 3; ++i) {
570 n[i] = io::readDouble<double>(token);
571 }
572 normals.push_back(n);
573 }
574 }
575 // read texcoords and save them in the vector of texcoords, we will
576 // store them in the mesh later
577 if (header == "vt") {
578 if constexpr (
579 HasPerVertexTexCoord<MeshType> ||
580 HasPerFaceWedgeTexCoords<MeshType>) {
581 // save the texcoord for later
582 TexCoordd tf;
583 for (uint i = 0; i < 2; ++i) {
584 tf[i] = io::readDouble<double>(token);
585 }
586 texCoords.push_back(tf);
587 }
588 }
589 // read faces and manage:
590 // - color
591 // - eventual texcoords
592 // - possibility to split polygonal face into several triangles
593 if (header == "f") {
594 loadedInfo.setFaces();
595 loadedInfo.setPerFaceVertexReferences();
596 if constexpr (HasFaces<MeshType>) {
597 detail::readObjFace(
598 m,
599 loadedInfo,
600 tokens,
601 normals,
602 texCoords,
603 currentMaterial,
604 settings);
605 }
606 }
607 // read edges and manage their color
608 if (header == "l") {
609 loadedInfo.setEdges();
610 loadedInfo.setPerEdgeVertexReferences();
611 if constexpr (HasEdges<MeshType>) {
612 detail::readObjEdge(
613 m, loadedInfo, tokens, currentMaterial, settings);
614 }
615 }
616 log.progress(inputObjStream.tellg());
617 }
618 } while (inputObjStream);
619
620 if constexpr (HasPerVertexNormal<MeshType>) {
621 using NormalType = typename MeshType::VertexType::NormalType;
622 using NST = typename NormalType::ScalarType;
623 // if we have not yet set per-vertex normals, try to set them now
624 if (!loadedInfo.hasPerVertexNormal()) {
625 if (normals.size() == m.vertexCount()) {
626 if (settings.enableOptionalComponents) {
627 enableIfPerVertexNormalOptional(m);
628 loadedInfo.setPerVertexNormal();
629 }
630 else {
631 if (isPerVertexNormalAvailable(m))
632 loadedInfo.setPerVertexNormal();
633 }
634
635 if (loadedInfo.hasPerVertexNormal()) {
636 for (uint i = 0; const auto& n : normals) {
637 m.vertex(i).normal() = n.template cast<NST>();
638 ++i;
639 }
640 }
641 }
642 }
643 }
644
645 if constexpr (HasPerVertexTexCoord<MeshType>) {
646 if (!loadedInfo.hasPerFaceWedgeTexCoords()) {
647 if (texCoords.size() == m.vertexCount()) {
648 // load texcoords into vertices only if there are no wedge
649 if (settings.enableOptionalComponents) {
650 enableIfPerVertexTexCoordOptional(m);
651 loadedInfo.setPerVertexTexCoord();
652 }
653 else {
654 if (isPerVertexTexCoordAvailable(m))
655 loadedInfo.setPerVertexTexCoord();
656 }
657 if (loadedInfo.hasPerVertexTexCoord()) {
658 using TexCoordType =
659 typename MeshType::VertexType::TexCoordType;
660 using TCT = typename TexCoordType::ScalarType;
661 for (uint i = 0; i < m.vertexCount(); ++i) {
662 m.vertex(i).texCoord() =
663 texCoords[i].template cast<TCT>();
664 }
665 }
666
667 // material index
668 if constexpr (HasMaterials<MeshType>) {
669 if (m.materialCount() > 0) {
670 if (settings.enableOptionalComponents) {
671 enableIfPerVertexMaterialIndexOptional(m);
672 loadedInfo.setPerVertexMaterialIndex();
673 }
674 else {
675 if (isPerVertexMaterialIndexAvailable(m))
676 loadedInfo.setPerVertexMaterialIndex();
677 }
678 if (loadedInfo.hasPerVertexMaterialIndex()) {
679 for (uint i = 0; i < m.vertexCount(); ++i) {
680 // assign the first material to all vertices
681 m.vertex(i).materialIndex() = 0;
682 }
683 }
684 }
685 }
686 }
687 }
688 }
689
690 if constexpr (HasMaterials<MeshType>) {
691 if (settings.loadTextureImages) {
692 using enum Material::TextureType;
693 loadTextureImages(m, m.meshBasePath(), {BASE_COLOR, EMISSIVE}, log);
694 }
695 }
696
697 log.endProgress();
698}
699
700} // namespace detail
701
730template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
732 MeshType& m,
733 std::istream& inputObjStream,
734 const std::vector<std::istream*>& inputMtlStreams,
736 const LoadSettings& settings = LoadSettings(),
737 LogType& log = nullLogger)
738{
739 detail::loadObj(
740 m,
744 "",
745 true,
746 settings,
747 log);
748}
749
777template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
779 MeshType& m,
780 const std::string& filename,
782 const LoadSettings& settings = LoadSettings(),
783 LogType& log = nullLogger)
784{
785 std::ifstream file = openInputFileStream(filename);
786
787 // some obj files do not declare the material file name with mtllib, but
788 // they assume that material file has the same name of the obj file.
789 // Therefore, we first load this file if it exists.
792 ".mtl";
793
794 std::ifstream f;
795 std::vector<std::istream*> mtlStreams;
796 try {
797 f = openInputFileStream(stdmtlfile);
798 mtlStreams.push_back(&f);
799 }
801 // nothing to do, this file was missing, but this was a fallback for
802 // some type of files...
803 }
804
805 detail::loadObj(
806 m, file, mtlStreams, loadedInfo, filename, false, settings, log);
807}
808
809} // namespace vcl
810
811#endif // VCL_IO_MESH_OBJ_LOAD_H
Exception thrown when the file cannot be opened.
Definition exceptions.h:58
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:220
static std::string pathWithoutFileName(const std::string &fullpath)
Get the path of a file.
Definition file_info.h:200
TextureType
Defines the types of textures used in the PBR material model.
Definition material.h:62
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:41
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:123
constexpr uint UINT_NULL
The UINT_NULL value represent a null value of uint that is the maximum value that can be represented ...
Definition base.h:49
TexCoord< double, ElementType, OPT > TexCoordd
Definition tex_coord.h:209
bool enableIfPerFaceWedgeTexCoordsOptional(MeshType &m)
If the input mesh has a FaceContainer, and the Face Element has a WedgeTexCoords Component,...
Definition face_requirements.h:936
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:910
bool isPerFaceMaterialIndexAvailable(const MeshType &m)
Returns true if the MaterialIndex component is available (enabled) in the Face element of the input m...
Definition face_requirements.h:608
bool enableIfPerFaceMaterialIndexOptional(MeshType &m)
If the input mesh has a FaceContainer, and the Face Element has a MaterialIndex Component,...
Definition face_requirements.h:633
void loadObj(MeshType &m, std::istream &inputObjStream, const std::vector< std::istream * > &inputMtlStreams, MeshInfo &loadedInfo, const LoadSettings &settings=LoadSettings(), LogType &log=nullLogger)
Loads from the given input obj stream and puts the content into the mesh m.
Definition load.h:731
Point3< double > Point3d
A convenience alias for a 3-dimensional Point with double-precision floating-point components.
Definition point.h:780
The LoadSettings structure contains the settings that can be used to load a mesh from a stream/file.
Definition settings.h:35