cgv
Loading...
Searching...
No Matches
stl_reader.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2018, Sebastian Reiter (s.b.reiter@gmail.com)
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*/
24
25
127#ifndef __H__STL_READER
128#define __H__STL_READER
129
130#include <algorithm>
131#include <exception>
132#include <fstream>
133#include <sstream>
134#include <vector>
135
136#ifdef STL_READER_NO_EXCEPTIONS
137 #define STL_READER_THROW(msg) return false;
138 #define STL_READER_COND_THROW(cond, msg) if(cond) return false;
139#else
141 #define STL_READER_THROW(msg) {std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
142
144 #define STL_READER_COND_THROW(cond, msg) if(cond){std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
145#endif
146
147
148namespace stl_reader {
149
151
191template <class TNumberContainer1, class TNumberContainer2,
192 class TIndexContainer1, class TIndexContainer2>
193bool ReadStlFile(const char* filename,
194 TNumberContainer1& coordsOut,
195 TNumberContainer2& normalsOut,
196 TIndexContainer1& trisOut,
197 TIndexContainer2& solidRangesOut);
198
199
201
204template <class TNumberContainer1, class TNumberContainer2,
205 class TIndexContainer1, class TIndexContainer2>
206bool ReadStlFile_ASCII(const char* filename,
207 TNumberContainer1& coordsOut,
208 TNumberContainer2& normalsOut,
209 TIndexContainer1& trisOut,
210 TIndexContainer2& solidRangesOut);
211
213
217template <class TNumberContainer1, class TNumberContainer2,
218 class TIndexContainer1, class TIndexContainer2>
219bool ReadStlFile_BINARY(const char* filename,
220 TNumberContainer1& coordsOut,
221 TNumberContainer2& normalsOut,
222 TIndexContainer1& trisOut,
223 TIndexContainer2& solidRangesOut);
224
226
230inline bool StlFileHasASCIIFormat(const char* filename);
231
232
234template <class TNumber = float, class TIndex = unsigned int>
235class StlMesh {
236public:
239 {
240 solids.resize (2, 0);
241 }
242
244
245 StlMesh (const char* filename)
246 {
247 read_file (filename);
248 }
249
250 StlMesh (const std::string& filename)
251 {
252 read_file (filename);
253 }
257
258 bool read_file (const char* filename)
259 {
260 bool res = false;
261
262 #ifndef STL_READER_NO_EXCEPTIONS
263 try {
264 #endif
265
266 res = ReadStlFile (filename, coords, normals, tris, solids);
267
268 #ifndef STL_READER_NO_EXCEPTIONS
269 } catch (std::exception& e) {
270 #else
271 if (!res) {
272 #endif
273
274 coords.clear ();
275 normals.clear ();
276 tris.clear ();
277 solids.clear ();
278 STL_READER_THROW (e.what());
279 }
280
281 return res;
282 }
283
284 bool read_file (const std::string& filename)
285 {
286 return read_file (filename.c_str());
287 }
291 size_t num_vrts () const
292 {
293 return coords.size() / 3;
294 }
295
297 const TNumber* vrt_coords (const size_t vi) const
298 {
299 return &coords[vi * 3];
300 }
301
303 size_t num_tris () const
304 {
305 return tris.size() / 3;
306 }
307
309 const TIndex* tri_corner_inds (const size_t ti) const
310 {
311 return &tris [ti * 3];
312 }
313
315 const TIndex tri_corner_ind (const size_t ti, const size_t ci) const
316 {
317 return tris [ti * 3 + ci];
318 }
319
327 const TNumber* tri_corner_coords (const size_t ti, const size_t ci) const
328 {
329 return &coords[tri_corner_ind(ti, ci) * 3];
330 }
331
333 const TNumber* tri_normal (const size_t ti) const
334 {
335 return &normals [ti * 3];
336 }
337
339
344 size_t num_solids () const
345 {
346 if(solids.empty ())
347 return 0;
348 return solids.size () - 1;
349 }
350
352 TIndex solid_tris_begin (const size_t si) const
353 {
354 return solids [si];
355 }
356
358 TIndex solid_tris_end (const size_t si) const
359 {
360 return solids [si + 1];
361 }
362
364
366 const TNumber* raw_coords () const
367 {
368 if(coords.empty())
369 return NULL;
370 return &coords[0];
371 }
372
374
376 const TNumber* raw_normals () const
377 {
378 if(normals.empty())
379 return NULL;
380 return &normals[0];
381 }
382
384
386 const TIndex* raw_tris () const
387 {
388 if(tris.empty())
389 return NULL;
390 return &tris[0];
391 }
392
394
396 const TIndex* raw_solids () const
397 {
398 if(solids.empty())
399 return NULL;
400 return &solids[0];
401 }
402
403private:
404 std::vector<TNumber> coords;
405 std::vector<TNumber> normals;
406 std::vector<TIndex> tris;
407 std::vector<TIndex> solids;
408};
409
410
412// IMPLEMENTATION
414
415
416namespace stl_reader_impl {
417
418 // a coordinate triple with an additional index. The index is required
419 // for RemoveDoubles, so that triangles can be reindexed properly.
420 template <typename number_t, typename index_t>
421 struct CoordWithIndex {
422 number_t data[3];
423 index_t index;
424
425 bool operator == (const CoordWithIndex& c) const
426 {
427 return (c[0] == data[0]) && (c[1] == data[1]) && (c[2] == data[2]);
428 }
429
430 bool operator != (const CoordWithIndex& c) const
431 {
432 return (c[0] != data[0]) || (c[1] != data[1]) || (c[2] != data[2]);
433 }
434
435 bool operator < (const CoordWithIndex& c) const
436 {
437 return (data[0] < c[0])
438 || (data[0] == c[0] && data[1] < c[1])
439 || (data[0] == c[0] && data[1] == c[1] && data[2] < c[2]);
440 }
441
442 inline number_t& operator [] (const size_t i) {return data[i];}
443 inline number_t operator [] (const size_t i) const {return data[i];}
444 };
445
446 // sorts the array coordsWithIndexInOut and copies unique indices to coordsOut.
447 // Triangle-corners are re-indexed on the fly and degenerated triangles are removed.
448 template <class TNumberContainer, class TIndexContainer>
449 void RemoveDoubles (TNumberContainer& uniqueCoordsOut,
450 TIndexContainer& trisInOut,
451 std::vector <CoordWithIndex<
452 typename TNumberContainer::value_type,
453 typename TIndexContainer::value_type> >
454 &coordsWithIndexInOut)
455 {
456 using namespace std;
457
458 typedef typename TNumberContainer::value_type number_t;
459 typedef typename TIndexContainer::value_type index_t;
460
461 sort (coordsWithIndexInOut.begin(), coordsWithIndexInOut.end());
462
463 // first count unique indices
464 index_t numUnique = 1;
465 for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
466 if(coordsWithIndexInOut[i] != coordsWithIndexInOut[i - 1])
467 ++numUnique;
468 }
469
470 uniqueCoordsOut.resize (numUnique * 3);
471 vector<index_t> newIndex (coordsWithIndexInOut.size());
472
473 // copy unique coordinates to 'uniqueCoordsOut' and create an index-map
474 // 'newIndex', which allows to re-index triangles later on.
475 index_t curInd = 0;
476 newIndex[0] = 0;
477 for(index_t i = 0; i < 3; ++i)
478 uniqueCoordsOut[i] = coordsWithIndexInOut[0][i];
479
480 for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
481 const CoordWithIndex <number_t, index_t> c = coordsWithIndexInOut[i];
482 if(c != coordsWithIndexInOut[i - 1]){
483 ++curInd;
484 for(index_t j = 0; j < 3; ++j)
485 uniqueCoordsOut[curInd * 3 + j] = coordsWithIndexInOut[i][j];
486 }
487
488 newIndex[c.index] = static_cast<index_t> (curInd);
489 }
490
491 // re-index triangles, so that they refer to 'uniqueCoordsOut'
492 // make sure to only add triangles which refer to three different indices
493 index_t numUniqueTriInds = 0;
494 for(index_t i = 0; i < trisInOut.size(); i+=3){
495 int ni[3];
496 for(int j = 0; j < 3; ++j)
497 ni[j] = newIndex[trisInOut[i+j]];
498
499 if((ni[0] != ni[1]) && (ni[0] != ni[2]) && (ni[1] != ni[2])){
500 for(int j = 0; j < 3; ++j)
501 trisInOut[numUniqueTriInds + j] = ni[j];
502 numUniqueTriInds += 3;
503 }
504 }
505
506 if(numUniqueTriInds < trisInOut.size())
507 trisInOut.resize (numUniqueTriInds);
508 }
509}// end of namespace stl_reader_impl
510
511
512template <class TNumberContainer1, class TNumberContainer2,
513 class TIndexContainer1, class TIndexContainer2>
514bool ReadStlFile(const char* filename,
515 TNumberContainer1& coordsOut,
516 TNumberContainer2& normalsOut,
517 TIndexContainer1& trisOut,
518 TIndexContainer2& solidRangesOut)
519{
520 if(StlFileHasASCIIFormat(filename))
521 return ReadStlFile_ASCII(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
522 else
523 return ReadStlFile_BINARY(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
524}
525
526
527template <class TNumberContainer1, class TNumberContainer2,
528 class TIndexContainer1, class TIndexContainer2>
529bool ReadStlFile_ASCII(const char* filename,
530 TNumberContainer1& coordsOut,
531 TNumberContainer2& normalsOut,
532 TIndexContainer1& trisOut,
533 TIndexContainer2& solidRangesOut)
534{
535 using namespace std;
536 using namespace stl_reader_impl;
537
538 typedef typename TNumberContainer1::value_type number_t;
539 typedef typename TIndexContainer1::value_type index_t;
540
541 coordsOut.clear();
542 normalsOut.clear();
543 trisOut.clear();
544 solidRangesOut.clear();
545
546 ifstream in(filename);
547 STL_READER_COND_THROW(!in, "Couldn't open file " << filename);
548
549 vector<CoordWithIndex <number_t, index_t> > coordsWithIndex;
550
551 string buffer;
552 vector<string> tokens;
553 int lineCount = 1;
554 int maxNumTokens = 0;
555 size_t numFaceVrts = 0;
556
557 while(!(in.eof() || in.fail()))
558 {
559 // read the line and tokenize.
560 // In order to reuse memory in between lines, 'tokens' won't be cleared.
561 // Instead we count the number of tokens using 'tokenCount'.
562 getline(in, buffer);
563
564 istringstream line(buffer);
565 int tokenCount = 0;
566 while(!(line.eof() || line.fail())){
567 if(tokenCount >= maxNumTokens){
568 maxNumTokens = tokenCount + 1;
569 tokens.resize(maxNumTokens);
570 }
571 line >> tokens[tokenCount];
572 ++tokenCount;
573 }
574
575 if(tokenCount > 0)
576 {
577 string& tok = tokens[0];
578 if(tok.compare("vertex") == 0){
579 if(tokenCount < 4){
580 STL_READER_THROW("ERROR while reading from " << filename <<
581 ": vertex not specified correctly in line " << lineCount);
582 }
583
584 // read the position
585 CoordWithIndex <number_t, index_t> c;
586 for(size_t i = 0; i < 3; ++i)
587 c[i] = static_cast<number_t> (atof(tokens[i+1].c_str()));
588 c.index = static_cast<index_t>(coordsWithIndex.size());
589 coordsWithIndex.push_back(c);
590 ++numFaceVrts;
591 }
592 else if(tok.compare("facet") == 0)
593 {
594 STL_READER_COND_THROW(tokenCount < 5,
595 "ERROR while reading from " << filename <<
596 ": triangle not specified correctly in line " << lineCount);
597
598 STL_READER_COND_THROW(tokens[1].compare("normal") != 0,
599 "ERROR while reading from " << filename <<
600 ": Missing normal specifier in line " << lineCount);
601
602 // read the normal
603 for(size_t i = 0; i < 3; ++i)
604 normalsOut.push_back (static_cast<number_t> (atof(tokens[i+2].c_str())));
605
606 numFaceVrts = 0;
607 }
608 else if(tok.compare("outer") == 0){
609 STL_READER_COND_THROW ((tokenCount < 2) || (tokens[1].compare("loop") != 0),
610 "ERROR while reading from " << filename <<
611 ": expecting outer loop in line " << lineCount);
612 }
613 else if(tok.compare("endfacet") == 0){
614 STL_READER_COND_THROW(numFaceVrts != 3,
615 "ERROR while reading from " << filename <<
616 ": bad number of vertices specified for face in line " << lineCount);
617
618 trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 3));
619 trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 2));
620 trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 1));
621 }
622 else if(tok.compare("solid") == 0){
623 solidRangesOut.push_back(static_cast<index_t> (trisOut.size() / 3));
624 }
625 }
626 lineCount++;
627 }
628
629 solidRangesOut.push_back(static_cast<index_t> (trisOut.size() / 3));
630
631 RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
632
633 return true;
634}
635
636
637template <class TNumberContainer1, class TNumberContainer2,
638 class TIndexContainer1, class TIndexContainer2>
639bool ReadStlFile_BINARY(const char* filename,
640 TNumberContainer1& coordsOut,
641 TNumberContainer2& normalsOut,
642 TIndexContainer1& trisOut,
643 TIndexContainer2& solidRangesOut)
644{
645 using namespace std;
646 using namespace stl_reader_impl;
647
648 typedef typename TNumberContainer1::value_type number_t;
649 typedef typename TIndexContainer1::value_type index_t;
650
651 coordsOut.clear();
652 normalsOut.clear();
653 trisOut.clear();
654 solidRangesOut.clear();
655
656 ifstream in(filename, ios::binary);
657 STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
658
659 char stl_header[80];
660 in.read(stl_header, 80);
661 STL_READER_COND_THROW(!in, "Error while parsing binary stl header in file " << filename);
662
663 unsigned int numTris = 0;
664 in.read((char*)&numTris, 4);
665 STL_READER_COND_THROW(!in, "Couldnt determine number of triangles in binary stl file " << filename);
666
667 vector<CoordWithIndex <number_t, index_t> > coordsWithIndex;
668
669 for(unsigned int tri = 0; tri < numTris; ++tri){
670 float d[12];
671 in.read((char*)d, 12 * 4);
672 STL_READER_COND_THROW(!in, "Error while parsing trianlge in binary stl file " << filename);
673
674 for(int i = 0; i < 3; ++i)
675 normalsOut.push_back (d[i]);
676
677 for(size_t ivrt = 1; ivrt < 4; ++ivrt){
678 CoordWithIndex <number_t, index_t> c;
679 for(size_t i = 0; i < 3; ++i)
680 c[i] = d[ivrt * 3 + i];
681 c.index = static_cast<index_t>(coordsWithIndex.size());
682 coordsWithIndex.push_back(c);
683 }
684
685 trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 3));
686 trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 2));
687 trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 1));
688
689 char addData[2];
690 in.read(addData, 2);
691 STL_READER_COND_THROW(!in, "Error while parsing additional triangle data in binary stl file " << filename);
692 }
693
694 solidRangesOut.push_back(0);
695 solidRangesOut.push_back(static_cast<index_t> (trisOut.size() / 3));
696
697 RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
698
699 return true;
700}
701
702
703inline bool StlFileHasASCIIFormat(const char* filename)
704{
705 using namespace std;
706 ifstream in(filename);
707 STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
708
709 char chars [256];
710 in.read (chars, 256);
711 string buffer (chars, in.gcount());
712 transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower);
713 return buffer.find ("solid") != string::npos &&
714 buffer.find ("\n") != string::npos &&
715 buffer.find ("facet") != string::npos &&
716 buffer.find ("normal") != string::npos;
717}
718
719} // end of namespace stl_reader
720
721#endif //__H__STL_READER
convenience mesh class which makes accessing the stl data more easy
Definition stl_reader.h:235
const TNumber * vrt_coords(const size_t vi) const
returns an array of 3 floating point values, one for each coordinate of the vertex
Definition stl_reader.h:297
size_t num_solids() const
returns the number of solids of the mesh
Definition stl_reader.h:344
const TNumber * tri_corner_coords(const size_t ti, const size_t ci) const
returns an array of 3 floating point values, one for each coordinate of the specified corner of the s...
Definition stl_reader.h:327
const TNumber * raw_coords() const
returns a pointer to the coordinate array, containing num_vrts()*3 entries.
Definition stl_reader.h:366
size_t num_tris() const
returns the number of triangles in the mesh
Definition stl_reader.h:303
const TNumber * raw_normals() const
returns a pointer to the normal array, containing num_tris()*3 entries.
Definition stl_reader.h:376
StlMesh(const char *filename)
initializes the mesh from the stl-file specified through filename
Definition stl_reader.h:245
TIndex solid_tris_begin(const size_t si) const
returns the index of the first triangle in the given solid
Definition stl_reader.h:352
const TIndex tri_corner_ind(const size_t ti, const size_t ci) const
returns the index of the corner with index 0<=ci<3 of triangle ti
Definition stl_reader.h:315
const TIndex * raw_tris() const
returns a pointer to the triangle array, containing num_tris()*3 entries.
Definition stl_reader.h:386
bool read_file(const char *filename)
fills the mesh with the contents of the specified stl-file
Definition stl_reader.h:258
const TIndex * raw_solids() const
returns a pointer to the solids array, containing num_solids()+1 entries.
Definition stl_reader.h:396
StlMesh()
initializes an empty mesh
Definition stl_reader.h:238
TIndex solid_tris_end(const size_t si) const
returns the index of the triangle behind the last triangle in the given solid
Definition stl_reader.h:358
size_t num_vrts() const
returns the number of vertices in the mesh
Definition stl_reader.h:291
const TIndex * tri_corner_inds(const size_t ti) const
returns an array of 3 indices, one for each corner vertex of the triangle
Definition stl_reader.h:309
const TNumber * tri_normal(const size_t ti) const
returns an array of 3 floating point values defining the normal of a tri
Definition stl_reader.h:333
bool ReadStlFile(const char *filename, TNumberContainer1 &coordsOut, TNumberContainer2 &normalsOut, TIndexContainer1 &trisOut, TIndexContainer2 &solidRangesOut)
Reads an ASCII or binary stl file into several arrays.
Definition stl_reader.h:514
bool ReadStlFile_ASCII(const char *filename, TNumberContainer1 &coordsOut, TNumberContainer2 &normalsOut, TIndexContainer1 &trisOut, TIndexContainer2 &solidRangesOut)
Reads an ASCII stl file into several arrays.
Definition stl_reader.h:529
bool StlFileHasASCIIFormat(const char *filename)
Determines whether a stl file has ASCII format.
Definition stl_reader.h:703
#define STL_READER_COND_THROW(cond, msg)
Throws an std::runtime_error with the given message, if the given condition evaluates to true.
Definition stl_reader.h:144
#define STL_READER_THROW(msg)
Throws an std::runtime_error with the given message.
Definition stl_reader.h:141
bool ReadStlFile_BINARY(const char *filename, TNumberContainer1 &coordsOut, TNumberContainer2 &normalsOut, TIndexContainer1 &trisOut, TIndexContainer2 &solidRangesOut)
Reads a binary stl file into several arrays.
Definition stl_reader.h:639