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_STL_LOAD_H
24#define VCL_LOAD_SAVE_STL_LOAD_H
25
26#include <vclib/io/read.h>
27#include <vclib/load_save/settings.h>
28#include <vclib/misc/logger.h>
29#include <vclib/space/complex/mesh_info.h>
30
31namespace vcl {
32
33namespace detail {
34
35inline bool isBinStlMalformed(
36 const std::string& filename,
37 bool& isBinary,
38 std::size_t& fsize)
39{
40 fsize = FileInfo::fileSize(filename);
41 isBinary = FileInfo::isFileBinary(filename);
42
43 if (isBinary) {
44 // we can check if the size of the file is the expected one
45 std::ifstream fp = openInputFileStream(filename);
46 fp.seekg(80); // size of the header
47 uint fnum = io::readUInt<uint>(fp, std::endian::little);
48 std::size_t expectedFileSize =
49 80 + 4 + // header and number of faces
50 fnum * // for each face
51 (3 * sizeof(float) + // 3 floats for the face normal
52 3 * 3 * sizeof(float) + // 3 floats for each vertex of the face
53 sizeof(unsigned short)); // a short containing attributes
54 if (expectedFileSize != fsize) {
55 // sometimes the size is a bit wrong
56 std::size_t diff =
57 std::abs((long int) expectedFileSize - (long int) fsize);
58 if (diff > fsize / 20)
59 return true;
60 }
61 }
62
63 return false;
64}
65
66inline bool isStlColored(std::istream& fp, bool& magicsMode)
67{
68 bool colored = false;
69
70 char buf[80];
71 fp.read(buf, 80);
72 std::string s(buf);
73 size_t cInd = s.rfind("COLOR=");
74 size_t mInd = s.rfind("MATERIAL=");
75 if (cInd != std::string::npos && mInd != std::string::npos)
76 magicsMode = true;
77 else
78 magicsMode = false;
79 uint fnum = io::readUInt<uint>(fp, std::endian::little);
80 static const uint fmax = 1000;
81 // 3 floats for normal and 9 for vcoords
82 static const uint fdataSize = 12 * sizeof(float);
83
84 for (uint i = 0; i < std::min(fnum, fmax); ++i) {
85 fp.seekg(fdataSize, std::ios::cur);
86 unsigned short attr =
87 io::readShort<unsigned short>(fp, std::endian::little);
88 Color c;
89 c.setBgr5(attr);
90 if (c != Color::White)
91 colored = true;
92 }
93 return colored;
94}
95
96template<MeshConcept MeshType, LoggerConcept LogType>
97void readStlBin(
98 MeshType& m,
99 std::istream& fp,
100 MeshInfo& loadedInfo,
101 LogType& log,
102 const LoadSettings& settings)
103{
104 bool magicsMode, colored;
105 colored = isStlColored(fp, magicsMode);
106
107 if (settings.enableOptionalComponents) {
108 if (colored)
109 loadedInfo.setFaceColors();
110 enableOptionalComponentsFromInfo(loadedInfo, m);
111 }
112 else if (colored) {
113 if constexpr (HasPerFaceColor<MeshType>) {
115 loadedInfo.setFaceColors();
116 }
117 }
118
119 fp.seekg(80); // size of the header
120 uint fnum = io::readUInt<uint>(fp, std::endian::little);
121 if (fnum > 0)
122 loadedInfo.setTriangleMesh();
123
124 log.startProgress("Loading STL file", fnum);
125
126 m.addVertices(fnum * 3);
127 if constexpr (HasFaces<MeshType>) {
128 m.reserveFaces(fnum);
129 }
130
131 uint vi = 0;
132 for (uint i = 0; i < fnum; ++i) {
133 Point3f norm;
134 norm.x() = io::readFloat<float>(fp, std::endian::little);
135 norm.y() = io::readFloat<float>(fp, std::endian::little);
136 norm.z() = io::readFloat<float>(fp, std::endian::little);
137
138 for (uint j = 0; j < 3; ++j) {
139 m.vertex(vi + j).coord().x() =
140 io::readFloat<float>(fp, std::endian::little);
141 m.vertex(vi + j).coord().y() =
142 io::readFloat<float>(fp, std::endian::little);
143 m.vertex(vi + j).coord().z() =
144 io::readFloat<float>(fp, std::endian::little);
145 }
146
147 unsigned short attr =
148 io::readShort<unsigned short>(fp, std::endian::little);
149
150 if constexpr (HasFaces<MeshType>) {
151 using FaceType = MeshType::FaceType;
152
153 uint fi = m.addFace();
154 FaceType& f = m.face(fi);
155 // we have a polygonal mesh
156 if constexpr (FaceType::VERTEX_NUMBER < 0) {
157 // need to resize the face to the right number of verts
158 f.resizeVertices(3);
159 }
160 for (uint j = 0; j < 3; ++j)
161 f.setVertex(j, vi + j);
162 if (HasPerFaceNormal<MeshType>) {
163 using ST = FaceType::NormalType::ScalarType;
165 f.normal() = norm.cast<ST>();
166 }
167 }
168 if (HasPerFaceColor<MeshType>) {
169 if (isPerFaceColorAvailable(m) && colored) {
170 Color c;
171 if (magicsMode)
172 c.setBgr5(attr);
173 else
174 c.setRgb5(attr);
175 f.color() = c;
176 }
177 }
178 }
179
180 vi += 3;
181
182 log.progress(i);
183 }
184 log.endProgress();
185}
186
187template<MeshConcept MeshType, LoggerConcept LogType>
188void readStlAscii(
189 MeshType& m,
190 std::istream& fp,
191 MeshInfo& loadedInfo,
192 LogType& log,
193 const LoadSettings& settings)
194{
195 if (settings.enableOptionalComponents) {
196 enableOptionalComponentsFromInfo(loadedInfo, m);
197 }
198
199 fp.seekg(0, fp.end);
200 std::size_t fsize = fp.tellg();
201 fp.seekg(0, fp.beg);
202 log.startProgress("Loading STL file", fsize);
203
204 Tokenizer tokens = readAndTokenizeNextNonEmptyLineNoThrow(fp);
205 if (fp) {
206 // cycle that reads a face starting from the actual tokenized line
207 do {
208 Tokenizer::iterator token = tokens.begin();
209 if (token != tokens.end() && *token == "facet") {
210 ++token; // skip the "facet" word
211 ++token; // skip the "normal" word
212
213 // add 3 vertices for the face
214 uint vi = m.addVertices(3);
215
216 // read the normal of the face
217 Point3f normal;
218
219 normal.x() = io::readFloat<float>(token, std::endian::little);
220 normal.y() = io::readFloat<float>(token, std::endian::little);
221 normal.z() = io::readFloat<float>(token, std::endian::little);
222
223 readAndTokenizeNextNonEmptyLine(fp); // outer loop
224 // vertex x y z
225 tokens = readAndTokenizeNextNonEmptyLine(fp);
226
227 for (uint i = 0; i < 3; i++) { // read the three vertices
228 token = tokens.begin();
229 ++token; // skip the "vertex" word
230
231 m.vertex(vi + i).coord().x() =
232 io::readFloat<float>(token, std::endian::little);
233 m.vertex(vi + i).coord().y() =
234 io::readFloat<float>(token, std::endian::little);
235 m.vertex(vi + i).coord().z() =
236 io::readFloat<float>(token, std::endian::little);
237
238 // next vertex
239 tokens = readAndTokenizeNextNonEmptyLine(fp);
240 }
241 readAndTokenizeNextNonEmptyLine(fp); // endfacet
242
243 if constexpr (HasFaces<MeshType>) {
244 using FaceType = MeshType::FaceType;
245 uint fi = m.addFace();
246
247 FaceType& f = m.face(fi);
248 // we have a polygonal mesh
249 if constexpr (FaceType::VERTEX_NUMBER < 0) {
250 // need to resize the face to the right number of verts
251 f.resizeVertices(3);
252 }
253 for (uint j = 0; j < 3; ++j)
254 f.setVertex(j, vi + j);
255 if (HasPerFaceNormal<MeshType>) {
256 using ST = FaceType::NormalType::ScalarType;
258 f.normal() = normal.cast<ST>();
259 }
260 }
261 }
262 }
263 tokens = readAndTokenizeNextNonEmptyLineNoThrow(fp);
264
265 log.progress(fp.tellg());
266 } while (fp);
267
268 if constexpr (HasFaces<MeshType>) {
269 if (m.faceNumber() > 0)
270 loadedInfo.setTriangleMesh();
271 }
272 }
273
274 log.endProgress();
275}
276
277} // namespace detail
278
309template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
311 MeshType& m,
312 std::istream& inputStlStream,
314 bool isBinary = false,
315 LogType& log = nullLogger,
316 const LoadSettings& settings = LoadSettings())
317{
318 loadedInfo.clear();
319 loadedInfo.setVertices();
320 loadedInfo.setVertexCoords();
321
322 if constexpr (HasFaces<MeshType>) {
323 loadedInfo.setFaces();
324 loadedInfo.setFaceVRefs();
325 loadedInfo.setFaceNormals();
326 }
327
328 log.log(0, "Loading STL file");
329
330 if (isBinary)
331 detail::readStlBin(m, inputStlStream, loadedInfo, log, settings);
332 else
333 detail::readStlAscii(m, inputStlStream, loadedInfo, log, settings);
334
335 log.log(100, "STL file loaded");
336}
337
363template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
365 MeshType& m,
366 std::istream& inputStlStream,
367 bool isBinary = false,
368 LogType& log = nullLogger,
369 const LoadSettings& settings = LoadSettings())
370{
372 loadStl(m, inputStlStream, loadedInfo, isBinary, log, settings);
373}
374
405template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
406MeshType loadStl(
407 std::istream& inputStlStream,
409 bool isBinary = false,
410 LogType& log = nullLogger,
411 const LoadSettings& settings = LoadSettings())
412{
413 MeshType m;
414 loadStl(m, inputStlStream, loadedInfo, isBinary, log, settings);
415 return m;
416}
417
443template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
444MeshType loadStl(
445 std::istream& inputStlStream,
446 bool isBinary = false,
447 LogType& log = nullLogger,
448 const LoadSettings& settings = LoadSettings())
449{
451 return loadStl<MeshType>(
452 inputStlStream, loadedInfo, isBinary, log, settings);
453}
454
479template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
481 MeshType& m,
482 const std::string& filename,
484 LogType& log = nullLogger,
485 const LoadSettings& settings = LoadSettings())
486{
487 log.log(0, "Checking STL file");
488
489 bool isBinary;
490 std::size_t filesize;
491 if (detail::isBinStlMalformed(filename, isBinary, filesize))
492 throw MalformedFileException(filename + " is malformed.");
493
494 log.log(0, "Opening STL file");
495
496 std::ifstream fp = openInputFileStream(filename);
497
498 if constexpr (HasName<MeshType>) {
500 }
501
502 loadStl(m, fp, loadedInfo, isBinary, log, settings);
503}
504
524template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
526 MeshType& m,
527 const std::string& filename,
528 LogType& log = nullLogger,
529 const LoadSettings& settings = LoadSettings())
530{
532 loadStl(m, filename, loadedInfo, log, settings);
533}
534
559template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
560MeshType loadStl(
561 const std::string& filename,
563 LogType& log = nullLogger,
564 const LoadSettings& settings = LoadSettings())
565{
566 MeshType m;
567 loadStl(m, filename, loadedInfo, log, settings);
568 return m;
569}
570
590template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
591MeshType loadStl(
592 const std::string& filename,
593 LogType& log = nullLogger,
594 const LoadSettings& settings = LoadSettings())
595{
597 return loadStl<MeshType>(filename, loadedInfo, log, settings);
598}
599
600} // namespace vcl
601
602#endif // VCL_LOAD_SAVE_STL_LOAD_H
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:217
Exception thrown when the file is malformed.
Definition io.h:76
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
HasFaces concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition face_container.h:133
Concept that checks if a Mesh has the Name component.
Definition per_mesh.h:95
bool isPerFaceNormalAvailable(const MeshType &m)
Returns true if the Normal component is available (enabled) in the Face element of the input mesh m.
Definition face_requirements.h:330
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
void loadStl(MeshType &m, std::istream &inputStlStream, MeshInfo &loadedInfo, bool isBinary=false, LogType &log=nullLogger, const LoadSettings &settings=LoadSettings())
Loads from the given input stl stream and puts the content into the mesh m.
Definition load.h:310
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
Point3< float > Point3f
A convenience alias for a 3-dimensional Point with floating-point components.
Definition point.h:792
The LoadSettings structure contains the settings that can be used to load a mesh from a stream/file.
Definition settings.h:35