/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2006 \/)\/ * * 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. * * * ****************************************************************************/ /**************************************************************************** History $Log: not supported by cvs2svn $ Revision 1.11 2006/06/08 13:55:16 cignoni Added ColorPreserving Cellbase template. Revision 1.10 2006/05/26 10:18:11 cignoni Re-adapted to ms compilers Revision 1.9 2006/05/25 09:37:14 cignoni Many changes for the different interpretation of hash_set between gcc and .net. Probably to be completed. Revision 1.8 2006/05/24 16:42:22 m_di_benedetto Corrected bbox inflation amount in case of _cellsize != 0 Revision 1.7 2006/05/24 15:16:01 cignoni better comment to the init parameters Revision 1.6 2006/05/24 08:54:04 cignoni Added missing std:: to swap Revision 1.5 2006/05/21 06:40:31 cignoni Added DoubleFace management Revision 1.4 2006/05/19 20:49:03 m_di_benedetto Added check for empty generated mesh (prevent call to mesh allocator with zero vertices or faces). Revision 1.3 2006/05/18 22:20:53 m_di_benedetto added check for deleted faces and modified/added std namespace qualifier. Revision 1.2 2006/05/18 13:59:20 cignoni Some minor optimizations Revision 1.1 2006/05/16 21:56:06 cignoni First working Version ****************************************************************************/ #ifndef __VCGLIB_CLUSTERING #define __VCGLIB_CLUSTERING #include #include #include #include #include #include #include #include // some stuff for portable hashes... #ifdef WIN32 #ifndef __MINGW32__ #include #include #define STDEXT stdext #else #include #include #define STDEXT __gnu_cxx #endif #else #include #include #define STDEXT __gnu_cxx #endif namespace vcg{ namespace tri{ #define HASH_P0 73856093 #define HASH_P1 19349663 #define HASH_P2 83492791 class HashedPoint3i : public Point3i { public: size_t Hash() const { return (V(0)*HASH_P0 ^ V(1)*HASH_P1 ^ V(2)*HASH_P2); } operator size_t () const {return Hash();} }; // needed for gcc compilation #ifndef _MSC_VER }} namespace STDEXT { template <> struct hash{ inline size_t operator ()(const vcg::tri::HashedPoint3i &p) const {return size_t(p);} }; } namespace vcg{ namespace tri{ #endif // template class NearestToCenter { typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::CoordType CoordType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::FaceType FaceType; typedef BasicGrid GridType; public: inline void AddVertex(MeshType &/*m*/, GridType &g, Point3i &pi, VertexType &v) { CoordType c; g.IPiToBoxCenter(pi,c); ScalarType newDist = Distance(c,v.cP()); if(!valid || newDist < bestDist) { valid=true; bestDist=newDist; bestPos=v.cP(); bestN=v.cN(); orig=&v; } } inline void AddFaceVertex(MeshType &m, FaceType &f, int i) { assert(0);} NearestToCenter(): valid(false){} CoordType bestPos; CoordType bestN; ScalarType bestDist; bool valid; int id; VertexType *orig; CoordType Pos() const { assert(valid); return bestPos; } Color4b Col() const {return Color4b::White;} CoordType N() const {return bestN;} VertexType * Ptr() const {return orig;} }; template class AverageColorCell { typedef typename MeshType::CoordType CoordType; typedef typename MeshType::FaceType FaceType; typedef typename MeshType::VertexType VertexType; typedef BasicGrid GridType; public: inline void AddFaceVertex(MeshType &/*m*/, FaceType &f, int i) { p+=f.cV(i)->cP(); c+=CoordType(f.cV(i)->C()[0],f.cV(i)->C()[1],f.cV(i)->C()[2]); // we prefer to use the un-normalized face normal so small faces facing away are dropped out // and the resulting average is weighed with the size of the faces falling here. n+=f.cN(); cnt++; } inline void AddVertex(MeshType &/*m*/, GridType &/*g*/, Point3i &/*pi*/, VertexType &v) { p+=v.cP(); n+=v.cN(); c+=CoordType(v.C()[0],v.C()[1],v.C()[2]); cnt++; } AverageColorCell(): p(0,0,0), n(0,0,0), c(0,0,0),cnt(0){} CoordType p; CoordType n; CoordType c; int cnt; int id; Color4b Col() const { return Color4b(c[0]/cnt,c[1]/cnt,c[2]/cnt,255); } CoordType N() const {return n;} VertexType * Ptr() const {return 0;} CoordType Pos() const { return p/cnt; } }; /* Metodo di clustering */ template class Clustering { public: typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::CoordType CoordType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::FaceType FaceType; typedef typename MeshType::VertexPointer VertexPointer; typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::FaceIterator FaceIterator; // DuplicateFace == bool means that during the clustering doublesided surface (like a thin shell) that would be clustered to a single surface // will be merged into two identical but opposite faces. // So in practice: // DuplicateFace=true a model with looks ok if you enable backface culling // DuplicateFace=false a model with looks ok if you enable doublesided lighting and disable backfaceculling bool DuplicateFaceParam; // This class keeps the references to the three cells where a face has its vertexes. class SimpleTri { public: CellType *v[3]; int ii(int i) const {return *((int *)(&(v[i])));} bool operator < ( const SimpleTri &p) const { return (v[2]!=p.v[2])?(v[2] v[1] ) std::swap(v[0],v[1]); // now v0 < v1 if(v[0] > v[2] ) std::swap(v[0],v[2]); // now v0 is the minimum if(v[1] > v[2] ) std::swap(v[1],v[2]); // sorted! } // Hashing Function; operator size_t () const { return (ii(0)*HASH_P0 ^ ii(1)*HASH_P1 ^ ii(2)*HASH_P2); } }; // The init function Take two parameters // _size is the approximate total number of cells composing the grid surrounding the objects (usually a large number) // eg _size==1.000.000 means a 100x100x100 grid // _cellsize is the absolute lenght of the edge of the grid cell. // eg _cellsize==2.0 means that all the vertexes in a 2.0x2.0x2.0 cell are clustered togheter // Notes: // _size is used only if the cell edge IS zero. // _cellsize gives you an absolute measure of the maximum error introduced // during the simplification (e.g. half of the cell edge lenght) void Init(Box3 _mbb, int _size, ScalarType _cellsize=0) { GridCell.clear(); TriSet.clear(); Grid.bbox=_mbb; ///inflate the bb calculated ScalarType infl = (_cellsize == (ScalarType)0) ? (Grid.bbox.Diag() / _size) : (_cellsize); Grid.bbox.min-=CoordType(infl,infl,infl); Grid.bbox.max+=CoordType(infl,infl,infl); Grid.dim = Grid.bbox.max - Grid.bbox.min; if( _cellsize==0) BestDim( _size, Grid.dim, Grid.siz ); else Grid.siz = Point3i::Construct(Grid.dim / _cellsize); // find voxel size Grid.voxel[0] = Grid.dim[0]/Grid.siz[0]; Grid.voxel[1] = Grid.dim[1]/Grid.siz[1]; Grid.voxel[2] = Grid.dim[2]/Grid.siz[2]; } BasicGrid Grid; #ifdef _MSC_VER STDEXT::hash_set TriSet; typedef typename STDEXT::hash_set::iterator TriHashSetIterator; #else struct SimpleTriHashFunc{ inline size_t operator ()(const SimpleTri &p) const {return size_t(p);} }; STDEXT::hash_set TriSet; typedef typename STDEXT::hash_set::iterator TriHashSetIterator; #endif STDEXT::hash_map GridCell; void AddPointSet(MeshType &m, bool UseOnlySelected=false) { VertexIterator vi; for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) if(!UseOnlySelected || (*vi).IsS()) { HashedPoint3i pi; Grid.PToIP((*vi).cP(), pi ); GridCell[pi].AddVertex(m,Grid,pi,*(vi)); } } void AddMesh(MeshType &m) { FaceIterator fi; for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) { HashedPoint3i pi; SimpleTri st; for(int i=0;i<3;++i) { Grid.PToIP((*fi).cV(i)->cP(), pi ); st.v[i]=&(GridCell[pi]); st.v[i]->AddFaceVertex(m,*(fi),i); } if( (st.v[0]!=st.v[1]) && (st.v[0]!=st.v[2]) && (st.v[1]!=st.v[2]) ) { // if we allow the duplication of faces we sort the vertex only partially (to maintain the original face orientation) if(DuplicateFaceParam) st.sortOrient(); else st.sort(); TriSet.insert(st); } // printf("Inserted %8i triangles, clustered to %8i tri and %i cells\n",distance(m.face.begin(),fi),TriSet.size(),GridCell.size()); } } int CountPointSet() {return GridCell.size(); } void SelectPointSet(MeshType &m) { typename STDEXT::hash_map::iterator gi; UpdateSelection::ClearVertex(m); for(gi=GridCell.begin();gi!=GridCell.end();++gi) { VertexType *ptr=(*gi).second.Ptr(); if(ptr && ( ptr >= &*m.vert.begin() ) && ( ptr <= &*(m.vert.end() - 1) ) ) ptr->SetS(); } } void ExtractPointSet(MeshType &m) { m.Clear(); if (GridCell.empty()) return; Allocator::AddVertices(m,GridCell.size()); typename STDEXT::hash_map::iterator gi; int i=0; for(gi=GridCell.begin();gi!=GridCell.end();++gi) { m.vert[i].P()=(*gi).second.Pos(); m.vert[i].N()=(*gi).second.N(); m.vert[i].C()=(*gi).second.Col(); ++i; } } void ExtractMesh(MeshType &m) { m.Clear(); if (TriSet.empty() || GridCell.empty()) { return; } Allocator::AddVertices(m,GridCell.size()); typename STDEXT::hash_map::iterator gi; int i=0; for(gi=GridCell.begin();gi!=GridCell.end();++gi) { m.vert[i].P()=(*gi).second.Pos(); if(m.vert[i].HasColor()) m.vert[i].C()=(*gi).second.Col(); (*gi).second.id=i; ++i; } Allocator::AddFaces(m,TriSet.size()); TriHashSetIterator ti; i=0; for(ti=TriSet.begin();ti!=TriSet.end();++ti) { m.face[i].V(0)=&(m.vert[(*ti).v[0]->id]); m.face[i].V(1)=&(m.vert[(*ti).v[1]->id]); m.face[i].V(2)=&(m.vert[(*ti).v[2]->id]); // if we are merging faces even when opposite we choose // the best orientation according to the averaged normal if(!DuplicateFaceParam) { CoordType N=vcg::Normal(m.face[i]); int badOrient=0; if( N.dot((*ti).v[0]->N()) <0) ++badOrient; if( N.dot((*ti).v[1]->N()) <0) ++badOrient; if( N.dot((*ti).v[2]->N()) <0) ++badOrient; if(badOrient>2) std::swap(m.face[i].V(0),m.face[i].V(1)); } i++; } } }; //end class clustering } // namespace tri } // namespace vcg #endif