diff --git a/.gitignore b/.gitignore index 4f22965f..caf98bf3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ debug/ release/ *-debug/ *-release/ +/docs/Doxygen/html/ + +# Intermediate Files +*.bc \ No newline at end of file diff --git a/apps/metro/readme.txt b/apps/metro/readme.txt index 7fd9e060..5f2ee761 100644 --- a/apps/metro/readme.txt +++ b/apps/metro/readme.txt @@ -1,5 +1,5 @@ - VCGLib http://vcg.sf.net o o + VCGLib http://www.vcglib.net o o Visual and Computer Graphics Library o o _ O _ Copyright(C) 2005-2006 \/)\/ @@ -31,14 +31,10 @@ Please, when using this tool, cite the following reference: P. Cignoni, C. Rocchini and R. Scopigno "Metro: measuring error on simplified surfaces" Computer Graphics Forum, Blackwell Publishers, vol. 17(2), June 1998, pp 167-174 -Available at http://vcg.sf.net - -You can find some sample mesh to test in the 'Metro Sample dataset' package downloadable from sourceforge. - For any question about this software please contact: -Paolo Cignoni ( p.cignoni@isti.cnr.it ) +Paolo Cignoni ( paolo.cignoni@isti.cnr.it ) --- General Info --- diff --git a/apps/tridecimator/readme.txt b/apps/tridecimator/readme.txt index b15eca5c..900b948d 100644 --- a/apps/tridecimator/readme.txt +++ b/apps/tridecimator/readme.txt @@ -1,5 +1,5 @@ - VCGLib http://vcg.sf.net o o + VCGLib http://www.vcglib.net o o Visual and Computer Graphics Library o o _ O _ Copyright(C) 2005-2006 \/)\/ diff --git a/apps/tridecimator/tridecimator.cpp b/apps/tridecimator/tridecimator.cpp index 6f4bc513..51c66bec 100644 --- a/apps/tridecimator/tridecimator.cpp +++ b/apps/tridecimator/tridecimator.cpp @@ -73,7 +73,6 @@ void Usage() "---------------------------------\n" " TriDecimator 1.0 \n" " http://vcg.isti.cnr.it\n" - " http://vcg.sourceforge.net\n" " release date: " __DATE__ "\n---------------------------------\n\n" "Copyright 2003-2016 Visual Computing Lab I.S.T.I. C.N.R.\n" diff --git a/apps/trimeshinfo/readme.txt b/apps/trimeshinfo/readme.txt index 9d4cb761..07f9720a 100644 --- a/apps/trimeshinfo/readme.txt +++ b/apps/trimeshinfo/readme.txt @@ -1,5 +1,5 @@ - VCGLib http://vcg.sf.net o o + VCGLib http://www.vcglib.net o o Visual and Computer Graphics Library o o _ O _ Copyright(C) 2004-2006 \/)\/ @@ -56,7 +56,7 @@ The application has no graphical interface but works as the "Metro" tool on comm TriMeshInfo is written in C++ and makes use of the VCG library. The tool supports the following file formats: - - PLY (http://vcg.sourceforge.net/img/wiki_up/plyformat.pdf) + - PLY - OFF (http://www.geomview.org/docs/html/geomview_41.html#SEC44) - STL (http://astronomy.swin.edu.au/~pbourke/dataformats/stl/) - OBJ (http://www.eg-models.de/formats/Format_Obj.html) diff --git a/apps/trimeshinfo/trimeshinfo1.01/trimeshinfo101/readme.txt b/apps/trimeshinfo/trimeshinfo1.01/trimeshinfo101/readme.txt index 89d28e3c..e2f349c1 100644 --- a/apps/trimeshinfo/trimeshinfo1.01/trimeshinfo101/readme.txt +++ b/apps/trimeshinfo/trimeshinfo1.01/trimeshinfo101/readme.txt @@ -1,5 +1,5 @@ - VCGLib http://vcg.sf.net o o + VCGLib http://www.vcglib.net o o Visual and Computer Graphics Library o o _ O _ Copyright(C) 2004-2005 \/)\/ @@ -55,6 +55,4 @@ The application has no graphical interface but works as the "Metro" tool on comm The application works under both Windows and Linux/Unix platforms. TriMeshInfo is written in C++ and makes use of the VCL library. -The tool supports two file formats ply (as described in the following document -http://vcg.sourceforge.net/img/wiki_up/plyformat.pdf) -and off (as described in http://www.geomview.org/docs/html/geomview_41.html#SEC44) . +The tool supports two file formats ply and off (as described in http://www.geomview.org/docs/html/geomview_41.html#SEC44) . diff --git a/apps/trimeshinfo/trimeshinfo1.02/trimeshinfo1.02/readme.txt b/apps/trimeshinfo/trimeshinfo1.02/trimeshinfo1.02/readme.txt index 6894c62a..4b550b82 100644 --- a/apps/trimeshinfo/trimeshinfo1.02/trimeshinfo1.02/readme.txt +++ b/apps/trimeshinfo/trimeshinfo1.02/trimeshinfo1.02/readme.txt @@ -1,5 +1,5 @@ - VCGLib http://vcg.sf.net o o + VCGLib http://www.vcglib.net o o Visual and Computer Graphics Library o o _ O _ Copyright(C) 2004-2005 \/)\/ @@ -51,6 +51,4 @@ For each analyzed dataset the following information are extracted: The application has no graphical interface but works as the "Metro" tool on command line. TriMeshInfo is written in C++ and makes use of the VCL library. -The tool supports two file formats ply (as described in the following document -http://vcg.sourceforge.net/img/wiki_up/plyformat.pdf) -and off (as described in http://www.geomview.org/docs/html/geomview_41.html#SEC44) . +The tool supports two file formats ply and off (as described in http://www.geomview.org/docs/html/geomview_41.html#SEC44) . diff --git a/docs/ContributorLicenseAgreement.md b/docs/ContributorLicenseAgreement.md new file mode 100644 index 00000000..79788115 --- /dev/null +++ b/docs/ContributorLicenseAgreement.md @@ -0,0 +1,37 @@ +# Contributor License Agreement +The following terms are used throughout this agreement: + +* **You** - the person or legal entity including its affiliates asked to accept this agreement. An affiliate is any entity that controls or is controlled by the legal entity, or is under common control with it. +* **VCLab** - the legal representative of the **Visual Computing Lab of the ISTI-CNR** (Istituto di Scienza e Tecnologie dell’Informazione “A. Faedo” an Institute of CNR, the National Research Council of Italy). +* **Project** - is an umbrella term that refers to any and all the open source projects managed by the VCLab, including, but not limited to, `MeshLab` and the `vcglib`. +* **Contribution** - any type of work that is submitted to a Project, including any modifications or additions to existing work. +* **Submitted** - conveyed to a Project via a pull request, commit, issue, or any form of electronic, written, or verbal communication with GitHub, contributors or maintainers. + +## 1. Grant of Copyright License. +Subject to the terms and conditions of this agreement, You grant to the Projects’ maintainers and to VCLab a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your contributions and such derivative works. Except for this license, You reserve all rights, title, and interest in your contributions. + +## 2. Grant of Patent License. +Subject to the terms and conditions of this agreement, You grant to the Projects’ maintainers and to VCLab a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer your contributions, where such license applies only to those patent claims licensable by you that are necessarily infringed by your contribution or by combination of your contribution with the project to which this contribution was submitted. + +If any entity institutes patent litigation - including cross-claim or counterclaim in a lawsuit - against You alleging that your contribution or any project it was submitted to constitutes or is responsible for direct or contributory patent infringement, then any patent licenses granted to that entity under this agreement shall terminate as of the date such litigation is filed. + +## 3. Source of Contribution. +Your contribution is either your original creation, based upon previous work that, to the best of your knowledge, is covered under an appropriate open source license and you have the right under that license to submit that work with modifications, whether created in whole or in part by you, or you have clearly identified the source of the contribution and any license or other restriction (like related patents, trademarks, and license agreements) of which you are personally aware. + +### 4. Details +Name + +--- +Project Contributed + +--- +Contribution (id of the pull request) + +--- +Signature + +--- +Date + +--- + diff --git a/docs/ContributorLicenseAgreement.pdf b/docs/ContributorLicenseAgreement.pdf new file mode 100644 index 00000000..f5e0a5fc Binary files /dev/null and b/docs/ContributorLicenseAgreement.pdf differ diff --git a/docs/Doxygen/attributes.dxy b/docs/Doxygen/attributes.dxy index 7f3260f0..66895a9f 100644 --- a/docs/Doxygen/attributes.dxy +++ b/docs/Doxygen/attributes.dxy @@ -1,9 +1,9 @@ /** \page attributes -User-defined attributes +User-Defined Attributes ======================= VCG Lib also provides a simple mechanism to associate user-defined typed 'attributes' to the simplicies and to the mesh. -Note that both 'attributes' and 'components' are basically accessory data that are bound to a simplex. +Note that both 'attributes' and 'components' are basically accessory data that are bound to a simplex. In short, components are statically defined member data, while attributes are run-time defined, handle accessed, data. Conceptually the difference is that with the term component VCGLib indicates those basic values that are considered to 'define' the simplex (its position, its normal, its connectivity information), while an user defined attribute is an accessory data that is useful for some specific algorithms, like "the average direction from which a vertex is visible". @@ -16,14 +16,14 @@ Once you have the handle retrieving and using the attributes is rather simple by \snippet trimesh_attribute.cpp Using an attribute -You also have functions to check if a given attribute exists, retrieve an handle for it and eventually deleting it (using the handle or by name). -Do not get mix up the scope of the handle with the memory allocation of the attribute. If you do not delete an attribute explicitly, it will be allocated until the mesh itself is destroyed, even if you do not have any more handles to it. +You also have functions to check if a given attribute exists, to retrieve an handle for it and for, eventually, deleting it (using the handle or by name). +Remember that the scope of a handle does not interfere with the memory allocation of the attribute. If you do not delete an attribute explicitly, it will stay allocated until the mesh itself is destroyed, even if you do not have any more handles to it. \snippet trimesh_attribute.cpp Deleting an attribute -The same can be done for the faces, just replace the occurences of PerVertex with PerFace. +The same can be done for edges, faces and for the mesh itself, just replace the occurences of PerVertex with PerFace, PerEdge and PerMesh. Note that if you call add an attribute without specifying a name and you lose the handle, you will not be able to get your handle back. -Attributes can also be specified per mesh; in this case the access is done in a slightly different way. +For attributes specified as per-mesh the access is done in a slightly different way. \snippet trimesh_attribute.cpp Per Mesh attribute diff --git a/docs/Doxygen/main_concept.dxy b/docs/Doxygen/basic_concepts.dxy similarity index 96% rename from docs/Doxygen/main_concept.dxy rename to docs/Doxygen/basic_concepts.dxy index 71099b37..b0ab1ff8 100644 --- a/docs/Doxygen/main_concept.dxy +++ b/docs/Doxygen/basic_concepts.dxy @@ -1,8 +1,7 @@ -/** \page basic_concepts -How to define a mesh type -===== - -The VCG Lib may encode a mesh in several ways, the most common of which is a set of vertices and set of triangles (i.e. triangles for triangle meshes, tetrahedra for tetrahedral meshes). The following line is an example of the definition of a VCG type of mesh: +/** \page basic_concepts Basic Concepts +How to define the type of a mesh +========================= +The VCG Lib may encode a mesh in several ways, the most common of which is a vector of vertices and vector of triangles (i.e. triangles for triangle meshes, tetrahedra for tetrahedral meshes). The following line is an example of the definition of a VCG type of mesh: \dontinclude trimesh_base.cpp \skip MyMesh diff --git a/docs/Doxygen/examples.dxy b/docs/Doxygen/examples.dxy deleted file mode 100644 index fa160531..00000000 --- a/docs/Doxygen/examples.dxy +++ /dev/null @@ -1,13 +0,0 @@ -/** -\page examples - -There are a number of very simple and short examples meant to document the various features of the library. - -trimesh_base.cpp -trimesh_optional -trimesh_attribute.cpp - -trimesh_normal.cpp - - -*/ diff --git a/docs/Doxygen/fileformat.dxy b/docs/Doxygen/fileformat.dxy index f5ebbd16..981cc7d0 100644 --- a/docs/Doxygen/fileformat.dxy +++ b/docs/Doxygen/fileformat.dxy @@ -1,4 +1,4 @@ -/** \page fileformat +/** \page fileformat File Formats File Formats ============ VCGLib provides importer and exporter for several file formats diff --git a/docs/Doxygen/groups.dxy b/docs/Doxygen/groups.dxy index b57eb7f9..7dcc31e1 100644 --- a/docs/Doxygen/groups.dxy +++ b/docs/Doxygen/groups.dxy @@ -28,8 +28,9 @@ This module contains the documentation for the types and the functions used for /** \defgroup code_sample Code Examples \brief This module contains a number of small examples to explain the library features -All the example here reported are located under vcglib/apps/sample -Each sample has a minimal '.pro' file +All the examples here listed are located in the folder `vcglib/apps/sample`. +To compile each sample, please use the corresponding `qmake` `*.pro` project files. + */ diff --git a/docs/Doxygen/index.dxy b/docs/Doxygen/index.dxy index f9b78370..fd90d182 100644 --- a/docs/Doxygen/index.dxy +++ b/docs/Doxygen/index.dxy @@ -3,6 +3,8 @@ The Visualization and Computer Graphics Library (VCG for short) is a open source portable C++ templated library for manipulation, processing and displaying with OpenGL of triangle and tetrahedral meshes. +VCG Lib uses a git repository hosted by github at http://github.com/cnr-isti-vclab/vcglib/ + The library, composed by more than 100k lines of code, is released under the GPL license, and it is the base of most of the software tools of the Visual Computing Lab of the Italian National Research Council Institute ISTI @@ -11,7 +13,7 @@ software tools of the Visual Computing Lab of the Italian National Resear The VCG library is tailored to mostly manage triangular meshes: The library is fairly large and offers many state of the art functionalities for processing meshes, like: -- high quality quadric-error edge-collapse based simplfication, +- high quality quadric-error edge-collapse based simplification, - efficient spatial query structures (uniform grids, hashed grids, kdtree, ...) , - advanced smoothing and fairing algorithms, - computation of curvature, @@ -41,7 +43,7 @@ Start from the following pages for basic concepts and examples. Notable Applications ------- A number of applications have been developed using the vcglib: -- MeshLab: the renowed open source mesh processing is based on this library. +- MeshLab: the renowned open source mesh processing is based on this library. - \subpage metro "Metro, the tool for measuring differences between meshes" - The first high quality out-of-core mesh simplifier that was used by the Stanford Digital Michelangelo project to process their huge 3D scanned models. diff --git a/docs/Doxygen/install.dxy b/docs/Doxygen/install.dxy index a1831cfb..5d72f675 100644 --- a/docs/Doxygen/install.dxy +++ b/docs/Doxygen/install.dxy @@ -3,33 +3,28 @@ Installation and folder structure ================ -VCG Lib uses a SVN repository hosted by sourceforge. -The main project page of the library is http://sourceforge.net/projects/vcg/ +VCG Lib uses a git repository hosted by github. +The main project page of the library is http://github.com/cnr-isti-vclab/vcglib/ -To get the right subset of the svn trunk in the devel folder you should issue the following svn command: +The stable version of the library are available under the release page +https://github.com/cnr-isti-vclab/vcglib/releases - svn checkout svn://svn.code.sf.net/p/vcg/code/trunk/vcglib vcglib +To get the development version of library clone the 'devel' branch of the library : -Windows users with tortoisesvn (http://www.tortoisesvn.net) installed should + git clone -b devel https://github.com/cnr-isti-vclab/vcglib.git -1. create a folder named vcglib. -2. right-click in the newly created vcglib folder and choose SVN Checkout -3. put in the first parameter: svn://svn.code.sf.net/p/vcg/code/trunk/vcglib -4. press ok and wait, it should start to check out the last revision of the whole tree. Folder Structure ================ -VCG Lib is mostly made of header files (and its core part it's only header files). +VCGLib is mostly made of header files (and its core part it's only header files) with no external dependencies. Just download the tarball from here and uncompress it in a folder, e.g. named vcg, inside you compiler "include" directory. -Afterwards, you have to just include the file you need. +Afterwards, you have to just include the files you need as shown in the apps/sample examples. Inside vcg folder you will find 5 sub-folders: - `vcg`: this is the core of the library, where all the algorithms and data structures are defined. This part is pure, quite heavily templated, C++ code with STL support for common data structures and algorithms. You will not find a single include from here to anything else than standard libraries. Note that core part is made of header files (.h files) only. - `wrap`: here you will find the wrapping of VCG concepts towards specific needs/contexts/libraries. For example you will find all the code to import/export meshes from the hard disk in many formats, or the routines for rendering triangle meshes with OpenGL, supports for common GUI tools like a trackball, and so on.. -- `apps`: this folder contains the command line applications developed with the VCG Lib. Many (much more) examples can be found in MeshLab. The apps/simple directory contains a sub-collection of very basic apps. A good starting point for beginners! +- `apps`: this folder contains the command line applications developed with the VCG Lib. Many (much more) examples can be found in MeshLab. The apps/sample directory contains a sub-collection of very basic apps. A good starting point for beginners! - `docs`: documentation lives here (including this tutorial) - `eigenlib` a copy of the latest stable version of the eigen library for linear algebra ( http://http://eigen.tuxfamily.org/ ). VCGlib relies on eigen for all the advanced matrix processing. - -You can browse the svn tree on the sourceforge page http://sourceforge.net/p/vcg/code/HEAD/tree/trunk/vcglib/ */ diff --git a/vcg/complex/algorithms/clean.h b/vcg/complex/algorithms/clean.h index 65606b59..c8f62c68 100644 --- a/vcg/complex/algorithms/clean.h +++ b/vcg/complex/algorithms/clean.h @@ -79,8 +79,6 @@ public: mp=&m; while(!sf.empty()) sf.pop(); UnMarkAll(m); - assert(p); - assert(!p->IsD()); tri::Mark(m,p); sf.push(p); } @@ -272,7 +270,6 @@ public: tri::Index(m,(*fi).V(2)), &*fi)); } - assert (size_t(m.fn) == fvec.size()); std::sort(fvec.begin(),fvec.end()); int total=0; for(int i=0;i::DeleteEdge(m, *(eVec[i].fp) ); - //qDebug("deleting face %i (pos in fvec %i)",tri::Index(m,fvec[i].fp) ,i); } } return total; @@ -440,12 +434,10 @@ public: CountNonManifoldVertexFF(m,true); tri::UpdateSelection::FaceFromVertexLoose(m); int count_removed = 0; - FaceIterator fi; - for(fi=m.face.begin(); fi!=m.face.end();++fi) + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) if(!(*fi).IsD() && (*fi).IsS()) Allocator::DeleteFace(m,*fi); - VertexIterator vi; - for(vi=m.vert.begin(); vi!=m.vert.end();++vi) + for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end();++vi) if(!(*vi).IsD() && (*vi).IsS()) { ++count_removed; Allocator::DeleteVertex(m,*vi); @@ -631,22 +623,15 @@ public: return count_fd; } - /* - The following functions remove faces that are geometrically "bad" according to edges and area criteria. - They remove the faces that are out of a given range of area or edges (e.g. faces too large or too small, or with edges too short or too long) - but that could be topologically correct. - These functions can optionally take into account only the selected faces. - */ - template - static int RemoveFaceOutOfRangeAreaSel(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) + /* Remove the faces that are out of a given range of area */ + static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)(), bool OnlyOnSelected=false) { - FaceIterator fi; int count_fd = 0; MinAreaThr*=2; MaxAreaThr*=2; - for(fi=m.face.begin(); fi!=m.face.end();++fi) + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi){ if(!(*fi).IsD()) - if(!Selected || (*fi).IsS()) + if(!OnlyOnSelected || (*fi).IsS()) { const ScalarType doubleArea=DoubleArea(*fi); if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) ) @@ -655,17 +640,13 @@ public: count_fd++; } } + } return count_fd; } - // alias for the old style. Kept for backward compatibility - static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m);} + static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m,0);} - // Aliases for the functions that do not look at selection - static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) - { - return RemoveFaceOutOfRangeAreaSel(m,MinAreaThr,MaxAreaThr); - } + /** * Is the mesh only composed by quadrilaterals? @@ -850,7 +831,7 @@ public: */ static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false) { - assert(m.fn == 0 && m.en >0); // just to be sure we are using an edge mesh... + MeshAssert::OnlyEdgeMesh(m); RequireEEAdjacency(m); tri::UpdateTopology::EdgeEdge(m); @@ -1031,61 +1012,27 @@ public: static int CountHoles( MeshType & m) { - int numholev=0; - FaceIterator fi; - - FaceIterator gi; - vcg::face::Pos he; - vcg::face::Pos hei; - - std::vector< std::vector > holes; //indices of vertices - - vcg::tri::UpdateFlags::VertexClearS(m); - - gi=m.face.begin(); fi=gi; - - for(fi=m.face.begin();fi!=m.face.end();fi++)//for all faces do + UpdateFlags::FaceClearV(m); + int loopNum=0; + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) if(!fi->IsD()) { - for(int j=0;j<3;j++)//for all edges - { - if(fi->V(j)->IsS()) continue; - - if(face::IsBorder(*fi,j))//found an unvisited border edge + for(int j=0;j<3;++j) { - he.Set(&(*fi),j,fi->V(j)); //set the face-face iterator to the current face, edge and vertex - std::vector hole; //start of a new hole - hole.push_back(fi->P(j)); // including the first vertex - numholev++; - he.v->SetS(); //set the current vertex as selected - he.NextB(); //go to the next boundary edge - - - while(fi->V(j) != he.v)//will we do not encounter the first boundary edge. + if(!fi->IsV() && face::IsBorder(*fi,j)) { - CoordType newpoint = he.v->P(); //select its vertex. - if(he.v->IsS())//check if this vertex was selected already, because then we have an additional hole. + face::Pos startPos(&*fi,j); + face::Pos curPos=startPos; + do { - //cut and paste the additional hole. - std::vector hole2; - int index = static_cast(find(hole.begin(),hole.end(),newpoint) - - hole.begin()); - for(unsigned int i=index; iSetV(); } - hole.push_back(newpoint); - numholev++; - he.v->SetS(); //set the current vertex as selected - he.NextB(); //go to the next boundary edge + while(curPos!=startPos); + ++loopNum; } - holes.push_back(hole); } - } } - return static_cast(holes.size()); + return loopNum; } /* @@ -1102,14 +1049,14 @@ public: { tri::RequireFFAdjacency(m); CCV.clear(); - tri::UpdateSelection::FaceClear(m); + tri::UpdateFlags::FaceClearV(m); std::stack sf; FacePointer fpt=&*(m.face.begin()); for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { - if(!((*fi).IsD()) && !(*fi).IsS()) + if(!((*fi).IsD()) && !(*fi).IsV()) { - (*fi).SetS(); + (*fi).SetV(); CCV.push_back(std::make_pair(0,&*fi)); sf.push(&*fi); while (!sf.empty()) @@ -1122,9 +1069,9 @@ public: if( !face::IsBorder(*fpt,j) ) { FacePointer l = fpt->FFp(j); - if( !(*l).IsS() ) + if( !(*l).IsV() ) { - (*l).SetS(); + (*l).SetV(); sf.push(l); } } @@ -1260,6 +1207,8 @@ public: static bool IsCoherentlyOrientedMesh(MeshType &m) { + RequireFFAdjacency(m); + MeshAssert::FFAdjacencyIsInitialized(m); for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) for(int i=0;i<3;++i) @@ -1269,26 +1218,22 @@ public: return true; } - static void OrientCoherentlyMesh(MeshType &m, bool &Oriented, bool &Orientable) + static void OrientCoherentlyMesh(MeshType &m, bool &_IsOriented, bool &_IsOrientable) { RequireFFAdjacency(m); - assert(&Oriented != &Orientable); - assert(m.face.back().FFp(0)); // This algorithms require FF topology initialized + MeshAssert::FFAdjacencyIsInitialized(m); + bool IsOrientable = true; + bool IsOriented = true; - Orientable = true; - Oriented = true; - - tri::UpdateSelection::FaceClear(m); + UpdateFlags::FaceClearV(m); std::stack faces; for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) { - if (!fi->IsD() && !fi->IsS()) + if (!fi->IsD() && !fi->IsV()) { // each face put in the stack is selected (and oriented) - fi->SetS(); + fi->SetV(); faces.push(&(*fi)); - - // empty the stack while (!faces.empty()) { FacePointer fp = faces.top(); @@ -1297,42 +1242,35 @@ public: // make consistently oriented the adjacent faces for (int j = 0; j < 3; j++) { - // get one of the adjacent face - FacePointer fpaux = fp->FFp(j); - int iaux = fp->FFi(j); - - if (!fpaux->IsD() && fpaux != fp && face::IsManifold(*fp, j)) + if (!face::IsBorder(*fp,j) && face::IsManifold(*fp, j)) { + FacePointer fpaux = fp->FFp(j); + int iaux = fp->FFi(j); if (!CheckOrientation(*fpaux, iaux)) { - Oriented = false; + IsOriented = false; - if (!fpaux->IsS()) - { + if (!fpaux->IsV()) face::SwapEdge(*fpaux, iaux); - assert(CheckOrientation(*fpaux, iaux)); - } else { - Orientable = false; + IsOrientable = false; break; } } - - // put the oriented face into the stack - - if (!fpaux->IsS()) + if (!fpaux->IsV()) { - fpaux->SetS(); + fpaux->SetV(); faces.push(fpaux); } } } } } - - if (!Orientable) break; + if (!IsOrientable) break; } + _IsOriented = IsOriented; + _IsOrientable = IsOrientable; } @@ -1666,7 +1604,6 @@ public: */ static bool TestFaceFaceIntersection(FaceType *f0,FaceType *f1) { - assert(f0!=f1); int sv = face::CountSharedVertex(f0,f1); if(sv==3) return true; if(sv==0) return (vcg::IntersectionTriangleTriangle((*f0),(*f1))); diff --git a/vcg/complex/algorithms/closest.h b/vcg/complex/algorithms/closest.h index f98f48d1..062404d0 100644 --- a/vcg/complex/algorithms/closest.h +++ b/vcg/complex/algorithms/closest.h @@ -82,7 +82,8 @@ namespace vcg { { public: typedef typename MESH_TYPE::VertexType VertexType; - typedef typename MESH_TYPE::EdgeType EdgeType; + typedef typename MESH_TYPE::EdgeType EdgeType; + typedef typename MESH_TYPE::FaceType FaceType; inline EmptyTMark(){} inline EmptyTMark(MESH_TYPE *){} inline void UnMarkAll() const {} @@ -90,6 +91,8 @@ namespace vcg { inline void Mark(VertexType*) const {} inline bool IsMarked(EdgeType*) const { return false; } inline void Mark(EdgeType*) const {} + inline bool IsMarked(FaceType*) const { return false; } + inline void Mark(FaceType*) const {} inline void SetMesh(void * /*m=0*/) const {} }; diff --git a/vcg/complex/algorithms/create/mc_trivial_walker.h b/vcg/complex/algorithms/create/mc_trivial_walker.h index b7fc9df1..8a745560 100644 --- a/vcg/complex/algorithms/create/mc_trivial_walker.h +++ b/vcg/complex/algorithms/create/mc_trivial_walker.h @@ -108,10 +108,8 @@ public: Vol.resize(this->siz[0]*this->siz[1]*this->siz[2]); this->ComputeDimAndVoxel(); } - - - }; + template class SimpleVoxel { @@ -163,18 +161,10 @@ private: typedef typename MeshType::VertexPointer VertexPointer; public: - // subbox is the portion of the volume to be computed - // resolution determine the sampling step: - // should be a divisor of bbox size (e.g. if bbox size is 256^3 resolution could be 128,64, etc) - - void Init(VolumeType &volume) - { - Init(volume,Box3i(Point3i(0,0,0),volume.ISize())); - } - - void Init(VolumeType &/*volume*/, Box3i subbox) + // SetExtractionBox set the portion of the volume to be traversed + void SetExtractionBox(Box3i subbox) { - _bbox = subbox; + _bbox = subbox; _slice_dimension = _bbox.DimX()*_bbox.DimZ(); _x_cs = new VertexIndex[ _slice_dimension ]; @@ -182,40 +172,39 @@ private: _z_cs = new VertexIndex[ _slice_dimension ]; _x_ns = new VertexIndex[ _slice_dimension ]; _z_ns = new VertexIndex[ _slice_dimension ]; - } - - ~TrivialWalker() - {_thr=0;} + + TrivialWalker() + { + _bbox.SetNull(); + _slice_dimension=0; + } template void BuildMesh(MeshType &mesh, VolumeType &volume, EXTRACTOR_TYPE &extractor, const float threshold, vcg::CallBackPos * cb=0) { - Init(volume); + if(_bbox.IsNull() || _slice_dimension==0) + SetExtractionBox(Box3i(Point3i(0,0,0),volume.ISize())); _volume = &volume; _mesh = &mesh; _mesh->Clear(); _thr=threshold; - vcg::Point3i p1, p2; - Begin(); extractor.Initialize(); for (int j=_bbox.min.Y(); j<(_bbox.max.Y()-1)-1; j+=1) { - if(cb && ((j%10)==0) ) cb(j*_bbox.DimY()/100.0,"Marching volume"); - for (int i=_bbox.min.X(); i<(_bbox.max.X()-1)-1; i+=1) { for (int k=_bbox.min.Z(); k<(_bbox.max.Z()-1)-1; k+=1) { - p1.X()=i; p1.Y()=j; p1.Z()=k; - p2.X()=i+1; p2.Y()=j+1; p2.Z()=k+1; + Point3i p1(i,j,k); + Point3i p2(i+1,j+1,k+1); if(volume.ValidCell(p1,p2)) extractor.ProcessCell(p1, p2); } } - NextSlice(); + NextYSlice(); } extractor.Finalize(); _volume = NULL; @@ -229,7 +218,7 @@ private: bool Exist(const vcg::Point3i &p0, const vcg::Point3i &p1, VertexPointer &v) { - int pos = p0.X()+p0.Z()*_bbox.max.X(); + int pos = p0.X()+p0.Z()*_bbox.DimX(); int vidx; if (p0.X()!=p1.X()) // punti allineati lungo l'asse X @@ -249,8 +238,8 @@ private: { int i = p1.X() - _bbox.min.X(); int z = p1.Z() - _bbox.min.Z(); - VertexIndex index = i+z*_bbox.max.X(); - VertexIndex pos; + VertexIndex index = i+z*_bbox.DimX(); + VertexIndex pos=-1; if (p1.Y()==_current_slice) { if ((pos=_x_cs[index])==-1) @@ -282,7 +271,7 @@ private: { int i = p1.X() - _bbox.min.X(); int z = p1.Z() - _bbox.min.Z(); - VertexIndex index = i+z*_bbox.max.X(); + VertexIndex index = i+z*_bbox.DimX(); VertexIndex pos; if ((pos=_y_cs[index])==-1) { @@ -298,7 +287,7 @@ private: { int i = p1.X() - _bbox.min.X(); int z = p1.Z() - _bbox.min.Z(); - VertexIndex index = i+z*_bbox.max.X(); + VertexIndex index = i+z*_bbox.DimX(); VertexIndex pos; if (p1.Y()==_current_slice) { @@ -343,7 +332,7 @@ protected: VolumeType *_volume; float _thr; - void NextSlice() + void NextYSlice() { memset(_x_cs, -1, _slice_dimension*sizeof(VertexIndex)); memset(_y_cs, -1, _slice_dimension*sizeof(VertexIndex)); diff --git a/vcg/complex/algorithms/create/platonic.h b/vcg/complex/algorithms/create/platonic.h index ef509320..f696c964 100644 --- a/vcg/complex/algorithms/create/platonic.h +++ b/vcg/complex/algorithms/create/platonic.h @@ -689,10 +689,14 @@ void SuperEllipsoid(MeshType &m, float rFeature, float sFeature, float tFeature, tri::Clean::OrientCoherentlyMesh(m,oriented,orientable); tri::UpdateSelection::Clear(m); } -// this function build a mesh starting from a vector of generic coords (objects having a triple of float at their beginning) -// and a vector of faces (objects having a triple of ints at theri beginning). -template -void BuildMeshFromCoordVectorIndexVector( MeshType & in, const V & v, const F & f) + +/** This function build a mesh starting from a vector of generic coords (InCoordType) and indexes (InFaceIndexType) + * InCoordsType needs to have a [] access method for accessing the three coordinates + * and similarly the InFaceIndexType requires [] access method for accessing the three indexes + */ + +template +void BuildMeshFromCoordVectorIndexVector(MeshType & in, const std::vector & v, const std::vector & f) { typedef typename MeshType::CoordType CoordType; typedef typename MeshType::VertexPointer VertexPointer; @@ -704,7 +708,7 @@ void BuildMeshFromCoordVectorIndexVector( MeshType & in, const V & v, const F & for(size_t i=0;i=0 ); assert( ff[1]>=0 ); assert( ff[2]>=0 ); @@ -833,12 +837,13 @@ void FaceGrid(MeshType & in, int w, int h) } -// Build a regular grid mesh of faces as a typical height field mesh -// Vertexes are assumed to be already be allocated, but not oll the grid vertexes are present. -// For this purpos a grid of indexes is also passed. negative indexes means that there is no vertex. +// Build a regular grid mesh of faces as the resulto of a sparsely regularly sampled height field. +// Vertexes are assumed to be already be allocated, but not all the grid vertexes are present. +// For this purpose vector with a grid of indexes is also passed. +// Negative indexes in this vector means that there is no vertex. template -void FaceGrid(MeshType & in, const std::vector &grid, int w, int h) +void SparseFaceGrid(MeshType & in, const std::vector &grid, int w, int h) { tri::RequireCompactness(in); assert(in.vn <= w*h); // the number of vertices should match the number of expected grid vertices diff --git a/vcg/complex/algorithms/create/plymc/plymc.h b/vcg/complex/algorithms/create/plymc/plymc.h index ed43bfe7..2f3315d3 100644 --- a/vcg/complex/algorithms/create/plymc/plymc.h +++ b/vcg/complex/algorithms/create/plymc/plymc.h @@ -35,23 +35,13 @@ #include #include -#include -#include - -#include -#include #include -#include -#include -#include -#include #include -#include #include #include #include -#include +//#include #include #include @@ -61,7 +51,6 @@ #include #include -//#include #include #include "volume.h" #include "tri_edge_collapse_mc.h" @@ -73,6 +62,15 @@ template void MCSimplify( MeshType &m, float perc, bool preserveBB=true, vcg::CallBackPos *cb=0); +/** Surface Reconstruction + * + * To allow the managment of a very large set of meshes to be merged, + * it is templated on a MeshProvider class that is able to provide the meshes to be merged. + * IT is the surface reconstrction algorithm that have been used for a long time inside the ISTI-Visual Computer Lab. + * It is mostly a variant of the Curless et al. e.g. a volumetric approach with some original weighting schemes," + * a different expansion rule, and another approach to hole filling through volume dilation/relaxations. + */ + template < class SMesh, class MeshProvider> class PlyMC { @@ -175,6 +173,7 @@ public: MeshProvider MP; Parameter p; Volume VV; + char errorMessage[1024]; /// PLYMC Methods @@ -192,21 +191,36 @@ public: { if(!(loadmask & tri::io::Mask::IOM_VERTNORMAL)) { - printf("Error, pointset MUST have normals"); - return false; + if(m.FN()==0) + { + sprintf(errorMessage,"%sError: mesh has not per vertex normals\n",errorMessage); + return false; + } + else + { + tri::Clean::RemoveUnreferencedVertex(m); + tri::Allocator::CompactEveryVector(m); + tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(m); + } } + tri::UpdateNormal::NormalizePerVertex(m); + int badNormalCnt=0; for(SVertexIterator vi=m.vert.begin(); vi!=m.vert.end();++vi) if(math::Abs(SquaredNorm((*vi).N())-1.0)>0.0001) { - printf("Error: mesh has not per vertex normalized normals\n"); + badNormalCnt++; + tri::Allocator::DeleteVertex(m,*vi); + } + tri::Allocator::CompactEveryVector(m); + if(badNormalCnt > m.VN()/10) + { + sprintf(errorMessage,"%sError: mesh has null normals\n",errorMessage); return false; } - + if(!(loadmask & tri::io::Mask::IOM_VERTQUALITY)) tri::UpdateQuality::VertexConstant(m,0); tri::UpdateNormal::PerVertexMatrix(m,Tr); - //if(!(loadmask & tri::io::Mask::IOM_VERTCOLOR)) - // saveMask &= ~tri::io::Mask::IOM_VERTCOLOR; } else // processing for triangle meshes { @@ -223,7 +237,6 @@ public: tri::UpdateTopology::VertexFace(m); tri::UpdateFlags::FaceBorderFromVF(m); tri::Geodesic::DistanceFromBorder(m); - // tri::UpdateQuality::VertexGeodesicFromBorder(m); } } @@ -325,8 +338,9 @@ public: return true; } -void Process(vcg::CallBackPos *cb=0) +bool Process(vcg::CallBackPos *cb=0) { + sprintf(errorMessage,""); printf("bbox scanning...\n"); fflush(stdout); Matrix44f Id; Id.SetIdentity(); MP.InitBBox(); @@ -344,7 +358,6 @@ void Process(vcg::CallBackPos *cb=0) voxdim = fullb.max - fullb.min; - int TotAdd=0,TotMC=0,TotSav=0; // if kcell==0 the number of cells is computed starting from required voxel size; __int64 cells; if(p.NCell>0) cells = (__int64)(p.NCell)*(__int64)(1000); @@ -364,6 +377,7 @@ void Process(vcg::CallBackPos *cb=0) } + int TotAdd=0,TotMC=0,TotSav=0; // partial timings counter for(p.IPos[0]=p.IPosS[0];p.IPos[0]<=p.IPosE[0];++p.IPos[0]) for(p.IPos[1]=p.IPosS[1];p.IPos[1]<=p.IPosE[1];++p.IPos[1]) @@ -405,8 +419,8 @@ void Process(vcg::CallBackPos *cb=0) res = InitMesh(*sm,MP.MeshName(i).c_str(),MP.Tr(i)); if(!res) { - printf("Failed Init of mesh %s",MP.MeshName(i).c_str()); - return; + sprintf(errorMessage,"%sFailed Init of mesh %s\n",errorMessage,MP.MeshName(i).c_str()); + return false ; } } res |= AddMeshToVolumeM(*sm, MP.MeshName(i),MP.W(i)); @@ -452,26 +466,20 @@ void Process(vcg::CallBackPos *cb=0) VV.SlicedPPM("final","__",p.SliceNum); VV.SlicedPPMQ("final","__",p.SliceNum); } - //MCMesh me; - // MCMesh me; if(res) { - typedef vcg::tri::TrivialWalker > Walker; + typedef vcg::tri::TrivialWalker > Walker; typedef vcg::tri::MarchingCubes MarchingCubes; - //typedef vcg::tri::ExtendedMarchingCubes ExtendedMarchingCubes; Walker walker; MarchingCubes mc(me, walker); - Box3i currentSubBox=VV.SubPartSafe; - Point3i currentSubBoxRes=VV.ssz; /**********************/ if(cb) cb(50,"Step 2: Marching Cube..."); else printf("Step 2: Marching Cube...\n"); /**********************/ - walker.Init(VV,currentSubBox); + walker.SetExtractionBox(VV.SubPartSafe); walker.BuildMesh(me,VV,mc,0); - // walker.BuildMesh(me,VV,mc,currentSubBox,currentSubBoxRes); typename MCMesh::VertexIterator vi; Box3f bbb; bbb.Import(VV.SubPart); @@ -481,8 +489,7 @@ void Process(vcg::CallBackPos *cb=0) vcg::tri::Allocator< MCMesh >::DeleteVertex(me,*vi); VV.DeInterize((*vi).P()); } - typename MCMesh::FaceIterator fi; - for (fi = me.face.begin(); fi != me.face.end(); ++fi) + for (typename MCMesh::FaceIterator fi = me.face.begin(); fi != me.face.end(); ++fi) { if((*fi).V(0)->IsD() || (*fi).V(1)->IsD() || (*fi).V(2)->IsD() ) vcg::tri::Allocator< MCMesh >::DeleteFace(me,*fi); @@ -526,6 +533,7 @@ void Process(vcg::CallBackPos *cb=0) { printf("----------- skipping SubBlock %2i %2i %2i ----------\n",p.IPos[0],p.IPos[1],p.IPos[2]); } + return true; } diff --git a/vcg/complex/algorithms/create/plymc/simplemeshprovider.h b/vcg/complex/algorithms/create/plymc/simplemeshprovider.h new file mode 100644 index 00000000..1ce3be53 --- /dev/null +++ b/vcg/complex/algorithms/create/plymc/simplemeshprovider.h @@ -0,0 +1,233 @@ +#ifndef SIMPLEMESHPROVIDER_H +#define SIMPLEMESHPROVIDER_H +#include "../../meshlab/alnParser.h" +#include +#include +#include +#include +#include + +/* + * A mesh provider class has the simpler role of passing the set of meshes to be merged to the surface reconstrcution algorithm. + * The only reason for this abstraction is that, plymc can work in a out-of-core way and the loading of the needed range maps can be optimized with a high level caching system. + */ +template +class MinimalMeshProvider +{ +private: + + std::vector< std::string > nameVec; + std::vector< TriMeshType * > meshVec; + std::vector trVec; + std::vector weightVec; // weight tot be applied to each mesh. + vcg::Box3f fullBBox; + +public: + bool Find(const std::string &name, TriMeshType * &sm) + { + for(int i=0;i +class MeshCache +{ + class Pair + { + public: + Pair(){used=0;} + TriMeshType *M; + std::string Name; + int used; // 'data' dell'ultimo accesso. si butta fuori quello lru + }; + + std::list MV; + +public: + void clear(); + + MeshCache() {MeshCacheSize=6;} + ~MeshCache() { + typename std::list::iterator mi; + for(mi=MV.begin();mi!=MV.end();++mi) + delete (*mi).M; + } + + + /** + * @brief Find load a mesh form the cache if it is in or from the disk otherwise + * @param name what mesh to find + * @param sm the pointer loaded mesh + * @return true if the mesh was already in cache + * + */ + bool Find(const std::string &name, TriMeshType * &sm) + { + typename std::list::iterator mi; + typename std::list::iterator oldest; // quello che e' piu' tempo che non viene acceduto. + int last; + + last = std::numeric_limits::max(); + oldest = MV.begin(); + + for(mi=MV.begin();mi!=MV.end();++mi) + { + if((*mi).usedMeshCacheSize) { + sm=(*oldest).M; + (*oldest).used=0; + (*oldest).Name=name; + } else { + MV.push_back(Pair()); + MV.back().Name=name; + MV.back().M=new TriMeshType(); + sm=MV.back().M; + } + return false; + } + + + size_t MeshCacheSize; + size_t size() const {return MV.size();} +}; + +template +class SimpleMeshProvider +{ +private: + std::vector< std::string > meshnames; + std::vector TrV; + std::vector WV; // weight tot be applied to each mesh. + std::vector BBV; // bbox of the transformed meshes.. + vcg::Box3f fullBBox; + MeshCache MC; + +public: + + int size() {return meshnames.size();} + + int getCacheSize() {return MC.MeshCacheSize;} + int setCacheSize(size_t newsize) + { + if(newsize == MC.MeshCacheSize) + return MC.MeshCacheSize; + if(newsize <= 0) + return MC.MeshCacheSize; + + MC.MeshCacheSize = newsize; + return newsize; + } + + bool openALN (const char* alnName) + { + vector rmaps; + ALNParser::ParseALN(rmaps, alnName); + + for(size_t i=0; i::FileExtension(meshnames[i],"PLY") || tri::io::Importer::FileExtension(meshnames[i],"ply")) + { + ret=ply::ScanBBox(meshnames[i].c_str(),BBV[i],TrV[i],true,0); + } + else + { + printf("Trying to import a non-ply file %s\n",meshnames[i].c_str());fflush(stdout); + TriMeshType m; + ret = (tri::io::Importer::Open(m,meshnames[i].c_str()) == tri::io::Importer::E_NOERROR); + tri::UpdatePosition::Matrix(m,TrV[i]); + tri::UpdateBounding::Box(m); + BBV[i].Import(m.bbox); + } + if( ! ret) + { + printf("\n\nwarning:\n file '%s' not found\n",meshnames[i].c_str());fflush(stdout); + continue; + } + fullBBox.Add(BBV[i]); + } + return true; + } + +}; + +class SVertex; +class SFace; +class SUsedTypes: public vcg::UsedTypes < vcg::Use::AsVertexType, + vcg::Use::AsFaceType >{}; + +class SVertex : public Vertex< SUsedTypes, vertex::Coord3f, vertex::Normal3f,vertex::VFAdj, vertex::BitFlags, vertex::Color4b, vertex::Qualityf>{}; +class SFace : public Face< SUsedTypes, face::VertexRef, face::Normal3f,face::Qualityf, face::VFAdj, face::BitFlags> {}; +class SMesh : public tri::TriMesh< std::vector< SVertex>, std::vector< SFace > > {}; + +} + +#endif // SIMPLEMESHPROVIDER_H diff --git a/vcg/complex/algorithms/create/plymc/tri_edge_collapse_mc.h b/vcg/complex/algorithms/create/plymc/tri_edge_collapse_mc.h index 733b4b96..72a391be 100644 --- a/vcg/complex/algorithms/create/plymc/tri_edge_collapse_mc.h +++ b/vcg/complex/algorithms/create/plymc/tri_edge_collapse_mc.h @@ -22,7 +22,8 @@ ****************************************************************************/ #ifndef __TRI_EDGE_COLLAPSE_MC__ #define __TRI_EDGE_COLLAPSE_MC__ -#include +namespace vcg{ +namespace tri{ class TriEdgeCollapseMCParameter : public BaseParameterClass { @@ -125,5 +126,7 @@ class MCTriEdgeCollapse: public tri::TriEdgeCollapse< MCTriMesh, VertexPair, MYT }; +} +} #endif diff --git a/vcg/complex/algorithms/create/plymc/volume.h b/vcg/complex/algorithms/create/plymc/volume.h index d543c32e..0a9ba7fc 100644 --- a/vcg/complex/algorithms/create/plymc/volume.h +++ b/vcg/complex/algorithms/create/plymc/volume.h @@ -24,26 +24,10 @@ #ifndef __VOLUME_H__ #define __VOLUME_H__ -#ifdef __MINGW32__ -#define _int64 __int64 -#endif - #include "voxel.h" -#include "svoxel.h" -#include #include -//#define BLOCKSIDE() 8 - -// Stato di un voxel - -// B() dice se ci sono dati in uno stadio usabile. -// Cnt() dice quanti ce ne sono stati sommati (per la normalizzazione) - -// b==false cnt==0 totalmente non inzializzato (Zero) -// b==false cnt >0 da normalizzare -// b==true cnt==0 gia' normalizzato -// b==true cnt >0 Errore!!! +namespace vcg { // forward definition template < class VOL > @@ -67,7 +51,7 @@ const char *SFormat( const char * f, ... ) template class Volume { public: - typedef SCALAR_TYPE scalar; + typedef SCALAR_TYPE scalar; typedef Point3 Point3x; typedef Box3 Box3x; @@ -172,7 +156,7 @@ bool Verbose; // se true stampa un sacco di info in piu su logfp; for(size_t i=0;i inside the subblock of voxel (x,y,z). + * return true if the subblock is allocated. + */ bool Pos(const int &_x,const int &_y,const int &_z, int & rpos,int &lpos) const { int x=_x-SubPartSafe.min[0]; int y=_y-SubPartSafe.min[1]; int z=_z-SubPartSafe.min[2]; assert(_x>=SubPartSafe.min[0] && _x=SubPartSafe.min[1] && _y=SubPartSafe.min[2] && _z=SubPartSafe.min[1] && _y=SubPartSafe.min[2] && _z=0 && x=0 && y=0 && z0 da normalizzare +// b==true cnt==0 gia' normalizzato +// b==true cnt >0 Errore!!! + + +/** + * + */ + template class Voxel { - public: - typedef SCALAR_TYPE scalar; +public: + typedef SCALAR_TYPE scalar; - Voxel(SCALAR_TYPE vv, bool bb, Point3 nn, short _cnt) {v=vv;b=bb;n=nn;cnt=_cnt;} - Voxel(SCALAR_TYPE vv, Point3 nn, scalar qq) {v=vv;b=true;n=nn;cnt=0;q=qq;} + Voxel(SCALAR_TYPE vv, bool bb, Point3 nn, short _cnt) {v=vv;b=bb;n=nn;cnt=_cnt;} + Voxel(SCALAR_TYPE vv, Point3 nn, scalar qq) {v=vv;b=true;n=nn;cnt=0;q=qq;} - const scalar &N(const int i) const { return n[i]; } + const Point3 &N() const { return n; } - const Point3 &N() const { return n; } + void SetN(const Point3 &nn) { n=nn; } + const scalar &V() const { return v; } - void SetN(const Point3 &nn) { n=nn; } - const scalar &V() const { return v; } + void SetV(const scalar &vv) { v=vv; } - void SetV(const scalar &vv) { v=vv; } + const scalar &Q() const { return q; } - const scalar &Q() const { return q; } - - void SetQ(const scalar &qq) { q=qq; } + void SetQ(const scalar &qq) { q=qq; } - bool B() const {return b;}; - void SetB(bool val) {b=val;} - int Cnt() const {return cnt;} - void SetCnt(int val) {cnt=val;} - inline void Blend( Voxel const & vx, scalar w) + bool B() const {return b;}; + void SetB(bool val) {b=val;} + int Cnt() const {return cnt;} + void SetCnt(int val) {cnt=val;} + inline void Blend( Voxel const & vx, scalar w) + { + float w1=1.0-w; + v=v*w1+vx.v*w; + q=q*w1+vx.q*w; + n=n*w1+vx.n*w; + //return *this; + } + + inline Voxel & operator += ( Voxel const & vx) + { + assert(!b); + if(cnt==0) { - float w1=1.0-w; - v=v*w1+vx.v*w; - q=q*w1+vx.q*w; - n=n*w1+vx.n*w; - //return *this; + v=vx.v; + q=vx.q; + n=vx.n; + cnt=1; + b=false; } - - inline Voxel & operator += ( Voxel const & vx) + else { - if(cnt==0) - { - assert(!b); - v=vx.v; - q=vx.q; - n=vx.n; - cnt=1; - b=false; - } - else - { - assert(!b); - v+=vx.v; - q+=vx.q; - n+=vx.n; - ++cnt; - } - return *this; + v+=vx.v; + q+=vx.q; + n+=vx.n; + ++cnt; } + return *this; + } - inline bool Normalize(int thr) + inline bool Normalize(int thr) + { + assert(cnt>0); + assert(!B()); + if(cnt0); - assert(!B()); - if(cnt n; +protected: + bool b; + short cnt; + scalar v; + scalar q; + Point3 n; }; @@ -129,67 +144,67 @@ class Voxelfc :public Voxel { public: - Voxelfc(float vv, bool bb, Point3f nn, short _cnt) :Voxel(vv,bb,nn,_cnt){} - Voxelfc(float vv, Point3f nn, scalar qq) :Voxel(vv,nn,qq) {} - Voxelfc(float vv, Point3f nn, scalar qq,Color4b cc) :Voxel(vv,nn,qq) - { - c[0]=cc[0]; - c[1]=cc[1]; - c[2]=cc[2]; - } + Voxelfc(float vv, bool bb, Point3f nn, short _cnt) :Voxel(vv,bb,nn,_cnt){} + Voxelfc(float vv, Point3f nn, scalar qq) :Voxel(vv,nn,qq) {} + Voxelfc(float vv, Point3f nn, scalar qq,Color4b cc) :Voxel(vv,nn,qq) + { + c[0]=cc[0]; + c[1]=cc[1]; + c[2]=cc[2]; + } - inline bool Normalize(int thr) - { - if(cnt>=thr) c/=cnt; - return Voxel::Normalize(thr); - } + inline bool Normalize(int thr) + { + if(cnt>=thr) c/=cnt; + return Voxel::Normalize(thr); + } - static const Voxelfc &Zero() { - static Voxelfc tt(0,false,Point3f(0,0,0),0); - return tt; - } + static const Voxelfc &Zero() { + static Voxelfc tt(0,false,Point3f(0,0,0),0); + return tt; + } - void Merge(const Voxelfc &VOX) - { - c=( c*q + VOX.C()*VOX.Q() )/(q+VOX.Q()); - Voxel::Merge(VOX); - } + void Merge(const Voxelfc &VOX) + { + c=( c*q + VOX.C()*VOX.Q() )/(q+VOX.Q()); + Voxel::Merge(VOX); + } - void Set(const Voxelfc &VOX) - { - Voxel::Set(VOX); - c=VOX.c; - } + void Set(const Voxelfc &VOX) + { + Voxel::Set(VOX); + c=VOX.c; + } - const float &C(const int i) const { return c[i]; } - const Point3f &C() const { return c; } - void SetC(const Point3f &cc) { c=cc; } - Color4b C4b() const - { - static Color4b cc; - cc=Color4b(c[0],c[1],c[2],255); - return cc; - } - inline void Blend( Voxelfc const & vx, scalar w) - { - float w1=1.0-w; - v=v*w1+vx.v*w; - q=q*w1+vx.q*w; - n=n*w1+vx.n*w; - c=c*w1+vx.c*w; - //return *this; - } + const float &C(const int i) const { return c[i]; } + const Point3f &C() const { return c; } + void SetC(const Point3f &cc) { c=cc; } + Color4b C4b() const + { + static Color4b cc; + cc=Color4b(c[0],c[1],c[2],255); + return cc; + } + inline void Blend( Voxelfc const & vx, scalar w) + { + float w1=1.0-w; + v=v*w1+vx.v*w; + q=q*w1+vx.q*w; + n=n*w1+vx.n*w; + c=c*w1+vx.c*w; + //return *this; + } - inline Voxelfc & operator += ( Voxelfc const & vx) - { - Voxel::operator +=(vx); - if(cnt==1) c =vx.c; - else c+=vx.c; - return *this; - } + inline Voxelfc & operator += ( Voxelfc const & vx) + { + Voxel::operator +=(vx); + if(cnt==1) c =vx.c; + else c+=vx.c; + return *this; + } private: - Point3f c; + Point3f c; }; - +} #endif diff --git a/vcg/complex/algorithms/hole.h b/vcg/complex/algorithms/hole.h index 1febb289..c906ccb6 100644 --- a/vcg/complex/algorithms/hole.h +++ b/vcg/complex/algorithms/hole.h @@ -36,6 +36,7 @@ namespace vcg { namespace tri { + /* An ear is identified by TWO pos. The Three vertexes of an Ear are: @@ -46,31 +47,32 @@ namespace vcg { e1 == e0.NextB(); e1.FlipV() == e0; - Situazioni ear non manifold, e degeneri (buco triangolare) - - T XXXXXXXXXXXXX A /XXXXX B en/XXXXX - /XXXXXXXXXXXXXXX /XXXXXX /XXXXXX - XXXXXXep==en XXX ep\ /en XXXX /e1 XXXX - XXXXXX ----/| XX ------ ----/| XX ------ ----/|XXX - XXXXXX| /e1 XX XXXXXX| /e1 XX XXXXXX| o/e0 XX - XXXXXX| /XXXXXX XXXXXX| /XXXXXX XXXXXX| /XXXXXX - XXX e0|o/XXXXXXX XXX e0|o/XXXXXXX XXX ep| /XXXXXXX - XXX \|/XXXXXXXX XXX \|/XXXXXXXX XXX \|/XXXXXXXX - XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX */ +/** + * Basic class for representing an 'ear' in a hole. + * + * Require FF-adajcncy and edge-manifoldness around the mesh (at most two triangles per edge) + * + * An ear is represented by two consecutive Pos e0,e1. + * The vertex pointed by the first pos is the 'corner' of the ear + * + * + */ template class TrivialEar { public: - typedef typename MESH::FaceType FaceType; - typedef typename MESH::FacePointer FacePointer; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::VertexType VertexType; + typedef typename MESH::FacePointer FacePointer; typedef typename MESH::VertexPointer VertexPointer; - typedef typename face::Pos PosType; - typedef typename MESH::ScalarType ScalarType; - typedef typename MESH::CoordType CoordType; - + typedef typename face::Pos PosType; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::CoordType CoordType; + PosType e0; PosType e1; CoordType n; // the normal of the face defined by the ear + const char * Dump() {return 0;} // The following members are useful to consider the Ear as a generic // with p0 the 'center' of the ear. @@ -116,14 +118,50 @@ public: virtual void ComputeQuality() { quality = QualityFace(*this) ; } bool IsUpToDate() {return ( e0.IsBorder() && e1.IsBorder());} // An ear is degenerated if both of its two endpoints are non manifold. - bool IsDegen(const int nonManifoldBit) + bool IsDegen() { - if(e0.VFlip()->IsUserBit(nonManifoldBit) && e1.V()->IsUserBit(nonManifoldBit)) + if(e0.VFlip()->IsUserBit(NonManifoldBit()) && e1.V()->IsUserBit(NonManifoldBit())) return true; else return false; } bool IsConcave() const {return(angleRad > (float)M_PI);} + + /** NonManifoldBit + * To handle non manifoldness situations we keep track + * of the vertices of the hole boundary that are traversed by more than a single boundary. + * + */ + static int &NonManifoldBit() { static int _NonManifoldBit=0; return _NonManifoldBit; } + static int InitNonManifoldBitOnHoleBoundary(const PosType &p) + { + if(NonManifoldBit()==0) + NonManifoldBit() = VertexType::NewBitFlag(); + int holeSize=0; + + //First loop around the hole to mark non manifold vertices. + PosType ip = p; // Pos iterator + do{ + ip.V()->ClearUserBit(NonManifoldBit()); + ip.V()->ClearV(); + ip.NextB(); + holeSize++; + } while(ip!=p); + + ip = p; // Re init the pos iterator for another loop (useless if everithing is ok!!) + do{ + if(!ip.V()->IsV()) + ip.V()->SetV(); + else // All the vertexes that are visited more than once are non manifold + ip.V()->SetUserBit(NonManifoldBit()); + ip.NextB(); + } while(ip!=p); + return holeSize; + } + + + + // When you close an ear you have to check that the newly added triangle does not create non manifold situations // This can happen if the new edge already exists in the mesh. // We test that looping around one extreme of the ear we do not find the other vertex @@ -141,8 +179,38 @@ public: while(!pp.IsBorder()); return true; } - - virtual bool Close(PosType &np0, PosType &np1, FaceType * f) + /** + * @brief Close the current ear by adding a triangle to the mesh + * and returning up to two new possible ears to be closed. + * + * @param np0 The first new pos to be inserted in the heap + * @param np1 The second new pos + * @param f the already allocated face to be used to close the ear + * @return true if it successfully add a triangle + * + * +\ + * +++\ ------- + * +++ep\ /| +++en/\ + * +++---| /e1 ++++++++\ + * ++++++| /++++++++++++++\ + * +++ e0|o /+++++++++++++++++++ + * +++ \|/+++++++++++++++++++++ + * +++++++++++++++++++++++++++++ + * + * There are three main peculiar cases: + + * (T)+++++++++++++ (A) /+++++ (B) /en+++++++ + * /+++++++++++++++ /++++++ /++++++++++ + * ++++++ep==en +++ ep\ /en ++++ /e1 ++++++++ + * ++++++ ----/| ++ ------ ----/| ++ ------------/|+++ + * ++++++| /e1 ++ ++++++| /e1 ++ ++++++| o/e0|+++ + * ++++++| /++++++ ++++++| /++++++ ++++++| /++++++++ + * +++ e0|o/+++++++ +++ e0|o/+++++++ +++ ep| /++++++++++ + * +++ \|/++++++++ +++ \|/++++++++ +++ \|/++++++++++++ + * ++++++++++++++++ ++++++++++++++++ ++++++++++++++++++++ + */ + + virtual bool Close(PosType &np0, PosType &np1, FaceType *f) { // simple topological check if(e0.f==e1.f) { @@ -150,9 +218,8 @@ public: return false; } - //usato per generare una delle due nuove orecchie. - PosType ep=e0; ep.FlipV(); ep.NextB(); ep.FlipV(); // he precedente a e0 - PosType en=e1; en.NextB(); // he successivo a e1 + PosType ep=e0; ep.FlipV(); ep.NextB(); ep.FlipV(); // ep previous + PosType en=e1; en.NextB(); // en next if(ep!=en) if(!CheckManifoldAfterEarClose()) return false; @@ -165,7 +232,7 @@ public: face::FFAttachManifold(f,1,e1.f,e1.z); face::FFSetBorder(f,2); - // caso ear degenere per buco triangolare + // First Special Case (T): Triangular hole if(ep==en) { //printf("Closing the last triangle"); @@ -173,30 +240,38 @@ public: np0.SetNull(); np1.SetNull(); } - // Caso ear non manifold a + // Second Special Case (A): Non Manifold on ep else if(ep.v==en.v) { //printf("Ear Non manif A\n"); + assert(ep.v->IsUserBit(NonManifoldBit())); + ep.v->ClearUserBit(NonManifoldBit()); PosType enold=en; en.NextB(); face::FFAttachManifold(f,2,enold.f,enold.z); np0=ep; - np1=en; + assert(!np0.v->IsUserBit(NonManifoldBit())); + np1.SetNull(); } - // Caso ear non manifold b + // Third Special Case (B): Non Manifold on e1 else if(ep.VFlip()==e1.v) { + assert(e1.v->IsUserBit(NonManifoldBit())); + e1.v->ClearUserBit(NonManifoldBit()); //printf("Ear Non manif B\n"); PosType epold=ep; ep.FlipV(); ep.NextB(); ep.FlipV(); face::FFAttachManifold(f,2,epold.f,epold.z); np0=ep; // assign the two new - np1=en; // pos that denote the ears + assert(!np0.v->IsUserBit(NonManifoldBit())); + np1.SetNull(); // pos that denote the ears } - else // caso standard // Now compute the new ears; + else // Standard Case. { np0=ep; + if(np0.v->IsUserBit(NonManifoldBit())) np0.SetNull(); np1=PosType(f,2,e1.v); + if(np1.v->IsUserBit(NonManifoldBit())) np1.SetNull(); } return true; @@ -315,174 +390,138 @@ public: } }; // end class SelfIntersectionEar -// Funzione principale per chiudier un buco in maniera topologicamente corretta. -// Gestisce situazioni non manifold ragionevoli -// (tutte eccetto quelle piu' di 2 facce per 1 edge). -// Controlla che non si generino nuove situazioni non manifold chiudendo orecchie -// che sottendono un edge che gia'esiste. + +/** Hole + * Main hole filling templated class. + * + */ template class Hole { public: - typedef typename MESH::VertexType VertexType; - typedef typename MESH::VertexPointer VertexPointer; - typedef typename MESH::ScalarType ScalarType; - typedef typename MESH::FaceType FaceType; - typedef typename MESH::FacePointer FacePointer; - typedef typename MESH::FaceIterator FaceIterator; - typedef typename MESH::CoordType CoordType; - typedef typename vcg::Box3 Box3Type; - typedef typename face::Pos PosType; - + typedef typename MESH::VertexType VertexType; + typedef typename MESH::VertexPointer VertexPointer; + typedef typename MESH::ScalarType ScalarType; + typedef typename MESH::FaceType FaceType; + typedef typename MESH::FacePointer FacePointer; + typedef typename MESH::FaceIterator FaceIterator; + typedef typename MESH::CoordType CoordType; + typedef typename vcg::Box3 Box3Type; + typedef typename face::Pos PosType; + public: - class Info - { - public: - Info(){} - Info(PosType const &pHole, int const pHoleSize, Box3 &pHoleBB) - { - p=pHole; - size=pHoleSize; - bb=pHoleBB; - } - - PosType p; - int size; - Box3Type bb; - - bool operator < (const Info & hh) const {return size < hh.size;} - - ScalarType Perimeter() - { - ScalarType sum=0; - PosType ip = p; - do - { - sum+=Distance(ip.v->cP(),ip.VFlip()->cP()); - ip.NextB(); - } - while (ip != p); - return sum; - } - - // Support function to test the validity of a single hole loop - // for now it test only that all the edges are border; - // The real test should check if all non manifold vertices - // are touched only by edges belonging to this hole loop. - bool CheckValidity() + class Info + { + public: + Info(){} + Info(PosType const &pHole, int const pHoleSize, Box3 &pHoleBB) + { + p=pHole; + size=pHoleSize; + bb=pHoleBB; + } + + PosType p; + int size; + Box3Type bb; + + bool operator < (const Info & hh) const {return size < hh.size;} + + ScalarType Perimeter() + { + ScalarType sum=0; + PosType ip = p; + do { - if(!p.IsBorder()) - return false; - PosType ip=p;ip.NextB(); - for(;ip!=p;ip.NextB()) - { - if(!ip.IsBorder()) - return false; - } - return true; + sum+=Distance(ip.v->cP(),ip.VFlip()->cP()); + ip.NextB(); } - }; - - - class EdgeToBeAvoided - { - VertexPointer v0,v1; - EdgeToBeAvoided(VertexPointer _v0, VertexPointer _v1):v0(_v0),v1(_v1) - { - if(v0>v1) swap(v0,v1); - } - bool operator < (const EdgeToBeAvoided &e) - { - if(this->v0!=e.v0) return this->v0v1 static void FillHoleEar(MESH &m, // The mesh to be filled - Info &h, // the particular hole to be filled + const PosType &p, // the particular hole to be filled std::vector &facePointersToBeUpdated) { - //Aggiungo le facce e aggiorno il puntatore alla faccia! - FaceIterator f = tri::Allocator::AddFaces(m, h.size-2, facePointersToBeUpdated); + + assert(tri::IsValidPointer(m,p.f)); + assert(p.IsBorder()); + int holeSize = EAR::InitNonManifoldBitOnHoleBoundary(p); + FaceIterator f = tri::Allocator::AddFaces(m, holeSize-2, facePointersToBeUpdated); - assert(h.p.f >= &*m.face.begin()); - assert(h.p.f <= &m.face.back()); - assert(h.p.IsBorder()); - - std::vector< EAR > EarHeap; - EarHeap.reserve(h.size); - int nmBit= VertexType::NewBitFlag(); // non manifoldness bit - - //First loops around the hole to mark non manifold vertices. - PosType ip = h.p; // Pos iterator - do{ - ip.V()->ClearUserBit(nmBit); - ip.V()->ClearV(); - ip.NextB(); - } while(ip!=h.p); - - ip = h.p; // Re init the pos iterator for another loop (useless if everithing is ok!!) - do{ - if(!ip.V()->IsV()) - ip.V()->SetV(); // All the vertexes that are visited more than once are non manifold - else ip.V()->SetUserBit(nmBit); - ip.NextB(); - } while(ip!=h.p); - - PosType fp = h.p; + std::priority_queue< EAR > EarHeap; + PosType fp = p; do{ EAR appEar = EAR(fp); - EarHeap.push_back( appEar ); + if(!fp.v->IsUserBit(EAR::NonManifoldBit())) + EarHeap.push( appEar ); //printf("Adding ear %s ",app.Dump()); fp.NextB(); assert(fp.IsBorder()); - }while(fp!=h.p); + }while(fp!=p); - int cnt=h.size; - - make_heap(EarHeap.begin(), EarHeap.end()); - - //finche' il buco non e' chiuso o non ci sono piu' orecchie da analizzare. - while( cnt > 2 && !EarHeap.empty() ) + // Main Ear closing Loop + while( holeSize > 2 && !EarHeap.empty() ) { - //printf("Front of the heap is %s", H.front().Dump()); - pop_heap(EarHeap.begin(), EarHeap.end()); // retrieve the MAXIMUM value and put in the back; - EAR BestEar=EarHeap.back(); - EarHeap.pop_back(); + EAR BestEar=EarHeap.top(); + EarHeap.pop(); - if(BestEar.IsUpToDate() && !BestEar.IsDegen(nmBit)) + if(BestEar.IsUpToDate() && !BestEar.IsDegen()) { if((*f).HasPolyInfo()) (*f).Alloc(3); PosType ep0,ep1; if(BestEar.Close(ep0,ep1,&*f)) { if(!ep0.IsNull()){ - EarHeap.push_back(EAR(ep0)); - push_heap( EarHeap.begin(), EarHeap.end()); + assert(!ep0.v->IsUserBit(EAR::NonManifoldBit())); + EarHeap.push(EAR(ep0)); } if(!ep1.IsNull()){ - EarHeap.push_back(EAR(ep1)); - push_heap( EarHeap.begin(), EarHeap.end()); + assert(!ep1.v->IsUserBit(EAR::NonManifoldBit())); + EarHeap.push(EAR(ep1)); } - --cnt; + --holeSize; ++f; } }//is update() - }//fine del while principale. - + } + + // If the hole had k non manifold vertexes it requires less than n-2 face ( it should be n - 2*(k+1) ), + // so we delete the remaining ones. while(f!=m.face.end()){ tri::Allocator::DeleteFace(m,*f); f++; } - - VertexType::DeleteBitFlag(nmBit); // non manifoldness bit } template @@ -504,7 +543,7 @@ template if(cb) (*cb)(indCb*10/vinfo.size(),"Closing Holes"); if((*ith).size < sizeHole){ holeCnt++; - FillHoleEar< EAR >(m, *ith,facePtrToBeUpdated); + FillHoleEar< EAR >(m, (*ith).p,facePtrToBeUpdated); } } return holeCnt; @@ -555,7 +594,7 @@ template for(fpi=EAR::AdjacencyRing().begin();fpi!=EAR::AdjacencyRing().end();++fpi) facePtrToBeUpdated.push_back( &*fpi ); - FillHoleEar(m, *ith,facePtrToBeUpdated); + FillHoleEar(m, ith->p,facePtrToBeUpdated); EAR::AdjacencyRing().clear(); } } @@ -671,207 +710,207 @@ template return false; } - static Weight computeWeight( int i, int j, int k, - std::vector pv, - std::vector< std::vector< int > > v) + static Weight computeWeight( int i, int j, int k, + std::vector pv, + std::vector< std::vector< int > > v) + { + PosType pi = pv[i]; + PosType pj = pv[j]; + PosType pk = pv[k]; + + //test complex edge + if(existEdge(pi,pj) || existEdge(pj,pk)|| existEdge(pk,pi) ) + { + return Weight(); + } + // Return an infinite weight, if one of the neighboring patches + // could not be created. + if(v[i][j] == -1){return Weight();} + if(v[j][k] == -1){return Weight();} + + //calcolo il massimo angolo diedrale, se esiste. + float angle = 0.0f; + PosType px; + if(i + 1 == j) + { + px = pj; + px.FlipE(); px.FlipV(); + angle = std::max(angle , ComputeDihedralAngle(pi.v->P(), pj.v->P(), pk.v->P(), px.v->P()) ); + } + else + { + angle = std::max( angle, ComputeDihedralAngle(pi.v->P(),pj.v->P(), pk.v->P(), pv[ v[i][j] ].v->P())); + } + + if(j + 1 == k) + { + px = pk; + px.FlipE(); px.FlipV(); + angle = std::max(angle , ComputeDihedralAngle(pj.v->P(), pk.v->P(), pi.v->P(), px.v->P()) ); + } + else + { + angle = std::max( angle, ComputeDihedralAngle(pj.v->P(),pk.v->P(), pi.v->P(), pv[ v[j][k] ].v->P())); + } + + if( i == 0 && k == (int)v.size() - 1) + { + px = pi; + px.FlipE(); px.FlipV(); + angle = std::max(angle , ComputeDihedralAngle(pk.v->P(), pi.v->P(), pj.v->P(),px.v->P() ) ); + } + + ScalarType area = ( (pj.v->P() - pi.v->P()) ^ (pk.v->P() - pi.v->P()) ).Norm() * 0.5; + + return Weight(angle, area); + } + + static void calculateMinimumWeightTriangulation(MESH &m, FaceIterator f,std::vector vv ) + { + std::vector< std::vector< Weight > > w; //matrice dei pesi minimali di ogni orecchio preso in conzideraione + std::vector< std::vector< int > > vi;//memorizza l'indice del terzo vertice del triangolo + + //hole size + int nv = vv.size(); + + w.clear(); + w.resize( nv, std::vector( nv, Weight() ) ); + + vi.resize( nv, std::vector( nv, 0 ) ); + + //inizializzo tutti i pesi possibili del buco + for ( int i = 0; i < nv-1; ++i ) + w[i][i+1] = Weight( 0, 0 ); + + //doppio ciclo for per calcolare di tutti i possibili triangoli i loro pesi. + for ( int j = 2; j < nv; ++j ) + { + for ( int i = 0; i + j < nv; ++i ) + { + //per ogni triangolazione mi mantengo il minimo valore del peso tra i triangoli possibili + Weight minval; + + //indice del vertice che da il peso minimo nella triangolazione corrente + int minIndex = -1; + + //ciclo tra i vertici in mezzo a i due prefissati + for ( int m = i + 1; m < i + j; ++m ) { - PosType pi = pv[i]; - PosType pj = pv[j]; - PosType pk = pv[k]; - - //test complex edge - if(existEdge(pi,pj) || existEdge(pj,pk)|| existEdge(pk,pi) ) - { - return Weight(); - } - // Return an infinite weight, if one of the neighboring patches - // could not be created. - if(v[i][j] == -1){return Weight();} - if(v[j][k] == -1){return Weight();} - - //calcolo il massimo angolo diedrale, se esiste. - float angle = 0.0f; - PosType px; - if(i + 1 == j) - { - px = pj; - px.FlipE(); px.FlipV(); - angle = std::max(angle , ComputeDihedralAngle(pi.v->P(), pj.v->P(), pk.v->P(), px.v->P()) ); - } - else - { - angle = std::max( angle, ComputeDihedralAngle(pi.v->P(),pj.v->P(), pk.v->P(), pv[ v[i][j] ].v->P())); - } - - if(j + 1 == k) - { - px = pk; - px.FlipE(); px.FlipV(); - angle = std::max(angle , ComputeDihedralAngle(pj.v->P(), pk.v->P(), pi.v->P(), px.v->P()) ); - } - else - { - angle = std::max( angle, ComputeDihedralAngle(pj.v->P(),pk.v->P(), pi.v->P(), pv[ v[j][k] ].v->P())); - } - - if( i == 0 && k == (int)v.size() - 1) - { - px = pi; - px.FlipE(); px.FlipV(); - angle = std::max(angle , ComputeDihedralAngle(pk.v->P(), pi.v->P(), pj.v->P(),px.v->P() ) ); - } - - ScalarType area = ( (pj.v->P() - pi.v->P()) ^ (pk.v->P() - pi.v->P()) ).Norm() * 0.5; - - return Weight(angle, area); + Weight a = w[i][m]; + Weight b = w[m][i+j]; + Weight newval = a + b + computeWeight( i, m, i+j, vv, vi); + if ( newval < minval ) + { + minval = newval; + minIndex = m; + } } - - static void calculateMinimumWeightTriangulation(MESH &m, FaceIterator f,std::vector vv ) - { - std::vector< std::vector< Weight > > w; //matrice dei pesi minimali di ogni orecchio preso in conzideraione - std::vector< std::vector< int > > vi;//memorizza l'indice del terzo vertice del triangolo - - //hole size - int nv = vv.size(); - - w.clear(); - w.resize( nv, std::vector( nv, Weight() ) ); - - vi.resize( nv, std::vector( nv, 0 ) ); - - //inizializzo tutti i pesi possibili del buco - for ( int i = 0; i < nv-1; ++i ) - w[i][i+1] = Weight( 0, 0 ); - - //doppio ciclo for per calcolare di tutti i possibili triangoli i loro pesi. - for ( int j = 2; j < nv; ++j ) - { - for ( int i = 0; i + j < nv; ++i ) - { - //per ogni triangolazione mi mantengo il minimo valore del peso tra i triangoli possibili - Weight minval; - - //indice del vertice che da il peso minimo nella triangolazione corrente - int minIndex = -1; - - //ciclo tra i vertici in mezzo a i due prefissati - for ( int m = i + 1; m < i + j; ++m ) - { - Weight a = w[i][m]; - Weight b = w[m][i+j]; - Weight newval = a + b + computeWeight( i, m, i+j, vv, vi); - if ( newval < minval ) - { - minval = newval; - minIndex = m; - } - } - w[i][i+j] = minval; - vi[i][i+j] = minIndex; - } - } - - //Triangulate - int i, j; - i=0; j=nv-1; - - triangulate(m,f, i, j, vi, vv); - - while(f!=m.face.end()) - { - (*f).SetD(); - ++f; - m.fn--; - } - } - - - static void triangulate(MESH &m, FaceIterator &f,int i, int j, + w[i][i+j] = minval; + vi[i][i+j] = minIndex; + } + } + + //Triangulate + int i, j; + i=0; j=nv-1; + + triangulate(m,f, i, j, vi, vv); + + while(f!=m.face.end()) + { + (*f).SetD(); + ++f; + m.fn--; + } + } + + + static void triangulate(MESH &m, FaceIterator &f,int i, int j, std::vector< std::vector > vi, std::vector vv) - { - if(i + 1 == j){return;} - if(i==j)return; - - int k = vi[i][j]; - - if(k == -1) return; - - //Setto i vertici - f->V(0) = vv[i].v; - f->V(1) = vv[k].v; - f->V(2) = vv[j].v; - - f++; - triangulate(m,f,i,k,vi,vv); - triangulate(m,f,k,j,vi,vv); - } - + { + if(i + 1 == j){return;} + if(i==j)return; + + int k = vi[i][j]; + + if(k == -1) return; + + //Setto i vertici + f->V(0) = vv[i].v; + f->V(1) = vv[k].v; + f->V(2) = vv[j].v; + + f++; + triangulate(m,f,i,k,vi,vv); + triangulate(m,f,k,j,vi,vv); + } + static void MinimumWeightFill(MESH &m, int holeSize, bool Selected) - { - std::vector vvi; - std::vector vfp; - - std::vector vinfo; - typename std::vector::iterator VIT; - GetInfo(m, Selected,vinfo); - - for(VIT = vinfo.begin(); VIT != vinfo.end();++VIT) - { - vvi.push_back(VIT->p); - } - - typename std::vector::iterator ith; - typename std::vector::iterator ithn; - typename std::vector::iterator itf; - - std::vector app; - PosType ps; - std::vector tr; - std::vector vf; - - for(ith = vvi.begin(); ith!= vvi.end(); ++ith) - { - tr.clear(); - vf.clear(); - app.clear(); - vfp.clear(); - - ps = *ith; - getBoundHole(ps,app); - - if(app.size() <= size_t(holeSize) ) - { - typename std::vector::iterator itP; - std::vector vfp; - - for(ithn = vvi.begin(); ithn!= vvi.end(); ++ithn) - vfp.push_back(&(ithn->f)); - - for(itP = app.begin (); itP != app.end ();++itP) - vfp.push_back( &(*itP).f ); - - //aggiungo le facce - FaceIterator f = tri::Allocator::AddFaces(m, (app.size()-2) , vfp); - - calculateMinimumWeightTriangulation(m,f, app); - } - } - - } - - static void getBoundHole (PosType sp,std::vector&ret) - { - PosType fp = sp; - //take vertex around the hole - do - { - assert(fp.IsBorder()); - ret.push_back(fp); - fp.NextB(); - }while(sp != fp); - } - -};//close class Hole + { + std::vector vvi; + std::vector vfp; + + std::vector vinfo; + typename std::vector::iterator VIT; + GetInfo(m, Selected,vinfo); + + for(VIT = vinfo.begin(); VIT != vinfo.end();++VIT) + { + vvi.push_back(VIT->p); + } + + typename std::vector::iterator ith; + typename std::vector::iterator ithn; + typename std::vector::iterator itf; + + std::vector app; + PosType ps; + std::vector tr; + std::vector vf; + + for(ith = vvi.begin(); ith!= vvi.end(); ++ith) + { + tr.clear(); + vf.clear(); + app.clear(); + vfp.clear(); + + ps = *ith; + getBoundHole(ps,app); + + if(app.size() <= size_t(holeSize) ) + { + typename std::vector::iterator itP; + std::vector vfp; + + for(ithn = vvi.begin(); ithn!= vvi.end(); ++ithn) + vfp.push_back(&(ithn->f)); + + for(itP = app.begin (); itP != app.end ();++itP) + vfp.push_back( &(*itP).f ); + + //aggiungo le facce + FaceIterator f = tri::Allocator::AddFaces(m, (app.size()-2) , vfp); + + calculateMinimumWeightTriangulation(m,f, app); + } + } + + } + + static void getBoundHole (PosType sp,std::vector&ret) + { + PosType fp = sp; + //take vertex around the hole + do + { + assert(fp.IsBorder()); + ret.push_back(fp); + fp.NextB(); + }while(sp != fp); + } + +};// class Hole } // end namespace tri } // end namespace vcg diff --git a/vcg/complex/algorithms/local_optimization.h b/vcg/complex/algorithms/local_optimization.h index e80a8bbb..b86edc35 100644 --- a/vcg/complex/algorithms/local_optimization.h +++ b/vcg/complex/algorithms/local_optimization.h @@ -20,83 +20,11 @@ * for more details. * * * ****************************************************************************/ -/**************************************************************************** - $Log: not supported by cvs2svn $ - Revision 1.20 2007/01/19 09:13:09 cignoni - Added Finalize() method to the interface - - Revision 1.19 2007/01/11 11:48:33 ganovelli - currMetric inizialied to heap.front() (it was heap.back()- wrong) - - Revision 1.18 2006/12/11 14:09:44 ganovelli - added missing initialization of currMetric - - Revision 1.17 2006/06/09 07:28:43 m_di_benedetto - Corrected ClearHeap(): iterator "hi" not decrementable if it was the first of the container. - - Revision 1.16 2005/11/10 15:38:46 cignoni - Added casts to remove warnings - - Revision 1.15 2005/10/02 23:23:52 cignoni - Changed the sense of the < operator for heap: it is reversed according to the stl where highest score elements must float in the heap - Completed TimeBudget Termination condition. - Parametrized the ClearHeap procedure now there is a HeapSimplexRatio param. Removed dirty printf. - - Revision 1.14 2005/04/14 11:34:33 ponchio - *** empty log message *** - - Revision 1.13 2005/01/19 10:33:50 cignoni - Improved ClearHeap management - - Revision 1.12 2004/12/10 01:02:48 cignoni - added an inline and removed loggng - - Revision 1.11 2004/12/03 21:14:39 ponchio - Fixed memory leak... - - Revision 1.10 2004/11/23 10:37:17 cignoni - Added a member with a cached copy of the floating Priority() value inside the HeapElem to optimize operator< in heap updating operator - - Revision 1.9 2004/11/05 10:03:47 fiorin - Added ModifierType::TriEdgeFlipOp - - Revision 1.8 2004/10/25 07:02:56 ganovelli - some inline function, logs on file (precompiler directive) - - Revision 1.7 2004/09/29 17:08:39 ganovelli - changed > to < in heapelem comparison - - Revision 1.6 2004/09/28 09:57:08 cignoni - Better Doxygen docs - - Revision 1.5 2004/09/15 10:40:20 ponchio - typedef LocalOptimization HeapType -> public: - - Revision 1.4 2004/09/08 15:10:59 ganovelli - *** empty log message *** - - Revision 1.3 2004/07/27 09:46:15 cignoni - First working version of the LocalOptimization/Simplification Framework - - Revision 1.1 2004/07/15 12:04:14 ganovelli - minor changes - - Revision 1.2 2004/07/09 10:22:56 ganovelli - working draft - - Revision 1.1 2004/07/08 08:25:15 ganovelli - first draft - -****************************************************************************/ #ifndef __VCGLIB_LOCALOPTIMIZATION #define __VCGLIB_LOCALOPTIMIZATION -#include -#include -#include -#include -#include - +#include +#include namespace vcg{ // Base class for Parameters // all parameters must be derived from this. diff --git a/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h b/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h index 3b2791bb..eb6b86b1 100644 --- a/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h +++ b/vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h @@ -78,7 +78,7 @@ namespace tri{ static void Init(){} static math::Quadric &Qd(VERTEX_TYPE &v) {return v.Qd();} static math::Quadric &Qd(VERTEX_TYPE *v) {return v->Qd();} - static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE */*v*/) {return 1.0;} + static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE * /*v*/) {return 1.0;} static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE &/*v*/) {return 1.0;} static void Merge(VERTEX_TYPE & /*v_dest*/, VERTEX_TYPE const & /*v_del*/){} }; diff --git a/vcg/complex/algorithms/local_optimization/tri_edge_flip.h b/vcg/complex/algorithms/local_optimization/tri_edge_flip.h index 53d3c5ed..29f26b5f 100644 --- a/vcg/complex/algorithms/local_optimization/tri_edge_flip.h +++ b/vcg/complex/algorithms/local_optimization/tri_edge_flip.h @@ -284,7 +284,7 @@ public: const char* Info(TRIMESH_TYPE &m) { static char dump[60]; - sprintf(dump,"%lu -> %lu %g\n", tri::Index(m,_pos.F()->V(0)), tri::Index(m,_pos.F()->V(1)),-_priority); + sprintf(dump,"%zu -> %zu %g\n", tri::Index(m,_pos.F()->V(0)), tri::Index(m,_pos.F()->V(1)),-_priority); return dump; } diff --git a/vcg/complex/algorithms/mesh_assert.h b/vcg/complex/algorithms/mesh_assert.h index f469e38c..09632022 100644 --- a/vcg/complex/algorithms/mesh_assert.h +++ b/vcg/complex/algorithms/mesh_assert.h @@ -104,6 +104,13 @@ public: } } + static void OnlyEdgeMesh(MeshType &m) + { + if(m.FN()>0) + throw vcg::MissingPreconditionException("Expecting a mesh composed only by edges (no faces needed or allowed)"); + } + + }; } // end namespace tri diff --git a/vcg/complex/algorithms/parametrization/tangent_field_operators.h b/vcg/complex/algorithms/parametrization/tangent_field_operators.h index 0ed1caf6..3da1a33a 100644 --- a/vcg/complex/algorithms/parametrization/tangent_field_operators.h +++ b/vcg/complex/algorithms/parametrization/tangent_field_operators.h @@ -666,7 +666,7 @@ public: CoordType dirR=vcg::tri::CrossField::Rotate(f0,f1,dir0); ///then get the closest upf to K*PI/2 rotations CoordType dir1=f1.cPD1(); - CoordType ret=vcg::tri::CrossField::K_PI(dirR,dir1,f1.cN()); + CoordType ret=vcg::tri::CrossField::K_PI(dir1,dirR,f1.cN()); return ret; } diff --git a/vcg/complex/algorithms/point_sampling.h b/vcg/complex/algorithms/point_sampling.h index a101653e..f7275dfc 100644 --- a/vcg/complex/algorithms/point_sampling.h +++ b/vcg/complex/algorithms/point_sampling.h @@ -105,6 +105,11 @@ private: std::vector *sampleVec; bool vectorOwner; public: + + std::vector &SampleVec() + { + return *sampleVec; + } void AddVert(const VertexType &p) { @@ -839,11 +844,11 @@ static void EdgeMeshUniform(MeshType &m, VertexSampler &ps, float radius, bool c /// \brief Sample all the border corner vertices /// /// It assumes that the border flag have been set over the mesh both for vertex and for faces. -/// All the vertices on the border where the surface forms an angle smaller than the given threshold are sampled. +/// All the vertices on the border where the edges of the boundary of the surface forms an angle smaller than the given threshold are sampled. /// static void VertexBorderCorner(MeshType & m, VertexSampler &ps, float angleRad) { - typename MeshType::template PerVertexAttributeHandle angleSumH = tri::Allocator:: template GetPerVertexAttribute (m); + typename MeshType::template PerVertexAttributeHandle angleSumH = tri::Allocator:: template GetPerVertexAttribute (m); for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) angleSumH[vi]=0; @@ -1846,9 +1851,14 @@ static void PoissonDiskPruningByNumber(VertexSampler &ps, MeshType &m, /// This is the main function that is used to build a poisson distribuition -/// starting from a dense sample cloud. -/// Trivial approach that puts all the samples in a hashed UG and randomly choose a sample +/// starting from a dense sample cloud (the montecarloMesh) by 'pruning' it. +/// it puts all the samples in a hashed UG and randomly choose a sample /// and remove all the points in the sphere centered on the chosen sample +/// +/// You can impose some constraint: all the vertices in the montecarloMesh +/// that are marked with a bool attribute called "fixed" are surely chosen +/// (if you also set the preGenFlag option) +/// static void PoissonDiskPruning(VertexSampler &ps, MeshType &montecarloMesh, ScalarType diskRadius, PoissonDiskParam &pp) { @@ -2256,18 +2266,18 @@ void PoissonPruning(MeshType &m, // the mesh that has to be pruned /// \brief Low level wrapper for Poisson Disk Pruning /// -/// This function simply takes a mesh and a radius and returns a vector -/// of vertex pointers listing the "surviving" points. -// +/// This function simply takes a mesh containing a point cloud to be pruned and a radius +/// It returns a vector of CoordType listing the "surviving" points. +/// template void PoissonPruning(MeshType &m, // the mesh that has to be pruned - std::vector &poissonSamples, // the vector that will contain the chosen set of points + std::vector &poissonSamples, // the vector that will contain the chosen set of points float radius, unsigned int randSeed=0) { std::vector poissonSamplesVP; PoissonPruning(m,poissonSamplesVP,radius,randSeed); poissonSamples.resize(poissonSamplesVP.size()); - for(size_t i=0;iP(); } diff --git a/vcg/complex/algorithms/quadrangulator.h b/vcg/complex/algorithms/quadrangulator.h index 1e2bdffe..b3f7d057 100644 --- a/vcg/complex/algorithms/quadrangulator.h +++ b/vcg/complex/algorithms/quadrangulator.h @@ -676,6 +676,8 @@ public: UV.clear(); Pmesh.Clear(); + vcg::tri::UpdateTopology::FaceFace(Tmesh); + TestIsProper(Tmesh); RoundInitial(Tmesh); diff --git a/vcg/complex/algorithms/skeleton.h b/vcg/complex/algorithms/skeleton.h new file mode 100644 index 00000000..7a7fa649 --- /dev/null +++ b/vcg/complex/algorithms/skeleton.h @@ -0,0 +1,132 @@ +/**************************************************************************** +* 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 VCG__SKELETON_H +#define VCG__SKELETON_H +#include + +namespace vcg +{ +namespace tri +{ + +template +class SampledSkeleton +{ +public: + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::BoxType BoxType; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceType FaceType; + typedef VoronoiVolumeSampling VoronoiVolumeSamplingType; + SampledSkeleton(VoronoiVolumeSamplingType &_vvs):vvs(_vvs){} + + VoronoiVolumeSamplingType &vvs; + + +/** + * @brief Compute an evaulation of the thickness as distance from the medial axis. + * It starts from a montecarlo volume sampling and try to search for the samples that can be part of the medial axis. + * It use a sampled representation of the surface. A volume sample is considered part + * of the medial axis if there are at least two points that are (almost) the same minimal distance to that point. + * + * + */ +void ThicknessEvaluator(float distThr, int smoothSize, int smoothIter, MeshType *skelM=0) +{ + tri::UpdateQuality::VertexConstant(vvs.psd.poissonSurfaceMesh,0); + std::vector medialSrc(vvs.psd.poissonSurfaceMesh.vert.size(),0); + for(VertexIterator vi=vvs.montecarloVolumeMesh.vert.begin(); vi!=vvs.montecarloVolumeMesh.vert.end(); ++vi) + { + unsigned int ind; + ScalarType sqdist; + this->vvs.psd.surfTree->doQueryClosest(vi->P(),ind,sqdist); + VertexPointer vp = &vvs.psd.poissonSurfaceMesh.vert[ind]; + ScalarType minDist = math::Sqrt(sqdist); + if(vp->Q() < minDist) + { + std::vector indVec; + std::vector sqDistVec; + + this->vvs.psd.surfTree->doQueryDist( vi->P(), minDist*distThr,indVec,sqDistVec); + if(indVec.size()>1) + { + for(size_t i=0;iQ() < minDist) { + vp->Q()=minDist; + medialSrc[indVec[i]]=&*vi; + } + } + } + } + } + // Now collect the vertexes of the volume mesh that are on the medial surface + if(skelM) + { + tri::UpdateFlags::VertexClearV(vvs.montecarloVolumeMesh); + for(size_t i=0;iSetV(); + for(VertexIterator vi=vvs.montecarloVolumeMesh.vert.begin(); vi!=vvs.montecarloVolumeMesh.vert.end(); ++vi) + if(vi->IsV()) tri::Allocator::AddVertex(*skelM,vi->P()); + printf("Generated a medial surf of %i vertexes\n",skelM->vn); + } + + + tri::Smooth::PointCloudQualityMedian(vvs.psd.poissonSurfaceMesh); + tri::Smooth::PointCloudQualityAverage(vvs.psd.poissonSurfaceMesh,smoothSize,smoothIter); + tri::UpdateColor::PerVertexQualityRamp(vvs.psd.poissonSurfaceMesh); + tri::RedetailSampler rs; + rs.init(&vvs.psd.poissonSurfaceMesh); + rs.dist_upper_bound = vvs.psd.poissonSurfaceMesh.bbox.Diag()*0.05 ; + rs.qualityFlag = true; + tri::SurfaceSampling >::VertexUniform(vvs.baseMesh, rs, vvs.baseMesh.vn, false); +} + +void RefineSkeletonVolume(MeshType &skelMesh) +{ + CoordType closestP; + int trialNum=0; + for(int i=0;iDistanceFromSurface(point, closestP); + if(d<0){ + vcg::tri::Allocator::AddVertex(vvs.montecarloVolumeMesh,point); + vvs.montecarloVolumeMesh.vert.back().Q() = fabs(d); + } + } +} + + +}; // end class + + +} // end namespace vcg +} // end namespace vcg +#endif // VCG__SKELETON_H diff --git a/vcg/complex/algorithms/symmetry.h b/vcg/complex/algorithms/symmetry.h index ec147268..f5f4d890 100644 --- a/vcg/complex/algorithms/symmetry.h +++ b/vcg/complex/algorithms/symmetry.h @@ -226,6 +226,7 @@ public: //create the sphere vcg::tri::Sphere(*sphere,SubDirections); vcg::tri::UpdateBounding::Box(*sphere); + sphere->face.EnableMark(); ///initialize grid GridSph.Set(sphere->face.begin(),sphere->face.end()); diff --git a/vcg/complex/algorithms/update/color.h b/vcg/complex/algorithms/update/color.h index 30d8beb1..95ddc5aa 100644 --- a/vcg/complex/algorithms/update/color.h +++ b/vcg/complex/algorithms/update/color.h @@ -368,7 +368,6 @@ Note: The faux bit is used to color polygonal faces uniformly } /*! \brief Perlin Noise. -\return the number of changed vertexes (the selected ones) Simple Perlin noise. To make things weirder each color band can have its own offset and frequency. Period is expressed in absolute terms. @@ -396,6 +395,34 @@ static void PerVertexPerlinNoise(MeshType& m, CoordType period, CoordType offset } + +/*! \brief Perlin Color mixing. + +Simple Perlin color mixing. Color 1 and 2 are mixed according the perlin noise function, with period and offset. +*/ +static void PerVertexPerlinColoring(MeshType& m, ScalarType period, CoordType offset = CoordType(0, 0, 0), Color4b color1 = Color4b::Black, Color4b color2 = Color4b::White, bool onSelected = false) +{ + RequirePerVertexColor(m); + + CoordType p; + + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if (!(*vi).IsD()) + if ((!onSelected) || ((*vi).IsS())) + { + // perlin noise is defined in 022 + p = (vi->P() / period) + offset; + double factor = (math::Perlin::Noise(p[0], p[1], p[2]) + 1.0) / 2.0; + + int rr = (color1[0] * factor) + (color2[0] * (1.0 - factor)); + int gg = (color1[1] * factor) + (color2[1] * (1.0 - factor)); + int bb = (color1[2] * factor) + (color2[2] * (1.0 - factor)); + int aa = (color1[3] * factor) + (color2[3] * (1.0 - factor)); + + (*vi).C() = Color4b(rr, gg, bb, aa); + } +} + /*! \brief Simple Noise adding function. It simply add signed noise to the color of the mesh. The noise has uniform distribution and the amplitude is +/-2^(noisebits-1). */ diff --git a/vcg/complex/algorithms/update/quality.h b/vcg/complex/algorithms/update/quality.h index d9779392..ba7a6afc 100644 --- a/vcg/complex/algorithms/update/quality.h +++ b/vcg/complex/algorithms/update/quality.h @@ -164,7 +164,10 @@ static void FaceFromVertex( MeshType &m) tri::RequirePerVertexQuality(m); for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) { - (*fi).Q() = ((*fi).V(0)->Q()+(*fi).V(1)->Q()+(*fi).V(2)->Q())/3.0f; + (*fi).Q() =0; + for (size_t i=0;i<(*fi).VN();i++) + (*fi).Q() += (*fi).V(i)->Q(); + (*fi).Q()/=(ScalarType)(*fi).VN(); } } diff --git a/vcg/complex/algorithms/update/selection.h b/vcg/complex/algorithms/update/selection.h index 6f5d0528..ef97bcaa 100644 --- a/vcg/complex/algorithms/update/selection.h +++ b/vcg/complex/algorithms/update/selection.h @@ -441,9 +441,8 @@ static size_t FaceOutOfRangeEdge(MeshType &m, ScalarType MinEdgeThr, ScalarType } /// \brief This function expand current selection to cover the whole connected component. -static size_t FaceConnectedFF(MeshType &m, bool preserveSelection=false) +static size_t FaceConnectedFF(MeshType &m) { - if(!preserveSelection) FaceClear(m); // it also assumes that the FF adjacency is well computed. RequireFFAdjacency(m); UpdateFlags::FaceClearV(m); diff --git a/vcg/complex/algorithms/update/texture.h b/vcg/complex/algorithms/update/texture.h index d0a8339a..c454ddd1 100644 --- a/vcg/complex/algorithms/update/texture.h +++ b/vcg/complex/algorithms/update/texture.h @@ -111,6 +111,7 @@ static void WedgeTexFromVertexTex(ComputeMeshType &m) { (*fi).WT(i).U() = (*fi).V(i)->T().U(); (*fi).WT(i).V() = (*fi).V(i)->T().V(); + (*fi).WT(i).N() = 0; } } } diff --git a/vcg/complex/algorithms/voronoi_processing.h b/vcg/complex/algorithms/voronoi_processing.h index 67ee1d98..7b9a6068 100644 --- a/vcg/complex/algorithms/voronoi_processing.h +++ b/vcg/complex/algorithms/voronoi_processing.h @@ -79,7 +79,7 @@ struct VoronoiProcessingParameter bool relaxOnlyConstrainedFlag; bool preserveFixedSeed; /// If true the 'fixed' seeds are not moved during relaxation. - /// \see FixVertexVector function to see how to fix a set of seeds. + /// \see MarkVertexVectorAsFixed function to see how to fix a set of seeds. float refinementRatio; /// It defines how much the input mesh has to be refined in order to have a supporting /// triangulation that is dense enough to well approximate the voronoi diagram. @@ -1195,7 +1195,7 @@ static void PruneSeedByRegionArea(std::vector &seedVec, /// Vertex pointers must belong to the mesh. /// The framework use a boolean attribute called "fixed" to store this info. /// -static void FixVertexVector(MeshType &m, std::vector &vertToFixVec) +static void MarkVertexVectorAsFixed(MeshType &m, std::vector &vertToFixVec) { typename MeshType::template PerVertexAttributeHandle fixed; fixed = tri::Allocator:: template GetPerVertexAttribute (m,"fixed"); diff --git a/vcg/complex/algorithms/voronoi_volume_sampling.h b/vcg/complex/algorithms/voronoi_volume_sampling.h index 5e1d2ffa..53a31a9c 100644 --- a/vcg/complex/algorithms/voronoi_volume_sampling.h +++ b/vcg/complex/algorithms/voronoi_volume_sampling.h @@ -33,6 +33,103 @@ namespace vcg namespace tri { +template +class PointSampledDistance +{ +public: + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::BoxType BoxType; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceType FaceType; + typedef typename vcg::GridStaticPtr GridType; + + typedef SimpleVolume > VVSVolume; + typedef typename vcg::tri::TrivialWalker VVSWalker; + typedef typename vcg::tri::MarchingCubes VVSMarchingCubes; + + PointSampledDistance(MeshType &_baseMesh) + :surfTree(0),baseMesh(_baseMesh) {} + typename KdTree::PriorityQueue pq; + GridType surfGrid; // used for fast inside query + typedef FaceTmark MarkerFace; + MarkerFace mf; + vcg::face::PointDistanceBaseFunctor PDistFunct; + KdTree *surfTree; // used for fast inside query + MeshType &baseMesh; + MeshType poissonSurfaceMesh; + ScalarType poissonRadiusSurface; + + void Init(ScalarType _poissonRadiusSurface=0) + { + MeshType montecarloSurfaceMesh; + if(_poissonRadiusSurface==0) poissonRadiusSurface = baseMesh.bbox.Diag()/50.0f; + else poissonRadiusSurface = _poissonRadiusSurface; + ScalarType meshArea = Stat::ComputeMeshArea(baseMesh); + int MontecarloSurfSampleNum = 10 * meshArea / (poissonRadiusSurface*poissonRadiusSurface); + tri::MeshSampler sampler(montecarloSurfaceMesh); + tri::SurfaceSampling >::Montecarlo(baseMesh, sampler, MontecarloSurfSampleNum); + montecarloSurfaceMesh.bbox = baseMesh.bbox; // we want the same bounding box + poissonSurfaceMesh.Clear(); + tri::MeshSampler mps(poissonSurfaceMesh); + typename tri::SurfaceSampling >::PoissonDiskParam pp; + pp.geodesicDistanceFlag=false; + + tri::SurfaceSampling >::PoissonDiskPruning(mps, montecarloSurfaceMesh, poissonRadiusSurface,pp); + vcg::tri::UpdateBounding::Box(poissonSurfaceMesh); + + printf("Surface Sampling radius %f - montecarlo %ivn - Poisson %ivn\n",poissonRadiusSurface,montecarloSurfaceMesh.vn,poissonSurfaceMesh.vn); + VertexConstDataWrapper ww(poissonSurfaceMesh); + if(surfTree) delete surfTree; + surfTree = new KdTree(ww); + + surfGrid.SetWithRadius(baseMesh.face.begin(),baseMesh.face.end(),poissonRadiusSurface); + mf.SetMesh(&baseMesh); + } + // Compute the signed distance from the surface exploting both a kdtree and a ugrid + // for a query point p first we use the kdtree with a good poisson sampling of the surface; + // to get the nearest point on the surface, then if the point is far from the surface we can use the point point distance, while if it is near (e.g. less than 3*poisson radius) we rely on point face distance with a grid. + ScalarType DistanceFromSurface(const CoordType &q, CoordType &closestP) + { + ScalarType squaredDist; + unsigned int ind; + surfTree->doQueryClosest(q,ind,squaredDist); + ScalarType dist = sqrt(squaredDist); + if( dist > 3.0f*poissonRadiusSurface) + { + // CoordType dir = surfTree->getNeighbor(0) - p; + CoordType dir = this->poissonSurfaceMesh.vert[ind].P() - q; + const CoordType &surfN = this->poissonSurfaceMesh.vert[ind].N(); + if(dir* surfN > 0) dist= -dist; + closestP=this->poissonSurfaceMesh.vert[ind].P(); + return dist; + } + + ScalarType _maxDist = this->poissonRadiusSurface*3.0f; + dist=_maxDist; + FacePointer f=surfGrid.GetClosest(PDistFunct,mf,q,_maxDist,dist,closestP); + assert(f); + assert (dist >=0); + CoordType dir = closestP - q; + if(dir*f->cN() > 0) dist = -dist; + + return dist; + } + + +}; + +/** Compute a well distributed set of samples (seeds) inside a watertight mesh. + * + * The main idea is that we have start from a poisson disk distribution and we improve it using Lloyd relaxation. + * To make things simpler and more controllable we estabilish since the beginning a Domain where we can choose the points. + * + */ + + + template< class MeshType> class VoronoiVolumeSampling { @@ -69,33 +166,26 @@ public: }; VoronoiVolumeSampling(MeshType &_baseMesh) - :surfTree(0),seedTree(0),baseMesh(_baseMesh),cb(0),restrictedRelaxationFlag(false) + :seedTree(0),baseMesh(_baseMesh),cb(0),restrictedRelaxationFlag(false),psd(_baseMesh) { tri::RequirePerFaceMark(baseMesh); tri::UpdateBounding::Box(baseMesh); tri::UpdateNormal::PerFaceNormalized(baseMesh); } - KdTree *surfTree; // used for fast inside query KdTree *seedTree; // used to accumulate barycenter in relaxation KdTree *seedDomainTree; // used to accumulate barycenter in relaxation - typename KdTree::PriorityQueue pq; - GridType surfGrid; // used for fast inside query - typedef FaceTmark MarkerFace; - MarkerFace mf; - vcg::face::PointDistanceBaseFunctor PDistFunct; + - MeshType &baseMesh; + MeshType &baseMesh; // The base mesh for which we compute all MeshType seedMesh; - MeshType poissonSurfaceMesh; - ScalarType poissonRadiusSurface; - MeshType montecarloVolumeMesh; // we use this mesh as volume evaluator + MeshType montecarloVolumeMesh; // we use this mesh as volume evaluator and to choose MeshType seedDomainMesh; // where we choose the seeds (by default is the montecarlo volume mesh) vcg::CallBackPos *cb; math::MarsenneTwisterRNG rng; bool restrictedRelaxationFlag; - + PointSampledDistance psd; // Build up the needed structure for efficient point in mesh search. // It uses a poisson disk sampling of the surface plus a @@ -103,62 +193,11 @@ public: // It initializes the surfGrid, surfTree and poissonSurfaceMesh members void Init(ScalarType _poissonRadiusSurface=0) { - MeshType montecarloSurfaceMesh; - - if(_poissonRadiusSurface==0) poissonRadiusSurface = baseMesh.bbox.Diag()/50.0f; - else poissonRadiusSurface = _poissonRadiusSurface; - ScalarType meshArea = Stat::ComputeMeshArea(baseMesh); - int MontecarloSurfSampleNum = 10 * meshArea / (poissonRadiusSurface*poissonRadiusSurface); - tri::MeshSampler sampler(montecarloSurfaceMesh); + psd.Init(_poissonRadiusSurface); tri::SurfaceSampling >::SamplingRandomGenerator()=rng; - tri::SurfaceSampling >::Montecarlo(baseMesh, sampler, MontecarloSurfSampleNum); - montecarloSurfaceMesh.bbox = baseMesh.bbox; // we want the same bounding box - poissonSurfaceMesh.Clear(); - tri::MeshSampler mps(poissonSurfaceMesh); - typename tri::SurfaceSampling >::PoissonDiskParam pp; - pp.geodesicDistanceFlag=false; - tri::SurfaceSampling >::PoissonDiskPruning(mps, montecarloSurfaceMesh, poissonRadiusSurface,pp); - vcg::tri::UpdateBounding::Box(poissonSurfaceMesh); - - printf("Surface Sampling radius %f - montecarlo %ivn - Poisson %ivn\n",poissonRadiusSurface,montecarloSurfaceMesh.vn,poissonSurfaceMesh.vn); - VertexConstDataWrapper ww(poissonSurfaceMesh); - if(surfTree) delete surfTree; - surfTree = new KdTree(ww); - - surfGrid.SetWithRadius(baseMesh.face.begin(),baseMesh.face.end(),poissonRadiusSurface); - mf.SetMesh(&baseMesh); } - // Compute the signed distance from the surface exploting both a kdtree and a ugrid - // for a query point p first we use the kdtree with a good poisson sampling of the surface; - // to get the nearest point on the surface, then if the point is far from the surface we can use the point point distance, while if it is near (e.g. less than 3*poisson radius) we rely on point face distance with a grid. -ScalarType DistanceFromSurface(const CoordType &q, CoordType &closestP) -{ - ScalarType squaredDist; - unsigned int ind; - surfTree->doQueryClosest(q,ind,squaredDist); - ScalarType dist = sqrt(squaredDist); - if( dist > 3.0f*poissonRadiusSurface) - { -// CoordType dir = surfTree->getNeighbor(0) - p; - CoordType dir = this->poissonSurfaceMesh.vert[ind].P() - q; - const CoordType &surfN = this->poissonSurfaceMesh.vert[ind].N(); - if(dir* surfN > 0) dist= -dist; - closestP=this->poissonSurfaceMesh.vert[ind].P(); - return dist; - } - - ScalarType _maxDist = this->poissonRadiusSurface*3.0f; - dist=_maxDist; - FacePointer f=surfGrid.GetClosest(PDistFunct,mf,q,_maxDist,dist,closestP); - assert(f); - assert (dist >=0); - CoordType dir = closestP - q; - if(dir*f->cN() > 0) dist = -dist; - - return dist; -} ScalarType DistanceFromVoronoiSeed(const CoordType &p_point) @@ -389,7 +428,7 @@ void QuadricRelaxVoronoiSamples(int relaxStep) ScalarType ImplicitFunction(const CoordType &p, const Param &pp) { CoordType closest; - ScalarType surfDist = this->DistanceFromSurface(p,closest); + ScalarType surfDist = this->psd.DistanceFromSurface(p,closest); ScalarType elemDist; switch(pp.elemType) @@ -543,86 +582,16 @@ void OptimizeIsosurf(MeshType &m, const Param &pp) printf("Optimize Isosurf performed %i edge flip in %5.2f s\n",flipCnt,float(t1-t0)/CLOCKS_PER_SEC); } +/** Given a surface sampling it adds to the montecarloVolumeMesh, a number of near surface samples. + * For each surface it try to add a sample generated as a point in the half ball of centered on the sample. +*/ - /** - * @brief Compute an evaulation of the thickness as distance from the medial axis. - * It starts from a montecarlo volume sampling and try to search for the samples that can be part of the medial axis. - * It use a sampled representation of the surface. A volume sample is considered part - * of the medial axis if there are at least two points that are (almost) the same minimal distance to that point. - * - * - */ - void ThicknessEvaluator(float distThr, int smoothSize, int smoothIter, MeshType *skelM=0) - { - tri::UpdateQuality::VertexConstant(poissonSurfaceMesh,0); - std::vector medialSrc(poissonSurfaceMesh.vert.size(),0); - for(VertexIterator vi=montecarloVolumeMesh.vert.begin(); vi!=montecarloVolumeMesh.vert.end(); ++vi) - { - unsigned int ind; - ScalarType sqdist; - this->surfTree->doQueryClosest(vi->P(),ind,sqdist); - VertexPointer vp = &poissonSurfaceMesh.vert[ind]; - ScalarType minDist = math::Sqrt(sqdist); - if(vp->Q() < minDist) - { - std::vector indVec; - std::vector sqDistVec; - - this->surfTree->doQueryDist( vi->P(), minDist*distThr,indVec,sqDistVec); - if(indVec.size()>1) - { - for(size_t i=0;iQ() < minDist) { - vp->Q()=minDist; - medialSrc[indVec[i]]=&*vi; - } - } - } - } - } - // Now collect the vertexes of the volume mesh that are on the medial surface - if(skelM) - { - tri::UpdateFlags::VertexClearV(montecarloVolumeMesh); - for(size_t i=0;iSetV(); - for(VertexIterator vi=montecarloVolumeMesh.vert.begin(); vi!=montecarloVolumeMesh.vert.end(); ++vi) - if(vi->IsV()) tri::Allocator::AddVertex(*skelM,vi->P()); - printf("Generated a medial surf of %i vertexes\n",skelM->vn); - } - - - tri::Smooth::PointCloudQualityMedian(poissonSurfaceMesh); - tri::Smooth::PointCloudQualityAverage(poissonSurfaceMesh,smoothSize,smoothIter); - tri::UpdateColor::PerVertexQualityRamp(poissonSurfaceMesh); - tri::RedetailSampler rs; - rs.init(&poissonSurfaceMesh); - rs.dist_upper_bound = poissonSurfaceMesh.bbox.Diag()*0.05 ; - rs.qualityFlag = true; - tri::SurfaceSampling >::VertexUniform(baseMesh, rs, baseMesh.vn, false); - } +void RefineMontecarloVolumeSamplingNearSurface(MeshType &surfaceSamplingMesh, ScalarType radius, int perSampleNum) +{ + +} - void RefineSkeletonVolume(MeshType &skelMesh) - { - CoordType closestP; - int trialNum=0; - for(int i=0;iDistanceFromSurface(point, closestP); - if(d<0){ - vcg::tri::Allocator::AddVertex(montecarloVolumeMesh,point); - montecarloVolumeMesh.vert.back().Q() = fabs(d); - } - } - } - - - void BuildMontecarloSampling(int montecarloSampleNum) + void BuildMontecarloVolumeSampling(int montecarloSampleNum) { montecarloVolumeMesh.Clear(); @@ -632,7 +601,7 @@ void OptimizeIsosurf(MeshType &m, const Param &pp) { CoordType point = math::GeneratePointInBox3Uniform(rng,baseMesh.bbox); trialNum++; - ScalarType d = this->DistanceFromSurface(point,closest); + ScalarType d = this->psd.DistanceFromSurface(point,closest); if(d<0){ vcg::tri::Allocator::AddVertex(montecarloVolumeMesh,point); montecarloVolumeMesh.vert.back().Q() = fabs(d); @@ -647,26 +616,23 @@ void OptimizeIsosurf(MeshType &m, const Param &pp) /* * Function: BuildVolumeSampling * ---------------------------- - * Build a Poisson-Disk Point cloud that cover all the space of the original mesh m + * Build and prepare the seed set. + * This is the starting point for the subsequent relaxation calls. + * You can insert some initial seeds into the seed set and they will be preserved + * * */ - void BuildVolumeSampling(int montecarloSampleNum, int poissonSampleNum, ScalarType &poissonRadius, int randSeed) + void BuildVolumeSampling(int montecarloSampleNum, ScalarType &poissonRadius, int randSeed) { if(montecarloSampleNum >0) - this->BuildMontecarloSampling(montecarloSampleNum); - if(seedDomainMesh.vn == 0) + this->BuildMontecarloVolumeSampling(montecarloSampleNum); + if(this->seedDomainMesh.vn == 0) tri::Append::MeshCopy(seedDomainMesh,montecarloVolumeMesh); - vector pruningVec; - if(poissonRadius ==0 && poissonSampleNum!=0) - tri::PoissonPruningExact(seedDomainMesh,pruningVec,poissonRadius,poissonSampleNum,0.04,10,randSeed); - else - tri::PoissonPruning(seedDomainMesh,pruningVec,poissonRadius,randSeed); - - std::vector seedPts(pruningVec.size()); - for(size_t i=0;iP(); + std::vector seedPts; + tri::PoissonPruning(seedDomainMesh,seedPts,poissonRadius,randSeed); tri::BuildMeshFromCoordVector(this->seedMesh,seedPts); + // Kdtree must be rebuilt at the end of each step; VertexConstDataWrapper vdw(seedMesh); if(seedTree) delete seedTree; diff --git a/vcg/complex/allocate.h b/vcg/complex/allocate.h index 81da27ef..00df47b9 100644 --- a/vcg/complex/allocate.h +++ b/vcg/complex/allocate.h @@ -52,13 +52,13 @@ template size_t Index(MeshType &m, const typename MeshType::HEdgeType* h) {return h-&*m.hedge.begin();} template -bool IsValidPointer( MeshType & m, const typename MeshType::VertexType *vp) { return ( (vp >= &*m.vert.begin()) && ( vp < &*m.vert.end()) ); } +bool IsValidPointer( MeshType & m, const typename MeshType::VertexType *vp) { return ( m.vert.size() > 0 && (vp >= &*m.vert.begin()) && (vp <= &m.vert.back()) ); } template -bool IsValidPointer( MeshType & m, const typename MeshType::EdgeType *ep) { return ( (ep >= &*m.edge.begin()) && ( ep < &*m.edge.end()) ); } +bool IsValidPointer(MeshType & m, const typename MeshType::EdgeType *ep) { return ( m.edge.size() > 0 && (ep >= &*m.edge.begin()) && (ep <= &m.edge.back())); } template -bool IsValidPointer( MeshType & m, const typename MeshType::FaceType *fp) { return ( (fp >= &*m.face.begin()) && ( fp < &*m.face.end()) ); } +bool IsValidPointer(MeshType & m, const typename MeshType::FaceType *fp) { return ( m.face.size() > 0 && (fp >= &*m.face.begin()) && (fp <= &m.face.back())); } template -bool IsValidPointer( MeshType & m, const typename MeshType::HEdgeType *hp) { return ( (hp >= &*m.hedge.begin())&& ( hp < &*m.hedge.end()) ); } +bool IsValidPointer(MeshType & m, const typename MeshType::HEdgeType *hp) { return ( m.hedge.size() > 0 && (hp >= &*m.hedge.begin()) && (hp <= &m.hedge.back())); } template void ReorderAttribute(ATTR_CONT &c, std::vector & newVertIndex, MeshType & /* m */){ @@ -1516,13 +1516,18 @@ public: return;} } - static void DeletePerMeshAttribute( MeshType & m, std::string name){ + // Generic DeleteAttribute. + // It must not crash if you try to delete a non existing attribute, + // because you do not have a way of asking for a handle of an attribute for which you do not know the type. + static bool DeletePerMeshAttribute( MeshType & m, std::string name){ AttrIterator i; PointerToAttribute h1; h1._name = name; i = m.mesh_attr.find(h1); - assert(i!=m.mesh_attr.end()); + if (i==m.mesh_attr.end()) + return false; delete ((SimpleTempDataBase *)(*i)._handle); m.mesh_attr.erase(i); + return true; } template diff --git a/vcg/math/base.h b/vcg/math/base.h index 41c2b102..107621da 100644 --- a/vcg/math/base.h +++ b/vcg/math/base.h @@ -89,6 +89,7 @@ Edited Comments and GPL license #include #include #include +#include #include #include @@ -196,7 +197,7 @@ template int IsNAN(T t) { return _isnan(t) || (!_finite(t)); } #elif defined(__MINGW32__) // GCC template int IsNAN(T t) { return std::isnan(t) || std::isinf(t); } #elif defined(__GNUC__) // GCC -template int IsNAN(T t) { return isnan(t) || isinf(t); } +template int IsNAN(T t) { return std::isnan(t) || std::isinf(t); } #else // generic template int IsNAN(T t) diff --git a/vcg/math/matrix44.h b/vcg/math/matrix44.h index fb2442e6..a138e57d 100644 --- a/vcg/math/matrix44.h +++ b/vcg/math/matrix44.h @@ -138,12 +138,18 @@ public: void operator*=( const T k ); template - void ToMatrix(Matrix44Type & m) const {for(int i = 0; i < 16; i++) m.V()[i]=V()[i];} + void ToMatrix(Matrix44Type & m) const + { + for(int i = 0; i < 16; i++) + { + m.V()[i]= V()[i]; + } + } void ToEulerAngles(T &alpha, T &beta, T &gamma); template - void FromMatrix(const Matrix44Type & m){for(int i = 0; i < 16; i++) V()[i]=m.V()[i];} + void FromMatrix(const Matrix44Type & m){for(int i = 0; i < 16; i++) V()[i]=T(m.V()[i]);} template void ToEigenMatrix(EigenMatrix44Type & m) const { diff --git a/vcg/math/random_generator.h b/vcg/math/random_generator.h index 3e1a649d..4983621f 100644 --- a/vcg/math/random_generator.h +++ b/vcg/math/random_generator.h @@ -412,18 +412,6 @@ public: return (((double)generate()) + 0.5)*(1.0/4294967296.0); } - /// Generate a random triple of baricentric coords - template - void generateBarycentric(PointType &p){ - p[1] = this->generate01(); - p[2] = this->generate01(); - - if(p[1] + p[2] > 1.0){ - p[1] = 1.0 - p[1]; - p[2] = 1.0 - p[2]; - } - p[0]=1.0-(p[1] + p[2]); - } }; // end class MarsenneTwisterRNG /* Returns a value with normal distribution with mean m, standard deviation s diff --git a/vcg/math/shot.h b/vcg/math/shot.h index f0a4b1df..978100b2 100644 --- a/vcg/math/shot.h +++ b/vcg/math/shot.h @@ -75,7 +75,7 @@ public: RotoType rot; // rotation Point3 tra; // viewpoint public: - ReferenceFrame(){} + ReferenceFrame():rot(),tra(){} void SetIdentity(){ rot.SetIdentity(); tra = Point3(0.0,0.0,0.0);} void SetTra(const Point3 & tr) {tra = tr;} @@ -87,18 +87,21 @@ public: Camera Intrinsics; // the camera that made the shot ReferenceFrame Extrinsics; // the position and orientation of the camera Shot(const Camera &i, const ReferenceFrame &e) + :Intrinsics(),Extrinsics() { Intrinsics = i; Extrinsics = e; } Shot(const Camera &c) + :Intrinsics(),Extrinsics() { Intrinsics = c; Extrinsics.SetIdentity(); } Shot() + :Intrinsics(),Extrinsics() { Extrinsics.SetIdentity(); } diff --git a/vcg/simplex/edge/topology.h b/vcg/simplex/edge/topology.h index a52da7b3..c3e4c8b8 100644 --- a/vcg/simplex/edge/topology.h +++ b/vcg/simplex/edge/topology.h @@ -57,7 +57,7 @@ inline bool IsEdgeBorder(EdgeType const & e, const int j ) } template -void VVStarVE(VertexType* vp, std::vector &starVec) +void VVStarVE(const VertexType* vp, std::vector &starVec) { starVec.clear(); edge::VEIterator vei(vp); diff --git a/vcg/simplex/face/topology.h b/vcg/simplex/face/topology.h index 64e990ff..47c1446b 100644 --- a/vcg/simplex/face/topology.h +++ b/vcg/simplex/face/topology.h @@ -321,8 +321,8 @@ void FFAttach(FaceType * &f, int z1, FaceType *&f2, int z2) template void FFAttachManifold(FaceType * &f1, int z1, FaceType *&f2, int z2) { - assert(IsBorder(*f1,z1)); - assert(IsBorder(*f2,z2)); + assert(IsBorder(*f1,z1) || f1->FFp(z1)==0); + assert(IsBorder(*f2,z2) || f2->FFp(z2)==0); assert(f1->V0(z1) == f2->V0(z2) || f1->V0(z1) == f2->V1(z2)); assert(f1->V1(z1) == f2->V0(z2) || f1->V1(z1) == f2->V1(z2)); f1->FFp(z1) = f2; @@ -566,10 +566,10 @@ bool CheckFlipEdgeNormal(FaceType &f, const int z, const float angleRad) assert((NewDiag1 != NewDiag0) && (NewDiag1 != OldDiag0) && (NewDiag1 != OldDiag1)); - CoordType oldN0 = NormalizedNormal( NewDiag0->cP(),OldDiag0->cP(),OldDiag1->cP()); - CoordType oldN1 = NormalizedNormal( NewDiag1->cP(),OldDiag1->cP(),OldDiag0->cP()); - CoordType newN0 = NormalizedNormal( OldDiag0->cP(),NewDiag1->cP(),NewDiag0->cP()); - CoordType newN1 = NormalizedNormal( OldDiag1->cP(),NewDiag0->cP(),NewDiag1->cP()); + CoordType oldN0 = Normal( NewDiag0->cP(),OldDiag0->cP(),OldDiag1->cP()).Normalize(); + CoordType oldN1 = Normal( NewDiag1->cP(),OldDiag1->cP(),OldDiag0->cP()).Normalize(); + CoordType newN0 = Normal( OldDiag0->cP(),NewDiag1->cP(),NewDiag0->cP()).Normalize(); + CoordType newN1 = Normal( OldDiag1->cP(),NewDiag0->cP(),NewDiag1->cP()).Normalize(); if(AngleN(oldN0,newN0) > angleRad) return false; if(AngleN(oldN0,newN1) > angleRad) return false; if(AngleN(oldN1,newN0) > angleRad) return false; @@ -629,13 +629,28 @@ bool CheckFlipEdge(FaceType &f, int z) /*! * Flip the z-th edge of the face f. -* Check for topological correctness first using CheckFlipFace(). +* Check for topological correctness first using CheckFlipEdge(). * \param f pointer to the face * \param z the edge index * -* Note: For edge flip we intend the swap of the diagonal of the rectangle +* Note: For edge flip we intend the swap of the diagonal of the quadrilater * formed by the face \a f and the face adjacent to the specified edge. +* +* 0__________ 2 0__________2 +* -> 1|\ | | /|1 +* | \ g | | g / | +* | \ | |w / | +* | f z\w | | / f z| +* | \ | | / | +* |__________\|1 <- 1|/__________| +* 2 0 2 0 +* +* Note that, after an operation FlipEdge(f,z) +* to topologically revert it should be sufficient to do FlipEdge(f,z+1) +* (even if the mesh is actually different: f and g will be swapped) +* */ + template void FlipEdge(FaceType &f, const int z) { @@ -644,14 +659,14 @@ void FlipEdge(FaceType &f, const int z) assert( !IsBorder(f,z) ); assert( face::IsManifold(f, z)); - FaceType *g = f.FFp(z); - int w = f.FFi(z); + FaceType *g = f.FFp(z); // The other face + int w = f.FFi(z); // and other side - assert( g->V(w) == f.V1(z) ); - assert( g->V1(w)== f.V(z) ); - assert( g->V2(w)!= f.V(z) ); - assert( g->V2(w)!= f.V1(z) ); - assert( g->V2(w)!= f.V2(z) ); + assert( g->V0(w) == f.V1(z) ); + assert( g->V1(w) == f.V0(z) ); + assert( g->V2(w) != f.V0(z) ); + assert( g->V2(w) != f.V1(z) ); + assert( g->V2(w) != f.V2(z) ); f.V1(z) = g->V2(w); g->V1(w) = f.V2(z); @@ -685,6 +700,7 @@ void FlipEdge(FaceType &f, const int z) g->FFp(w)->FFp( g->FFi(w) ) = g; g->FFp(w)->FFi( g->FFi(w) ) = w; } + } template diff --git a/vcg/simplex/vertex/component_ocf.h b/vcg/simplex/vertex/component_ocf.h index 5bc39475..72b42fbd 100644 --- a/vcg/simplex/vertex/component_ocf.h +++ b/vcg/simplex/vertex/component_ocf.h @@ -435,8 +435,17 @@ public: ScalarTypeCur &Kh(){ assert((*this).Base().CurvatureEnabled); return (*this).Base().CuV[(*this).Index()][0]; } ScalarTypeCur &Kg(){ assert((*this).Base().CurvatureEnabled); return (*this).Base().CuV[(*this).Index()][1]; } - ScalarTypeCur cKh() const { assert((*this).Base().CurvatureEnabled); return (*this).Base().CuV[(*this).Index()][0]; } - ScalarTypeCur cKg() const { assert((*this).Base().CurvatureEnabled); return (*this).Base().CuV[(*this).Index()][1]; } + ScalarTypeCur cKh() const + { + assert((*this).Base().CurvatureEnabled); + return (*this).Base().CuV[(*this).Index()][0]; + } + + ScalarTypeCur cKg() const + { + assert((*this).Base().CurvatureEnabled); + return (*this).Base().CuV[(*this).Index()][1]; + } template void ImportData(const RightVertexType & rightV){ @@ -448,7 +457,11 @@ public: TT::ImportData(rightV); } - inline bool IsCurvatureEnabled( ) const { return this->Base().IsCurvatureDirEnabled(); } + inline bool IsCurvatureEnabled( ) const + { + return this->Base().IsCurvatureEnabled(); + } + static bool HasCurvature() { return true; } static bool HasCurvatureOcf() { return true; } }; diff --git a/vcg/space/box3.h b/vcg/space/box3.h index dfdba174..f9e3fccd 100644 --- a/vcg/space/box3.h +++ b/vcg/space/box3.h @@ -72,35 +72,36 @@ public: { return min!=p.min || max!=p.max; } - /** Varia le dimensioni del bounding box scalandole rispetto al parametro scalare. - @param s Valore scalare che indica di quanto deve variare il bounding box + /** Offset of a vector (s,s,s) */ void Offset( const BoxScalarType s ) { Offset( Point3 (s,s,s)); } - /** Varia le dimensioni del bounding box del valore fornito attraverso il parametro. - @param delta Point in 3D space + /** Offset the two corner of the box of a vector delta. + * adding delta to max and -delta to min. + @param delta offset vector */ void Offset( const Point3 & delta ) { min -= delta; max += delta; } - /// Initializing the bounding box + /// Initializing the bounding box void Set( const Point3 & p ) { min = max = p; } - /// Set the bounding box to a null value + + /// Set the bounding box to a null value void SetNull() { min.X()= 1; max.X()= -1; min.Y()= 1; max.Y()= -1; min.Z()= 1; max.Z()= -1; } - /** Function to add two bounding box - @param b Il bounding box che si vuole aggiungere + /** Modify the current bbox to contain also the passed box. + * Adding a null bounding box does nothing */ void Add( Box3 const & b ) { @@ -117,9 +118,7 @@ public: if(max.Z() < b.max.Z()) max.Z() = b.max.Z(); } } - /** Funzione per aggiungere un punto al bounding box. Il bounding box viene modificato se il punto - cade fuori da esso. - @param p The point 3D + /** Modify the current bbox to contain also the passed point */ void Add( const Point3 & p ) { @@ -136,9 +135,7 @@ public: } } - /** Function to add a sphere (a point + radius) to a bbox - @param p The point 3D - @param radius the radius of the sphere centered on p + /** Modify the current bbox to contain also the passed sphere */ void Add( const Point3 & p, const BoxScalarType radius ) { @@ -154,19 +151,22 @@ void Add( const Point3 & p, const BoxScalarType radius ) max.Z() = std::max(max.Z(),p.Z()+radius); } } - // Aggiunge ad un box un altro box trasformato secondo la matrice m + /** Modify the current bbox to contain also the box b trasformed according to the matrix m + */ void Add( const Matrix44 &m, const Box3 & b ) { - const Point3 &mn= b.min; - const Point3 &mx= b.max; + if(b.IsNull()) return; // Adding a null bbox should do nothing + + const Point3 &mn= b.min; + const Point3 &mx= b.max; Add(m*(Point3(mn[0],mn[1],mn[2]))); - Add(m*(Point3(mx[0],mn[1],mn[2]))); - Add(m*(Point3(mn[0],mx[1],mn[2]))); - Add(m*(Point3(mx[0],mx[1],mn[2]))); - Add(m*(Point3(mn[0],mn[1],mx[2]))); - Add(m*(Point3(mx[0],mn[1],mx[2]))); - Add(m*(Point3(mn[0],mx[1],mx[2]))); - Add(m*(Point3(mx[0],mx[1],mx[2]))); + Add(m*(Point3(mx[0],mn[1],mn[2]))); + Add(m*(Point3(mn[0],mx[1],mn[2]))); + Add(m*(Point3(mx[0],mx[1],mn[2]))); + Add(m*(Point3(mn[0],mn[1],mx[2]))); + Add(m*(Point3(mx[0],mn[1],mx[2]))); + Add(m*(Point3(mn[0],mx[1],mx[2]))); + Add(m*(Point3(mx[0],mx[1],mx[2]))); } /** Calcola l'intersezione tra due bounding box. Al bounding box viene assegnato il valore risultante. @param b Il bounding box con il quale si vuole effettuare l'intersezione @@ -191,9 +191,7 @@ void Add( const Point3 & p, const BoxScalarType radius ) min += p; max += p; } - /** Verifica se un punto appartiene ad un bounding box. - @param p The point 3D - @return True se p appartiene al bounding box, false altrimenti + /** true if the point belong to the closed box */ bool IsIn( Point3 const & p ) const { @@ -203,9 +201,8 @@ void Add( const Point3 & p, const BoxScalarType radius ) min.Z() <= p.Z() && p.Z() <= max.Z() ); } - /** Verifica se un punto appartiene ad un bounding box aperto sul max. - @param p The point 3D - @return True se p appartiene al bounding box, false altrimenti + /** true if the point belong to the open box (open on the max side) + * e.g. if p in [min,max) */ bool IsInEx( Point3 const & p ) const { @@ -234,15 +231,14 @@ void Add( const Point3 & p, const BoxScalarType radius ) b.min.Y()min.Y() && b.min.Z()min.Z() ; } - /** Controlla se il bounding box e' nullo. - @return True se il bounding box e' nullo, false altrimenti + /** + return true if the box is null (e.g. invalid or not initialized); */ bool IsNull() const { return min.X()>max.X() || min.Y()>max.Y() || min.Z()>max.Z(); } - /** Controlla se il bounding box e' vuoto. - @return True se il bounding box e' vuoto, false altrimenti + /** return true if the box is empty (e.g. if min == max) */ bool IsEmpty() const { return min==max; } - /// Restituisce la lunghezza della diagonale del bounding box. + /// Return the lenght of the diagonal of the box . BoxScalarType Diag() const { return Distance(min,max); @@ -252,7 +248,7 @@ void Add( const Point3 & p, const BoxScalarType radius ) { return SquaredDistance(min,max); } - /// Calcola il centro del bounding box. + /// Return the center of the box. Point3 Center() const { return (min+max)/2; @@ -277,7 +273,7 @@ void Add( const Point3 & p, const BoxScalarType radius ) (p[2]-min[2])/(max[2]-min[2]) ); } - /// Calcola il volume del bounding box. + /// Return the volume of the box. BoxScalarType Volume() const { return (max.X()-min.X())*(max.Y()-min.Y())*(max.Z()-min.Z()); diff --git a/vcg/space/color4.h b/vcg/space/color4.h index a16381da..e98cdc5f 100644 --- a/vcg/space/color4.h +++ b/vcg/space/color4.h @@ -180,6 +180,8 @@ public: (*this)[3]=255; return; } + float dummy; + h = modff(h,&dummy); if(h==1.0) h = 0.0; int i = int( floor(h*6.0) ); @@ -279,6 +281,15 @@ inline void Color4::Import(const Color4 &b) (*this)[3]=b[3]/255.0f; } +template <> template <> +inline void Color4::Import(const Color4 &b) +{ + (*this)[0]=b[0]/255.0; + (*this)[1]=b[1]/255.0; + (*this)[2]=b[2]/255.0; + (*this)[3]=b[3]/255.0; +} + template <> template <> inline void Color4::Import(const Color4 &b) { @@ -297,6 +308,15 @@ inline void Color4::Import(const Point4 &b) (*this)[3]=(unsigned char)(b[3]*255.0f); } +template <> template <> +inline void Color4::Import(const Point4 &b) +{ + (*this)[0]=(unsigned char)(b[0]*255.0); + (*this)[1]=(unsigned char)(b[1]*255.0); + (*this)[2]=(unsigned char)(b[2]*255.0); + (*this)[3]=(unsigned char)(b[3]*255.0); +} + template <> template <> inline Color4 Color4::Construct( const Color4 & b ) { @@ -317,6 +337,16 @@ inline Color4 Color4::Construct( const Color4 & b ) (float)(b[3])/255.0f); } +template <> template <> +inline Color4 Color4::Construct( const Color4 & b ) +{ + return Color4( + (double)(b[0])/255.0, + (double)(b[1])/255.0, + (double)(b[2])/255.0, + (double)(b[3])/255.0); +} + template<> inline Color4::Color4(Color4::ColorConstant cc) { @@ -329,6 +359,12 @@ inline Color4::Color4(Color4::ColorConstant cc) Import(Color4((Color4::ColorConstant)cc)); } +template<> +inline Color4::Color4(Color4::ColorConstant cc) +{ + Import(Color4((Color4::ColorConstant)cc)); +} + template<> inline Color4::Color4(unsigned int cc) { @@ -341,6 +377,12 @@ inline Color4::Color4(unsigned int cc) Import(Color4(cc)); } +template<> +inline Color4::Color4(unsigned int cc) +{ + Import(Color4(cc)); +} + inline Color4 Clamp(Color4 &c) { c[0]=math::Clamp(c[0],0.0f,1.0f); @@ -350,6 +392,15 @@ inline Color4 Clamp(Color4 &c) return c; } +inline Color4 Clamp(Color4 &c) +{ + c[0]=math::Clamp(c[0],0.0,1.0); + c[1]=math::Clamp(c[1],0.0,1.0); + c[2]=math::Clamp(c[2],0.0,1.0); + c[3]=math::Clamp(c[3],0.0,1.0); + return c; +} + template<> inline Color4 Color4::operator + ( const Color4 & p) const { diff --git a/vcg/space/deprecated_point3.h b/vcg/space/deprecated_point3.h index 793b9c0e..bf52253d 100644 --- a/vcg/space/deprecated_point3.h +++ b/vcg/space/deprecated_point3.h @@ -145,10 +145,10 @@ public: _v[1] = nv[1]; _v[2] = nv[2]; } - inline Point3 & operator =( Point3 const & p ) + inline Point3 & operator =(Point3 const & p) { - _v[0]= p._v[0]; _v[1]= p._v[1]; _v[2]= p._v[2]; - return *this; + _v[0] = p._v[0]; _v[1] = p._v[1]; _v[2] = p._v[2]; + return *this; } inline void SetZero() { diff --git a/vcg/space/index/kdtree/kdtree.h b/vcg/space/index/kdtree/kdtree.h index 0ff9052f..51ddc574 100755 --- a/vcg/space/index/kdtree/kdtree.h +++ b/vcg/space/index/kdtree/kdtree.h @@ -35,481 +35,498 @@ namespace vcg { - template - class ConstDataWrapper + template + class ConstDataWrapper + { + public: + typedef _DataType DataType; + inline ConstDataWrapper() + : mpData(0), mStride(0), mSize(0) + {} + inline ConstDataWrapper(const DataType* pData, int size, int64_t stride = sizeof(DataType)) + : mpData(reinterpret_cast(pData)), mStride(stride), mSize(size) + {} + inline const DataType& operator[] (int i) const { - public: - typedef _DataType DataType; - inline ConstDataWrapper() - : mpData(0), mStride(0), mSize(0) - {} - inline ConstDataWrapper(const DataType* pData, int size, int64_t stride = sizeof(DataType)) - : mpData(reinterpret_cast(pData)), mStride(stride), mSize(size) - {} - inline const DataType& operator[] (int i) const - { - return *reinterpret_cast(mpData + i*mStride); - } - inline size_t size() const { return mSize; } - protected: - const unsigned char* mpData; - int64_t mStride; - size_t mSize; - }; + return *reinterpret_cast(mpData + i*mStride); + } + inline size_t size() const { return mSize; } + protected: + const unsigned char* mpData; + int64_t mStride; + size_t mSize; + }; - template - class VectorConstDataWrapper :public ConstDataWrapper + template + class VectorConstDataWrapper :public ConstDataWrapper + { + public: + inline VectorConstDataWrapper(StdVectorType &vec) : + ConstDataWrapper(&(vec[0]), vec.size(), sizeof(typename StdVectorType::value_type)) + {} + }; + + template + class VertexConstDataWrapper :public ConstDataWrapper + { + public: + inline VertexConstDataWrapper(MeshType &m) : + ConstDataWrapper(&(m.vert[0].P()), m.vert.size(), sizeof(typename MeshType::VertexType)) + {} + }; + + /** + * This class allows to create a Kd-Tree thought to perform the neighbour query (radius search, knn-nearest serach and closest search). + * The class implemetantion is thread-safe. + */ + template + class KdTree + { + public: + + typedef _Scalar Scalar; + typedef vcg::Point3 VectorType; + typedef vcg::Box3 AxisAlignedBoxType; + + typedef HeapMaxPriorityQueue PriorityQueue; + + struct Node { - public: - inline VectorConstDataWrapper(StdVectorType &vec): - ConstDataWrapper ( &(vec[0]), vec.size(), sizeof(typename StdVectorType::value_type)) - {} - }; - - template - class VertexConstDataWrapper :public ConstDataWrapper - { - public: - inline VertexConstDataWrapper(MeshType &m): - ConstDataWrapper ( &(m.vert[0].P()), m.vert.size(), sizeof(typename MeshType::VertexType)) - {} - }; - - /** - * This class allows to create a Kd-Tree thought to perform the neighbour query (radius search, knn-nearest serach and closest search). - * The class implemetantion is thread-safe. - */ - template - class KdTree - { - public: - - typedef _Scalar Scalar; - typedef vcg::Point3 VectorType; - typedef vcg::Box3 AxisAlignedBoxType; - - typedef HeapMaxPriorityQueue PriorityQueue; - - struct Node - { - union { - //standard node - struct { - Scalar splitValue; - unsigned int firstChildId:24; - unsigned int dim:2; - unsigned int leaf:1; - }; - //leaf - struct { - unsigned int start; - unsigned short size; - }; - }; + union { + //standard node + struct { + Scalar splitValue; + unsigned int firstChildId : 24; + unsigned int dim : 2; + unsigned int leaf : 1; }; - typedef std::vector NodeList; - - // return the protected members which store the nodes and the points list - inline const NodeList& _getNodes(void) { return mNodes; } - inline const std::vector& _getPoints(void) { return mPoints; } - - public: - - KdTree(const ConstDataWrapper& points, unsigned int nofPointsPerCell = 16, unsigned int maxDepth = 64); - - ~KdTree(); - - void doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue); - - void doQueryDist(const VectorType& queryPoint, float dist, std::vector& points, std::vector& sqrareDists); - - void doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist); - - protected: - - // element of the stack - struct QueryNode - { - QueryNode() {} - QueryNode(unsigned int id) : nodeId(id) {} - unsigned int nodeId; // id of the next node - Scalar sq; // squared distance to the next node + //leaf + struct { + unsigned int start; + unsigned short size; }; + }; + }; + typedef std::vector NodeList; - // used to build the tree: split the subset [start..end[ according to dim and splitValue, - // and returns the index of the first element of the second subset - unsigned int split(int start, int end, unsigned int dim, float splitValue); + // return the protected members which store the nodes and the points list + inline const NodeList& _getNodes(void) { return mNodes; } + inline const std::vector& _getPoints(void) { return mPoints; } + inline unsigned int _getNumLevel(void) { return numLevel; } + inline const AxisAlignedBoxType& _getAABBox(void) { return mAABB; } - int createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level, unsigned int targetCellsize, unsigned int targetMaxDepth); + public: - protected: + KdTree(const ConstDataWrapper& points, unsigned int nofPointsPerCell = 16, unsigned int maxDepth = 64, bool balanced = false); - AxisAlignedBoxType mAABB; //BoundingBox - NodeList mNodes; //kd-tree nodes - std::vector mPoints; //points read from the input DataWrapper - std::vector mIndices; //points indices + ~KdTree(); + + void doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue); + + void doQueryDist(const VectorType& queryPoint, float dist, std::vector& points, std::vector& sqrareDists); + + void doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist); + + protected: + + // element of the stack + struct QueryNode + { + QueryNode() {} + QueryNode(unsigned int id) : nodeId(id) {} + unsigned int nodeId; // id of the next node + Scalar sq; // squared distance to the next node }; - template - KdTree::KdTree(const ConstDataWrapper& points, unsigned int nofPointsPerCell, unsigned int maxDepth) - : mPoints(points.size()), mIndices(points.size()) - { - // compute the AABB of the input - mPoints[0] = points[0]; - mAABB.Set(mPoints[0]); - for (unsigned int i=1 ; i mPoints; //points read from the input DataWrapper + std::vector mIndices; //points indices + unsigned int targetCellSize; //min number of point in a leaf + unsigned int targetMaxDepth; //max tree depth + unsigned int numLevel; //actual tree depth + bool isBalanced; //true if the tree is balanced + }; + + + template + KdTree::KdTree(const ConstDataWrapper& points, unsigned int nofPointsPerCell, unsigned int maxDepth, bool balanced) + : mPoints(points.size()), mIndices(points.size()) + { + // compute the AABB of the input + mPoints[0] = points[0]; + mAABB.Set(mPoints[0]); + for (unsigned int i = 1; i < mPoints.size(); ++i) + { + mPoints[i] = points[i]; + mIndices[i] = i; + mAABB.Add(mPoints[i]); + } + + targetMaxDepth = maxDepth; + targetCellSize = nofPointsPerCell; + isBalanced = balanced; + //mNodes.reserve(4 * mPoints.size() / nofPointsPerCell); //first node inserted (no leaf). The others are made by the createTree function (recursively) - mNodes.resize(1); - mNodes.back().leaf = 0; - /*int numLevel = */ - createTree(0, 0, mPoints.size(), 1, nofPointsPerCell, maxDepth); - } + mNodes.resize(1); + mNodes.back().leaf = 0; + numLevel = createTree(0, 0, mPoints.size(), 1); + } - template - KdTree::~KdTree() + template + KdTree::~KdTree() + { + } + + + /** Performs the kNN query. + * + * This algorithm uses the simple distance to the split plane to prune nodes. + * A more elaborated approach consists to track the closest corner of the cell + * relatively to the current query point. This strategy allows to save about 5% + * of the leaves. However, in practice the slight overhead due to this tracking + * reduces the overall performance. + * + * This algorithm also use a simple stack while a priority queue using the squared + * distances to the cells as a priority values allows to save about 10% of the leaves. + * But, again, priority queue insertions and deletions are quite involved, and therefore + * a simple stack is by far much faster. + * + * The result of the query, the k-nearest neighbors, are stored into the stack mNeighborQueue, where the + * topmost element [0] is NOT the nearest but the farthest!! (they are not sorted but arranged into a heap). + */ + template + void KdTree::doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue) + { + mNeighborQueue.setMaxSize(k); + mNeighborQueue.init(); + + std::vector mNodeStack(numLevel + 1); + mNodeStack[0].nodeId = 0; + mNodeStack[0].sq = 0.f; + unsigned int count = 1; + + while (count) { - } + //we select the last node (AABB) inserted in the stack + QueryNode& qnode = mNodeStack[count - 1]; + //while going down the tree qnode.nodeId is the nearest sub-tree, otherwise, + //in backtracking, qnode.nodeId is the other sub-tree that will be visited iff + //the actual nearest node is further than the split distance. + Node& node = mNodes[qnode.nodeId]; - /** Performs the kNN query. - * - * This algorithm uses the simple distance to the split plane to prune nodes. - * A more elaborated approach consists to track the closest corner of the cell - * relatively to the current query point. This strategy allows to save about 5% - * of the leaves. However, in practice the slight overhead due to this tracking - * reduces the overall performance. - * - * This algorithm also use a simple stack while a priority queue using the squared - * distances to the cells as a priority values allows to save about 10% of the leaves. - * But, again, priority queue insertions and deletions are quite involved, and therefore - * a simple stack is by far much faster. - * - * The result of the query, the k-nearest neighbors, are stored into the stack mNeighborQueue, where the - * topmost element [0] is NOT the nearest but the farthest!! (they are not sorted but arranged into a heap). - */ - template - void KdTree::doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue) - { - mNeighborQueue.setMaxSize(k); - mNeighborQueue.init(); - - QueryNode mNodeStack[64]; - mNodeStack[0].nodeId = 0; - mNodeStack[0].sq = 0.f; - unsigned int count = 1; - - while (count) + //if the distance is less than the top of the max-heap, it could be one of the k-nearest neighbours + if (mNeighborQueue.getNofElements() < k || qnode.sq < mNeighborQueue.getTopWeight()) + { + //when we arrive to a leaf + if (node.leaf) { - //we select the last node (AABB) inserted in the stack - QueryNode& qnode = mNodeStack[count-1]; + --count; //pop of the leaf - //while going down the tree qnode.nodeId is the nearest sub-tree, otherwise, - //in backtracking, qnode.nodeId is the other sub-tree that will be visited iff - //the actual nearest node is further than the split distance. - Node& node = mNodes[qnode.nodeId]; - - //if the distance is less than the top of the max-heap, it could be one of the k-nearest neighbours - if (mNeighborQueue.getNofElements() < k || qnode.sq < mNeighborQueue.getTopWeight()) - { - //when we arrive to a leaf - if (node.leaf) - { - --count; //pop of the leaf - - //end is the index of the last element of the leaf in mPoints - unsigned int end = node.start+node.size; - //adding the element of the leaf to the heap - for (unsigned int i=node.start ; i - void KdTree::doQueryDist(const VectorType& queryPoint, float dist, std::vector& points, std::vector& sqrareDists) - { - QueryNode mNodeStack[64]; - mNodeStack[0].nodeId = 0; - mNodeStack[0].sq = 0.f; - unsigned int count = 1; - - float sqrareDist = dist*dist; - while (count) - { - QueryNode& qnode = mNodeStack[count-1]; - Node & node = mNodes[qnode.nodeId]; - - if (qnode.sq < sqrareDist) - { - if (node.leaf) - { - --count; // pop - unsigned int end = node.start+node.size; - for (unsigned int i=node.start ; i - void KdTree::doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist) - { - QueryNode mNodeStack[64]; - mNodeStack[0].nodeId = 0; - mNodeStack[0].sq = 0.f; - unsigned int count = 1; - - int minIndex = mIndices.size() / 2; - Scalar minDist = vcg::SquaredNorm(queryPoint - mPoints[minIndex]); - minIndex = mIndices[minIndex]; - - while (count) - { - QueryNode& qnode = mNodeStack[count-1]; - Node & node = mNodes[qnode.nodeId]; - - if (qnode.sq < minDist) - { - if (node.leaf) - { - --count; // pop - unsigned int end = node.start+node.size; - for (unsigned int i=node.start ; i - unsigned int KdTree::split(int start, int end, unsigned int dim, float splitValue) - { - int l(start), r(end-1); - for ( ; l= start && mPoints[r][dim] >= splitValue) - r--; - if (l > r) - break; - std::swap(mPoints[l],mPoints[r]); - std::swap(mIndices[l],mIndices[r]); - } - //returns the index of the first element on the second part - return (mPoints[l][dim] < splitValue ? l+1 : l); - } - - /** recursively builds the kdtree - * - * The heuristic is the following: - * - if the number of points in the node is lower than targetCellsize then make a leaf - * - else compute the AABB of the points of the node and split it at the middle of - * the largest AABB dimension. - * - * This strategy might look not optimal because it does not explicitly prune empty space, - * unlike more advanced SAH-like techniques used for RT. On the other hand it leads to a shorter tree, - * faster to traverse and our experience shown that in the special case of kNN queries, - * this strategy is indeed more efficient (and much faster to build). Moreover, for volume data - * (e.g., fluid simulation) pruning the empty space is useless. - * - * Actually, storing at each node the exact AABB (we therefore have a binary BVH) allows - * to prune only about 10% of the leaves, but the overhead of this pruning (ball/ABBB intersection) - * is more expensive than the gain it provides and the memory consumption is x4 higher ! - */ - template - int KdTree::createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level, unsigned int targetCellSize, unsigned int targetMaxDepth) - { - //select the first node - Node& node = mNodes[nodeId]; - AxisAlignedBoxType aabb; - - //putting all the points in the bounding box - aabb.Set(mPoints[start]); - for (unsigned int i=start+1 ; i diag.Y()) - dim = diag.X() > diag.Z() ? 0 : 2; + //otherwise, if we're not on a leaf else - dim = diag.Y() > diag.Z() ? 1 : 2; + { + // the new offset is the distance between the searched point and the actual split coordinate + float new_off = queryPoint[node.dim] - node.splitValue; - node.dim = dim; - //we divide the bounding box in 2 partitions, considering the average of the "dim" dimension - node.splitValue = Scalar(0.5*(aabb.max[dim] + aabb.min[dim])); - - //midId is the index of the first element in the second partition - unsigned int midId = split(start, end, dim, node.splitValue); + //left sub-tree + if (new_off < 0.) + { + mNodeStack[count].nodeId = node.firstChildId; + //in the father's nodeId we save the index of the other sub-tree (for backtracking) + qnode.nodeId = node.firstChildId + 1; + } + //right sub-tree (same as above) + else + { + mNodeStack[count].nodeId = node.firstChildId + 1; + qnode.nodeId = node.firstChildId; + } + //distance is inherited from the father (while descending the tree it's equal to 0) + mNodeStack[count].sq = qnode.sq; + //distance of the father is the squared distance from the split plane + qnode.sq = new_off*new_off; + ++count; + } + } + else + { + // pop + --count; + } + } + } - node.firstChildId = mNodes.size(); - mNodes.resize(mNodes.size()+2); + /** Performs the distance query. + * + * The result of the query, all the points within the distance dist form the query point, is the vector of the indeces + * and the vector of the squared distances from the query point. + */ + template + void KdTree::doQueryDist(const VectorType& queryPoint, float dist, std::vector& points, std::vector& sqrareDists) + { + std::vector mNodeStack(numLevel + 1); + mNodeStack[0].nodeId = 0; + mNodeStack[0].sq = 0.f; + unsigned int count = 1; + + float sqrareDist = dist*dist; + while (count) + { + QueryNode& qnode = mNodeStack[count - 1]; + Node & node = mNodes[qnode.nodeId]; + + if (qnode.sq < sqrareDist) + { + if (node.leaf) + { + --count; // pop + unsigned int end = node.start + node.size; + for (unsigned int i = node.start; i < end; ++i) + { + float pointSquareDist = vcg::SquaredNorm(queryPoint - mPoints[i]); + if (pointSquareDist < sqrareDist) + { + points.push_back(mIndices[i]); + sqrareDists.push_back(pointSquareDist); + } + } + } + else + { + // replace the stack top by the farthest and push the closest + float new_off = queryPoint[node.dim] - node.splitValue; + if (new_off < 0.) + { + mNodeStack[count].nodeId = node.firstChildId; + qnode.nodeId = node.firstChildId + 1; + } + else + { + mNodeStack[count].nodeId = node.firstChildId + 1; + qnode.nodeId = node.firstChildId; + } + mNodeStack[count].sq = qnode.sq; + qnode.sq = new_off*new_off; + ++count; + } + } + else + { + // pop + --count; + } + } + } + + + /** Searchs the closest point. + * + * The result of the query, the closest point to the query point, is the index of the point and + * and the squared distance from the query point. + */ + template + void KdTree::doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist) + { + std::vector mNodeStack(numLevel + 1); + mNodeStack[0].nodeId = 0; + mNodeStack[0].sq = 0.f; + unsigned int count = 1; + + int minIndex = mIndices.size() / 2; + Scalar minDist = vcg::SquaredNorm(queryPoint - mPoints[minIndex]); + minIndex = mIndices[minIndex]; + + while (count) + { + QueryNode& qnode = mNodeStack[count - 1]; + Node & node = mNodes[qnode.nodeId]; + + if (qnode.sq < minDist) + { + if (node.leaf) + { + --count; // pop + unsigned int end = node.start + node.size; + for (unsigned int i = node.start; i < end; ++i) + { + float pointSquareDist = vcg::SquaredNorm(queryPoint - mPoints[i]); + if (pointSquareDist < minDist) + { + minDist = pointSquareDist; + minIndex = mIndices[i]; + } + } + } + else + { + // replace the stack top by the farthest and push the closest + float new_off = queryPoint[node.dim] - node.splitValue; + if (new_off < 0.) + { + mNodeStack[count].nodeId = node.firstChildId; + qnode.nodeId = node.firstChildId + 1; + } + else + { + mNodeStack[count].nodeId = node.firstChildId + 1; + qnode.nodeId = node.firstChildId; + } + mNodeStack[count].sq = qnode.sq; + qnode.sq = new_off*new_off; + ++count; + } + } + else + { + // pop + --count; + } + } + index = minIndex; + dist = minDist; + } + + + + /** + * Split the subarray between start and end in two part, one with the elements less than splitValue, + * the other with the elements greater or equal than splitValue. The elements are compared + * using the "dim" coordinate [0 = x, 1 = y, 2 = z]. + */ + template + unsigned int KdTree::split(int start, int end, unsigned int dim, float splitValue) + { + int l(start), r(end - 1); + for (; l < r; ++l, --r) + { + while (l < end && mPoints[l][dim] < splitValue) + l++; + while (r >= start && mPoints[r][dim] >= splitValue) + r--; + if (l > r) + break; + std::swap(mPoints[l], mPoints[r]); + std::swap(mIndices[l], mIndices[r]); + } + //returns the index of the first element on the second part + return (mPoints[l][dim] < splitValue ? l + 1 : l); + } + + /** recursively builds the kdtree + * + * The heuristic is the following: + * - if the number of points in the node is lower than targetCellsize then make a leaf + * - else compute the AABB of the points of the node and split it at the middle of + * the largest AABB dimension. + * + * This strategy might look not optimal because it does not explicitly prune empty space, + * unlike more advanced SAH-like techniques used for RT. On the other hand it leads to a shorter tree, + * faster to traverse and our experience shown that in the special case of kNN queries, + * this strategy is indeed more efficient (and much faster to build). Moreover, for volume data + * (e.g., fluid simulation) pruning the empty space is useless. + * + * Actually, storing at each node the exact AABB (we therefore have a binary BVH) allows + * to prune only about 10% of the leaves, but the overhead of this pruning (ball/ABBB intersection) + * is more expensive than the gain it provides and the memory consumption is x4 higher ! + */ + template + int KdTree::createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level) + { + //select the first node + Node& node = mNodes[nodeId]; + AxisAlignedBoxType aabb; + + //putting all the points in the bounding box + aabb.Set(mPoints[start]); + for (unsigned int i = start + 1; i < end; ++i) + aabb.Add(mPoints[i]); + + //bounding box diagonal + VectorType diag = aabb.max - aabb.min; + + //the split "dim" is the dimension of the box with the biggest value + unsigned int dim; + if (diag.X() > diag.Y()) + dim = diag.X() > diag.Z() ? 0 : 2; + else + dim = diag.Y() > diag.Z() ? 1 : 2; + + node.dim = dim; + if (isBalanced) //we divide the points using the median value along the "dim" dimension + { + std::vector tempVector; + for (unsigned int i = start + 1; i < end; ++i) + tempVector.push_back(mPoints[i][dim]); + std::sort(tempVector.begin(), tempVector.end()); + node.splitValue = (tempVector[tempVector.size() / 2.0] + tempVector[tempVector.size() / 2.0 + 1]) / 2.0; + } + else //we divide the bounding box in 2 partitions, considering the average of the "dim" dimension + node.splitValue = Scalar(0.5*(aabb.max[dim] + aabb.min[dim])); + + //midId is the index of the first element in the second partition + unsigned int midId = split(start, end, dim, node.splitValue); + + node.firstChildId = mNodes.size(); + mNodes.resize(mNodes.size() + 2); + bool flag = (midId == start) || (midId == end); int leftLevel, rightLevel; - - { - // left child - unsigned int childId = mNodes[nodeId].firstChildId; - Node& child = mNodes[childId]; - if (midId - start <= targetCellSize || level>=targetMaxDepth) - { - child.leaf = 1; - child.start = start; - child.size = midId - start; + { + // left child + unsigned int childId = mNodes[nodeId].firstChildId; + Node& child = mNodes[childId]; + if (flag || (midId - start) <= targetCellSize || level >= targetMaxDepth) + { + child.leaf = 1; + child.start = start; + child.size = midId - start; leftLevel = level; - } - else - { - child.leaf = 0; - leftLevel = createTree(childId, start, midId, level+1, targetCellSize, targetMaxDepth); - } - } + } + else + { + child.leaf = 0; + leftLevel = createTree(childId, start, midId, level + 1); + } + } - { - // right child - unsigned int childId = mNodes[nodeId].firstChildId+1; - Node& child = mNodes[childId]; - if (end - midId <= targetCellSize || level>=targetMaxDepth) - { - child.leaf = 1; - child.start = midId; - child.size = end - midId; + { + // right child + unsigned int childId = mNodes[nodeId].firstChildId + 1; + Node& child = mNodes[childId]; + if (flag || (end - midId) <= targetCellSize || level >= targetMaxDepth) + { + child.leaf = 1; + child.start = midId; + child.size = end - midId; rightLevel = level; - } - else - { - child.leaf = 0; - rightLevel = createTree(childId, midId, end, level+1, targetCellSize, targetMaxDepth); - } - } + } + else + { + child.leaf = 0; + rightLevel = createTree(childId, midId, end, level + 1); + } + } if (leftLevel > rightLevel) return leftLevel; return rightLevel; - } + } + } #endif diff --git a/vcg/space/index/kdtree/kdtree_face.h b/vcg/space/index/kdtree/kdtree_face.h new file mode 100644 index 00000000..3fc373ee --- /dev/null +++ b/vcg/space/index/kdtree/kdtree_face.h @@ -0,0 +1,300 @@ +/**************************************************************************** +* 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 KDTREE_FACE_H +#define KDTREE_FACE_H + +#include +#include + +namespace vcg { + + /** + * This class allows to create a Kd-Tree thought to perform the neighbour query using the mesh faces (closest search). + * The class implemetantion is thread-safe. + */ + template + class KdTreeFace + { + public: + + typedef typename MeshType::ScalarType Scalar; + typedef typename MeshType::CoordType VectorType; + typedef typename MeshType::BoxType AxisAlignedBoxType; + typedef typename MeshType::FacePointer FacePointer; + + class Node + { + public: + Scalar splitValue; + unsigned int firstChildId : 24; + unsigned int dim : 2; + unsigned int leaf : 1; + AxisAlignedBoxType aabb; + std::vector list; + }; + typedef std::vector NodeListPointer; + + public: + + KdTreeFace(MeshType& mesh, unsigned int maxObjPerCell = 64, unsigned int maxDepth = 64) : epsilon(std::numeric_limits::epsilon()) + { + targetCellSize = maxObjPerCell; + targetMaxDepth = maxDepth; + mNodes.resize(1); + Node& node = mNodes.back(); + node.leaf = 0; + node.aabb = mesh.bbox; + node.aabb.Offset(VectorType(epsilon, epsilon, epsilon)); + for (int i = 0; i < mesh.face.size(); i++) + node.list.push_back(&mesh.face[i]); + numLevel = createTree(0, 1); + }; + + ~KdTreeFace() + { + + }; + + + template FacePointer doQueryClosest(const VectorType& queryPoint, VectorType& narestPoint, Scalar& dist, ObjectMarker& marker, Scalar maxDist = std::numeric_limits::max()) + { + if (maxDist < std::numeric_limits::max() && !mNodes[0].aabb.IsIn(queryPoint) && vcg::PointFilledBoxDistance(queryPoint, mNodes[0].aabb) >= maxDist) + { + dist = maxDist; + return NULL; + } + std::vector mNodeStack(numLevel + 1); + mNodeStack[0].nodeId = 0; + mNodeStack[0].sq = 0.f; + unsigned int count = 1; + + Scalar minDist = maxDist; + VectorType p; + FacePointer face = NULL; + while (count) + { + QueryNode& qnode = mNodeStack[count - 1]; + Node& node = mNodes[qnode.nodeId]; + + if (qnode.sq < minDist) + { + if (node.leaf) + { + --count; // pop + for (int i = 0; i < node.list.size(); i++) + { + if (!marker.IsMarked(node.list[i])) + { + marker.Mark(node.list[i]); + Scalar tempDist = minDist; + VectorType tempP; + if (vcg::face::PointDistanceBase(*node.list[i], queryPoint, tempDist, tempP)) + { + if (tempDist < minDist) + { + minDist = tempDist; + p = tempP; + face = node.list[i]; + } + } + } + } + } + else + { + // replace the stack top by the farthest and push the closest + float new_off = queryPoint[node.dim] - node.splitValue; + float abs_off = abs(new_off); + if (abs_off < minDist) + { + if (new_off < 0.) + { + mNodeStack[count].nodeId = node.firstChildId; + qnode.nodeId = node.firstChildId + 1; + new_off = vcg::PointFilledBoxDistance(queryPoint, mNodes[node.firstChildId + 1].aabb); + } + else + { + mNodeStack[count].nodeId = node.firstChildId + 1; + qnode.nodeId = node.firstChildId; + new_off = vcg::PointFilledBoxDistance(queryPoint, mNodes[node.firstChildId].aabb); + } + mNodeStack[count].sq = qnode.sq; + qnode.sq = new_off; + ++count; + } + else + { + if (new_off < 0.) + qnode.nodeId = node.firstChildId; + else + qnode.nodeId = node.firstChildId + 1; + } + } + } + else + { + // pop + --count; + } + } + dist = minDist; + narestPoint = p; + return face; + } + + protected: + + // element of the stack + struct QueryNode + { + QueryNode() {} + QueryNode(unsigned int id) : nodeId(id) {} + unsigned int nodeId; // id of the next node + Scalar sq; // distance to the next node + }; + + + int createTree(unsigned int nodeId, unsigned int level) + { + Node& node = mNodes[nodeId]; + VectorType diag = node.aabb.max - node.aabb.min; + unsigned int dim; + if (diag.X() > diag.Y()) + dim = diag.X() > diag.Z() ? 0 : 2; + else + dim = diag.Y() > diag.Z() ? 1 : 2; + + node.splitValue = Scalar(0.5*(node.aabb.max[dim] + node.aabb.min[dim])); + node.dim = dim; + + AxisAlignedBoxType leftBox, rightBox; + leftBox.Add(node.aabb.min); + rightBox.Add(node.aabb.max); + if (node.dim == 0) + { + leftBox.Add(VectorType(node.splitValue, node.aabb.max[1], node.aabb.max[2])); + rightBox.Add(VectorType(node.splitValue, node.aabb.min[1], node.aabb.min[2])); + } + else if (node.dim == 1) + { + leftBox.Add(VectorType(node.aabb.max[0], node.splitValue, node.aabb.max[2])); + rightBox.Add(VectorType(node.aabb.min[0], node.splitValue, node.aabb.min[2])); + } + else if (node.dim == 2) + { + leftBox.Add(VectorType(node.aabb.max[0], node.aabb.max[1], node.splitValue)); + rightBox.Add(VectorType(node.aabb.min[0], node.aabb.min[1], node.splitValue)); + } + leftBox.Offset(VectorType(epsilon, epsilon, epsilon)); + rightBox.Offset(VectorType(epsilon, epsilon, epsilon)); + + node.firstChildId = mNodes.size(); + int firstChildId = node.firstChildId; + mNodes.resize(mNodes.size() + 2); + Node& parent = mNodes[nodeId]; + Node& leftChild = mNodes[firstChildId]; + Node& rightChild = mNodes[firstChildId + 1]; + leftChild.aabb.SetNull(); + rightChild.aabb.SetNull(); + + for (int i = 0; i < parent.list.size(); i++) + { + unsigned int state = 0; + FacePointer fp = parent.list[i]; + for (int j = 0; j < 3; j++) + { + if (fp->P(j)[dim] < parent.splitValue) + state |= (1 << 0); + else if (fp->P(j)[dim] > parent.splitValue) + state |= (1 << 1); + else + { + state |= (1 << 0); + state |= (1 << 1); + } + } + if (state & (1 << 0)) + { + leftChild.list.push_back(fp); + leftChild.aabb.Add(fp->P(0)); + leftChild.aabb.Add(fp->P(1)); + leftChild.aabb.Add(fp->P(2)); + } + if (state & (1 << 1)) + { + rightChild.list.push_back(fp); + rightChild.aabb.Add(fp->P(0)); + rightChild.aabb.Add(fp->P(1)); + rightChild.aabb.Add(fp->P(2)); + } + } + parent.list.clear(); + leftChild.aabb.Intersect(leftBox); + rightChild.aabb.Intersect(rightBox); + + int leftLevel, rightLevel; + { + if (leftChild.list.size() <= targetCellSize || level >= targetMaxDepth) + { + leftChild.leaf = 1; + leftLevel = level; + } + else + { + leftChild.leaf = 0; + leftLevel = createTree(firstChildId, level + 1); + } + } + + { + Node& rightChild = mNodes[firstChildId + 1]; + if (rightChild.list.size() <= targetCellSize || level >= targetMaxDepth) + { + rightChild.leaf = 1; + rightLevel = level; + } + else + { + rightChild.leaf = 0; + rightLevel = createTree(firstChildId + 1, level + 1); + } + } + if (leftLevel > rightLevel) + return leftLevel; + return rightLevel; + }; + + + protected: + + NodeListPointer mNodes; //kd-tree nodes + unsigned int numLevel; + const Scalar epsilon; + unsigned int targetCellSize; + unsigned int targetMaxDepth; + }; + +} +#endif diff --git a/vcg/space/rect_packer.h b/vcg/space/rect_packer.h index d5033a3a..d56bc144 100644 --- a/vcg/space/rect_packer.h +++ b/vcg/space/rect_packer.h @@ -240,6 +240,148 @@ static bool PackOccupancyMulti(const std::vector & rectVec, /// the se return true; } +/* This is the low level function that packs a set of int rects onto a grid. + +Based on the criptic code written by Claudio Rocchini + +Greedy algorithm. +Sort the rect according their height (larger first) +and then place them in the position that minimize the area of the bbox of all the placed rectangles + +To efficiently skip occupied areas it fills the grid with the id of the already placed rectangles. +*/ +static bool PackInt(const std::vector & sizes, // the sizes of the rect to be packed + const vcg::Point2i & max_size, // the size of the container + std::vector & posiz, // the found positionsof each rect + vcg::Point2i & global_size) // the size of smallest rect covering all the packed rect +{ + int n = (int)(sizes.size()); + assert(n>0 && max_size[0]>0 && max_size[1]>0); + + int gridSize = max_size[0] * max_size[1]; // Size dell griglia + int i, j, x, y; + + posiz.resize(n, Point2i(-1, -1)); + std::vector grid(gridSize, 0); // Creazione griglia + +#define Grid(q,w) (grid[(q)+(w)*max_size[0]]) + + // Build a permutation that keeps the reordiering of the sizes vector according to their width + std::vector perm(n); + for (i = 0; imax_size[0] || sizes[perm[0]][1]>max_size[1]) + return false; + + // Posiziono il primo + j = perm[0]; + global_size = sizes[j]; + posiz[j] = Point2i(0, 0); + + // Fill the grid with the id(+1) of the first + for (y = 0; y= 0 && x= 0 && y= 0 && j0 && sy>0); + + // Calcolo la posizione limite + int lx = std::min(global_size[0], max_size[0] - sx); + int ly = std::min(global_size[1], max_size[1] - sy); + + assert(lx>0 && ly>0); + + int finterior = 0; + + for (y = 0; y <= ly; y++) + { + for (x = 0; x <= lx;) + { + int px; + int c = Grid(x, y + sy - 1); + // Intersection check + if (!c) c = Grid(x + sx - 1, y + sy - 1); + if (!c) + { + for (px = x; px= 0 && carea) + { + bestx = x; + besty = y; + bestsx = nsx; + bestsy = nsy; + bestArea = area; + if (bestsx == global_size[0] && bestsy == global_size[1]) + finterior = 1; + } + break; + } + if (finterior) break; + } + if (finterior) break; + } + + if (bestArea == -1) + { + return false; + } + + posiz[j][0] = bestx; + posiz[j][1] = besty; + global_size[0] = bestsx; + global_size[1] = bestsy; + for (y = posiz[j][1]; y= 0 && x= 0 && y & sizes, // the sizes of the rect to be packed - const vcg::Point2i & max_size, // the size of the container - std::vector & posiz, // the found positionsof each rect - vcg::Point2i & global_size) // the size of smallest rect covering all the packed rect -{ - int n = (int)(sizes.size()); - assert(n>0 && max_size[0]>0 && max_size[1]>0); - - int gridSize = max_size[0]*max_size[1]; // Size dell griglia - int i,j,x,y; - - posiz.resize(n,Point2i(-1,-1)); - std::vector grid(gridSize,0); // Creazione griglia - - #define Grid(q,w) (grid[(q)+(w)*max_size[0]]) - - // Build a permutation that keeps the reordiering of the sizes vector according to their width - std::vector perm(n); - for(i=0;imax_size[0] || sizes[perm[0]][1]>max_size[1] ) - return false; - - // Posiziono il primo - j = perm[0]; - global_size = sizes[j]; - posiz[j] = Point2i(0,0); - - // Fill the grid with the id(+1) of the first - for(y=0;y=0 && x=0 && y=0 && j0 && sy>0); - - // Calcolo la posizione limite - int lx = std::min(global_size[0],max_size[0]-sx); - int ly = std::min(global_size[1],max_size[1]-sy); - - assert(lx>0 && ly>0); - - int finterior = 0; - - for(y=0;y<=ly;y++) - { - for(x=0;x<=lx;) - { - int px; - int c = Grid(x,y+sy-1); - // Intersection check - if(!c) c = Grid(x+sx-1,y+sy-1); - if(!c) - { - for(px=x;px=0 && carea) - { - bestx = x; - besty = y; - bestsx = nsx; - bestsy = nsy; - bestArea = area; - if( bestsx==global_size[0] && bestsy==global_size[1] ) - finterior = 1; - } - break; - } - if(finterior) break; - } - if( finterior ) break; - } - - if(bestArea==-1) - { - return false; - } - - posiz[j][0] = bestx; - posiz[j][1] = besty; - global_size[0] = bestsx; - global_size[1] = bestsy; - for(y=posiz[j][1];y=0 && x=0 && y & sizes, const int ntexture, diff --git a/wrap/gl/gl_mesh_attributes_info.h b/wrap/gl/gl_mesh_attributes_info.h index 53a32e59..60d96fb1 100644 --- a/wrap/gl/gl_mesh_attributes_info.h +++ b/wrap/gl/gl_mesh_attributes_info.h @@ -2,7 +2,7 @@ * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * -* Copyright(C) 2004-2016 \/)\/ * +* Copyright(C) 2004 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * @@ -119,9 +119,9 @@ namespace vcg class RenderingAtts { public: - RenderingAtts() + RenderingAtts(bool defaultvalue = false) { - reset(); + reset(defaultvalue); } RenderingAtts(const RenderingAtts& att) @@ -174,12 +174,12 @@ namespace vcg return _atts[ind]; } - void reset() + void reset(bool defaultvalue = false) { //delete[] _atts; //_atts = new bool[ATT_NAMES_DERIVED_CLASS::enumArity()]; for(unsigned int ii = 0;ii < ATT_NAMES_DERIVED_CLASS::enumArity();++ii) - _atts[ii] = false; + _atts[ii] = defaultvalue; } static RenderingAtts unionSet(const RenderingAtts& a,const RenderingAtts& b) @@ -288,6 +288,7 @@ namespace vcg } }; + protected: struct INT_ATT_NAMES : public ATT_NAMES { diff --git a/wrap/gl/gl_mesh_attributes_multi_viewer_bo_manager.h b/wrap/gl/gl_mesh_attributes_multi_viewer_bo_manager.h index 7d245569..3ead992c 100644 --- a/wrap/gl/gl_mesh_attributes_multi_viewer_bo_manager.h +++ b/wrap/gl/gl_mesh_attributes_multi_viewer_bo_manager.h @@ -59,8 +59,6 @@ namespace vcg bool _perpoint_mesh_color_enabled; bool _perwire_mesh_color_enabled; bool _persolid_mesh_color_enabled; - - Color4b _permesh_color; bool _perpoint_noshading; bool _perwire_noshading; @@ -77,21 +75,21 @@ namespace vcg RenderingModalityGLOptions() { _perbbox_enabled = false; + _perbbox_fixed_color_enabled = true; _perpoint_fixed_color_enabled = false; _perwire_fixed_color_enabled = true; - - _perbbox_fixed_color = Color4b(Color4b::White); - _perpoint_fixed_color = Color4b(Color4b::White); - _perwire_fixed_color = Color4b(Color4b::DarkGray); - _persolid_fixed_color = Color4b(Color4b::White); + _persolid_fixed_color_enabled = true; + + _perbbox_fixed_color = vcg::Color4b(Color4b::White); + _perpoint_fixed_color = vcg::Color4b(Color4b::White); + _perwire_fixed_color = Color4b(Color4b::DarkGray); + _persolid_fixed_color = vcg::Color4b(Color4b::White); - _persolid_fixed_color_enabled = false; _perbbox_mesh_color_enabled = false; _perpoint_mesh_color_enabled = false; _perwire_mesh_color_enabled = false; - - _permesh_color = Color4b(Color4d::Magenta); + _persolid_mesh_color_enabled = false; _perpoint_dot_enabled = false; @@ -136,8 +134,6 @@ namespace vcg _perwire_fixed_color_enabled = opts._perwire_fixed_color_enabled; _persolid_fixed_color_enabled = opts._persolid_fixed_color_enabled; - _permesh_color = opts._permesh_color; - _perbbox_mesh_color_enabled = opts._perbbox_mesh_color_enabled; _perpoint_mesh_color_enabled = opts._perpoint_mesh_color_enabled; _perwire_mesh_color_enabled = opts._perwire_mesh_color_enabled; @@ -292,7 +288,7 @@ namespace vcg /*************************************************************************************************************************************************************************/ NotThreadSafeGLMeshAttributesMultiViewerBOManager(/*const*/ MESH_TYPE& mesh,MemoryInfo& meminfo, size_t perbatchprimitives) - :_mesh(mesh),_gpumeminfo(meminfo),_bo(INT_ATT_NAMES::enumArity(),NULL),_currallocatedboatt(),_perbatchprim(perbatchprimitives),_chunkmap(),_borendering(false),_edge(),_meshverticeswhenedgeindiceswerecomputed(0),_meshtriangleswhenedgeindiceswerecomputed(0),_tr(),_debugmode(false),_loginfo(),_meaningfulattsperprimitive(PR_ARITY,InternalRendAtts()),_tmpbuffer(0) + :_mesh(mesh),_gpumeminfo(meminfo),_bo(INT_ATT_NAMES::enumArity(),NULL),_currallocatedboatt(),_perbatchprim(perbatchprimitives),_chunkmap(),_borendering(false),_edge(),_meshverticeswhenedgeindiceswerecomputed(0),_meshtriangleswhenedgeindiceswerecomputed(0),_tr(),_debugmode(false),_loginfo(),_meaningfulattsperprimitive(PR_ARITY,InternalRendAtts()) { _tr.SetIdentity(); _bo[INT_ATT_NAMES::ATT_VERTPOSITION] = new GLBufferObject(3,GL_FLOAT,GL_VERTEX_ARRAY,GL_ARRAY_BUFFER); @@ -353,6 +349,16 @@ namespace vcg _perviewreqatts[viewid] = copydt; } + void setPerAllViewsInfo(const PVData& data) + { + ///cleanup stage...if an attribute impossible for a primitive modality is still here (it should not be...) we change the required atts into the view + PVData copydt(data); + for (PRIMITIVE_MODALITY pm = PRIMITIVE_MODALITY(0); pm < PR_ARITY; pm = next(pm)) + copydt._intatts[pm] = InternalRendAtts::intersectionSet(copydt._intatts[size_t(pm)], _meaningfulattsperprimitive[size_t(pm)]); + for (typename ViewsMap::iterator it = _perviewreqatts.begin(); it != _perviewreqatts.end(); ++it) + it->second = copydt; + } + bool removeView(UNIQUE_VIEW_ID_TYPE viewid) { typename ViewsMap::iterator it = _perviewreqatts.find(viewid); @@ -375,59 +381,37 @@ namespace vcg const PVData& dt = it->second; //const InternalRendAtts& atts = it->second._intatts; - glPushAttrib(GL_ALL_ATTRIB_BITS); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glMultMatrix(_tr); - - if ((dt._glopts != NULL) && (dt._glopts->_perbbox_enabled)) - drawBBox(dt._glopts); - - if (dt.isPrimitiveActive(PR_SOLID)) - { - bool somethingmore = dt.isPrimitiveActive(PR_WIREFRAME_EDGES) || dt.isPrimitiveActive(PR_WIREFRAME_TRIANGLES) || dt.isPrimitiveActive(PR_POINTS); - if (somethingmore) - { - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(1.0, 1); - } - drawFilledTriangles(dt._intatts[size_t(PR_SOLID)],dt._glopts,textid); - if (somethingmore) - glDisable(GL_POLYGON_OFFSET_FILL); - } - - if (dt.isPrimitiveActive(PR_WIREFRAME_EDGES) || dt.isPrimitiveActive(PR_WIREFRAME_TRIANGLES)) - { - //InternalRendAtts tmpatts = atts; - bool pointstoo = dt.isPrimitiveActive(PR_POINTS); - - if (pointstoo) - { - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(1.0, 1); - } - bool solidtoo = dt.isPrimitiveActive(PR_SOLID); - - if (dt.isPrimitiveActive(PR_WIREFRAME_TRIANGLES)) - { - drawWiredTriangles(dt._intatts[size_t(PR_WIREFRAME_TRIANGLES)],dt._glopts,textid); - } - else - { - if (dt.isPrimitiveActive(PR_WIREFRAME_EDGES)) - drawEdges(dt._intatts[size_t(PR_WIREFRAME_EDGES)],dt._glopts); - } - - if (pointstoo || solidtoo) - glDisable(GL_POLYGON_OFFSET_FILL); - } - if (dt.isPrimitiveActive(PR_POINTS)) - drawPoints(dt._intatts[size_t(PR_POINTS)],it->second._glopts); - - glPopMatrix(); - glPopAttrib(); + drawFun(dt, textid); } + + void drawAllocatedAttributesSubset(UNIQUE_VIEW_ID_TYPE viewid,const PVData& dt, const std::vector& textid = std::vector()) const + { + typename ViewsMap::const_iterator it = _perviewreqatts.find(viewid); + if (it == _perviewreqatts.end()) + return; + + PVData tmp = dt; + + if (!(_currallocatedboatt[INT_ATT_NAMES::ATT_VERTPOSITION])) + { + for (PRIMITIVE_MODALITY pm = PRIMITIVE_MODALITY(0); pm < PR_ARITY; pm = next(pm)) + { + tmp._pmmask[size_t(pm)] = 0; + tmp._intatts[size_t(pm)] = InternalRendAtts(); + } + } + else + { + for (PRIMITIVE_MODALITY pm = PRIMITIVE_MODALITY(0); pm < PR_ARITY; pm = next(pm)) + { + tmp._intatts[size_t(pm)] = InternalRendAtts::intersectionSet(tmp._intatts[size_t(pm)],_meaningfulattsperprimitive[size_t(pm)]); + tmp._intatts[size_t(pm)] = InternalRendAtts::intersectionSet(tmp._intatts[size_t(pm)],_currallocatedboatt); + } + } + drawFun(dt, textid); + } + bool isBORenderingAvailable() const { return _borendering; @@ -531,9 +515,9 @@ namespace vcg case(INT_ATT_NAMES::ATT_WEDGETEXTURE): return vcg::tri::HasPerWedgeTexCoord(_mesh); case(INT_ATT_NAMES::ATT_VERTINDICES): - return true; + return (_mesh.VN() != 0) && (_mesh.FN() != 0); case(INT_ATT_NAMES::ATT_EDGEINDICES): - return vcg::tri::HasPerVertexFlags(_mesh); + return vcg::tri::HasPerVertexFlags(_mesh) || ((_mesh.VN() != 0) && (_mesh.FN() == 0) && (_mesh.EN() == 0)); default: return false; } @@ -557,7 +541,7 @@ namespace vcg { //If a primitive_modality is not rendered (== no att_VERTPOSITION) all the referred attributes by this view can be eventually deallocated IF they are not used //by some other rendered primitive - //the vertindices is, as usual a diffrent case + //the vertindices is, as usual, a different case if (it->second._intatts[size_t(pm)][INT_ATT_NAMES::ATT_VERTPOSITION]) meaningfulrequiredbyatleastoneview = InternalRendAtts::unionSet(meaningfulrequiredbyatleastoneview,it->second._intatts[size_t(pm)]); else @@ -565,12 +549,16 @@ namespace vcg } } bool thereisreplicatedview = InternalRendAtts::replicatedPipelineNeeded(meaningfulrequiredbyatleastoneview); - meaningfulrequiredbyatleastoneview[INT_ATT_NAMES::ATT_VERTINDICES] = !thereisreplicatedview; + meaningfulrequiredbyatleastoneview[INT_ATT_NAMES::ATT_VERTINDICES] &= !thereisreplicatedview; InternalRendAtts reallyuseless = InternalRendAtts::complementSet(probabilyuseless,meaningfulrequiredbyatleastoneview); bool switchreplicatedindexed = (!InternalRendAtts::replicatedPipelineNeeded(_currallocatedboatt) && thereisreplicatedview) || (InternalRendAtts::replicatedPipelineNeeded(_currallocatedboatt) && !thereisreplicatedview); + /*in some way the vertices number changed. If i use the indexed pipeline i have to deallocate/allocate/update the vertex indices*/ + bool numvertchanged = boExpectedSize(INT_ATT_NAMES::ATT_VERTPOSITION,thereisreplicatedview) != _bo[INT_ATT_NAMES::ATT_VERTPOSITION]->_size; + bool vertindforcedupdate = numvertchanged && meaningfulrequiredbyatleastoneview[INT_ATT_NAMES::ATT_VERTINDICES]; + InternalRendAtts probablytoallocate = InternalRendAtts::complementSet(meaningfulrequiredbyatleastoneview,_currallocatedboatt); InternalRendAtts probablytodeallocate = InternalRendAtts::complementSet(_currallocatedboatt,meaningfulrequiredbyatleastoneview); for(unsigned int ii = 0;ii < INT_ATT_NAMES::enumArity();++ii) @@ -588,8 +576,11 @@ namespace vcg tobedeallocated[boname] = (notempty && !hasmeshattribute) || (notempty && probablytodeallocate[boname]) || (notempty && reallyuseless[boname]) || - (notempty && (_bo[boname]->_size != sz) && meaningfulrequiredbyatleastoneview[boname]); - tobeallocated[boname] = (hasmeshattribute && (sz > 0) && (sz != _bo[boname]->_size) && meaningfulrequiredbyatleastoneview[boname]) || (hasmeshattribute && (sz > 0) && probablytoallocate[boname]); + (notempty && (_bo[boname]->_size != sz) && meaningfulrequiredbyatleastoneview[boname]) || + (notempty && (boname == INT_ATT_NAMES::ATT_VERTINDICES) && (vertindforcedupdate)); + tobeallocated[boname] = (hasmeshattribute && (sz > 0) && (sz != _bo[boname]->_size) && meaningfulrequiredbyatleastoneview[boname]) || + (hasmeshattribute && (sz > 0) && probablytoallocate[boname]) || + (hasmeshattribute && (boname == INT_ATT_NAMES::ATT_VERTINDICES) && (vertindforcedupdate)); tobeupdated[boname] = tobeallocated[boname] || (hasmeshattribute && (sz > 0) && !(isvalid) && meaningfulrequiredbyatleastoneview[boname]); } else @@ -654,22 +645,6 @@ namespace vcg bool buffersMemoryManagementFunction(const InternalRendAtts& tobeallocated,const InternalRendAtts& tobedeallocated,const InternalRendAtts& tobeupdated) { - if (_tmpbuffer == 0) - { - GLfloat tmpdata[9] = {-0.5, -0.5, 0.5, - 0.5, -0.5, 0.5, - -0.5, 0.5, 0.5} ; - glGenBuffers(1,&(_tmpbuffer)); - glBindBuffer(GL_ARRAY_BUFFER,_tmpbuffer); - glBufferData(GL_ARRAY_BUFFER,4 * 9,(GLvoid*) &(tmpdata[0]),GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER,0); - GLuint index[3] = {1,2,0}; - glGenBuffers(1,&(_tmpind)); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_tmpind); - glBufferData(GL_ELEMENT_ARRAY_BUFFER,3 * 4 ,(GLvoid*) &(index),GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); - } - //GLenum err = glGetError(); bool replicated = isThereAReplicatedPipelineView(); std::ptrdiff_t newallocatedmem = bufferObjectsMemoryRequired(tobeallocated); @@ -871,7 +846,6 @@ namespace vcg if (attributestobeupdated[INT_ATT_NAMES::ATT_VERTINDICES]) { - size_t tsz = _bo[INT_ATT_NAMES::ATT_VERTINDICES]->_components * _bo[INT_ATT_NAMES::ATT_VERTINDICES]->getSizeOfGLType() * chunksize; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bo[INT_ATT_NAMES::ATT_VERTINDICES]->_bohandle); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER,chunkingpu * facechunk * _bo[INT_ATT_NAMES::ATT_VERTINDICES]->_components * _bo[INT_ATT_NAMES::ATT_VERTINDICES]->getSizeOfGLType(),_bo[INT_ATT_NAMES::ATT_VERTINDICES]->_components * _bo[INT_ATT_NAMES::ATT_VERTINDICES]->getSizeOfGLType() * chunksize,&ti[0]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -905,6 +879,7 @@ namespace vcg bool isthereaquadview = false; for(typename ViewsMap::const_iterator it = _perviewreqatts.begin();it != _perviewreqatts.end();++it) isthereaquadview = (it->second._intatts[size_t(PR_WIREFRAME_EDGES)][INT_ATT_NAMES::ATT_VERTPOSITION]) || isthereaquadview; + return isthereaquadview; } @@ -983,13 +958,13 @@ namespace vcg _texindnumtriangles.resize(_chunkmap.size()); } - std::vector vpatlas; + std::vector vpatlas; if (attributestobeupdated[INT_ATT_NAMES::ATT_EDGEINDICES]) vpatlas.resize(_mesh.VN(),UINT_MAX); int faceind = 0; size_t chunkindex = faceind; - size_t triangles = 0; + GLuint triangles = 0; for(ChunkMap::const_iterator mit = _chunkmap.begin();mit != _chunkmap.end();++mit) @@ -1180,7 +1155,7 @@ namespace vcg { glDisableClientState(bobj->_clientstatetag); } - + //glBufferData(bobj->_target, sizeof(vcg::Point3f)*_primitivebatch, 0, GL_DYNAMIC_DRAW); glDeleteBuffers(1,&(bobj->_bohandle)); glFlush(); glFinish(); @@ -1282,6 +1257,70 @@ namespace vcg return 0; } + void drawFun(const PVData& dt, const std::vector& textid = std::vector()) const + { + glPushAttrib(GL_ALL_ATTRIB_BITS); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultMatrix(_tr); + + if ((dt._glopts != NULL) && (dt._glopts->_perbbox_enabled)) + drawBBox(dt._glopts); + + if (dt.isPrimitiveActive(PR_SOLID)) + { + bool somethingmore = dt.isPrimitiveActive(PR_WIREFRAME_EDGES) || dt.isPrimitiveActive(PR_WIREFRAME_TRIANGLES) || dt.isPrimitiveActive(PR_POINTS); + if (somethingmore) + { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0, 1); + } + drawFilledTriangles(dt._intatts[size_t(PR_SOLID)], dt._glopts, textid); + if (somethingmore) + glDisable(GL_POLYGON_OFFSET_FILL); + } + + if (dt.isPrimitiveActive(PR_WIREFRAME_EDGES) || dt.isPrimitiveActive(PR_WIREFRAME_TRIANGLES)) + { + //InternalRendAtts tmpatts = atts; + bool pointstoo = dt.isPrimitiveActive(PR_POINTS); + + if (pointstoo) + { + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0, 1); + } + bool solidtoo = dt.isPrimitiveActive(PR_SOLID); + + /*EDGE | TRI | DRAW + --------------------------------- + TRUE TRUE EDGE + TRUE FALSE EDGE + FALSE TRUE TRI + FALSE FALSE NOTHING */ + + if (dt.isPrimitiveActive(PR_WIREFRAME_EDGES)) + drawEdges(dt._intatts[size_t(PR_WIREFRAME_EDGES)], dt._glopts); + else + { + if (dt.isPrimitiveActive(PR_WIREFRAME_TRIANGLES)) + { + drawWiredTriangles(dt._intatts[size_t(PR_WIREFRAME_TRIANGLES)], dt._glopts, textid); + } + } + + if (pointstoo || solidtoo) + glDisable(GL_POLYGON_OFFSET_FILL); + } + if (dt.isPrimitiveActive(PR_POINTS)) + drawPoints(dt._intatts[size_t(PR_POINTS)], dt._glopts,textid); + + glPopMatrix(); + glPopAttrib(); + glFlush(); + glFinish(); + } + void drawFilledTriangles(const InternalRendAtts& req,const GL_OPTIONS_DERIVED_TYPE* glopts,const std::vector& textureindex = std::vector()) const { if (_mesh.VN() == 0) @@ -1290,14 +1329,14 @@ namespace vcg glPushAttrib(GL_ALL_ATTRIB_BITS); bool isgloptsvalid = (glopts != NULL); - - if ((!isgloptsvalid) || (req[INT_ATT_NAMES::ATT_VERTNORMAL]) || (req[INT_ATT_NAMES::ATT_FACENORMAL])) - { - glEnable(GL_LIGHTING); - } - else if (isgloptsvalid && glopts->_persolid_noshading) + + if (isgloptsvalid && glopts->_persolid_noshading) glDisable(GL_LIGHTING); - + else + if ((!isgloptsvalid) || (req[INT_ATT_NAMES::ATT_VERTNORMAL]) || (req[INT_ATT_NAMES::ATT_FACENORMAL])) + { + glEnable(GL_LIGHTING); + } glEnable(GL_COLOR_MATERIAL); if ((isgloptsvalid) && (glopts->_persolid_fixed_color_enabled)) @@ -1305,7 +1344,8 @@ namespace vcg else { if ((isgloptsvalid) && (glopts->_persolid_mesh_color_enabled)) - glColor(glopts->_permesh_color); + glColor(_mesh.C()); + else { if ((req[INT_ATT_NAMES::ATT_VERTCOLOR]) || (req[INT_ATT_NAMES::ATT_FACECOLOR])) glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); @@ -1332,21 +1372,22 @@ namespace vcg bool isgloptsvalid = (glopts != NULL); - if ((!isgloptsvalid) || (req[INT_ATT_NAMES::ATT_VERTNORMAL])) - { - glEnable(GL_LIGHTING); - } - else if (isgloptsvalid && glopts->_perwire_noshading) + if (isgloptsvalid && glopts->_perwire_noshading) glDisable(GL_LIGHTING); - - + else + if ((!isgloptsvalid) || (req[INT_ATT_NAMES::ATT_VERTNORMAL])) + { + glEnable(GL_LIGHTING); + } + glEnable(GL_COLOR_MATERIAL); if ((isgloptsvalid) && (glopts->_perwire_fixed_color_enabled)) glColor(glopts->_perwire_fixed_color); else { if ((isgloptsvalid) && (glopts->_perwire_mesh_color_enabled)) - glColor(glopts->_permesh_color); + glColor(_mesh.C()); + else { if (req[INT_ATT_NAMES::ATT_VERTCOLOR]) glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); @@ -1538,7 +1579,7 @@ namespace vcg glEnd(); } - void drawPoints(const InternalRendAtts& req,GL_OPTIONS_DERIVED_TYPE* glopts) const + void drawPoints(const InternalRendAtts& req,GL_OPTIONS_DERIVED_TYPE* glopts, const std::vector& textureindex = std::vector()) const { if (_mesh.VN() == 0) return; @@ -1547,32 +1588,44 @@ namespace vcg bool isgloptsvalid = (glopts != NULL); - if ((!isgloptsvalid) || req[INT_ATT_NAMES::ATT_VERTNORMAL]) - { - glEnable(GL_LIGHTING); - } - else if (isgloptsvalid && glopts->_perpoint_noshading) + + if ((isgloptsvalid && glopts->_perpoint_noshading) || (isgloptsvalid && glopts->_perpoint_dot_enabled)) glDisable(GL_LIGHTING); + else + if ((!isgloptsvalid) || req[INT_ATT_NAMES::ATT_VERTNORMAL]) + { + glEnable(GL_LIGHTING); + } glEnable(GL_COLOR_MATERIAL); - if ((isgloptsvalid) && ((glopts->_perpoint_fixed_color_enabled) || (glopts->_perpoint_mesh_color_enabled))) - glColor(glopts->_perpoint_fixed_color); - + if ((isgloptsvalid) && ((glopts->_perpoint_fixed_color_enabled) || (glopts->_perpoint_mesh_color_enabled))){ + if (glopts->_perpoint_fixed_color_enabled) + glColor(glopts->_perpoint_fixed_color); + else + glColor(_mesh.C()); + } if (req[INT_ATT_NAMES::ATT_VERTCOLOR]) glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); - if (req[INT_ATT_NAMES::ATT_VERTTEXTURE]) - glEnable(GL_TEXTURE_2D); - else - glDisable(GL_TEXTURE_2D); + if (req[INT_ATT_NAMES::ATT_VERTTEXTURE]) + { + glEnable(GL_TEXTURE_2D); + if (textureindex.size() > 0) + glBindTexture(GL_TEXTURE_2D, textureindex[0]); + else + glBindTexture(GL_TEXTURE_2D, 0); + } + else + glDisable(GL_TEXTURE_2D); //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bo[GLMeshAttributesInfo::ATT_VERTINDEX]->_bohandle); if (glopts != NULL) { - glPointSize(glopts->_perpoint_pointsize); - if(glopts->_perpoint_pointsmooth_enabled) + if (!glopts->_perpoint_dot_enabled) + glPointSize(glopts->_perpoint_pointsize); + if ((glopts->_perpoint_pointsmooth_enabled) || (glopts->_perpoint_dot_enabled)) glEnable(GL_POINT_SMOOTH); else glDisable(GL_POINT_SMOOTH); @@ -1596,11 +1649,31 @@ namespace vcg pointsize = glopts->_perpoint_pointsize; glPointSize(pointsize); } + if (glopts->_perpoint_dot_enabled) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthRange(0.0, 0.9999); + glDepthFunc(GL_LEQUAL); + glPointSize(glopts->_perpoint_pointsize + 0.5); + } } if (isBORenderingAvailable()) drawPointsBO(req); else drawPointsIM(req); + + if ((glopts != NULL) && (glopts->_perpoint_dot_enabled)) + { + float psize = 0.0001f; + if ((glopts->_perpoint_pointsize - 1) > 0) + psize = (glopts->_perpoint_pointsize - 1); + glPointSize(psize); + if (isBORenderingAvailable()) + drawPointsBO(req); + else + drawPointsIM(req); + } glPopAttrib(); } @@ -1609,10 +1682,8 @@ namespace vcg size_t pointsnum = _mesh.VN(); if (InternalRendAtts::replicatedPipelineNeeded(_currallocatedboatt)) pointsnum = _mesh.FN() * 3; - updateClientState(req); glDrawArrays(GL_POINTS,0,GLsizei(pointsnum)); - /*disable all client state buffers*/ InternalRendAtts tmp; updateClientState(tmp); @@ -1644,28 +1715,32 @@ namespace vcg if (_mesh.VN() == 0) return; glPushAttrib(GL_ALL_ATTRIB_BITS); - - + bool isgloptsvalid = (glopts != NULL); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); - if ((!isgloptsvalid) || req[INT_ATT_NAMES::ATT_VERTNORMAL]) - { - glEnable(GL_LIGHTING); - } - else if (isgloptsvalid && glopts->_perwire_noshading) + if (isgloptsvalid && glopts->_perwire_noshading) glDisable(GL_LIGHTING); - + else + if ((!isgloptsvalid) || (req[INT_ATT_NAMES::ATT_VERTNORMAL])) + { + glEnable(GL_LIGHTING); + } bool colordefinedenabled = (isgloptsvalid) && ((glopts->_perwire_fixed_color_enabled) || (glopts->_perwire_mesh_color_enabled)); if (!(isgloptsvalid) || colordefinedenabled) { vcg::Color4b tmpcol = vcg::Color4b(vcg::Color4b::DarkGray); - if (colordefinedenabled) - tmpcol = glopts->_perwire_fixed_color; + if (colordefinedenabled) + { + if (glopts->_perwire_fixed_color_enabled) + tmpcol = glopts->_perwire_fixed_color; + else + tmpcol = _mesh.cC(); + } glColor(tmpcol); } @@ -1771,7 +1846,7 @@ namespace vcg else { if ((isgloptsvalid) && (glopts->_perbbox_mesh_color_enabled)) - glColor(glopts->_permesh_color); + glColor(_mesh.C()); else glColor(vcg::Color4b(vcg::Color4b::White)); } @@ -2096,8 +2171,8 @@ namespace vcg assert(nz>=0); assert(nzVN()); - _v[0] = size_t(vcg::tri::Index(m,pf->V(nz)));; - _v[1] = size_t(vcg::tri::Index(m,pf->V(pf->Next(nz)))); + _v[0] = GLuint(vcg::tri::Index(m,pf->V(nz)));; + _v[1] = GLuint(vcg::tri::Index(m,pf->V(pf->Next(nz)))); assert(_v[0] != _v[1]); // The face pointed by 'f' is Degenerate (two coincident vertexes) if( _v[0] > _v[1] ) @@ -2249,8 +2324,6 @@ namespace vcg DebugInfo _loginfo; std::vector _meaningfulattsperprimitive; - GLuint _tmpbuffer; - GLuint _tmpind; }; } diff --git a/wrap/gl/gl_type_name.h b/wrap/gl/gl_type_name.h new file mode 100644 index 00000000..fa1b8fbc --- /dev/null +++ b/wrap/gl/gl_type_name.h @@ -0,0 +1,46 @@ +/**************************************************************************** +* 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 __VCG_GL_TYPE_NAME +#define __VCG_GL_TYPE_NAME + +namespace vcg +{ + template + class GL_TYPE_NM + {public: + static GLenum SCALAR() { assert(0); return 0;} + }; + template <> class GL_TYPE_NM + { public: + typedef GLfloat ScalarType; + static GLenum SCALAR() { return GL_FLOAT; } + }; + template <> class GL_TYPE_NM + {public: + typedef GLdouble ScalarType; + static GLenum SCALAR() { return GL_DOUBLE; } + }; +} + +#endif \ No newline at end of file diff --git a/wrap/gl/pick.h b/wrap/gl/pick.h index 70c6d90c..d4ea2e8d 100644 --- a/wrap/gl/pick.h +++ b/wrap/gl/pick.h @@ -26,6 +26,8 @@ #include #include +#include "gl_type_name.h" + namespace vcg{ template @@ -153,7 +155,7 @@ public: Box3 reg =ComputeDCBox(x,y,width,height); - if(M!=lastM || &m != lastm) + if ((M != lastM) || (&m != lastm) || (pVec.size() != m.VN())) { FillProjectedVector(m,pVec,M,viewportF); lastM = M; @@ -182,21 +184,24 @@ public: reg.Add(CoordType(x-width/ScalarType(2.0),y-height/ScalarType(2.0),ScalarType(-1.0))); reg.Add(CoordType(x+width/ScalarType(2.0),y+height/ScalarType(2.0),ScalarType(1.0))); - if(M!=lastM || &m != lastm) + if((M!=lastM) || (&m != lastm) || (pVec.size() != m.VN())) { FillProjectedVector(m,pVec,M,viewportF); lastM = M; lastm = &m; } - for(size_t i=0;i-1.0f) && (p1[2]>-1.0f) && (p2[2]>-1.0f) && IntersectionTriangleBox(reg,p0,p1,p2)) - result.push_back(&m.face[i]); - } + for (size_t i = 0; i < m.face.size(); ++i) + { + if (!m.face[i].IsD()) + { + const CoordType &p0 = pVec[tri::Index(m, m.face[i].V(0))]; + const CoordType &p1 = pVec[tri::Index(m, m.face[i].V(1))]; + const CoordType &p2 = pVec[tri::Index(m, m.face[i].V(2))]; + if ((p0[2] > -1.0f) && (p1[2] > -1.0f) && (p2[2] > -1.0f) && IntersectionTriangleBox(reg, p0, p1, p2)) + result.push_back(&m.face[i]); + } + } return result.size(); } diff --git a/wrap/gl/trimesh.h b/wrap/gl/trimesh.h index deba3b37..43365152 100644 --- a/wrap/gl/trimesh.h +++ b/wrap/gl/trimesh.h @@ -34,26 +34,10 @@ #include #include #include +#include - -namespace vcg { - - template - class GL_TYPE_NM - {public: - static GLenum SCALAR() { assert(0); return 0;} - }; - template <> class GL_TYPE_NM - { public: - typedef GLfloat ScalarType; - static GLenum SCALAR() { return GL_FLOAT; } - }; - template <> class GL_TYPE_NM - {public: - typedef GLdouble ScalarType; - static GLenum SCALAR() { return GL_DOUBLE; } - }; - +namespace vcg +{ //template <> GL_TYPE::SCALAR() { return GL_DOUBLE; } // classe base di glwrap usata solo per poter usare i vari drawmode, normalmode senza dover diff --git a/wrap/gui/rubberband.cpp b/wrap/gui/rubberband.cpp index 4ba2001b..f08d0002 100644 --- a/wrap/gui/rubberband.cpp +++ b/wrap/gui/rubberband.cpp @@ -146,36 +146,39 @@ void Rubberband::RenderLine(QGLWidget* gla, Point3f AA, Point3f BB) glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glDepthMask(false); - glLineWidth(2.5); - glPointSize(6.0); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glColor(color); - glLineWidth(2.0); - glPointSize(5.0); - glBegin(GL_LINES); - glVertex(AA); - glVertex(BB); - glEnd(); - glBegin(GL_POINTS); - glVertex(AA); - glVertex(BB); - glEnd(); - glDepthFunc(GL_GREATER); - glLineWidth(1.0f); - glPointSize(2.0f); - glBegin(GL_LINES); - glVertex(AA); - glVertex(BB); - glEnd(); - glBegin(GL_POINTS); - glVertex(AA); - glVertex(BB); - glEnd(); - glDepthFunc(GL_LESS); + // IN FRONT OF SURFACE + glDepthFunc(GL_LESS); + glLineWidth(2.5); + glPointSize(6.0); + glBegin(GL_LINES); + glVertex(AA); + glVertex(BB); + glEnd(); + glBegin(GL_POINTS); + glVertex(AA); + glVertex(BB); + glEnd(); + + // BEHIND SURFACE + glDepthFunc(GL_GREATER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR); + glLineWidth(1.5f); + glPointSize(4.0f); + glBegin(GL_LINES); + glVertex(AA); + glVertex(BB); + glEnd(); + glBegin(GL_POINTS); + glVertex(AA); + glVertex(BB); + glEnd(); + + glDepthFunc(GL_LESS); glPopAttrib(); assert(!glGetError()); } diff --git a/wrap/gui/trackmode.cpp b/wrap/gui/trackmode.cpp index c2688cd4..f8157f62 100644 --- a/wrap/gui/trackmode.cpp +++ b/wrap/gui/trackmode.cpp @@ -95,7 +95,8 @@ void PanMode::Apply (Trackball * tb, Point3f new_point) } void PanMode::Draw(Trackball * tb){ - DrawSphereIcon(tb,true ); + DrawSphereIcon(tb,true); + DrawSphereAxis(tb); DrawUglyPanMode(tb); } diff --git a/wrap/gui/trackutils.h b/wrap/gui/trackutils.h index 08c9cc0c..4e3d1ede 100644 --- a/wrap/gui/trackutils.h +++ b/wrap/gui/trackutils.h @@ -176,7 +176,7 @@ bool HitHyper (Point3f center, float radius, Point3f viewpoint, Plane3f viewplan * in the simple ortho case, the hit point is just the value of * y = 1/x * (r^2 /2 ) on the hitOnViewPlane */ -bool HitHyperOrtho(Point3f center, float radius, Point3f viewpoint, Plane3f viewplane, +bool HitHyperOrtho(Point3f center, float radius, Point3f /*viewpoint*/, Plane3f viewplane, Point3f hitOnViewplane, Point3f & hit) { float xval = Distance (center, hitOnViewplane); @@ -707,15 +707,15 @@ void DrawSphereIcon (Trackball * tb, bool active, bool planeshandle=false) glRotatef (90, 1, 0, 0); col[0] = .40f; col[1] = .85f; col[2] = .40f; glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, col); - DrawCircle(planeshandle); + DrawCircle(planeshandle); glRotatef (90, 0, 1, 0); col[0] = .85f; col[1] = .40f; col[2] = .40f; glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, col); DrawCircle(planeshandle); - glPopMatrix (); - glPopAttrib (); + glPopMatrix(); + glPopAttrib(); } // TEMPORARY drawing section @@ -832,6 +832,66 @@ void DrawUglyScaleMode(Trackball * tb) DrawUglyLetter(tb,ugly_s); } +/*! +@brief function to draw X,Y and Z axis in the trackball sphere. + +Draws the three colored axis inside the trackball sphere. added to better see the trackball center when panning + +@param tb the manipulator. +*/ +void DrawSphereAxis(Trackball * tb) +{ + glPushAttrib(GL_TRANSFORM_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glDepthMask(GL_FALSE); + + Point3f center = tb->center + tb->track.InverseMatrix()*Point3f(0, 0, 0); + glTranslate(center); + glScale(tb->radius / tb->track.sca); + + float amb[4] = { .35f, .35f, .35f, 1.0f }; + float col[4] = { .5f, .5f, .8f, 1.0f }; + glEnable(GL_LINE_SMOOTH); + glLineWidth(DH.LineWidthMoving); + glDisable(GL_COLOR_MATERIAL); // has to be disabled, it is used by wrapper to draw meshes, and prevent direct material setting, used here + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor(DH.color); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, amb); + + col[0] = 1.0f; col[1] = 0.0f; col[2] = 0.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col); + glBegin(GL_LINES); + glNormal3d(-1.0, 0.0, 0.0); + glVertex3d(-1.2, 0.0, 0.0); + glNormal3d( 1.0, 0.0, 0.0); + glVertex3d( 1.2, 0.0, 0.0); + glEnd(); + col[0] = 0.0f; col[1] = 1.0f; col[2] = 0.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col); + glBegin(GL_LINES); + glNormal3d(0.0,-1.0, 0.0); + glVertex3d(0.0,-1.2, 0.0); + glNormal3d(0.0, 1.0, 0.0); + glVertex3d(0.0, 1.2, 0.0); + glEnd(); + col[0] = 0.0f; col[1] = 0.0f; col[2] = 1.0f; + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col); + glBegin(GL_LINES); + glNormal3d(0.0, 0.0,-1.0); + glVertex3d(0.0, 0.0,-1.2); + glNormal3d(0.0, 0.0, 1.0); + glVertex3d(0.0, 0.0, 1.2); + glEnd(); + + glPopMatrix(); + glPopAttrib(); +} + /*! @brief AxisMode drawing function, member of the \e DrawUgly series. diff --git a/wrap/io_trimesh/export_gts.h b/wrap/io_trimesh/export_gts.h index ea7c0c7b..83bf9cfe 100644 --- a/wrap/io_trimesh/export_gts.h +++ b/wrap/io_trimesh/export_gts.h @@ -53,7 +53,7 @@ namespace vcg { typedef typename SaveMeshType::VertexIterator VertexIterator; typedef typename SaveMeshType::FaceIterator FaceIterator; - static int Save(SaveMeshType &m, const char * filename, int mask=0 ) + static int Save(SaveMeshType &m, const char * filename, int /*mask*/ ) { QFile device(filename); if (!device.open(QFile::WriteOnly)) diff --git a/wrap/io_trimesh/export_ply.h b/wrap/io_trimesh/export_ply.h index 385b4487..73ed0622 100644 --- a/wrap/io_trimesh/export_ply.h +++ b/wrap/io_trimesh/export_ply.h @@ -405,7 +405,7 @@ namespace vcg { fprintf(fpout,"%.*g %.*g %.*g " ,DGT,vp->P()[0],DGT,vp->P()[1],DGT,vp->P()[2]); if( HasPerVertexNormal(m) && (pi.mask & Mask::IOM_VERTNORMAL) ) - fprintf(fpout,"%.*g %.*g %.*g " ,DGT,vp->N()[0],DGT,vp->N()[1],DGT,vp->N()[2]); + fprintf(fpout,"%.*g %.*g %.*g " ,DGT,double(vp->N()[0]),DGT,double(vp->N()[1]),DGT,double(vp->N()[2])); if( HasPerVertexFlags(m) && (pi.mask & Mask::IOM_VERTFLAGS)) fprintf(fpout,"%d ",vp->Flags()); @@ -559,7 +559,7 @@ namespace vcg { else if( HasPerWedgeTexCoord(m) && (pi.mask & Mask::IOM_WEDGTEXCOORD) ) { fprintf(fpout,"%d ",fp->VN()*2); - for(int k=0;kVN()*2;++k) + for(int k=0;kVN();++k) fprintf(fpout,"%f %f " ,fp->WT(k).u() ,fp->WT(k).v() diff --git a/wrap/io_trimesh/import.h b/wrap/io_trimesh/import.h index 39ef6555..96d161bb 100644 --- a/wrap/io_trimesh/import.h +++ b/wrap/io_trimesh/import.h @@ -93,6 +93,9 @@ return lastType; } public: +enum ImporterError { + E_NOERROR =0 // No error =0 is the standard for ALL the imported files. +}; // simple aux function that returns true if a given file has a given extesnion static bool FileExtension(std::string filename, std::string extension) { diff --git a/wrap/io_trimesh/import_gts.h b/wrap/io_trimesh/import_gts.h index daa48a18..9753e1db 100644 --- a/wrap/io_trimesh/import_gts.h +++ b/wrap/io_trimesh/import_gts.h @@ -185,7 +185,7 @@ namespace vcg } sa = line.split(' '); - if (!sa.size()>=3) + if (!(sa.size()>=3)) { std::cerr << "Error parsing vertex " << line.toLocal8Bit().data() << "\n"; return InvalidFile; @@ -213,7 +213,7 @@ namespace vcg } sa = line.split(' '); - if (!sa.size()>=2) + if (!(sa.size()>=2)) { std::cerr << "Error parsing edge " << line.toLocal8Bit().data() << "\n"; return InvalidFile; @@ -238,7 +238,7 @@ namespace vcg } sa = line.split(' '); - if (!sa.size()>=3) + if (!(sa.size()>=3)) { std::cerr << "Error parsing face " << line.toLocal8Bit().data() << "\n"; return InvalidFile; diff --git a/wrap/io_trimesh/import_obj.h b/wrap/io_trimesh/import_obj.h index 3c3001eb..cdedc588 100644 --- a/wrap/io_trimesh/import_obj.h +++ b/wrap/io_trimesh/import_obj.h @@ -524,7 +524,6 @@ namespace vcg { locInd[iii]=indexTriangulatedVect[pi+iii]; ff.v[iii]=indexVVect[ locInd[iii] ]; ff.n[iii]=indexNVect[ locInd[iii] ]; -// qDebug("ff.n[iii]=indexNVect[ locInd[iii] ]; %i", ff.n[iii]); ff.t[iii]=indexTVect[ locInd[iii] ]; } @@ -644,14 +643,11 @@ namespace vcg { for(int i=0; i= 0 && size_t(e.v0) < m.vert.size() && e.v1 >= 0 && size_t(e.v1) < m.vert.size()); // TODO add proper handling of bad indices - - edge.V(0) = &(m.vert[e.v0]); - edge.V(1) = &(m.vert[e.v1]); + m.edge[i].V(0) = &(m.vert[e.v0]); + m.edge[i].V(1) = &(m.vert[e.v1]); } } //------------------------------------------------------------------------------- @@ -664,7 +660,9 @@ namespace vcg { m.face[i].Alloc(indexedFaces[i].v.size()); // it does not do anything if it is a trimesh for(unsigned int j=0;j=0 && vertInd < m.vn); m.face[i].V(j) = &(m.vert[indexedFaces[i].v[j]]); if (((oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD) != 0) && (HasPerWedgeTexCoord(m))) @@ -687,7 +685,6 @@ namespace vcg { if ( oi.mask & vcg::tri::io::Mask::IOM_VERTNORMAL ) { -// qDebug("XXXXXX %i",indexedFaces[i].n[j]); m.face[i].V(j)->N().Import(normals[indexedFaces[i].n[j]]); } @@ -734,10 +731,10 @@ namespace vcg { /*! - * Read the next valid line and parses it into "tokens", allowing - * the tokens to be read one at a time. - * \param stream The object providing the input stream - * \param tokens The "tokens" in the next line + * Read the next valid line and parses it into "tokens" (e.g. groups like 234/234/234), allowing + * the tokens to be read one at a time. It read multiple lines concatenating them if they end with '\' + * \param stream The object providing the input stream + * \param tokens The "tokens" in the next line */ inline static void TokenizeNextLine(std::ifstream &stream, std::vector< std::string > &tokens, std::vector *colVec) { @@ -746,6 +743,16 @@ namespace vcg { do { std::getline(stream, line); + // We have to manage backspace terminated lines, + // joining them together before parsing them + if(!line.empty() && line.back()==13) line.pop_back(); + while(!line.empty() && line.back()=='\\') { + std::string tmpLine; + std::getline(stream, tmpLine); + if(tmpLine.back()==13) line.pop_back(); + line.pop_back(); + line.append(tmpLine); + } const size_t len = line.length(); if((len > 0) && colVec && line[0] == '#') { @@ -798,6 +805,12 @@ namespace vcg { while (from %i %i %i",token.c_str(),vId,nId,tId); - /* - const std::string vStr = (hasPosition) ? (token.substr(0, firstSep)) : ("0"); - const std::string tStr = (hasTexcoord) ? (token.substr(firstSep + 1, secondSep - firstSep - 1)) : ("0"); - const std::string nStr = (hasNormal) ? (token.substr(secondSep + 1)) : ("0"); - - if (!vStr.empty()) vId = atoi(vStr.c_str()) - 1; - if (!tStr.empty()) tId = atoi(tStr.c_str()) - 1; - if (!nStr.empty()) nId = atoi(nStr.c_str()) - 1; - */ } -#if 0 - // This function takes a token and, according to the mask, it returns the indexes of the involved vertex, normal and texcoord indexes. - // Example. if the obj file has vertex texcoord (e.g. lines 'vt 0.444 0.5555') - // when parsing a line like - // f 46/303 619/325 624/326 623/327 - // if in the mask you have specified to read wedge tex coord - // for the first token it will return inside vId and tId the corresponding indexes 46 and 303 ) - inline static void SplitToken(std::string token, int &vId, int &nId, int &tId, int mask) - { - std::string vertex; - std::string texcoord; - std::string normal; - - if( ( mask & Mask::IOM_WEDGTEXCOORD ) && (mask & Mask::IOM_WEDGNORMAL) ) SplitVVTVNToken(token, vertex, texcoord, normal); - if(!( mask & Mask::IOM_WEDGTEXCOORD ) && (mask & Mask::IOM_WEDGNORMAL) ) SplitVVNToken(token, vertex, normal); - if( ( mask & Mask::IOM_WEDGTEXCOORD ) &&!(mask & Mask::IOM_WEDGNORMAL) ) SplitVVTToken(token, vertex, texcoord); - if(!( mask & Mask::IOM_WEDGTEXCOORD ) &&!(mask & Mask::IOM_WEDGNORMAL) ) SplitVToken(token, vertex); - - vId = atoi(vertex.c_str()) - 1; - if(mask & Mask::IOM_WEDGTEXCOORD) tId = atoi(texcoord.c_str()) - 1; - if(mask & Mask::IOM_WEDGNORMAL) nId = atoi(normal.c_str()) - 1; - } - - inline static void SplitVToken(std::string token, std::string &vertex) - { - vertex = token; - } - - inline static void SplitVVTToken(std::string token, std::string &vertex, std::string &texcoord) - { - vertex.clear(); - texcoord.clear(); - - size_t from = 0; - size_t to = 0; - size_t length = token.size(); - - if(from!=length) - { - char c = token[from]; - vertex.push_back(c); - - to = from+1; - while (to namespace vcg { -namespace tri { -namespace io { + namespace tri { + namespace io { -// /** \addtogroup */ -// /* @{ */ -/** - This class encapsulate a filter for importing OFF meshes. - A basic description of the OFF file format can be found at http://www.geomview.org/docs/html/geomview_41.html - */ -template -class ImporterOFF -{ -public: + // /** \addtogroup */ + // /* @{ */ + /** + This class encapsulate a filter for importing OFF meshes. + A basic description of the OFF file format can be found at http://www.geomview.org/docs/html/geomview_41.html + */ + template + class ImporterOFF + { + public: - typedef typename MESH_TYPE::VertexType VertexType; - typedef typename MESH_TYPE::VertexIterator VertexIterator; - typedef typename MESH_TYPE::VertexPointer VertexPointer; - typedef typename MESH_TYPE::FaceType FaceType; - typedef typename MESH_TYPE::FaceIterator FaceIterator; - typedef typename MESH_TYPE::FacePointer FacePointer; - typedef typename MESH_TYPE::CoordType CoordType; - typedef typename MESH_TYPE::ScalarType ScalarType; + typedef typename MESH_TYPE::VertexType VertexType; + typedef typename MESH_TYPE::VertexIterator VertexIterator; + typedef typename MESH_TYPE::VertexPointer VertexPointer; + typedef typename MESH_TYPE::FaceType FaceType; + typedef typename MESH_TYPE::FaceIterator FaceIterator; + typedef typename MESH_TYPE::FacePointer FacePointer; + typedef typename MESH_TYPE::CoordType CoordType; + typedef typename MESH_TYPE::ScalarType ScalarType; - // OFF codes - enum OFFCodes {NoError=0, CantOpen, InvalidFile, - InvalidFile_MissingOFF, - UnsupportedFormat, ErrorNotTriangularFace,ErrorHighDimension,ErrorDegenerateFace}; + // OFF codes + enum OFFCodes { + NoError = 0, CantOpen, InvalidFile, + InvalidFile_MissingOFF, + UnsupportedFormat, ErrorNotTriangularFace, ErrorHighDimension, ErrorDegenerateFace + }; - /*! - * Standard call for knowing the meaning of an error code - * \param message_code The code returned by Open - * \return The string describing the error code - */ - static const char* ErrorMsg(int message_code) - { - static const char* error_msg[] = - { - "No errors", "Can't open file", "Invalid file", - "Invalid file: OFF file should have in the first line the OFF keyword as a first token", - "Unsupported format", "Face with more than 3 vertices","File with high dimensional vertexes are not supported", "Error Degenerate Face with less than 3 vertices" }; + /*! + * Standard call for knowing the meaning of an error code + * \param message_code The code returned by Open + * \return The string describing the error code + */ + static const char* ErrorMsg(int message_code) + { + static const char* error_msg[] = + { + "No errors", "Can't open file", "Invalid file", + "Invalid file: OFF file should have in the first line the OFF keyword as a first token", + "Unsupported format", "Face with more than 3 vertices","File with high dimensional vertexes are not supported", "Error Degenerate Face with less than 3 vertices" }; - if(message_code>6 || message_code<0) - return "Unknown error"; - else - return error_msg[message_code]; - }; + if (message_code > 6 || message_code < 0) + return "Unknown error"; + else + return error_msg[message_code]; + }; - /** - * Load only the properties of the 3D objects. - * - * \param filename the name of the file to read from - * \param loadmask the mask which encodes the properties - * \return the operation result - */ - static bool LoadMask(const char *filename, int &loadmask) - { - // To obtain the loading mask all the file must be parsed - // to distinguish between per-vertex and per-face color attribute. - loadmask=0; - MESH_TYPE dummyMesh; - return (Open(dummyMesh, filename, loadmask)==NoError); - } + /** + * Load only the properties of the 3D objects. + * + * \param filename the name of the file to read from + * \param loadmask the mask which encodes the properties + * \return the operation result + */ + static bool LoadMask(const char *filename, int &loadmask) + { + // To obtain the loading mask all the file must be parsed + // to distinguish between per-vertex and per-face color attribute. + loadmask = 0; + MESH_TYPE dummyMesh; + return (Open(dummyMesh, filename, loadmask) == NoError); + } - static int Open(MESH_TYPE &mesh, const char *filename,CallBackPos *cb=0) - { - int loadmask; - return Open(mesh,filename,loadmask,cb); - } + static int Open(MESH_TYPE &mesh, const char *filename, CallBackPos *cb = 0) + { + int loadmask; + return Open(mesh, filename, loadmask, cb); + } - static int OpenMem(MESH_TYPE &mesh, const char *mem, size_t sz, int &loadmask, - CallBackPos *cb=0) - { - std::string str; - str.append(mem,sz); - std::istringstream strm(str); - return OpenStream(mesh,strm,loadmask,cb); - } + static int OpenMem(MESH_TYPE &mesh, const char *mem, size_t sz, int &loadmask, + CallBackPos *cb = 0) + { + std::string str; + str.append(mem, sz); + std::istringstream strm(str); + return OpenStream(mesh, strm, loadmask, cb); + } - /*! - * Standard call for reading a mesh. - * - * \param mesh the destination mesh - * \param filename the name of the file to read from - * \return the operation result - */ - static int Open(MESH_TYPE &mesh, const char *filename, int &loadmask, - CallBackPos *cb=0) - { - std::ifstream stream(filename); - if (stream.fail()) - return CantOpen; - return OpenStream(mesh,stream,loadmask,cb); - } + /*! + * Standard call for reading a mesh. + * + * \param mesh the destination mesh + * \param filename the name of the file to read from + * \return the operation result + */ + static int Open(MESH_TYPE &mesh, const char *filename, int &loadmask, + CallBackPos *cb = 0) + { + std::ifstream stream(filename); + if (stream.fail()) + return CantOpen; + return OpenStream(mesh, stream, loadmask, cb); + } - static int OpenStream(MESH_TYPE &mesh, std::istream &stream, int &loadmask, - CallBackPos *cb=0) - { - std::vector< std::string > tokens; - TokenizeNextLine(stream, tokens); - if(tokens.empty()) return InvalidFile_MissingOFF; + static int OpenStream(MESH_TYPE &mesh, std::istream &stream, int &loadmask, + CallBackPos *cb = 0) + { + std::vector< std::string > tokens; + TokenizeNextLine(stream, tokens); + if (tokens.empty()) return InvalidFile_MissingOFF; - bool isNormalDefined = false; - bool isColorDefined = false; - bool isTexCoordDefined = false; - int dimension = 3; - bool homogeneousComponents = false; + bool isNormalDefined = false; + bool isColorDefined = false; + bool isTexCoordDefined = false; + int dimension = 3; + bool homogeneousComponents = false; - /* - [ST][C][N][4][n]OFF # Header keyword - [Ndim] # Space dimension of vertices, present only if nOFF - NVertices NFaces NEdges # NEdges not used or checked + /* + [ST][C][N][4][n]OFF # Header keyword + [Ndim] # Space dimension of vertices, present only if nOFF + NVertices NFaces NEdges # NEdges not used or checked - x[0] y[0] z[0] # Vertices, possibly with normals, colors, and/or texture coordinates, in that order, if the prefixes N, C, ST are present. - # If 4OFF, each vertex has 4 components including a final homogeneous component. - # If nOFF, each vertex has Ndim components. - # If 4nOFF, each vertex has Ndim+1 components. - ... - x[NVertices-1] y[NVertices-1] z[NVertices-1] + x[0] y[0] z[0] # Vertices, possibly with normals, colors, and/or texture coordinates, in that order, if the prefixes N, C, ST are present. + # If 4OFF, each vertex has 4 components including a final homogeneous component. + # If nOFF, each vertex has Ndim components. + # If 4nOFF, each vertex has Ndim+1 components. + ... + x[NVertices-1] y[NVertices-1] z[NVertices-1] - # Faces - # Nv = # vertices on this face - # v[0] ... v[Nv-1]: vertex indices - # in range 0..NVertices-1 - Nv v[0] v[1] ... v[Nv-1] colorspec - ... - # colorspec continues past v[Nv-1] to end-of-line; may be 0 to 4 numbers - # nothing: default - # integer: colormap index - # 3 or 4 integers: RGB[A] values 0..255 - # 3 or 4 floats: RGB[A] values 0..1 - */ - std::string header = tokens[0]; - if (header.rfind("OFF") != std::basic_string::npos) - { // the OFF string is in the header go on parsing it. - for (int u = static_cast(header.rfind("OFF")-1); u>=0; u--) - { - if (header[u] == 'C') isColorDefined = true; - else if (header[u] == 'N') isNormalDefined = true; - else if (u>0 && header[u-1] == 'S' && header[u] == 'T') isTexCoordDefined = true; - else if (header[u] == '4') homogeneousComponents = true; - else if (header[u] == 'n') return ErrorHighDimension; - } - } - else return InvalidFile_MissingOFF; + # Faces + # Nv = # vertices on this face + # v[0] ... v[Nv-1]: vertex indices + # in range 0..NVertices-1 + Nv v[0] v[1] ... v[Nv-1] colorspec + ... + # colorspec continues past v[Nv-1] to end-of-line; may be 0 to 4 numbers + # nothing: default + # integer: colormap index + # 3 or 4 integers: RGB[A] values 0..255 + # 3 or 4 floats: RGB[A] values 0..1 + */ + std::string header = tokens[0]; + if (header.rfind("OFF") != std::basic_string::npos) + { // the OFF string is in the header go on parsing it. + for (int u = static_cast(header.rfind("OFF") - 1); u >= 0; u--) + { + if (header[u] == 'C') + isColorDefined = true; + else if (header[u] == 'N') + isNormalDefined = true; + else if (u > 0 && header[u - 1] == 'S' && header[u] == 'T') + isTexCoordDefined = true; + else if (header[u] == '4') + homogeneousComponents = true; + else if (header[u] == 'n') + return ErrorHighDimension; + } + } + else return InvalidFile_MissingOFF; - // If the file is slightly malformed and it has nvert and nface AFTER the OFF string instead of in the next line - // we manage it here... - if(tokens.size()==1) TokenizeNextLine(stream, tokens); - else tokens.erase(tokens.begin(),tokens.begin()+1); + // If the file is slightly malformed and it has nvert and nface AFTER the OFF string instead of in the next line + // we manage it here... + if (tokens.size() == 1) TokenizeNextLine(stream, tokens); + else tokens.erase(tokens.begin(), tokens.begin() + 1); - // Update loading mask - /////////////////////////////////////// + // Update loading mask + /////////////////////////////////////// - loadmask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; + loadmask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; - if (isNormalDefined) loadmask |= Mask::IOM_VERTNORMAL; - if (isTexCoordDefined) loadmask |= Mask::IOM_VERTTEXCOORD; - if (isColorDefined) { loadmask |= Mask::IOM_VERTCOLOR;loadmask |= Mask::IOM_FACECOLOR;} + if (isNormalDefined) loadmask |= Mask::IOM_VERTNORMAL; + if (isTexCoordDefined) loadmask |= Mask::IOM_VERTTEXCOORD; + //if (isColorDefined) { loadmask |= Mask::IOM_VERTCOLOR;loadmask |= Mask::IOM_FACECOLOR;} - //if(onlyMaskFlag) return NoError; + //if(onlyMaskFlag) return NoError; - mesh.Clear(); + mesh.Clear(); - // check on next 2 lines to detect corrupted files - if(tokens.size() < 3) - return InvalidFile; + // check on next 2 lines to detect corrupted files + if (tokens.size() < 3) + return InvalidFile; - unsigned int nVertices, nFaces, nEdges; - nVertices = atoi(tokens[0].c_str()); - nFaces = atoi(tokens[1].c_str()); - nEdges = atoi(tokens[2].c_str()); + unsigned int nVertices, nFaces, nEdges; + nVertices = atoi(tokens[0].c_str()); + nFaces = atoi(tokens[1].c_str()); + nEdges = atoi(tokens[2].c_str()); - // dimension is the space dimension of vertices => it must be three(!) - if (dimension != 3) - return UnsupportedFormat; + // dimension is the space dimension of vertices => it must be three(!) + if (dimension != 3) + return UnsupportedFormat; - if (homogeneousComponents) - return UnsupportedFormat; + if (homogeneousComponents) + return UnsupportedFormat; - // READ VERTICES - ////////////////////////////////////////////////////// + // READ VERTICES + ////////////////////////////////////////////////////// - VertexIterator v_iter = Allocator::AddVertices(mesh, nVertices); - TokenizeNextLine(stream, tokens); - size_t k = 0; // next token to read + VertexIterator v_iter = Allocator::AddVertices(mesh, nVertices); + TokenizeNextLine(stream, tokens); + size_t k = 0; // next token to read - for (unsigned int i=0; i(tokens.size()) - - static_cast(k) /* tokens already parsed */ - - 2 * (isTexCoordDefined ? 1 : 0); + // NOTE: It is assumed that colored vertex takes exactly one text line + // (otherwise it is impossible to parse color information since + // color components can vary) + if (isColorDefined) + { + // The number of color components varies from 0 to 4. + // The OFF format guaranties that there is 1 vertex per line. + int nb_color_components = static_cast(tokens.size()) + - static_cast(k) /* tokens already parsed */ + - 2 * (isTexCoordDefined ? 1 : 0); - if (nb_color_components < 0 || nb_color_components > 4) - return InvalidFile; + if (nb_color_components < 0 || nb_color_components > 4) + return InvalidFile; - // set per-vertex color attribute - if (nb_color_components > 0) - loadmask |= Mask::IOM_VERTCOLOR; + // set per-vertex color attribute + if (nb_color_components > 0) + loadmask |= Mask::IOM_VERTCOLOR; - // Store color components - if (tri::HasPerVertexColor(mesh)) - { - // Read color components + // Store color components + if (tri::HasPerVertexColor(mesh)) + { + // Read color components - if (nb_color_components == 1) - { - // read color index - (*v_iter).C().Import(ColorMap(atoi(tokens[k].c_str()))); - } - else if (nb_color_components == 3) - { - // read RGB color - if (tokens[k].find(".") == size_t(-1))// if it is a float there is a dot - { - // integers - unsigned char r = - static_cast(atoi(tokens[k].c_str())); - unsigned char g = - static_cast(atoi(tokens[k+1].c_str())); - unsigned char b = - static_cast(atoi(tokens[k+2].c_str())); + if (nb_color_components == 1) + { + // read color index + (*v_iter).C().Import(ColorMap(atoi(tokens[k].c_str()))); + } + else if (nb_color_components == 3) + { + // read RGB color + if (tokens[k].find(".") == size_t(-1))// if it is a float there is a dot + { + // integers + unsigned char r = + static_cast(atoi(tokens[k].c_str())); + unsigned char g = + static_cast(atoi(tokens[k + 1].c_str())); + unsigned char b = + static_cast(atoi(tokens[k + 2].c_str())); - vcg::Color4b color(r, g, b, 255); - (*v_iter).C().Import(color); - } - else - { - // floats - float r = static_cast(atof(tokens[k].c_str())); - float g = static_cast(atof(tokens[k+1].c_str())); - float b = static_cast(atof(tokens[k+2].c_str())); + vcg::Color4b color(r, g, b, 255); + (*v_iter).C().Import(color); + } + else + { + // floats + float r = static_cast(atof(tokens[k].c_str())); + float g = static_cast(atof(tokens[k + 1].c_str())); + float b = static_cast(atof(tokens[k + 2].c_str())); - vcg::Color4f color(r, g, b, 1.0); - (*v_iter).C().Import(color); - } - } - else if (nb_color_components == 4) - { - // read RGBA color - if (tokens[k].find(".") == size_t(-1)) - { - // integers - unsigned char r = - static_cast(atoi(tokens[k].c_str())); - unsigned char g = - static_cast(atoi(tokens[k+1].c_str())); - unsigned char b = - static_cast(atoi(tokens[k+2].c_str())); - unsigned char a = - static_cast(atoi(tokens[k+3].c_str())); + vcg::Color4f color(r, g, b, 1.0); + (*v_iter).C().Import(color); + } + } + else if (nb_color_components == 4) + { + // read RGBA color + if (tokens[k].find(".") == size_t(-1)) + { + // integers + unsigned char r = + static_cast(atoi(tokens[k].c_str())); + unsigned char g = + static_cast(atoi(tokens[k + 1].c_str())); + unsigned char b = + static_cast(atoi(tokens[k + 2].c_str())); + unsigned char a = + static_cast(atoi(tokens[k + 3].c_str())); - Color4b color(r, g, b, a); - (*v_iter).C().Import(color); - } - else - { - // floats - float r = static_cast(atof(tokens[k].c_str())); - float g = static_cast(atof(tokens[k+1].c_str())); - float b = static_cast(atof(tokens[k+2].c_str())); - float a = static_cast(atof(tokens[k+3].c_str())); + Color4b color(r, g, b, a); + (*v_iter).C().Import(color); + } + else + { + // floats + float r = static_cast(atof(tokens[k].c_str())); + float g = static_cast(atof(tokens[k + 1].c_str())); + float b = static_cast(atof(tokens[k + 2].c_str())); + float a = static_cast(atof(tokens[k + 3].c_str())); - vcg::Color4f color(r, g, b, a); - (*v_iter).C().Import(color); - } - } - } + vcg::Color4f color(r, g, b, a); + (*v_iter).C().Import(color); + } + } + } - k += nb_color_components; - } + k += nb_color_components; + } - if (isTexCoordDefined) - { - for (unsigned int j=0; j<2; j++) - { - // Go to next line when needed - if (k == tokens.size()) // if EOL - { - TokenizeNextLine(stream, tokens); - if (tokens.size() == 0) // if EOF - return InvalidFile; - k = 0; - } + if (isTexCoordDefined) + { + for (unsigned int j = 0; j < 2; j++) + { + // Go to next line when needed + if (k == tokens.size()) // if EOL + { + TokenizeNextLine(stream, tokens); + if (tokens.size() == 0) // if EOF + return InvalidFile; + k = 0; + } - std::string str = tokens[k]; - k++; + std::string str = tokens[k]; + k++; - // Store texture coordinates - if (tri::HasPerWedgeTexCoord(mesh)) - { - //...TODO... - } - } - } - } // for i=... + // Store texture coordinates + if (tri::HasPerWedgeTexCoord(mesh)) + { + //...TODO... + } + } + } + } // for i=... - // READ FACES - ////////////////////////////////////////////////////// - if(FaceType::HasPolyInfo()) - { - for (unsigned int f=0; f < nFaces; f++) - { - if(cb && (f%1000)==0) cb(50+f*50/nFaces,"Face Loading"); - TokenizeNextLine(stream, tokens); - int vert_per_face = atoi(tokens[0].c_str()); - std::vector vInd(vert_per_face); - k = 1; - for (int j=0; j < vert_per_face; j++) - { - if (k == tokens.size()) // if EOL // Go to next line when needed - { - TokenizeNextLine(stream, tokens); - if (tokens.size() == 0) return InvalidFile; // if EOF - k = 0; - } - vInd[j] = atoi(tokens[k].c_str()); - k++; - } - if(vert_per_face==3) - Allocator::AddFace(mesh, &mesh.vert[ vInd[0] ], &mesh.vert[ vInd[1] ], &mesh.vert[ vInd[2] ]); + // READ FACES + ////////////////////////////////////////////////////// + if (FaceType::HasPolyInfo()) + { + for (unsigned int f = 0; f < nFaces; f++) + { + if (cb && (f % 1000) == 0) cb(50 + f * 50 / nFaces, "Face Loading"); + TokenizeNextLine(stream, tokens); + int vert_per_face = atoi(tokens[0].c_str()); + std::vector vInd(vert_per_face); + k = 1; + for (int j = 0; j < vert_per_face; j++) + { + if (k == tokens.size()) // if EOL // Go to next line when needed + { + TokenizeNextLine(stream, tokens); + if (tokens.size() == 0) return InvalidFile; // if EOF + k = 0; + } + vInd[j] = atoi(tokens[k].c_str()); + k++; + } + if (vert_per_face == 3) + Allocator::AddFace(mesh, &mesh.vert[vInd[0]], &mesh.vert[vInd[1]], &mesh.vert[vInd[2]]); - if(vert_per_face==4) - Allocator::AddQuadFace(mesh, &mesh.vert[ vInd[0] ], &mesh.vert[ vInd[1] ], &mesh.vert[ vInd[2] ],&mesh.vert[ vInd[3] ]); + if (vert_per_face == 4) + Allocator::AddQuadFace(mesh, &mesh.vert[vInd[0]], &mesh.vert[vInd[1]], &mesh.vert[vInd[2]], &mesh.vert[vInd[3]]); - } - } - else // Standard Triangular Mesh Loading - { - Allocator::AddFaces(mesh, nFaces); - unsigned int f0=0; + } + } + else // Standard Triangular Mesh Loading + { + Allocator::AddFaces(mesh, nFaces); + unsigned int f0 = 0; - // Initial call to the QuadTriangulate with an empty vector to just reset the static set of existing diagonals - std::vector qtmp; - BitQuad::QuadTriangulate(qtmp); + // Initial call to the QuadTriangulate with an empty vector to just reset the static set of existing diagonals + std::vector qtmp; + BitQuad::QuadTriangulate(qtmp); - for (unsigned int f=0; f < nFaces; f++) - { - f0 = f; - if (stream.fail()) - return InvalidFile; + for (unsigned int f = 0; f < nFaces; f++) + { + f0 = f; + if (stream.fail()) + return InvalidFile; - if(cb && (f%1000)==0) - cb(50+f*50/nFaces,"Face Loading"); + if (cb && (f % 1000) == 0) + cb(50 + f * 50 / nFaces, "Face Loading"); - TokenizeNextLine(stream, tokens); - int vert_per_face = atoi(tokens[0].c_str()); - if(vert_per_face < 3) - return ErrorDegenerateFace; - k = 1; - if (vert_per_face == 3) - { - for (int j = 0; j < 3; j++) - { - if (k == tokens.size()) // if EOL // Go to next line when needed - { - TokenizeNextLine(stream, tokens); - if (tokens.size() == 0) return InvalidFile; // if EOF - k = 0; - } + TokenizeNextLine(stream, tokens); + int vert_per_face = atoi(tokens[0].c_str()); + if (vert_per_face < 3) + return ErrorDegenerateFace; + k = 1; + if (vert_per_face == 3) + { + for (int j = 0; j < 3; j++) + { + if (k == tokens.size()) // if EOL // Go to next line when needed + { + TokenizeNextLine(stream, tokens); + if (tokens.size() == 0) return InvalidFile; // if EOF + k = 0; + } - mesh.face[f].V(j) = &(mesh.vert[ atoi(tokens[k].c_str()) ]); - k++; - } - } - else - { - // The face must be triangulated - unsigned int trigs = vert_per_face-3; // number of extra faces to add - nFaces += trigs; - Allocator::AddFaces(mesh, trigs); - std::vector vertIndices(vert_per_face); - std::vector polygonVect(vert_per_face); // vec of polygon loops used for the triangulation of polygonal face - for (int j=0; j < vert_per_face; j++) - { - if (k == tokens.size()) // if EOL // Go to next line when needed - { - TokenizeNextLine(stream, tokens); - if (tokens.size() == 0) return InvalidFile; // if EOF - k = 0; - } - vertIndices[j] = atoi(tokens[k].c_str()); - polygonVect[j].Import (mesh.vert[ vertIndices[j] ].P()); - k++; - } - if(vert_per_face==4) - { // To well triangulate use the bitquad support function that reorders vertex for a simple fan - std::vector q(4); - for(int qqi=0;qqi<4;++qqi) - q[qqi]=& mesh.vert[vertIndices[qqi]]; - BitQuad::QuadTriangulate(q); - for(int qqi=0;qqi<4;++qqi) - vertIndices[qqi] = q[qqi]- & mesh.vert[0]; - // build a two face fan - for (int j=0; j<2; j++) - { - mesh.face[f+j].V(0) = &(mesh.vert[ vertIndices[0 ] ]); - mesh.face[f+j].V(1) = &(mesh.vert[ vertIndices[1+j] ]); - mesh.face[f+j].V(2) = &(mesh.vert[ vertIndices[2+j] ]); - if (tri::HasPerFaceFlags(mesh)) { - // tag internal polygonal edges as "faux" - if (j>0) mesh.face[f+j].SetF(0); - if (j indexTriangulatedVect; - // TessellatePlanarPolygon3(polygonVect,indexTriangulatedVect); - std::vector< std::vector > loopVect; - loopVect.push_back(polygonVect); + mesh.face[f].V(j) = &(mesh.vert[atoi(tokens[k].c_str())]); + k++; + } + } + else + { + // The face must be triangulated + unsigned int trigs = vert_per_face - 3; // number of extra faces to add + nFaces += trigs; + Allocator::AddFaces(mesh, trigs); + std::vector vertIndices(vert_per_face); + std::vector polygonVect(vert_per_face); // vec of polygon loops used for the triangulation of polygonal face + for (int j = 0; j < vert_per_face; j++) + { + if (k == tokens.size()) // if EOL // Go to next line when needed + { + TokenizeNextLine(stream, tokens); + if (tokens.size() == 0) return InvalidFile; // if EOF + k = 0; + } + vertIndices[j] = atoi(tokens[k].c_str()); + polygonVect[j].Import(mesh.vert[vertIndices[j]].P()); + k++; + } + if (vert_per_face == 4) + { // To well triangulate use the bitquad support function that reorders vertex for a simple fan + std::vector q(4); + for (int qqi = 0; qqi < 4; ++qqi) + q[qqi] = &mesh.vert[vertIndices[qqi]]; + BitQuad::QuadTriangulate(q); + for (int qqi = 0; qqi < 4; ++qqi) + vertIndices[qqi] = q[qqi] - &mesh.vert[0]; + // build a two face fan + for (int j = 0; j < 2; j++) + { + mesh.face[f + j].V(0) = &(mesh.vert[vertIndices[0]]); + mesh.face[f + j].V(1) = &(mesh.vert[vertIndices[1 + j]]); + mesh.face[f + j].V(2) = &(mesh.vert[vertIndices[2 + j]]); + if (tri::HasPerFaceFlags(mesh)) { + // tag internal polygonal edges as "faux" + if (j > 0) mesh.face[f + j].SetF(0); + if (j < vert_per_face - 3) mesh.face[f + j].SetF(2); + loadmask |= Mask::IOM_BITPOLYGONAL; + } + } + } + else // standard fan triangulation (we hope the polygon is convex...) + { + std::vector indexTriangulatedVect; + // TessellatePlanarPolygon3(polygonVect,indexTriangulatedVect); + std::vector< std::vector > loopVect; + loopVect.push_back(polygonVect); #ifdef __gl_h_ - //qDebug("OK: using opengl tessellation for a polygon of %i vertices",vertexesPerFace); - vcg::glu_tesselator::tesselate(loopVect, indexTriangulatedVect); + //qDebug("OK: using opengl tessellation for a polygon of %i vertices",vertexesPerFace); + vcg::glu_tesselator::tesselate(loopVect, indexTriangulatedVect); #else - //qDebug("Warning: using fan tessellation for a polygon of %i vertices",vertexesPerFace); - tri::io::FanTessellator(loopVect, indexTriangulatedVect); + //qDebug("Warning: using fan tessellation for a polygon of %i vertices",vertexesPerFace); + tri::io::FanTessellator(loopVect, indexTriangulatedVect); #endif - for (size_t j=0; j0); - if(isColorDefined) loadmask |= Mask::IOM_FACECOLOR; + // NOTE: It is assumed that colored face takes exactly one text line + // (otherwise it is impossible to parse color information since + // color components can vary) + size_t color_elements = tokens.size() - vert_per_face - 1; + //isColorDefined |= (color_elements>0); + //if(isColorDefined) loadmask |= Mask::IOM_FACECOLOR; - if( (color_elements>0) && tri::HasPerFaceColor(mesh) ) - { + if (color_elements > 0) + { + loadmask |= Mask::IOM_FACECOLOR; + if (tri::HasPerFaceColor(mesh)) + { + switch (color_elements) + { + case 0: + { + for (; f0 <= f; f0++) + mesh.face[f0].C().Import(vcg::Color4f(.666f, .666f, .666f, .666f)); + break; + } + case 1: + { + for (; f0 <= f; f0++) + mesh.face[f0].C().Import(ColorMap(atoi(tokens[vert_per_face + 1].c_str()))); + break; + } + case 3: + { + if (tokens[vert_per_face + 1].find('.') == std::string::npos) // if there is a float there is a dot + { + Color4b cc(Color4b::White); + cc[0] = (unsigned char)atoi(tokens[vert_per_face + 1].c_str()); + cc[1] = (unsigned char)atoi(tokens[vert_per_face + 2].c_str()); + cc[2] = (unsigned char)atoi(tokens[vert_per_face + 3].c_str()); + for (; f0 <= f; f0++) + mesh.face[f0].C() = cc; + } + else + { + float color[3]; + color[0] = (float)atof(tokens[vert_per_face + 1].c_str()); + color[1] = (float)atof(tokens[vert_per_face + 2].c_str()); + color[2] = (float)atof(tokens[vert_per_face + 3].c_str()); + for (; f0 <= f; f0++) + mesh.face[f0].C().Import(vcg::Color4f(color[0], color[1], color[2], 1.0f)); + } + break; + } + case 4: + { + if (tokens[vert_per_face + 1].find('.') == std::string::npos) // if it is a float there is a dot + { + Color4b cc; + cc[0] = (unsigned char)atoi(tokens[vert_per_face + 1].c_str()); + cc[1] = (unsigned char)atoi(tokens[vert_per_face + 2].c_str()); + cc[2] = (unsigned char)atoi(tokens[vert_per_face + 3].c_str()); + cc[3] = (unsigned char)atoi(tokens[vert_per_face + 4].c_str()); + for (; f0 <= f; f0++) + mesh.face[f0].C() = cc; + } + else + { + float color[4]; + color[0] = float(atof(tokens[vert_per_face + 1].c_str())); + color[1] = float(atof(tokens[vert_per_face + 2].c_str())); + color[2] = float(atof(tokens[vert_per_face + 3].c_str())); + color[3] = float(atof(tokens[vert_per_face + 4].c_str())); + for (; f0 <= f; f0++) + mesh.face[f0].C().Import(vcg::Color4f(color[0], color[1], color[2], color[3])); + } + break; + } + } //end switch + } + } // end if (isColorDefined) + } // end of for f=... + } + return NoError; - // set per-face color attribute - if (color_elements > 0) - loadmask |= Mask::IOM_FACECOLOR; + } // end Open - switch (color_elements) - { - case 0: - { - for ( ; f0<=f; f0++) - mesh.face[f0].C().Import(vcg::Color4f(.666f, .666f, .666f, .666f)); - break; - } - case 1: - { - for ( ; f0<=f; f0++) - mesh.face[f0].C().Import( ColorMap( atoi(tokens[vert_per_face+1].c_str()) ) ); - break; - } - case 3: - { - if (tokens[vert_per_face+1].find('.')==std::string::npos) // if there is a float there is a dot - { - Color4b cc(Color4b::White); - cc[0] = (unsigned char)atoi( tokens[vert_per_face+1].c_str() ); - cc[1] = (unsigned char)atoi( tokens[vert_per_face+2].c_str() ); - cc[2] = (unsigned char)atoi( tokens[vert_per_face+3].c_str() ); - for ( ; f0<=f; f0++) - mesh.face[f0].C()=cc; - } - else - { - float color[3]; - color[0] = (float) atof( tokens[vert_per_face+1].c_str() ); - color[1] = (float) atof( tokens[vert_per_face+2].c_str() ); - color[2] = (float) atof( tokens[vert_per_face+3].c_str() ); - for ( ; f0<=f; f0++) - mesh.face[f0].C().Import(vcg::Color4f(color[0], color[1], color[2], 1.0f)); - } - break; - } - case 4: - { - if (tokens[vert_per_face+1].find('.')==std::string::npos) // if it is a float there is a dot - { - Color4b cc; - cc[0] = (unsigned char) atoi(tokens[vert_per_face+1].c_str()); - cc[1] = (unsigned char) atoi(tokens[vert_per_face+2].c_str()); - cc[2] = (unsigned char) atoi(tokens[vert_per_face+3].c_str()); - cc[3] = (unsigned char) atoi(tokens[vert_per_face+4].c_str()); - for ( ; f0<=f; f0++) - mesh.face[f0].C()=cc; - } - else - { - float color[4]; - color[0] = float( atof(tokens[vert_per_face+1].c_str()) ); - color[1] = float( atof(tokens[vert_per_face+2].c_str()) ); - color[2] = float( atof(tokens[vert_per_face+3].c_str()) ); - color[3] = float( atof(tokens[vert_per_face+4].c_str()) ); - for ( ; f0<=f; f0++) - mesh.face[f0].C().Import(vcg::Color4f(color[0], color[1], color[2], color[3])); - } - break; - } - } //end switch - } // end if (isColorDefined) - } // end of for f=... - } - return NoError; + protected: - } // end Open + /*! + * Read the next valid line and parses it into "tokens", allowing the tokens to be read one at a time. + * \param stream The object providing the input stream + * \param tokens The "tokens" in the next line + */ + inline static void TokenizeNextLine(std::istream &stream, std::vector< std::string > &tokens) + { + std::string line; + do + std::getline(stream, line, '\n'); + while ((line[0] == '#' || line.length() == 0 || line[0] == '\r') && (!stream.eof())); -protected: + size_t from = 0; + size_t to = 0; + size_t length = line.size(); + tokens.clear(); + do + { + while (from != length && (line[from] == ' ' || line[from] == '\t' || line[from] == '\r')) + from++; + if (from != length) + { + to = from + 1; + while (to != length && (((line[to] != ' ') && (line[to] != '\t')) || (line[to] == '\r'))) + to++; + tokens.push_back(line.substr(from, to - from).c_str()); + from = to; + } + } while (from < length); + } // end Tokenize - /*! - * Read the next valid line and parses it into "tokens", allowing the tokens to be read one at a time. - * \param stream The object providing the input stream - * \param tokens The "tokens" in the next line - */ - inline static void TokenizeNextLine(std::istream &stream, std::vector< std::string > &tokens) - { - std::string line; - do - std::getline(stream, line, '\n'); - while ((line[0] == '#' || line.length()==0 || line[0]=='\r' ) && (!stream.eof())); - - size_t from = 0; - size_t to = 0; - size_t length = line.size(); - tokens.clear(); - do - { - while (from!=length && (line[from]==' ' || line[from] == '\t' || line[from] == '\r')) - from++; - if(from!=length) - { - to = from+1; - while ( to!=length && (((line[to]!=' ') && (line[to] != '\t')) || (line[to] == '\r'))) - to++; - tokens.push_back(line.substr(from, to-from).c_str()); - from = to; - } - } - while (fromcolor mapping, according to the Geomview's `cmap.fmap' file. - * \param i the color index - * \return the corresponding vcg::Color4f color - */ - static const vcg::Color4f ColorMap(int i) - { - static const float colorMap[148][4] = - { - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.2f, 0.2f, 0.2f, 0.2f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.1f, 0.1f, 0.1f, 0.1f }, - { 0.1f, 0.1f, 0.1f, 0.1f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.2f, 0.2f, 0.2f, 0.2f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - { 0.05f, 0.05f, 0.05f, 0.05f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.2f, 0.2f, 0.2f, 0.2f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.1f, 0.1f, 0.1f, 0.1f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.7f, 0.7f, 0.7f, 0.7f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.9f, 0.9f, 0.9f, 0.9f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.75f, 0.75f, 0.75f, 0.75f }, - { 0.8f, 0.8f, 0.8f, 0.8f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.4f, 0.4f, 0.4f, 0.4f }, - { 0.8f, 0.8f, 0.8f, 0.8f } - }; - return Color4f(colorMap[i][0], colorMap[i][1], colorMap[i][2], colorMap[i][3]); - } -}; -// /*! @} */ -} //namespace io -}//namespace tri + /*! + * Provide the int->color mapping, according to the Geomview's `cmap.fmap' file. + * \param i the color index + * \return the corresponding vcg::Color4f color + */ + static const vcg::Color4f ColorMap(int i) + { + static const float colorMap[148][4] = + { + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.2f, 0.2f, 0.2f, 0.2f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.1f, 0.1f, 0.1f, 0.1f }, + { 0.1f, 0.1f, 0.1f, 0.1f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.2f, 0.2f, 0.2f, 0.2f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + { 0.05f, 0.05f, 0.05f, 0.05f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.2f, 0.2f, 0.2f, 0.2f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.1f, 0.1f, 0.1f, 0.1f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.7f, 0.7f, 0.7f, 0.7f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.9f, 0.9f, 0.9f, 0.9f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.75f, 0.75f, 0.75f, 0.75f }, + { 0.8f, 0.8f, 0.8f, 0.8f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.4f, 0.4f, 0.4f, 0.4f }, + { 0.8f, 0.8f, 0.8f, 0.8f } + }; + return Color4f(colorMap[i][0], colorMap[i][1], colorMap[i][2], colorMap[i][3]); + } + }; + // /*! @} */ + } //namespace io + }//namespace tri } // namespace vcg #endif //__VCGLIB_IMPORT_OFF diff --git a/wrap/io_trimesh/import_ply.h b/wrap/io_trimesh/import_ply.h index 171f39c1..5104b07d 100644 --- a/wrap/io_trimesh/import_ply.h +++ b/wrap/io_trimesh/import_ply.h @@ -469,12 +469,14 @@ static int Open( OpenMeshType &m, const char * filename, PlyInfo &pi ) { pf.AddToRead(VertDesc(6)); pf.AddToRead(VertDesc(7)); + pf.AddToRead(VertDesc(8)); pi.mask |= Mask::IOM_VERTCOLOR; } if( pf.AddToRead(VertDesc(9))!=-1 ) { pf.AddToRead(VertDesc(10)); pf.AddToRead(VertDesc(11)); + pf.AddToRead(VertDesc(12)); pi.mask |= Mask::IOM_VERTCOLOR; } if( pf.AddToRead(VertDesc(21))!=-1 ) @@ -518,6 +520,7 @@ static int Open( OpenMeshType &m, const char * filename, PlyInfo &pi ) { pf.AddToRead(FaceDesc(7)); pf.AddToRead(FaceDesc(8)); + pf.AddToRead(FaceDesc(9)); pi.mask |= Mask::IOM_FACECOLOR; } } @@ -646,6 +649,7 @@ static int Open( OpenMeshType &m, const char * filename, PlyInfo &pi ) for(j=0;j + inline std::size_t hashEnum(const TEnum & value) + { + const std::hash h{}; + return h((unsigned int)value); + } + +} // end namespace nanoply + +// Cast of an enum to an unsigned int (maybe only for Android) +namespace std +{ + template <> struct hash { std::size_t operator () (const nanoply::PlyElemEntity & t) const { return nanoply::hashEnum(t); } }; + template <> struct hash { std::size_t operator () (const nanoply::PlyEntity & t) const { return nanoply::hashEnum(t); } }; + template <> struct hash { std::size_t operator () (const nanoply::PlyType & t) const { return nanoply::hashEnum(t); } }; +} // end namespace std + +namespace nanoply +{ /** * @cond HIDDEN_SYMBOLS @@ -161,8 +180,6 @@ namespace nanoply typedef std::unordered_map ElementMap; typedef std::unordered_map::iterator ElementMapIterator; - static NameVector emptyVec; - /* Names used for the PlyType */ static TypeMap mapType({ { PlyType::NNP_UNKNOWN_TYPE, NameVector({ "unknonw" }) }, @@ -246,8 +263,9 @@ namespace nanoply /* Returns the vector of possible name for the input PlyEntity */ - static const NameVector& PlyPropertyName(PlyEntity ent) + static inline const NameVector& PlyPropertyName(PlyEntity ent) { + static NameVector emptyVec; if (mapProp.find(ent) != mapProp.end()) return mapProp[ent]; return emptyVec; @@ -255,8 +273,9 @@ namespace nanoply /* Returns the vector of possible name for the input PlyType */ - static const NameVector& PlyTypeName(PlyType ent) + static inline const NameVector& PlyTypeName(PlyType ent) { + static NameVector emptyVec; if (mapType.find(ent) != mapType.end()) return mapType[ent]; return emptyVec; @@ -264,8 +283,9 @@ namespace nanoply /* Returns the vector of possible name for the input PlyElement */ - static const NameVector& PlyElementName(PlyElemEntity ent) + static inline const NameVector& PlyElementName(PlyElemEntity ent) { + static NameVector emptyVec; if (mapElem.find(ent) != mapElem.end()) return mapElem[ent]; return emptyVec; @@ -273,7 +293,7 @@ namespace nanoply /* Return 1 for little endian, 0 for big endian*/ - int checkEndianness() + inline int checkEndianness() { unsigned int x = 1; char *c = (char*)&x; @@ -281,7 +301,7 @@ namespace nanoply }; /* Adjust the endianess of the input buffer*/ - void adjustEndianess(unsigned char* buffer, int typeSize, int count) + inline void adjustEndianess(unsigned char* buffer, int typeSize, int count) { for (int i = 0; i < count; i++) { @@ -326,7 +346,7 @@ namespace nanoply * @param filename name of the file. * @return If successful returns true. Otherwise, it returns false. */ - bool OpenFileToRead(const std::string &filename); + inline bool OpenFileToRead(const std::string &filename); /** * Open the file in write mode. @@ -334,7 +354,7 @@ namespace nanoply * @param filename name of the file. * @return If successful returns true. Otherwise, it returns false. */ - bool OpenFileToWrite(const std::string &filename); + inline bool OpenFileToWrite(const std::string &filename); /** * Read the next header line. @@ -343,7 +363,7 @@ namespace nanoply * @param last true if the line is the last of header. * @return If successful returns true. Otherwise, it returns false. */ - bool NextHeaderLine(std::string &line, bool &last); + inline bool NextHeaderLine(std::string &line, bool &last); /** * Write the line in the header. @@ -351,7 +371,7 @@ namespace nanoply * @param line line to write. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteHeaderLine(std::string &line); + inline bool WriteHeaderLine(const std::string &line); /** * Read binary data from the file. @@ -360,7 +380,7 @@ namespace nanoply * @param nByte number of byte to read. * @return If successful returns true. Otherwise, it returns false. */ - bool ReadBinaryData(void *dest, int nByte); + inline bool ReadBinaryData(char * & dest, int nByte); /** * Read ASCII data from the file. @@ -369,7 +389,7 @@ namespace nanoply * @return If successful returns true. Otherwise, it returns false. */ template - bool ReadAsciiData(T &dest); + inline bool ReadAsciiData(T &dest); /** * Write binary data in the file. @@ -378,7 +398,7 @@ namespace nanoply * @param nByte number of byte to write. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteBinaryData(void *src, int nByte); + inline bool WriteBinaryData(void *src, int nByte); /** * Write ASCII data in the file. @@ -387,31 +407,31 @@ namespace nanoply * @return If successful returns true. Otherwise, it returns false. */ template - bool WriteAsciiData(T &src); + inline bool WriteAsciiData(const T &src); /** * Set the maximum size of the buffer. * * @return size size of the buffer. */ - void SetBufferSize(int64_t size); + inline void SetBufferSize(int64_t size); /** * Force the write of the buffer in the file. */ - void Flush(); + inline void Flush(); }; - PlyFile::PlyFile() + inline PlyFile::PlyFile() { buffer = NULL; - maxSize = 1000000; + maxSize = 10 * (1 << 20); mode = -1; } - PlyFile::~PlyFile() + inline PlyFile::~PlyFile() { Flush(); if (buffer != NULL) @@ -421,7 +441,7 @@ namespace nanoply } - bool PlyFile::OpenFileToRead(const std::string& filename) + inline bool PlyFile::OpenFileToRead(const std::string& filename) { if (fileStream.is_open()) fileStream.close(); @@ -434,7 +454,7 @@ namespace nanoply } - bool PlyFile::OpenFileToWrite(const std::string& filename) + inline bool PlyFile::OpenFileToWrite(const std::string& filename) { if (fileStream.is_open()) fileStream.close(); @@ -449,7 +469,7 @@ namespace nanoply } - bool PlyFile::NextHeaderLine(std::string& line, bool& last) + inline bool PlyFile::NextHeaderLine(std::string& line, bool& last) { if (mode != 0) return false; @@ -462,7 +482,7 @@ namespace nanoply } - bool PlyFile::WriteHeaderLine(std::string& line) + inline bool PlyFile::WriteHeaderLine(const std::string& line) { if (mode != 1) return false; @@ -471,7 +491,7 @@ namespace nanoply } - bool PlyFile::ReadBinaryData(void* dest, int nByte) + inline bool PlyFile::ReadBinaryData(char * & dest, int nByte) { if (mode != 0) return false; @@ -484,20 +504,21 @@ namespace nanoply } else if (bufferOffset + nByte > bufferSize) { - unsigned int lastByte = bufferSize - bufferOffset; + const size_t lastByte = bufferSize - bufferOffset; memcpy(buffer, &buffer[bufferOffset], lastByte); fileStream.read(&buffer[lastByte], maxSize - lastByte); bufferSize = fileStream.gcount() + lastByte; bufferOffset = 0; } - memcpy(dest, &buffer[bufferOffset], nByte); + //memcpy(dest, &buffer[bufferOffset], nByte); + dest = buffer + bufferOffset; bufferOffset += nByte; return true; } template - bool PlyFile::ReadAsciiData(T& dest) + inline bool PlyFile::ReadAsciiData(T& dest) { if (mode != 0) return false; @@ -505,7 +526,7 @@ namespace nanoply return true; } - bool PlyFile::WriteBinaryData(void* src, int nByte) + inline bool PlyFile::WriteBinaryData(void* src, int nByte) { if (mode != 1) return false; @@ -527,7 +548,7 @@ namespace nanoply template - bool PlyFile::WriteAsciiData(T& src) + inline bool PlyFile::WriteAsciiData(const T& src) { if (mode != 1) return false; @@ -535,13 +556,13 @@ namespace nanoply return true; } - void PlyFile::SetBufferSize(int64_t size) + inline void PlyFile::SetBufferSize(int64_t size) { maxSize = size; } - void PlyFile::Flush() + inline void PlyFile::Flush() { if (mode == 1) fileStream.write(buffer, bufferOffset); @@ -568,7 +589,7 @@ namespace nanoply * @param _t Property type. * @param _e Property entity. */ - PlyProperty(PlyType _t, PlyEntity _e) :type(_t), elem(_e), name(PlyPropertyName(_e)[0]), validToWrite(false){} + inline PlyProperty(PlyType _t, PlyEntity _e) :type(_t), elem(_e), name(PlyPropertyName(_e)[0]), validToWrite(false){} /** * Constructor that sets the type, the entity and the name of a standard PLY property. @@ -577,7 +598,7 @@ namespace nanoply * @param _e Property entity. * @param _n Property name. */ - PlyProperty(PlyType _t, PlyEntity _e, std::string _n) :type(_t), elem(_e), name(_n), validToWrite(false){} + inline PlyProperty(PlyType _t, PlyEntity _e, std::string _n) :type(_t), elem(_e), name(_n), validToWrite(false){} /** * Constructor that sets the type and the name of a custom PLY property. @@ -585,49 +606,49 @@ namespace nanoply * @param _t Property type. * @param _n Property name. */ - PlyProperty(PlyType _t, std::string _n) :type(_t), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_n), validToWrite(false){} + inline PlyProperty(PlyType _t, std::string _n) :type(_t), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_n), validToWrite(false){} /** * Get the description string of the property entity. * * @return Description string of the property entity. */ - const char* EntityStr(); + inline const char* EntityStr(); /** * Get the name of the property entity. * * @return Name of the property entity. */ - const char* EntityName(); + inline const char* EntityName(); /** * Get the description string of the property type. * * @return Description string of the property type. */ - const char* TypeStr(); + inline const char* TypeStr(); /** * Get the size in byte of the property type. * * @return Size in byte of the property type. */ - int TypeSize(); + inline int TypeSize(); /** * Get the number of component of the property entity. * * @return Number of component. */ - int CountValue(); + inline int CountValue(); /** * Check if the property type is signed or unsigned. * * @return true = signed, false = unsigned. */ - bool IsSigned(); + inline bool IsSigned(); /** * Skip the property in an Ascii file. @@ -635,7 +656,7 @@ namespace nanoply * @param file Opened file. * @return If successful returns true. Otherwise, it returns false. */ - bool SkipAsciiPropertyInFile(PlyFile &file); + inline bool SkipAsciiPropertyInFile(PlyFile &file); /** * Skip the property in a binary file. @@ -643,7 +664,7 @@ namespace nanoply * @param file Opened file. * @return If successful returns true. Otherwise, it returns false. */ - bool SkipBinaryPropertyInFile(PlyFile &file); + inline bool SkipBinaryPropertyInFile(PlyFile &file); /** * Write the property string in the header of the PLY file. @@ -651,11 +672,11 @@ namespace nanoply * @param file Opened file. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteHeader(PlyFile &file); + inline bool WriteHeader(PlyFile &file); }; - const char* PlyProperty::EntityStr() + inline const char* PlyProperty::EntityStr() { switch (this->elem) { @@ -705,7 +726,7 @@ namespace nanoply } - const char* PlyProperty::EntityName() + inline const char* PlyProperty::EntityName() { if (this->elem == PlyEntity::NNP_UNKNOWN_ENTITY) @@ -732,7 +753,7 @@ namespace nanoply } - const char* PlyProperty::TypeStr() + inline const char* PlyProperty::TypeStr() { switch (this->type) { @@ -768,7 +789,7 @@ namespace nanoply } - int PlyProperty::TypeSize() + inline int PlyProperty::TypeSize() { switch (this->type) { @@ -814,7 +835,7 @@ namespace nanoply } - bool PlyProperty::IsSigned() + inline bool PlyProperty::IsSigned() { switch (this->type) { @@ -850,7 +871,7 @@ namespace nanoply } - int PlyProperty::CountValue() + inline int PlyProperty::CountValue() { if (this->elem == NNP_CRGB || this->elem == NNP_NXYZ || this->elem == NNP_PXYZ || this->elem == NNP_TEXTURE3D) return 3; @@ -862,7 +883,7 @@ namespace nanoply } - bool PlyProperty::SkipAsciiPropertyInFile(PlyFile &file) + inline bool PlyProperty::SkipAsciiPropertyInFile(PlyFile &file) { int count = CountValue(); if (this->type >= NNP_LIST_UINT8_UINT32) @@ -926,40 +947,35 @@ namespace nanoply } - bool PlyProperty::SkipBinaryPropertyInFile(PlyFile& file) + inline bool PlyProperty::SkipBinaryPropertyInFile(PlyFile& file) { - unsigned char* temp; + char * temp = nullptr; int count = CountValue(); if (this->type >= NNP_LIST_UINT8_UINT32) { int size; if (this->IsSigned()) { - unsigned char cntList = 0; - file.ReadBinaryData(&cntList, sizeof(char)); - size = this->TypeSize() * cntList; + file.ReadBinaryData(temp, sizeof(char)); + size = this->TypeSize() * int(*(reinterpret_cast(temp))); } else { - char cntList = 0; - file.ReadBinaryData(&cntList, sizeof(char)); - size = this->TypeSize() * cntList; + file.ReadBinaryData(temp, sizeof(char)); + size = this->TypeSize() * int(*(reinterpret_cast(temp))); } - temp = new unsigned char[size]; file.ReadBinaryData(temp, size); } else { - int size = this->TypeSize()*count; - temp = new unsigned char[size]; + int size = this->TypeSize() * count; file.ReadBinaryData(temp, size); } - delete[] temp; return true; } - bool PlyProperty::WriteHeader(PlyFile& file) + inline bool PlyProperty::WriteHeader(PlyFile& file) { if (!validToWrite) return true; @@ -1044,7 +1060,7 @@ namespace nanoply /** * Default Constructor */ - PlyElement() :validToWrite(false){}; + inline PlyElement() :validToWrite(false){}; /** * Constructor that sets the name, the properties and the number of instances of the element. @@ -1053,7 +1069,7 @@ namespace nanoply * @param prop Vector of properties. * @param nElem Number of instances. */ - PlyElement(std::string& _name, std::vector &prop, size_t nElem) :name(_name), cnt(nElem), propVec(prop), plyElem(PlyElemEntity::NNP_UNKNOWN_ELEM), validToWrite(false){}; + inline PlyElement(std::string& _name, std::vector &prop, size_t nElem) :name(_name), cnt(nElem), propVec(prop), plyElem(PlyElemEntity::NNP_UNKNOWN_ELEM), validToWrite(false){}; /** * Constructor that sets the entity, the properties and the number of instances of the element. @@ -1062,7 +1078,7 @@ namespace nanoply * @param prop Vector of properties. * @param nElem Number of instances. */ - PlyElement(PlyElemEntity ent, std::vector &prop, size_t nElem) :name(PlyElementName(ent)[0]), cnt(nElem), propVec(prop), plyElem(ent), validToWrite(false){}; + inline PlyElement(PlyElemEntity ent, std::vector &prop, size_t nElem) :name(PlyElementName(ent)[0]), cnt(nElem), propVec(prop), plyElem(ent), validToWrite(false){}; /** * Parse the input line and add the properties to the element. @@ -1071,7 +1087,7 @@ namespace nanoply * @param line Input line. * @return If successful returns true. Otherwise, it returns false. */ - bool AddProperty(std::string &line); + inline bool AddProperty(std::string &line); /** * Initialize an element from the header line. @@ -1080,7 +1096,7 @@ namespace nanoply * @param propStr Strings with the property definition. * @return If successful returns true. Otherwise, it returns false. */ - bool InitFromHeader(std::string &elemStr, std::vector &propStr); + inline bool InitFromHeader(std::string &elemStr, std::vector &propStr); /** * Write the element descriport in the file header. @@ -1088,7 +1104,7 @@ namespace nanoply * @param file Ply file. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteHeader(PlyFile &file); + inline bool WriteHeader(PlyFile &file); /** * Skip the element in an Ascii file. @@ -1096,7 +1112,7 @@ namespace nanoply * @param file Ply file * @return If successful returns true. Otherwise, it returns false. */ - bool SkipAsciiElementsInFile(PlyFile &file); + inline bool SkipAsciiElementsInFile(PlyFile &file); /** * Skip the element in a binary file. @@ -1104,7 +1120,7 @@ namespace nanoply * @param file Ply file. * @return If successful returns true. Otherwise, it returns false. */ - bool SkipBinaryElementsInFile(PlyFile &file); + inline bool SkipBinaryElementsInFile(PlyFile &file); /** * Check if the input entity is in the property of the element. @@ -1112,11 +1128,11 @@ namespace nanoply * @param entity Input entity. * @return If successful returns true. Otherwise, it returns false. */ - bool Contains(NNP_ENTITY entity); + inline bool Contains(NNP_ENTITY entity); }; - bool PlyElement::InitFromHeader(std::string &elemStr, std::vector &propStr) + inline bool PlyElement::InitFromHeader(std::string &elemStr, std::vector &propStr) { char* token; char* tempStr = &elemStr[0]; @@ -1225,7 +1241,7 @@ namespace nanoply } - bool PlyElement::WriteHeader(PlyFile &file) + inline bool PlyElement::WriteHeader(PlyFile &file) { if (!validToWrite || cnt == 0) return true; @@ -1243,7 +1259,7 @@ namespace nanoply } - bool PlyElement::SkipAsciiElementsInFile(PlyFile &file) + inline bool PlyElement::SkipAsciiElementsInFile(PlyFile &file) { for (int i = 0; i < this->cnt; ++i) for (int j = 0; j < this->propVec.size(); ++j) @@ -1252,7 +1268,7 @@ namespace nanoply } - bool PlyElement::SkipBinaryElementsInFile(PlyFile &file) + inline bool PlyElement::SkipBinaryElementsInFile(PlyFile &file) { for (int i = 0; i < this->cnt; ++i) for (int j = 0; j < this->propVec.size(); ++j) @@ -1261,7 +1277,7 @@ namespace nanoply } - bool PlyElement::AddProperty(std::string &line) + inline bool PlyElement::AddProperty(std::string &line) { char* token; char* tempStr = &line[0]; @@ -1334,7 +1350,7 @@ namespace nanoply } - bool PlyElement::Contains(PlyEntity entity) + inline bool PlyElement::Contains(PlyEntity entity) { for (int i = 0; i < propVec.size(); i++) { @@ -1364,14 +1380,14 @@ namespace nanoply /** * Default Constructor */ - Info(); + inline Info(); /** * Constructor that reads the header info from a file. * * @param filename Path of the file to read. */ - Info(const std::string& filename); + inline Info(const std::string& filename); /** * Load the ply info from the header of the input filename. @@ -1379,7 +1395,7 @@ namespace nanoply * @param filename Path of the file to read. * @return If successful returns true. Otherwise, it returns false. */ - bool LoadHeader(const std::string& filename); + inline bool LoadHeader(const std::string& filename); /** * Write the ply info in the header of the input file. @@ -1387,19 +1403,19 @@ namespace nanoply * @param file File to write. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteHeader(PlyFile& file); + inline bool WriteHeader(PlyFile& file); /** * Add the ply element to the header. * * @param pe Ply element to write in the header. */ - void AddPlyElement(PlyElement& pe); + inline void AddPlyElement(PlyElement& pe); /** * Clear the error code. */ - void Clear() { errInfo = NNP_OK; } + inline void Clear() { errInfo = NNP_OK; } /** * Return the number of instances of the element with the input name @@ -1407,7 +1423,7 @@ namespace nanoply * @param name Name of the element. * @return The number of instances */ - size_t GetElementCount(std::string& name); + inline size_t GetElementCount(std::string& name); /** * Return the number of instances of the element with the input element type @@ -1415,28 +1431,28 @@ namespace nanoply * @param e Element type. * @return The number of instances */ - size_t GetElementCount(PlyElemEntity e); + inline size_t GetElementCount(PlyElemEntity e); /** * Return the number of vertex instances * * @return The number of vertex instances */ - size_t GetVertexCount(); + inline size_t GetVertexCount(); /** * Return the number of face instances * * @return The number of face instances */ - size_t GetFaceCount(); + inline size_t GetFaceCount(); /** * Return the number of edge instances * * @return The number of edge instances */ - size_t GetEdgeCount(); + inline size_t GetEdgeCount(); /** * Return a reference to the element with a specific name @@ -1444,7 +1460,7 @@ namespace nanoply * @param name Name of the element. * @return The reference to the element */ - PlyElement* GetElement(std::string& name); + inline PlyElement* GetElement(std::string& name); /** * Return a reference to the element with a specific element type @@ -1452,33 +1468,33 @@ namespace nanoply * @param e Element type. * @return The reference to the element */ - PlyElement* GetElement(PlyElemEntity e); + inline PlyElement* GetElement(PlyElemEntity e); /** * Return a reference to the vertex element * * @return The reference to the vertex element */ - PlyElement* GetVertexElement(); + inline PlyElement* GetVertexElement(); /** * Return a reference to the face element * * @return The reference to the face element */ - PlyElement* GetFaceElement(); + inline PlyElement* GetFaceElement(); /** * Return a reference to the edge element * * @return The reference to the edge element */ - PlyElement* GetEdgeElement(); + inline PlyElement* GetEdgeElement(); }; - Info::Info() + inline Info::Info() { this->binary = true; this->bigEndian = true; @@ -1486,13 +1502,13 @@ namespace nanoply } - Info::Info(const std::string& filename) + inline Info::Info(const std::string& filename) { this->LoadHeader(filename); } - bool Info::LoadHeader(const std::string& filename) + inline bool Info::LoadHeader(const std::string& filename) { this->filename = filename; this->errInfo = NNP_OK; @@ -1542,20 +1558,24 @@ namespace nanoply return false; } std::getline(input, buffer); - std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); - while (buffer != "end_header") + std::string lowBuffer; + lowBuffer.resize(buffer.size()); + std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower); + while (lowBuffer != "end_header") { - if (buffer.find("element") != std::string::npos) + if (lowBuffer.find("element") != std::string::npos) { - std::string elemStr = buffer; + std::string elemStr = lowBuffer; std::vector propStr; do { std::getline(input, buffer); - std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); - pos = buffer.find("property"); + lowBuffer.clear(); + lowBuffer.resize(buffer.size()); + std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower); + pos = lowBuffer.find("property"); if (pos != std::string::npos) - propStr.push_back(buffer); + propStr.push_back(lowBuffer); } while (pos != std::string::npos); PlyElement pe; if (!pe.InitFromHeader(elemStr, propStr)) @@ -1568,10 +1588,12 @@ namespace nanoply } else { - if (buffer.find("comment TextureFile")) - textureFile.push_back(buffer.substr(21)); + if (lowBuffer.find("comment texture") != std::string::npos) + textureFile.push_back(buffer.substr(buffer.find(" ", 10) + 1)); std::getline(input, buffer); - std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); + lowBuffer.clear(); + lowBuffer.resize(buffer.size()); + std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower); } } input.close(); @@ -1579,7 +1601,7 @@ namespace nanoply } - bool Info::WriteHeader(PlyFile& file) + inline bool Info::WriteHeader(PlyFile& file) { bool ok = true; ok = file.WriteHeaderLine(std::string("ply\n")); @@ -1589,7 +1611,7 @@ namespace nanoply ok = file.WriteHeaderLine(std::string("format ascii 1.0\n")); ok = file.WriteHeaderLine(std::string("comment nanoply generated\n")); for (int i = 0; i < this->textureFile.size(); i++) - ok = file.WriteHeaderLine(std::string("comment TextureName ") + this->textureFile[i] + "\n"); + ok = file.WriteHeaderLine(std::string("comment TextureFile ") + this->textureFile[i] + "\n"); for (int i = 0; i < this->elemVec.size(); i++) ok = this->elemVec[i].WriteHeader(file); ok = file.WriteHeaderLine(std::string("end_header\n")); @@ -1597,13 +1619,13 @@ namespace nanoply } - void Info::AddPlyElement(PlyElement& pe) + inline void Info::AddPlyElement(PlyElement& pe) { elemVec.push_back(pe); } - size_t Info::GetElementCount(std::string& name) + inline size_t Info::GetElementCount(std::string& name) { PlyElement* pe = GetElement(name); if (pe != NULL) @@ -1612,7 +1634,7 @@ namespace nanoply } - size_t Info::GetElementCount(PlyElemEntity e) + inline size_t Info::GetElementCount(PlyElemEntity e) { PlyElement* pe = GetElement(e); if (pe != NULL) @@ -1621,25 +1643,25 @@ namespace nanoply } - size_t Info::GetVertexCount() + inline size_t Info::GetVertexCount() { return GetElementCount(PlyElemEntity::NNP_VERTEX_ELEM); } - size_t Info::GetFaceCount() + inline size_t Info::GetFaceCount() { return GetElementCount(PlyElemEntity::NNP_FACE_ELEM); } - size_t Info::GetEdgeCount() + inline size_t Info::GetEdgeCount() { return GetElementCount(PlyElemEntity::NNP_EDGE_ELEM); } - PlyElement* Info::GetElement(std::string& name) + inline PlyElement* Info::GetElement(std::string& name) { for (int i = 0; i < elemVec.size(); i++) { @@ -1650,7 +1672,7 @@ namespace nanoply } - PlyElement* Info::GetElement(PlyElemEntity e) + inline PlyElement* Info::GetElement(PlyElemEntity e) { for (int i = 0; i < elemVec.size(); i++) { @@ -1661,19 +1683,19 @@ namespace nanoply } - PlyElement* Info::GetVertexElement() + inline PlyElement* Info::GetVertexElement() { return GetElement(PlyElemEntity::NNP_VERTEX_ELEM); } - PlyElement* Info::GetFaceElement() + inline PlyElement* Info::GetFaceElement() { return GetElement(PlyElemEntity::NNP_FACE_ELEM); } - PlyElement* Info::GetEdgeElement() + inline PlyElement* Info::GetEdgeElement() { return GetElement(PlyElemEntity::NNP_EDGE_ELEM); } @@ -1702,7 +1724,7 @@ namespace nanoply * @param _e Ply entity managed by the descriptor. * @param _b Pointer to the memory location that contains the data of the property. */ - DescriptorInterface(PlyEntity _e, void *_b) :curPos(0), elem(_e), base(_b), name(PlyPropertyName(_e)[0]){}; + inline DescriptorInterface(PlyEntity _e, void *_b) :curPos(0), elem(_e), base(_b), name(PlyPropertyName(_e)[0]){}; /** * Constructor of the descriptor. @@ -1710,7 +1732,7 @@ namespace nanoply * @param _s Name of the PlyProperty. * @param _b Pointer to the memory location that contains the data of the property. */ - DescriptorInterface(std::string& _s, void *_b) :curPos(0), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_s), base(_b){}; + inline DescriptorInterface(std::string& _s, void *_b) :curPos(0), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_s), base(_b){}; /** * Restart the descriptor. @@ -1776,14 +1798,14 @@ namespace nanoply * * @param _e Ply Element entity managed by the descriptor. */ - ElementDescriptor(PlyElemEntity _e) : elem(_e), name(PlyElementName(_e)[0]){}; + inline ElementDescriptor(PlyElemEntity _e) : elem(_e), name(PlyElementName(_e)[0]){}; /** * Constructor of the Ply element descriptor. * * @param _s Name of the Ply element managed by the descriptor. */ - ElementDescriptor(std::string &_s) : elem(PlyElemEntity::NNP_UNKNOWN_ELEM), name(_s){}; + inline ElementDescriptor(std::string &_s) : elem(PlyElemEntity::NNP_UNKNOWN_ELEM), name(_s){}; /** * Read all the properties of the element from the binary file. @@ -1793,7 +1815,7 @@ namespace nanoply * @param fixEndian If true the method adjust the endianess of the data. * @return If successful returns true. Otherwise, it returns false. */ - bool ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian); + inline bool ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian); /** * Read all the property data of the element from the ascii file. @@ -1802,7 +1824,7 @@ namespace nanoply * @param elem PLY element to read from the file. * @return If successful returns true. Otherwise, it returns false. */ - bool ReadElemAscii(PlyFile &file, PlyElement &elem); + inline bool ReadElemAscii(PlyFile &file, PlyElement &elem); /** * Write all the property data of the element in the binary file. @@ -1812,7 +1834,7 @@ namespace nanoply * @param fixEndian If true the method adjust the endianess of the data. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian); + inline bool WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian); /** * Write all the property data of the element in the ascii file. @@ -1821,7 +1843,7 @@ namespace nanoply * @param elem PLY element to write from the file. * @return If successful returns true. Otherwise, it returns false. */ - bool WriteElemAscii(PlyFile &file, PlyElement &elem); + inline bool WriteElemAscii(PlyFile &file, PlyElement &elem); /** * Check if the properties defined in input element have a proper data descriport to write in the file. @@ -1829,16 +1851,16 @@ namespace nanoply * * @param elem PLY element to write from the file. */ - void CheckDescriptor(PlyElement &elem); + inline void CheckDescriptor(PlyElement &elem); private: - void ExtractDescriptor(PropertyDescriptor &descr, PlyElement &elem); + inline void ExtractDescriptor(PropertyDescriptor &descr, PlyElement &elem); }; - void ElementDescriptor::ExtractDescriptor(PropertyDescriptor& descr, PlyElement &elem) + inline void ElementDescriptor::ExtractDescriptor(PropertyDescriptor& descr, PlyElement &elem) { for (int j = 0; j < elem.propVec.size(); j++) { @@ -1873,7 +1895,7 @@ namespace nanoply } - bool ElementDescriptor::ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian) + inline bool ElementDescriptor::ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); @@ -1892,7 +1914,7 @@ namespace nanoply } - bool ElementDescriptor::ReadElemAscii(PlyFile &file, PlyElement &elem) + inline bool ElementDescriptor::ReadElemAscii(PlyFile &file, PlyElement &elem) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); @@ -1910,7 +1932,7 @@ namespace nanoply return true; } - bool ElementDescriptor::WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian) + inline bool ElementDescriptor::WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); @@ -1925,7 +1947,7 @@ namespace nanoply return true; } - bool ElementDescriptor::WriteElemAscii(PlyFile &file, PlyElement &elem) + inline bool ElementDescriptor::WriteElemAscii(PlyFile &file, PlyElement &elem) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); @@ -1949,7 +1971,7 @@ namespace nanoply } - void ElementDescriptor::CheckDescriptor(PlyElement &elem) + inline void ElementDescriptor::CheckDescriptor(PlyElement &elem) { if (elem.propVec.size() == 0) { @@ -1980,7 +2002,7 @@ namespace nanoply { public: - DataDescriptor(); + inline DataDescriptor(); /** * Constructor of the descriptor. @@ -1988,7 +2010,7 @@ namespace nanoply * @param _e Ply entity managed by the descriptor. * @param _b Pointer to the memory location that contains the data of the property. */ - DataDescriptor(PlyEntity _e, void *_b) :DescriptorInterface(_e, _b){}; + inline DataDescriptor(PlyEntity _e, void *_b) :DescriptorInterface(_e, _b){}; /** * Constructor of the descriptor. @@ -1996,93 +2018,86 @@ namespace nanoply * @param _s Name of the PlyProperty. * @param _b Pointer to the memory location that contains the data of the property. */ - DataDescriptor(std::string& _s, void *_b) :DescriptorInterface(_s, _b){}; + inline DataDescriptor(std::string& _s, void *_b) :DescriptorInterface(_s, _b){}; - void Restart(); + inline void Restart(); - bool ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); + inline bool ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); - bool ReadElemAscii(PlyFile &file, PlyProperty &prop); + inline bool ReadElemAscii(PlyFile &file, PlyProperty &prop); - bool WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); + inline bool WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); - bool WriteElemAscii(PlyFile &file, PlyProperty &prop); + inline bool WriteElemAscii(PlyFile &file, PlyProperty &prop); private: template - void ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); + inline void ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); template - void ReadAscii(PlyFile &file, PlyProperty &prop); + inline void ReadAscii(PlyFile &file, PlyProperty &prop); template - void WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); + inline void WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); template - void WriteAscii(PlyFile &file, PlyProperty &prop); + inline void WriteAscii(PlyFile &file, PlyProperty &prop); }; template - void DataDescriptor::Restart() + inline void DataDescriptor::Restart() { this->curPos = 0; } template template - void DataDescriptor::ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) + inline void DataDescriptor::ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { - unsigned char* buffer; + char * buffer = nullptr; int size; int count = prop.CountValue(); int typeSize = prop.TypeSize(); if (prop.type >= NNP_LIST_UINT8_UINT32) { - if (prop.IsSigned()) - { - char cntList = 0; - file.ReadBinaryData(&cntList, sizeof(char)); - size = typeSize * cntList; - count = cntList; - } - else - { - unsigned char cntList = 0; - file.ReadBinaryData(&cntList, sizeof(char)); - size = typeSize * cntList; - count = cntList; - } + file.ReadBinaryData(buffer, sizeof(char)); + const int cntList = int(*(reinterpret_cast(buffer))); + size = typeSize * cntList; + count = cntList; } else size = typeSize * count; - buffer = new unsigned char[size]; file.ReadBinaryData(buffer, size); if (typeSize > 1 && fixEndian) - adjustEndianess(buffer, typeSize, count); + adjustEndianess(reinterpret_cast(buffer), typeSize, count); + unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); C* temp = (C*)buffer; - float norm = 1.0f; if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { + float norm = 1.0f; if (std::is_same::value && std::is_same::value) norm = 1.0f / 255.0f; else if (std::is_same::value && std::is_same::value) norm = 255.0f; + for (int i = 0; i < std::min(VectorSize, count); i++) + *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i] * norm); } - unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); - for (int i = 0; i < std::min(VectorSize, count); i++) - *(ScalarType *)(baseProp + i*sizeof(ScalarType)) = ScalarType(temp[i] * norm); + else + { + for (int i = 0; i < std::min(VectorSize, count); i++) + *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i]); + } ++(this->curPos); - delete[] buffer; } template - bool DataDescriptor::ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) + inline bool DataDescriptor::ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { if (prop.elem != elem) return false; @@ -2120,7 +2135,7 @@ namespace nanoply template template - void DataDescriptor::ReadAscii(PlyFile &file, PlyProperty &prop) + inline void DataDescriptor::ReadAscii(PlyFile &file, PlyProperty &prop) { int count = prop.CountValue(); if (prop.type >= NNP_LIST_UINT8_UINT32) @@ -2130,18 +2145,22 @@ namespace nanoply for (int i = 0; i < count; i++) file.ReadAsciiData(temp[i]); - float norm = 1.0f; + unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { + float norm = 1.0f; if (std::is_same::value && prop.type == NNP_UINT8) norm = 1.0f / 255.0f; else if (std::is_same::value && prop.type == NNP_FLOAT32) norm = 255.0f; + for (int i = 0; i < std::min(VectorSize, count); i++) + *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i] * norm); } - unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); - for (int i = 0; i < std::min(VectorSize, count); i++) - *(ScalarType *)(baseProp + i*sizeof(ScalarType)) = ScalarType(temp[i] * norm); - + else + { + for (int i = 0; i < std::min(VectorSize, count); i++) + *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i]); + } delete[] temp; ++(this->curPos); } @@ -2149,7 +2168,7 @@ namespace nanoply template - bool DataDescriptor::ReadElemAscii(PlyFile &file, PlyProperty &prop) + inline bool DataDescriptor::ReadElemAscii(PlyFile &file, PlyProperty &prop) { if (prop.elem != elem) return false; @@ -2187,8 +2206,9 @@ namespace nanoply template template - void DataDescriptor::WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) + inline void DataDescriptor::WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { + (void)fixEndian; int count = prop.CountValue(); C data[VectorSize]; if (prop.type >= NNP_LIST_UINT8_UINT32) @@ -2207,19 +2227,24 @@ namespace nanoply } } - float norm = 1.0f; + C temp = 0; + unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { + float norm = 1.0f; if (std::is_same::value && std::is_same::value) norm = 255.0f; else if (std::is_same::value && std::is_same::value) norm = 1.0f / 255.0f; + for (int i = 0; i < std::min(VectorSize, count); i++) + data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm); } + else + { + for (int i = 0; i < std::min(VectorSize, count); i++) + data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType)))); + } - C temp = 0; - unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); - for (int i = 0; i < std::min(VectorSize, count); i++) - data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm); if (sizeof(C) > 1 && fixEndian) adjustEndianess((unsigned char*)data, sizeof(C), std::min(VectorSize, count)); @@ -2231,7 +2256,7 @@ namespace nanoply template - bool DataDescriptor::WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) + inline bool DataDescriptor::WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { if (prop.elem != elem) return false; @@ -2268,7 +2293,7 @@ namespace nanoply template template - void DataDescriptor::WriteAscii(PlyFile &file, PlyProperty &prop) + inline void DataDescriptor::WriteAscii(PlyFile &file, PlyProperty &prop) { int count = prop.CountValue(); if (prop.type >= NNP_LIST_UINT8_UINT32) @@ -2288,19 +2313,25 @@ namespace nanoply file.WriteAsciiData(std::string(" ")); } - float norm = 1.0; + C data[VectorSize]; + unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { + float norm = 1.0; if (std::is_same::value && prop.type == NNP_UINT8) norm = 255.0f; else if (std::is_same::value && prop.type == NNP_FLOAT32) norm = 1.0f / 255.0f; + for (int i = 0; i < std::min(VectorSize, count); i++) + data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm); } - - C data[VectorSize]; - unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); - for (int i = 0; i < std::min(VectorSize, count); i++) - data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm); + else + { + for (int i = 0; i < std::min(VectorSize, count); i++) + data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType)))); + } + + for (int i = 0; i < (count - VectorSize); i++) data[i] = 0; @@ -2315,7 +2346,7 @@ namespace nanoply template - bool DataDescriptor::WriteElemAscii(PlyFile &file, PlyProperty& prop) + inline bool DataDescriptor::WriteElemAscii(PlyFile &file, PlyProperty& prop) { if (prop.elem != elem) return false; @@ -2382,7 +2413,7 @@ namespace nanoply * @param meshElements Vector that defines how to manage the ply element data in memory. * @param info Info of the file to load. */ - bool OpenModel(Info& info, MeshDescriptor& meshElements) + inline bool OpenModel(Info& info, MeshDescriptor& meshElements) { PlyFile file; if (!file.OpenFileToRead(info.filename)) @@ -2415,7 +2446,7 @@ namespace nanoply PlyElement& pe = info.elemVec[i]; int j = 0; for (; j < meshElements.size(); j++) - if (ElemProcessing<0>(*meshElements[j], pe, file, false)) + if (ElemProcessing<0>(*meshElements[j], pe, file, fixEndian)) break; if (j == meshElements.size()) pe.SkipBinaryElementsInFile(file); @@ -2450,7 +2481,7 @@ namespace nanoply * @param meshElements Vector that defines how to manage the ply element data in memory. * @param info Info to saved in the PLY header. */ - bool SaveModel(std::string& filename, MeshDescriptor& meshElements, Info& info) + inline bool SaveModel(std::string& filename, MeshDescriptor& meshElements, Info& info) { PlyFile file; if (!file.OpenFileToWrite(filename)) diff --git a/wrap/nanoply/include/nanoplyWrapper.hpp b/wrap/nanoply/include/nanoplyWrapper.hpp index 197c0169..6fd77eab 100644 --- a/wrap/nanoply/include/nanoplyWrapper.hpp +++ b/wrap/nanoply/include/nanoplyWrapper.hpp @@ -496,7 +496,7 @@ namespace nanoply for (int i = 0; i < faceDescr.dataDescriptor.size(); i++) if (faceDescr.dataDescriptor[i]->elem != NNP_UNKNOWN_ENTITY) delete faceDescr.dataDescriptor[i]; - + mesh.textures = info.textureFile; return info.errInfo; } @@ -687,6 +687,7 @@ namespace nanoply infoSave.AddPlyElement(vertexElem); infoSave.AddPlyElement(edgeElem); infoSave.AddPlyElement(faceElem); + infoSave.textureFile = mesh.textures; std::vector meshDescr; meshDescr.push_back(&cameraDescr); meshDescr.push_back(&vertexDescr); diff --git a/wrap/nanoply/nanoply_vcg/main.cpp b/wrap/nanoply/nanoply_vcg/main.cpp index 14236dd3..62a95b41 100644 --- a/wrap/nanoply/nanoply_vcg/main.cpp +++ b/wrap/nanoply/nanoply_vcg/main.cpp @@ -71,8 +71,8 @@ public: faceBarycenter[i] = vcg::Barycenter(face[i]); material().resize(2); - material()[0] = { vcg::Point3f(0.1, 0.2, 0.3), vcg::Point3f(0.3, 0.3, 0.3), 5.0 }; - material()[1] = { vcg::Point3f(0.1, 0.1, 0.1), vcg::Point3f(0.5, 0.3, 0.4), 50.0 }; + material()[0] = { vcg::Point3f(0.1f, 0.2f, 0.3f), vcg::Point3f(0.3f, 0.3f, 0.3f), 5.0f }; + material()[1] = { vcg::Point3f(0.1f, 0.1f, 0.1f), vcg::Point3f(0.5f, 0.3f, 0.4f), 50.0f }; } }; @@ -87,9 +87,12 @@ bool Load(const char* filename, MyMesh& mesh) mesh.material().resize(count); customAttrib.AddVertexAttribDescriptor(std::string("materialId"), nanoply::NNP_INT32, NULL); customAttrib.AddFaceAttribDescriptor(std::string("barycenter"), nanoply::NNP_LIST_UINT8_FLOAT32, NULL); - customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("kd"), nanoply::NNP_FLOAT32, mesh.material()[0].kd.V()); - customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("ks"), nanoply::NNP_FLOAT32, mesh.material()[0].ks.V()); - customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("rho"), nanoply::NNP_FLOAT32, &mesh.material()[0].rho); + if (count > 0) + { + customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("kd"), nanoply::NNP_FLOAT32, mesh.material()[0].kd.V()); + customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("ks"), nanoply::NNP_FLOAT32, mesh.material()[0].ks.V()); + customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("rho"), nanoply::NNP_FLOAT32, &mesh.material()[0].rho); + } //Load the ply file unsigned int mask = 0; @@ -102,7 +105,7 @@ bool Load(const char* filename, MyMesh& mesh) mask |= nanoply::NanoPlyWrapper::IO_FACENORMAL; mask |= nanoply::NanoPlyWrapper::IO_FACEATTRIB; mask |= nanoply::NanoPlyWrapper::IO_MESHATTRIB; - return (nanoply::NanoPlyWrapper::LoadModel(filename, mesh, mask, customAttrib) != 0); + return (nanoply::NanoPlyWrapper::LoadModel(filename, mesh, mask, customAttrib) != 0); } @@ -113,10 +116,13 @@ bool Save(const char* filename, MyMesh& mesh, bool binary) nanoply::NanoPlyWrapper::CustomAttributeDescriptor customAttrib; customAttrib.AddVertexAttribDescriptor(std::string("materialId"), nanoply::NNP_INT32, &mesh.vertexMaterial[0]); customAttrib.AddFaceAttribDescriptor(std::string("barycenter"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.faceBarycenter[0].V()); - customAttrib.AddMeshAttrib(std::string("material"), 2); - customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("kd"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].kd.V()); - customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("ks"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].ks.V()); - customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("rho"), nanoply::NNP_FLOAT32, &mesh.material()[0].rho); + if (mesh.material().size() > 0) + { + customAttrib.AddMeshAttrib(std::string("material"), mesh.material().size()); + customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("kd"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].kd.V()); + customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("ks"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].ks.V()); + customAttrib.AddMeshAttribDescriptor(std::string("material"), std::string("rho"), nanoply::NNP_FLOAT32, &mesh.material()[0].rho); + } //Save the ply file unsigned int mask = 0; diff --git a/wrap/ply/plystuff.h b/wrap/ply/plystuff.h index 54a056f5..e799d29a 100644 --- a/wrap/ply/plystuff.h +++ b/wrap/ply/plystuff.h @@ -56,12 +56,14 @@ Cleaning of the automatic bbox caching support for ply files. First working vers #include #include -#include #include #ifdef WIN32 #include +#else +#include #endif + #include #include using namespace vcg; diff --git a/wrap/qt/qt_thread_safe_mesh_attributes_multi_viewer_bo_manager.h b/wrap/qt/qt_thread_safe_mesh_attributes_multi_viewer_bo_manager.h index ecd7e8e7..cd09ff7a 100644 --- a/wrap/qt/qt_thread_safe_mesh_attributes_multi_viewer_bo_manager.h +++ b/wrap/qt/qt_thread_safe_mesh_attributes_multi_viewer_bo_manager.h @@ -47,7 +47,7 @@ namespace vcg void meshAttributesUpdated(bool hasmeshconnectivitychanged,const GLMeshAttributesInfo::RendAtts& changedrendatts) { QWriteLocker locker(&_lock); - vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::meshAttributesUpdated(hasmeshconnectivitychanged,changedrendatts); + vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::meshAttributesUpdated(hasmeshconnectivitychanged, changedrendatts); } bool getPerViewInfo(UNIQUE_VIEW_ID_TYPE viewid,PerViewData& dt) const @@ -62,6 +62,12 @@ namespace vcg vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::setPerViewInfo(viewid,dt); } + void setPerAllViewsInfo(const PerViewData& dt) + { + QWriteLocker locker(&_lock); + vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::setPerAllViewsInfo(dt); + } + void removeView(UNIQUE_VIEW_ID_TYPE viewid) { QWriteLocker locker(&_lock); @@ -74,6 +80,13 @@ namespace vcg vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::draw(viewid,_textids.textId()); } + void drawAllocatedAttributesSubset(UNIQUE_VIEW_ID_TYPE viewid,const PerViewData& dt) const + { + QReadLocker locker(&_lock); + vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::drawAllocatedAttributesSubset(viewid,dt,_textids.textId()); + } + + bool isBORenderingAvailable() const { QReadLocker locker(&_lock); @@ -113,7 +126,7 @@ namespace vcg vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::setDebugMode(activatedebugmodality); } - void getLog(vcg::GLMeshAttributesInfo::DebugInfo& info) + void getLog(GLMeshAttributesInfo::DebugInfo& info) { QWriteLocker locker(&_lock); vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager::getLog(info); diff --git a/wrap/qt/shot_qt.h b/wrap/qt/shot_qt.h index 4313ab8e..cf0bd586 100644 --- a/wrap/qt/shot_qt.h +++ b/wrap/qt/shot_qt.h @@ -30,6 +30,7 @@ template shot.Extrinsics.SetRot(rot); vcg::Camera &cam = shot.Intrinsics; + if(attr.contains("CameraType")) cam.cameraType = attr.namedItem("CameraType").nodeValue().toInt(); cam.FocalMm = attr.namedItem("FocalMm").nodeValue().toDouble(); cam.ViewportPx.X() = attr.namedItem("ViewportPx").nodeValue().section(' ',0,0).toInt(); cam.ViewportPx.Y() = attr.namedItem("ViewportPx").nodeValue().section(' ',1,1).toInt(); @@ -118,6 +119,8 @@ template const vcg::Camera &cam = shot.Intrinsics; + shotElem.setAttribute("CameraType", cam.cameraType); + shotElem.setAttribute( "FocalMm", cam.FocalMm); str = QString("%1 %2").arg(cam.k[0]).arg(cam.k[1]);