/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef __VCGLIB_IMPORT_STL #define __VCGLIB_IMPORT_STL #include #include #include namespace vcg { namespace tri { namespace io { /** This class encapsulate a filter for importing stl (stereolitograpy) meshes. The stl format is quite simple and rather un-flexible. It just stores, in ascii or binary the, unindexed, geometry of the faces. Warning: this code assume little endian (PC) architecture!!! */ template class ImporterSTL { public: typedef typename OpenMeshType::VertexPointer VertexPointer; typedef typename OpenMeshType::ScalarType ScalarType; typedef typename OpenMeshType::VertexType VertexType; typedef typename OpenMeshType::FaceType FaceType; typedef typename OpenMeshType::VertexIterator VertexIterator; typedef typename OpenMeshType::FaceIterator FaceIterator; // if it is binary there are 80 char of comment, the number fn of faces and then exactly fn*4*3 bytes. enum {STL_LABEL_SIZE=80}; class STLFacet { public: Point3f n; Point3f v[3]; // short attr; }; enum STLError { E_NOERROR, // 0 E_CANTOPEN, // 1 E_UNESPECTEDEOF, // 2 E_MALFORMED, // 3 E_LAST }; static const char *ErrorMsg(int error) { static const char * stl_error_msg[] = { "No errors", "Can't open file", "Premature end of file", "Malformed file", }; if(error>=E_LAST || error<0) return "Unknown error"; else return stl_error_msg[error]; } static bool LoadMask(const char * filename, int &mask) { bool magicMode,colored; mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; if(!IsSTLColored(filename, colored, magicMode)) return false; if(colored) mask |= Mask::IOM_FACECOLOR; return true; } /* Try to guess if a stl has color * * rules: * - It has to be binary * - The per face attribute should be not zero * * return false in case of malformed files */ static bool IsSTLColored(const char * filename, bool &coloredFlag, bool &magicsMode) { coloredFlag=false; magicsMode=false; bool binaryFlag; if(IsSTLMalformed(filename,binaryFlag)==false) return false; if(binaryFlag==false) return true; FILE *fp = fopen(filename, "rb"); char buf[STL_LABEL_SIZE+1]; fread(buf,sizeof(char),STL_LABEL_SIZE,fp); std::string strInput(buf); size_t cInd = strInput.rfind("COLOR="); size_t mInd = strInput.rfind("MATERIAL="); if(cInd!=std::string::npos && mInd!=std::string::npos) magicsMode = true; else magicsMode = false; int facenum; fread(&facenum, sizeof(int), 1, fp); for(int i=0;i 127) { binaryFlag=true; std::size_t diff = (file_size > expected_file_size) ? file_size-expected_file_size : expected_file_size-file_size; if(diff > file_size/20 ) return false; // break; } } // Now we know if the stl file is ascii or binary. return true; } static int Open( OpenMeshType &m, const char * filename, int &loadMask, CallBackPos *cb=0) { FILE *fp = fopen(filename, "r"); if(fp == NULL) return E_CANTOPEN; fclose(fp); loadMask |= Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; bool binaryFlag; if(!IsSTLMalformed(filename,binaryFlag)) return E_MALFORMED; if(binaryFlag) return OpenBinary(m,filename,loadMask,cb); else return OpenAscii(m,filename,cb); } static int OpenBinary( OpenMeshType &m, const char * filename, int &loadMask, CallBackPos *cb=0) { FILE *fp; fp = fopen(filename, "rb"); if(fp == NULL) { return E_CANTOPEN; } bool magicsMode,coloredFlag; if(!IsSTLColored(filename,coloredFlag, magicsMode)) return E_MALFORMED; if(!coloredFlag) loadMask = loadMask & (~Mask::IOM_FACECOLOR); int facenum; fseek(fp, STL_LABEL_SIZE, SEEK_SET); fread(&facenum, sizeof(int), 1, fp); m.Clear(); FaceIterator fi=Allocator::AddFaces(m,facenum); VertexIterator vi=Allocator::AddVertices(m,facenum*3); // For each triangle read the normal, the three coords and a short set to zero for(int i=0;i "facet normal 0 0 0" if(ret!=3) { // we could be in the case of a multiple solid object, where after a endfaced instead of another facet we have to skip two lines: // endloop // endfacet //endsolid <- continue on ret==0 will skip this line //solid ascii <- and this one. // facet normal 0.000000e+000 7.700727e-001 -6.379562e-001 lineCnt++; continue; } ret=fscanf(fp, "%*s %*s"); // --> "outer loop" ret=fscanf(fp, "%*s %f %f %f\n", &f.v[0].X(), &f.v[0].Y(), &f.v[0].Z()); // --> "vertex x y z" if(ret!=3) return E_UNESPECTEDEOF; ret=fscanf(fp, "%*s %f %f %f\n", &f.v[1].X(), &f.v[1].Y(), &f.v[1].Z()); // --> "vertex x y z" if(ret!=3) return E_UNESPECTEDEOF; ret=fscanf(fp, "%*s %f %f %f\n", &f.v[2].X(), &f.v[2].Y(), &f.v[2].Z()); // --> "vertex x y z" if(ret!=3) return E_UNESPECTEDEOF; ret=fscanf(fp, "%*s"); // --> "endloop" ret=fscanf(fp, "%*s"); // --> "endfacet" lineCnt+=7; if(feof(fp)) break; FaceIterator fi=Allocator::AddFaces(m,1); VertexIterator vi=Allocator::AddVertices(m,3); for(int k=0;k<3;++k) { (*vi).P().Import(f.v[k]); (*fi).V(k)=&*vi; ++vi; } } fclose(fp); return E_NOERROR; } }; // end class } // end Namespace tri } // end Namespace io } // end Namespace vcg #endif