Visual Computing Library  devel
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_IO_MESH_STL_LOAD_H
24#define VCL_IO_MESH_STL_LOAD_H
25
26#include <vclib/io/mesh/settings.h>
27#include <vclib/io/read.h>
28
29#include <vclib/space/complex.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 v positions
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 const LoadSettings& settings,
102 LogType& log)
103{
104 bool magicsMode, colored;
105 colored = isStlColored(fp, magicsMode);
106
107 if (settings.enableOptionalComponents) {
108 if (colored)
109 loadedInfo.setPerFaceColor();
110 enableOptionalComponentsFromInfo(loadedInfo, m);
111 }
112 else if (colored) {
113 if constexpr (HasPerFaceColor<MeshType>) {
115 loadedInfo.setPerFaceColor();
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).position().x() =
140 io::readFloat<float>(fp, std::endian::little);
141 m.vertex(vi + j).position().y() =
142 io::readFloat<float>(fp, std::endian::little);
143 m.vertex(vi + j).position().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 const LoadSettings& settings,
193 LogType& log)
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).position().x() =
232 io::readFloat<float>(token, std::endian::little);
233 m.vertex(vi + i).position().y() =
234 io::readFloat<float>(token, std::endian::little);
235 m.vertex(vi + i).position().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 const LoadSettings& settings = LoadSettings(),
316 LogType& log = nullLogger)
317{
318 loadedInfo.clear();
319 loadedInfo.setVertices();
320 loadedInfo.setPerVertexPosition();
321
322 if constexpr (HasFaces<MeshType>) {
323 loadedInfo.setFaces();
324 loadedInfo.setPerFaceVertexReferences();
325 loadedInfo.setPerFaceNormal();
326 }
327
328 log.log(0, "Loading STL file");
329
330 if (isBinary)
331 detail::readStlBin(m, inputStlStream, loadedInfo, settings, log);
332 else
333 detail::readStlAscii(m, inputStlStream, loadedInfo, settings, log);
334
335 log.log(100, "STL file loaded");
336}
337
362template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
364 MeshType& m,
365 const std::string& filename,
367 const LoadSettings& settings = LoadSettings(),
368 LogType& log = nullLogger)
369{
370 log.log(0, "Checking STL file");
371
372 bool isBinary;
373 std::size_t filesize;
374 if (detail::isBinStlMalformed(filename, isBinary, filesize))
375 throw MalformedFileException(filename + " is malformed.");
376
377 log.log(0, "Opening STL file");
378
379 std::ifstream fp = openInputFileStream(filename);
380
381 if constexpr (HasName<MeshType>) {
383 }
384
385 loadStl(m, fp, loadedInfo, isBinary, settings, log);
386}
387
388} // namespace vcl
389
390#endif // VCL_IO_MESH_STL_LOAD_H
A class representing a box in N-dimensional space.
Definition box.h:46
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:220
Exception thrown when the file is malformed.
Definition exceptions.h:76
A simple class that allows to store which elements and their components have been imported/loaded or ...
Definition mesh_info.h:76
HasFaces concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition face_container.h:1389
Concept that checks if a Mesh has the Name component.
Definition mesh_requirements.h:88
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
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:592
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:476
void loadStl(MeshType &m, std::istream &inputStlStream, MeshInfo &loadedInfo, bool isBinary=false, const LoadSettings &settings=LoadSettings(), LogType &log=nullLogger)
Loads from the given input stl stream and puts the content into the mesh m.
Definition load.h:310
Point3< float > Point3f
A convenience alias for a 3-dimensional Point with floating-point components.
Definition point.h:767
The LoadSettings structure contains the settings that can be used to load a mesh from a stream/file.
Definition settings.h:35