stl_reader.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /*
  2. Copyright (c) 2018, Sebastian Reiter (s.b.reiter@gmail.com)
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright
  7. notice, this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright
  9. notice, this list of conditions and the following disclaimer in the
  10. documentation and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  12. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  13. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  14. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  15. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  16. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  17. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  18. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  19. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  20. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  21. */
  22. /** \file
  23. * \brief Provides functions to read **stl files** into user provided arrays
  24. *
  25. * The central function of this file is `ReadStlFile(...)`. It automatically recognizes
  26. * whether an *ASCII* or a *Binary* file is to be read. It identifies matching corner
  27. * coordinates of triangles with each other, so that the resulting coordinate
  28. * array does not contain the same coordinate-triple multiple times.
  29. *
  30. * The function operates on template container types. Those containers should
  31. * have similar interfaces as `std::vector` and operate on `float` or `double` types
  32. * (`TNumberContainer`) or on `int` or `size_t` types (`TIndexContainer`).
  33. *
  34. *
  35. * A conveniance class `StlMesh` is also provided, which makes accessing triangle
  36. * corners and corresponding corner coordinates much more easy. It still provides
  37. * raw access to the underlying data arrays.
  38. *
  39. *
  40. * ### Usage example 1 (using `StlMesh`):
  41. *
  42. * \code
  43. * try {
  44. * stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
  45. *
  46. * for(size_t itri = 0; itri < mesh.num_tris(); ++itri) {
  47. * std::cout << "coordinates of triangle " << itri << ": ";
  48. * for(size_t icorner = 0; icorner < 3; ++icorner) {
  49. * const float* c = mesh.tri_corner_coords (itri, icorner);
  50. * // or alternatively:
  51. * // float* c = mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner));
  52. * std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
  53. * }
  54. * std::cout << std::endl;
  55. *
  56. * float* n = mesh.tri_normal (itri);
  57. * std::cout << "normal of triangle " << itri << ": "
  58. * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
  59. * }
  60. * }
  61. * catch (std::exception& e) {
  62. * std::cout << e.what() << std::endl;
  63. * }
  64. * \endcode
  65. *
  66. *
  67. * ### Usage example 2 (using `StlMesh` and *solids*)
  68. *
  69. * \code
  70. * try {
  71. * stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
  72. *
  73. * for(size_t isolid = 0; isolid < mesh.num_solids(); ++isolid) {
  74. * std::cout << "solid " << isolid << std::endl;
  75. *
  76. * for(size_t itri = mesh.solid_tris_begin(isolid);
  77. * itri < mesh.solid_tris_end(isolid); ++itri)
  78. * {
  79. * const float* n = mesh.tri_normal (itri);
  80. * std::cout << "normal of triangle " << itri << ": "
  81. * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
  82. * }
  83. * }
  84. * }
  85. * catch (std::exception& e) {
  86. * std::cout << e.what() << std::endl;
  87. * }
  88. * \endcode
  89. *
  90. *
  91. * ### Usage example 3 (using raw data arrays)
  92. *
  93. * \code
  94. * std::vector<float> coords, normals;
  95. * std::vector<unsigned int> tris, solids;
  96. *
  97. * try {
  98. * stl_reader::ReadStlFile ("geometry.stl", coords, normals, tris, solids);
  99. * const size_t numTris = tris.size() / 3;
  100. * for(size_t itri = 0; itri < numTris; ++itri) {
  101. * std::cout << "coordinates of triangle " << itri << ": ";
  102. * for(size_t icorner = 0; icorner < 3; ++icorner) {
  103. * float* c = &coords[3 * tris [3 * itri + icorner]];
  104. * std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
  105. * }
  106. * std::cout << std::endl;
  107. *
  108. * float* n = &normals [3 * itri];
  109. * std::cout << "normal of triangle " << itri << ": "
  110. * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
  111. * }
  112. * }
  113. * catch (std::exception& e) {
  114. * std::cout << e.what() << std::endl;
  115. * }
  116. * \endcode
  117. *
  118. * If you do not want to use exceptions, you may define the macro
  119. * STL_READER_NO_EXCEPTIONS before including 'stl_reader.h'. In that case,
  120. * functions will return `false` if an error occurred.
  121. */
  122. #ifndef __H__STL_READER
  123. #define __H__STL_READER
  124. #include <algorithm>
  125. #include <exception>
  126. #include <fstream>
  127. #include <sstream>
  128. #include <vector>
  129. #ifdef STL_READER_NO_EXCEPTIONS
  130. #define STL_READER_THROW(msg) return false;
  131. #define STL_READER_COND_THROW(cond, msg) if(cond) return false;
  132. #else
  133. /// Throws an std::runtime_error with the given message.
  134. #define STL_READER_THROW(msg) {std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
  135. /// Throws an std::runtime_error with the given message, if the given condition evaluates to true.
  136. #define STL_READER_COND_THROW(cond, msg) if(cond){std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
  137. #endif
  138. namespace stl_reader {
  139. /// Reads an ASCII or binary stl file into several arrays
  140. /** Reads a stl file and writes its coordinates, normals and triangle-corner-indices
  141. * to the provided containers. It also fills a container solidRangesOut, which
  142. * provides the triangle ranges for individual solids.
  143. *
  144. * Double vertex entries are removed on the fly, so that triangle corners with
  145. * equal coordinates are represented by a single coordinate entry in coordsOut.
  146. *
  147. *
  148. * \param filename [in] The name of the file which shall be read
  149. *
  150. * \param coordsOut [out] Coordinates are written to this container. On termination,
  151. * it has size numVertices * 3. Each triple of entries forms a
  152. * 3d coordinate. The type TNumberContainer should have the same
  153. * interface as std::vector<float>.
  154. *
  155. * \param normalsOut [out] Face normals are written to this container. On termination,
  156. * it has size numFaces * 3. Each triple of entries forms a
  157. * 3d normal. The type TNumberContainer should have the same
  158. * interface as std::vector<float>.
  159. *
  160. * \param trisOut [out] Triangle corner indices are written to this container.
  161. * On termination, it has size numFaces * 3. Each triple of
  162. * entries defines a triangle. The type TIndexContainer should
  163. * have the same interface as std::vector<size_t>.
  164. * Multiply corner indices from trisOut by 3 to obtain the index
  165. * of the first coordinate of that corner in coordsOut.
  166. *
  167. * \param solidRangesOut [out] On termination, it holds the ranges of triangle indices
  168. * for each solid. It has the size numSolids + 1. Each entry
  169. * can be interpreted as a end/begin triangle index for the
  170. * previous/next solid. E.g., if there are 3 solids, the
  171. * returned array would look like this:
  172. * \code
  173. * {sol1Begin, sol1End/sol2Begin, sol2End/sol3Begin, sol3End}.
  174. * \endcode
  175. * The type TIndexContainer should have the same interface
  176. * as std::vector<size_t>.
  177. *
  178. * \returns true if the file was successfully read into the provided container.
  179. */
  180. template <class TNumberContainer1, class TNumberContainer2,
  181. class TIndexContainer1, class TIndexContainer2>
  182. bool ReadStlFile(const char* filename,
  183. TNumberContainer1& coordsOut,
  184. TNumberContainer2& normalsOut,
  185. TIndexContainer1& trisOut,
  186. TIndexContainer2& solidRangesOut);
  187. /// Reads an ASCII stl file into several arrays
  188. /** \copydetails ReadStlFile
  189. * \sa ReadStlFile, ReadStlFile_ASCII
  190. */
  191. template <class TNumberContainer1, class TNumberContainer2,
  192. class TIndexContainer1, class TIndexContainer2>
  193. bool ReadStlFile_ASCII(const char* filename,
  194. TNumberContainer1& coordsOut,
  195. TNumberContainer2& normalsOut,
  196. TIndexContainer1& trisOut,
  197. TIndexContainer2& solidRangesOut);
  198. /// Reads a binary stl file into several arrays
  199. /** \copydetails ReadStlFile
  200. * \todo support systems with big endianess
  201. * \sa ReadStlFile, ReadStlFile_BINARY
  202. */
  203. template <class TNumberContainer1, class TNumberContainer2,
  204. class TIndexContainer1, class TIndexContainer2>
  205. bool ReadStlFile_BINARY(const char* filename,
  206. TNumberContainer1& coordsOut,
  207. TNumberContainer2& normalsOut,
  208. TIndexContainer1& trisOut,
  209. TIndexContainer2& solidRangesOut);
  210. /// Determines whether a stl file has ASCII format
  211. /** The underlying mechanism is simply checks whether the provided file starts
  212. * with the keyword solid. This should work for many stl files, but may
  213. * fail, of course.
  214. */
  215. inline bool StlFileHasASCIIFormat(const char* filename);
  216. /// convenience mesh class which makes accessing the stl data more easy
  217. template <class TNumber = float, class TIndex = unsigned int>
  218. class StlMesh {
  219. public:
  220. /// initializes an empty mesh
  221. StlMesh ()
  222. {
  223. solids.resize (2, 0);
  224. }
  225. /// initializes the mesh from the stl-file specified through filename
  226. /** \{ */
  227. StlMesh (const char* filename)
  228. {
  229. read_file (filename);
  230. }
  231. StlMesh (const std::string& filename)
  232. {
  233. read_file (filename);
  234. }
  235. /** \} */
  236. /// fills the mesh with the contents of the specified stl-file
  237. /** \{ */
  238. bool read_file (const char* filename)
  239. {
  240. bool res = false;
  241. #ifndef STL_READER_NO_EXCEPTIONS
  242. try {
  243. #endif
  244. res = ReadStlFile (filename, coords, normals, tris, solids);
  245. #ifndef STL_READER_NO_EXCEPTIONS
  246. } catch (std::exception& e) {
  247. #else
  248. if (!res) {
  249. #endif
  250. coords.clear ();
  251. normals.clear ();
  252. tris.clear ();
  253. solids.clear ();
  254. STL_READER_THROW (e.what());
  255. }
  256. return res;
  257. }
  258. bool read_file (const std::string& filename)
  259. {
  260. return read_file (filename.c_str());
  261. }
  262. /** \} */
  263. /// returns the number of vertices in the mesh
  264. size_t num_vrts () const
  265. {
  266. return coords.size() / 3;
  267. }
  268. /// returns an array of 3 floating point values, one for each coordinate of the vertex
  269. const TNumber* vrt_coords (const size_t vi) const
  270. {
  271. return &coords[vi * 3];
  272. }
  273. /// returns the number of triangles in the mesh
  274. size_t num_tris () const
  275. {
  276. return tris.size() / 3;
  277. }
  278. /// returns an array of 3 indices, one for each corner vertex of the triangle
  279. const TIndex* tri_corner_inds (const size_t ti) const
  280. {
  281. return &tris [ti * 3];
  282. }
  283. /// returns the index of the corner with index `0<=ci<3` of triangle ti
  284. const TIndex tri_corner_ind (const size_t ti, const size_t ci) const
  285. {
  286. return tris [ti * 3 + ci];
  287. }
  288. /** \brief returns an array of 3 floating point values, one for each
  289. * coordinate of the specified corner of the specified tri.
  290. * \note same result as calling on a `StlMesh mesh`:
  291. * \code
  292. * mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner))
  293. * \endcode
  294. */
  295. const TNumber* tri_corner_coords (const size_t ti, const size_t ci) const
  296. {
  297. return &coords[tri_corner_ind(ti, ci) * 3];
  298. }
  299. /// returns an array of 3 floating point values defining the normal of a tri
  300. const TNumber* tri_normal (const size_t ti) const
  301. {
  302. return &normals [ti * 3];
  303. }
  304. /// returns the number of solids of the mesh
  305. /** solids can be seen as a partitioning of the triangles of a mesh.
  306. * By iterating consecutively from the index of the first triangle of a
  307. * solid `si` (using `solid_tris_begin(si)`) to the index of the last
  308. * triangle of a solid (using `solid_tris_end(...)-1`), one visits all
  309. * triangles of the solid `si`.*/
  310. size_t num_solids () const
  311. {
  312. if(solids.empty ())
  313. return 0;
  314. return solids.size () - 1;
  315. }
  316. /// returns the index of the first triangle in the given solid
  317. TIndex solid_tris_begin (const size_t si) const
  318. {
  319. return solids [si];
  320. }
  321. /// returns the index of the triangle behind the last triangle in the given solid
  322. TIndex solid_tris_end (const size_t si) const
  323. {
  324. return solids [si + 1];
  325. }
  326. /// returns a pointer to the coordinate array, containing `num_vrts()*3` entries.
  327. /** Storage layout: `x0,y0,z0,x1,y1,z1,...`
  328. * \returns pointer to a contiguous array of numbers, or `NULL` if no coords exist.*/
  329. const TNumber* raw_coords () const
  330. {
  331. if(coords.empty())
  332. return NULL;
  333. return &coords[0];
  334. }
  335. /// returns a pointer to the normal array, containing `num_tris()*3` entries.
  336. /** Storage layout: `nx0,ny0,nz0,nx1,ny1,nz1,...`
  337. * \returns pointer to a contiguous array of numbers, or `NULL` if no normals exist.*/
  338. const TNumber* raw_normals () const
  339. {
  340. if(normals.empty())
  341. return NULL;
  342. return &normals[0];
  343. }
  344. /// returns a pointer to the triangle array, containing `num_tris()*3` entries.
  345. /** Storage layout: `t0c0,t0c1,t0c2,t1c0,t1c1,t1c2,...`
  346. * \returns pointer to a contiguous array of indices, or `NULL` if no tris exist.*/
  347. const TIndex* raw_tris () const
  348. {
  349. if(tris.empty())
  350. return NULL;
  351. return &tris[0];
  352. }
  353. /// returns a pointer to the solids array, containing `num_solids()+1` entries.
  354. /** Storage layout: `s0begin, s0end/s1begin, s1end/s2begin, ..., sNend`
  355. * \returns pointer to a contiguous array of indices, or `NULL` if no solids exist.*/
  356. const TIndex* raw_solids () const
  357. {
  358. if(solids.empty())
  359. return NULL;
  360. return &solids[0];
  361. }
  362. private:
  363. std::vector<TNumber> coords;
  364. std::vector<TNumber> normals;
  365. std::vector<TIndex> tris;
  366. std::vector<TIndex> solids;
  367. };
  368. ////////////////////////////////////////////////////////////////////////////////
  369. // IMPLEMENTATION
  370. ////////////////////////////////////////////////////////////////////////////////
  371. namespace stl_reader_impl {
  372. // a coordinate triple with an additional index. The index is required
  373. // for RemoveDoubles, so that triangles can be reindexed properly.
  374. template <typename number_t, typename index_t>
  375. struct CoordWithIndex {
  376. number_t data[3];
  377. index_t index;
  378. bool operator == (const CoordWithIndex& c) const
  379. {
  380. return (c[0] == data[0]) && (c[1] == data[1]) && (c[2] == data[2]);
  381. }
  382. bool operator != (const CoordWithIndex& c) const
  383. {
  384. return (c[0] != data[0]) || (c[1] != data[1]) || (c[2] != data[2]);
  385. }
  386. bool operator < (const CoordWithIndex& c) const
  387. {
  388. return (data[0] < c[0])
  389. || (data[0] == c[0] && data[1] < c[1])
  390. || (data[0] == c[0] && data[1] == c[1] && data[2] < c[2]);
  391. }
  392. inline number_t& operator [] (const size_t i) {return data[i];}
  393. inline number_t operator [] (const size_t i) const {return data[i];}
  394. };
  395. // sorts the array coordsWithIndexInOut and copies unique indices to coordsOut.
  396. // Triangle-corners are re-indexed on the fly and degenerated triangles are removed.
  397. template <class TNumberContainer, class TIndexContainer>
  398. void RemoveDoubles (TNumberContainer& uniqueCoordsOut,
  399. TIndexContainer& trisInOut,
  400. std::vector <CoordWithIndex<
  401. typename TNumberContainer::value_type,
  402. typename TIndexContainer::value_type> >
  403. &coordsWithIndexInOut)
  404. {
  405. using namespace std;
  406. typedef typename TNumberContainer::value_type number_t;
  407. typedef typename TIndexContainer::value_type index_t;
  408. sort (coordsWithIndexInOut.begin(), coordsWithIndexInOut.end());
  409. // first count unique indices
  410. index_t numUnique = 1;
  411. for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
  412. if(coordsWithIndexInOut[i] != coordsWithIndexInOut[i - 1])
  413. ++numUnique;
  414. }
  415. uniqueCoordsOut.resize (numUnique * 3);
  416. vector<index_t> newIndex (coordsWithIndexInOut.size());
  417. // copy unique coordinates to 'uniqueCoordsOut' and create an index-map
  418. // 'newIndex', which allows to re-index triangles later on.
  419. index_t curInd = 0;
  420. newIndex[0] = 0;
  421. for(index_t i = 0; i < 3; ++i)
  422. uniqueCoordsOut[i] = coordsWithIndexInOut[0][i];
  423. for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
  424. const CoordWithIndex <number_t, index_t> c = coordsWithIndexInOut[i];
  425. if(c != coordsWithIndexInOut[i - 1]){
  426. ++curInd;
  427. for(index_t j = 0; j < 3; ++j)
  428. uniqueCoordsOut[curInd * 3 + j] = coordsWithIndexInOut[i][j];
  429. }
  430. newIndex[c.index] = static_cast<index_t> (curInd);
  431. }
  432. // re-index triangles, so that they refer to 'uniqueCoordsOut'
  433. // make sure to only add triangles which refer to three different indices
  434. index_t numUniqueTriInds = 0;
  435. for(index_t i = 0; i < trisInOut.size(); i+=3){
  436. int ni[3];
  437. for(int j = 0; j < 3; ++j)
  438. ni[j] = newIndex[trisInOut[i+j]];
  439. if((ni[0] != ni[1]) && (ni[0] != ni[2]) && (ni[1] != ni[2])){
  440. for(int j = 0; j < 3; ++j)
  441. trisInOut[numUniqueTriInds + j] = ni[j];
  442. numUniqueTriInds += 3;
  443. }
  444. }
  445. if(numUniqueTriInds < trisInOut.size())
  446. trisInOut.resize (numUniqueTriInds);
  447. }
  448. }// end of namespace stl_reader_impl
  449. template <class TNumberContainer1, class TNumberContainer2,
  450. class TIndexContainer1, class TIndexContainer2>
  451. bool ReadStlFile(const char* filename,
  452. TNumberContainer1& coordsOut,
  453. TNumberContainer2& normalsOut,
  454. TIndexContainer1& trisOut,
  455. TIndexContainer2& solidRangesOut)
  456. {
  457. if(StlFileHasASCIIFormat(filename))
  458. return ReadStlFile_ASCII(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
  459. else
  460. return ReadStlFile_BINARY(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
  461. }
  462. template <class TNumberContainer1, class TNumberContainer2,
  463. class TIndexContainer1, class TIndexContainer2>
  464. bool ReadStlFile_ASCII(const char* filename,
  465. TNumberContainer1& coordsOut,
  466. TNumberContainer2& normalsOut,
  467. TIndexContainer1& trisOut,
  468. TIndexContainer2& solidRangesOut)
  469. {
  470. using namespace std;
  471. using namespace stl_reader_impl;
  472. typedef typename TNumberContainer1::value_type number_t;
  473. typedef typename TIndexContainer1::value_type index_t;
  474. coordsOut.clear();
  475. normalsOut.clear();
  476. trisOut.clear();
  477. solidRangesOut.clear();
  478. ifstream in(filename);
  479. STL_READER_COND_THROW(!in, "Couldn't open file " << filename);
  480. vector<CoordWithIndex <number_t, index_t> > coordsWithIndex;
  481. string buffer;
  482. vector<string> tokens;
  483. int lineCount = 1;
  484. int maxNumTokens = 0;
  485. size_t numFaceVrts = 0;
  486. while(!(in.eof() || in.fail()))
  487. {
  488. // read the line and tokenize.
  489. // In order to reuse memory in between lines, 'tokens' won't be cleared.
  490. // Instead we count the number of tokens using 'tokenCount'.
  491. getline(in, buffer);
  492. istringstream line(buffer);
  493. int tokenCount = 0;
  494. while(!(line.eof() || line.fail())){
  495. if(tokenCount >= maxNumTokens){
  496. maxNumTokens = tokenCount + 1;
  497. tokens.resize(maxNumTokens);
  498. }
  499. line >> tokens[tokenCount];
  500. ++tokenCount;
  501. }
  502. if(tokenCount > 0)
  503. {
  504. string& tok = tokens[0];
  505. if(tok.compare("vertex") == 0){
  506. if(tokenCount < 4){
  507. STL_READER_THROW("ERROR while reading from " << filename <<
  508. ": vertex not specified correctly in line " << lineCount);
  509. }
  510. // read the position
  511. CoordWithIndex <number_t, index_t> c;
  512. for(size_t i = 0; i < 3; ++i)
  513. c[i] = static_cast<number_t> (atof(tokens[i+1].c_str()));
  514. c.index = static_cast<index_t>(coordsWithIndex.size());
  515. coordsWithIndex.push_back(c);
  516. ++numFaceVrts;
  517. }
  518. else if(tok.compare("facet") == 0)
  519. {
  520. STL_READER_COND_THROW(tokenCount < 5,
  521. "ERROR while reading from " << filename <<
  522. ": triangle not specified correctly in line " << lineCount);
  523. STL_READER_COND_THROW(tokens[1].compare("normal") != 0,
  524. "ERROR while reading from " << filename <<
  525. ": Missing normal specifier in line " << lineCount);
  526. // read the normal
  527. for(size_t i = 0; i < 3; ++i)
  528. normalsOut.push_back (static_cast<number_t> (atof(tokens[i+2].c_str())));
  529. numFaceVrts = 0;
  530. }
  531. else if(tok.compare("outer") == 0){
  532. STL_READER_COND_THROW ((tokenCount < 2) || (tokens[1].compare("loop") != 0),
  533. "ERROR while reading from " << filename <<
  534. ": expecting outer loop in line " << lineCount);
  535. }
  536. else if(tok.compare("endfacet") == 0){
  537. STL_READER_COND_THROW(numFaceVrts != 3,
  538. "ERROR while reading from " << filename <<
  539. ": bad number of vertices specified for face in line " << lineCount);
  540. trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 3));
  541. trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 2));
  542. trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 1));
  543. }
  544. else if(tok.compare("solid") == 0){
  545. solidRangesOut.push_back(static_cast<index_t> (trisOut.size() / 3));
  546. }
  547. }
  548. lineCount++;
  549. }
  550. solidRangesOut.push_back(static_cast<index_t> (trisOut.size() / 3));
  551. RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
  552. return true;
  553. }
  554. template <class TNumberContainer1, class TNumberContainer2,
  555. class TIndexContainer1, class TIndexContainer2>
  556. bool ReadStlFile_BINARY(const char* filename,
  557. TNumberContainer1& coordsOut,
  558. TNumberContainer2& normalsOut,
  559. TIndexContainer1& trisOut,
  560. TIndexContainer2& solidRangesOut)
  561. {
  562. using namespace std;
  563. using namespace stl_reader_impl;
  564. typedef typename TNumberContainer1::value_type number_t;
  565. typedef typename TIndexContainer1::value_type index_t;
  566. coordsOut.clear();
  567. normalsOut.clear();
  568. trisOut.clear();
  569. solidRangesOut.clear();
  570. ifstream in(filename, ios::binary);
  571. STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
  572. char stl_header[80];
  573. in.read(stl_header, 80);
  574. STL_READER_COND_THROW(!in, "Error while parsing binary stl header in file " << filename);
  575. unsigned int numTris = 0;
  576. in.read((char*)&numTris, 4);
  577. STL_READER_COND_THROW(!in, "Couldnt determine number of triangles in binary stl file " << filename);
  578. vector<CoordWithIndex <number_t, index_t> > coordsWithIndex;
  579. for(unsigned int tri = 0; tri < numTris; ++tri){
  580. float d[12];
  581. in.read((char*)d, 12 * 4);
  582. STL_READER_COND_THROW(!in, "Error while parsing trianlge in binary stl file " << filename);
  583. for(int i = 0; i < 3; ++i)
  584. normalsOut.push_back (d[i]);
  585. for(size_t ivrt = 1; ivrt < 4; ++ivrt){
  586. CoordWithIndex <number_t, index_t> c;
  587. for(size_t i = 0; i < 3; ++i)
  588. c[i] = d[ivrt * 3 + i];
  589. c.index = static_cast<index_t>(coordsWithIndex.size());
  590. coordsWithIndex.push_back(c);
  591. }
  592. trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 3));
  593. trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 2));
  594. trisOut.push_back(static_cast<index_t> (coordsWithIndex.size() - 1));
  595. char addData[2];
  596. in.read(addData, 2);
  597. STL_READER_COND_THROW(!in, "Error while parsing additional triangle data in binary stl file " << filename);
  598. }
  599. solidRangesOut.push_back(0);
  600. solidRangesOut.push_back(static_cast<index_t> (trisOut.size() / 3));
  601. RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
  602. return true;
  603. }
  604. inline bool StlFileHasASCIIFormat(const char* filename)
  605. {
  606. using namespace std;
  607. ifstream in(filename);
  608. STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
  609. char chars [256];
  610. in.read (chars, 256);
  611. string buffer (chars, in.gcount());
  612. transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower);
  613. return buffer.find ("solid") != string::npos &&
  614. buffer.find ("\n") != string::npos &&
  615. buffer.find ("facet") != string::npos &&
  616. buffer.find ("normal") != string::npos;
  617. }
  618. } // end of namespace stl_reader
  619. #endif //__H__STL_READER