Merge branch 'devel'
This commit is contained in:
commit
a8e87662b6
|
@ -6,3 +6,7 @@ debug/
|
|||
release/
|
||||
*-debug/
|
||||
*-release/
|
||||
/docs/Doxygen/html/
|
||||
|
||||
# Intermediate Files
|
||||
*.bc
|
|
@ -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 ---
|
||||
|
||||
|
|
|
@ -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 \/)\/
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) .
|
||||
|
|
|
@ -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) .
|
||||
|
|
|
@ -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
|
||||
|
||||
---
|
||||
|
Binary file not shown.
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
/** \page fileformat
|
||||
/** \page fileformat File Formats
|
||||
File Formats
|
||||
============
|
||||
VCGLib provides importer and exporter for several file formats
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
@ -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 <b>Visual Computing Lab</b> of the Italian National Research Council Institute ISTI
|
||||
|
@ -11,7 +13,7 @@ software tools of the <b>Visual Computing Lab</b> 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:
|
||||
- <a href="http://meshlab.sourceforge.net">MeshLab</a>: the renowed open source mesh processing is based on this library.
|
||||
- <a href="http://www.meshlab.net">MeshLab</a>: 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 <a href="http://dl.acm.org/citation.cfm?id=940008"> out-of-core mesh simplifier </a> that was used by the Stanford Digital Michelangelo project to process their huge 3D scanned models.
|
||||
|
||||
|
|
|
@ -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/
|
||||
*/
|
||||
|
|
|
@ -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<int(fvec.size())-1;++i)
|
||||
|
@ -300,8 +297,6 @@ public:
|
|||
{
|
||||
eVec.push_back(SortedPair( tri::Index(m,(*ei).V(0)), tri::Index(m,(*ei).V(1)), &*ei));
|
||||
}
|
||||
assert (size_t(m.en) == eVec.size());
|
||||
//for(int i=0;i<fvec.size();++i) qDebug("fvec[%i] = (%i %i %i)(%i)",i,fvec[i].v[0],fvec[i].v[1],fvec[i].v[2],tri::Index(m,fvec[i].fp));
|
||||
std::sort(eVec.begin(),eVec.end());
|
||||
int total=0;
|
||||
for(int i=0;i<int(eVec.size())-1;++i)
|
||||
|
@ -310,7 +305,6 @@ public:
|
|||
{
|
||||
total++;
|
||||
tri::Allocator<MeshType>::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<MeshType>::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<MeshType>::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<MeshType>::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<bool Selected>
|
||||
static int RemoveFaceOutOfRangeAreaSel(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits<ScalarType>::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<ScalarType>::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<FaceType>(*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<ScalarType>::max)())
|
||||
{
|
||||
return RemoveFaceOutOfRangeAreaSel<false>(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<MeshType>::OnlyEdgeMesh(m);
|
||||
RequireEEAdjacency(m);
|
||||
tri::UpdateTopology<MeshType>::EdgeEdge(m);
|
||||
|
||||
|
@ -1031,61 +1012,27 @@ public:
|
|||
|
||||
static int CountHoles( MeshType & m)
|
||||
{
|
||||
int numholev=0;
|
||||
FaceIterator fi;
|
||||
|
||||
FaceIterator gi;
|
||||
vcg::face::Pos<FaceType> he;
|
||||
vcg::face::Pos<FaceType> hei;
|
||||
|
||||
std::vector< std::vector<CoordType> > holes; //indices of vertices
|
||||
|
||||
vcg::tri::UpdateFlags<MeshType>::VertexClearS(m);
|
||||
|
||||
gi=m.face.begin(); fi=gi;
|
||||
|
||||
for(fi=m.face.begin();fi!=m.face.end();fi++)//for all faces do
|
||||
UpdateFlags<MeshType>::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<CoordType> 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<FaceType> startPos(&*fi,j);
|
||||
face::Pos<FaceType> curPos=startPos;
|
||||
do
|
||||
{
|
||||
//cut and paste the additional hole.
|
||||
std::vector<CoordType> hole2;
|
||||
int index = static_cast<int>(find(hole.begin(),hole.end(),newpoint)
|
||||
- hole.begin());
|
||||
for(unsigned int i=index; i<hole.size(); i++)
|
||||
hole2.push_back(hole[i]);
|
||||
|
||||
hole.resize(index);
|
||||
if(hole2.size()!=0) //annoying in degenerate cases
|
||||
holes.push_back(hole2);
|
||||
curPos.NextB();
|
||||
curPos.F()->SetV();
|
||||
}
|
||||
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<int>(holes.size());
|
||||
return loopNum;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1102,14 +1049,14 @@ public:
|
|||
{
|
||||
tri::RequireFFAdjacency(m);
|
||||
CCV.clear();
|
||||
tri::UpdateSelection<MeshType>::FaceClear(m);
|
||||
tri::UpdateFlags<MeshType>::FaceClearV(m);
|
||||
std::stack<FacePointer> 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<MeshType>::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<MeshType>::FFAdjacencyIsInitialized(m);
|
||||
bool IsOrientable = true;
|
||||
bool IsOriented = true;
|
||||
|
||||
Orientable = true;
|
||||
Oriented = true;
|
||||
|
||||
tri::UpdateSelection<MeshType>::FaceClear(m);
|
||||
UpdateFlags<MeshType>::FaceClearV(m);
|
||||
std::stack<FacePointer> 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<FaceType>(*fp, j))
|
||||
if (!face::IsBorder(*fp,j) && face::IsManifold<FaceType>(*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<FaceType,true>(*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<FaceType>((*f0),(*f1)));
|
||||
|
|
|
@ -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 {}
|
||||
};
|
||||
|
||||
|
|
|
@ -108,10 +108,8 @@ public:
|
|||
Vol.resize(this->siz[0]*this->siz[1]*this->siz[2]);
|
||||
this->ComputeDimAndVoxel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
template <class _ScalarType=float>
|
||||
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<class EXTRACTOR_TYPE>
|
||||
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));
|
||||
|
|
|
@ -689,10 +689,14 @@ void SuperEllipsoid(MeshType &m, float rFeature, float sFeature, float tFeature,
|
|||
tri::Clean<MeshType>::OrientCoherentlyMesh(m,oriented,orientable);
|
||||
tri::UpdateSelection<MeshType>::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 <class MeshType,class V, class F >
|
||||
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 <class MeshType, class InCoordType, class InFaceIndexType >
|
||||
void BuildMeshFromCoordVectorIndexVector(MeshType & in, const std::vector<InCoordType> & v, const std::vector<InFaceIndexType> & 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<v.size();++i)
|
||||
{
|
||||
float *vv=(float *)(&v[i]);
|
||||
const InCoordType &vv = v[i];
|
||||
in.vert[i].P() = CoordType( vv[0],vv[1],vv[2]);
|
||||
}
|
||||
|
||||
|
@ -716,7 +720,7 @@ void BuildMeshFromCoordVectorIndexVector( MeshType & in, const V & v, const F &
|
|||
|
||||
for(size_t i=0;i<f.size();++i)
|
||||
{
|
||||
int * ff=(int *)(&f[i]);
|
||||
const InFaceIndexType &ff= f[i];
|
||||
assert( ff[0]>=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 <class MeshType>
|
||||
void FaceGrid(MeshType & in, const std::vector<int> &grid, int w, int h)
|
||||
void SparseFaceGrid(MeshType & in, const std::vector<int> &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
|
||||
|
|
|
@ -35,23 +35,13 @@
|
|||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <locale>
|
||||
#include <iostream>
|
||||
|
||||
#include <list>
|
||||
#include <vcg/space/index/grid_static_ptr.h>
|
||||
#include <vcg/complex/complex.h>
|
||||
|
||||
#include <vcg/complex/algorithms/update/position.h>
|
||||
#include <vcg/complex/algorithms/update/normal.h>
|
||||
#include <vcg/complex/algorithms/update/quality.h>
|
||||
#include <vcg/complex/algorithms/update/topology.h>
|
||||
#include <vcg/math/histogram.h>
|
||||
#include <vcg/complex/algorithms/clean.h>
|
||||
#include <vcg/complex/algorithms/geodesic.h>
|
||||
#include <wrap/io_trimesh/import.h>
|
||||
#include <wrap/io_trimesh/export_ply.h>
|
||||
#include <wrap/ply/plystuff.h>
|
||||
//#include <wrap/ply/plystuff.h>
|
||||
|
||||
#include <vcg/complex/algorithms/create/marching_cubes.h>
|
||||
#include <vcg/complex/algorithms/create/mc_trivial_walker.h>
|
||||
|
@ -61,7 +51,6 @@
|
|||
#include <vcg/complex/algorithms/local_optimization/tri_edge_collapse.h>
|
||||
#include <vcg/complex/algorithms/local_optimization/tri_edge_collapse_quadric.h>
|
||||
|
||||
//#include <vcg/simplex/edge/base.h>
|
||||
#include <stdarg.h>
|
||||
#include "volume.h"
|
||||
#include "tri_edge_collapse_mc.h"
|
||||
|
@ -73,6 +62,15 @@ template<class MeshType>
|
|||
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<Voxelf> 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<SMesh>::RemoveUnreferencedVertex(m);
|
||||
tri::Allocator<SMesh>::CompactEveryVector(m);
|
||||
tri::UpdateNormal<SMesh>::PerVertexNormalizedPerFaceNormalized(m);
|
||||
}
|
||||
}
|
||||
tri::UpdateNormal<SMesh>::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<SMesh>::DeleteVertex(m,*vi);
|
||||
}
|
||||
tri::Allocator<SMesh>::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<SMesh>::VertexConstant(m,0);
|
||||
tri::UpdateNormal<SMesh>::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<SMesh>::VertexFace(m);
|
||||
tri::UpdateFlags<SMesh>::FaceBorderFromVF(m);
|
||||
tri::Geodesic<SMesh>::DistanceFromBorder(m);
|
||||
// tri::UpdateQuality<SMesh>::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<MCMesh, Volume <Voxelf> > Walker;
|
||||
typedef vcg::tri::TrivialWalker<MCMesh, Volume <Voxelf> > Walker;
|
||||
typedef vcg::tri::MarchingCubes<MCMesh, Walker> MarchingCubes;
|
||||
//typedef vcg::tri::ExtendedMarchingCubes<MCMesh, Walker> 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
#ifndef SIMPLEMESHPROVIDER_H
|
||||
#define SIMPLEMESHPROVIDER_H
|
||||
#include "../../meshlab/alnParser.h"
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <vcg/space/box3.h>
|
||||
#include <wrap/ply/plystuff.h>
|
||||
#include <wrap/io_trimesh/import.h>
|
||||
|
||||
/*
|
||||
* 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 TriMeshType>
|
||||
class MinimalMeshProvider
|
||||
{
|
||||
private:
|
||||
|
||||
std::vector< std::string > nameVec;
|
||||
std::vector< TriMeshType * > meshVec;
|
||||
std::vector<vcg::Matrix44f> trVec;
|
||||
std::vector<float> 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<nameVec.size();++i)
|
||||
if(nameVec[i]==name) {
|
||||
sm=meshVec[i];
|
||||
return true;
|
||||
}
|
||||
sm=0; return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache based Loading of meshes to avoid reloading an processing of the same mesh multiple times.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace vcg {
|
||||
|
||||
template<class TriMeshType>
|
||||
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<Pair> MV;
|
||||
|
||||
public:
|
||||
void clear();
|
||||
|
||||
MeshCache() {MeshCacheSize=6;}
|
||||
~MeshCache() {
|
||||
typename std::list<Pair>::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<Pair>::iterator mi;
|
||||
typename std::list<Pair>::iterator oldest; // quello che e' piu' tempo che non viene acceduto.
|
||||
int last;
|
||||
|
||||
last = std::numeric_limits<int>::max();
|
||||
oldest = MV.begin();
|
||||
|
||||
for(mi=MV.begin();mi!=MV.end();++mi)
|
||||
{
|
||||
if((*mi).used<last)
|
||||
{
|
||||
last=(*mi).used;
|
||||
oldest=mi;
|
||||
}
|
||||
if((*mi).Name==name) {
|
||||
sm=(*mi).M;
|
||||
(*mi).used++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// we have not found the requested mesh
|
||||
// either allocate a new mesh or give back a previous mesh.
|
||||
|
||||
if(MV.size()>MeshCacheSize) {
|
||||
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 TriMeshType>
|
||||
class SimpleMeshProvider
|
||||
{
|
||||
private:
|
||||
std::vector< std::string > meshnames;
|
||||
std::vector<vcg::Matrix44f> TrV;
|
||||
std::vector<float> WV; // weight tot be applied to each mesh.
|
||||
std::vector<vcg::Box3f> BBV; // bbox of the transformed meshes..
|
||||
vcg::Box3f fullBBox;
|
||||
MeshCache<TriMeshType> 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<RangeMap> rmaps;
|
||||
ALNParser::ParseALN(rmaps, alnName);
|
||||
|
||||
for(size_t i=0; i<rmaps.size(); i++)
|
||||
AddSingleMesh(rmaps[i].filename.c_str(), rmaps[i].trasformation, rmaps[i].quality);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddSingleMesh(const char* meshName, const Matrix44f &tr= Matrix44f::Identity(), float meshWeight=1)
|
||||
{
|
||||
assert(WV.size()==meshnames.size() && TrV.size() == WV.size());
|
||||
TrV.push_back(tr);
|
||||
meshnames.push_back(meshName);
|
||||
WV.push_back(meshWeight);
|
||||
BBV.push_back(Box3f());
|
||||
return true;
|
||||
}
|
||||
|
||||
vcg::Box3f bb(int i) {return BBV[i];}
|
||||
vcg::Box3f fullBB(){ return fullBBox;}
|
||||
vcg::Matrix44f Tr(int i) const {return TrV[i];}
|
||||
std::string MeshName(int i) const {return meshnames[i];}
|
||||
float W(int i) const {return WV[i];}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
meshnames.clear();
|
||||
TrV.clear();
|
||||
WV.clear();
|
||||
BBV.clear();
|
||||
fullBBox.SetNull();
|
||||
MC.clear();
|
||||
}
|
||||
|
||||
bool Find(int i, TriMeshType * &sm)
|
||||
{
|
||||
return MC.Find(meshnames[i],sm);
|
||||
}
|
||||
|
||||
bool InitBBox()
|
||||
{
|
||||
fullBBox.SetNull();
|
||||
for(int i=0;i<int(meshnames.size());++i)
|
||||
{
|
||||
bool ret;
|
||||
printf("bbox scanning %4i/%i [%16s] \r",i+1,(int)meshnames.size(), meshnames[i].c_str());
|
||||
if(tri::io::Importer<TriMeshType>::FileExtension(meshnames[i],"PLY") || tri::io::Importer<TriMeshType>::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<TriMeshType>::Open(m,meshnames[i].c_str()) == tri::io::Importer<TriMeshType>::E_NOERROR);
|
||||
tri::UpdatePosition<TriMeshType>::Matrix(m,TrV[i]);
|
||||
tri::UpdateBounding<TriMeshType>::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<SVertex>::AsVertexType,
|
||||
vcg::Use<SFace >::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
|
|
@ -22,7 +22,8 @@
|
|||
****************************************************************************/
|
||||
#ifndef __TRI_EDGE_COLLAPSE_MC__
|
||||
#define __TRI_EDGE_COLLAPSE_MC__
|
||||
#include<vcg/simplex/face/topology.h>
|
||||
namespace vcg{
|
||||
namespace tri{
|
||||
|
||||
class TriEdgeCollapseMCParameter : public BaseParameterClass
|
||||
{
|
||||
|
@ -125,5 +126,7 @@ class MCTriEdgeCollapse: public tri::TriEdgeCollapse< MCTriMesh, VertexPair, MYT
|
|||
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,26 +24,10 @@
|
|||
#ifndef __VOLUME_H__
|
||||
#define __VOLUME_H__
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define _int64 __int64
|
||||
#endif
|
||||
|
||||
#include "voxel.h"
|
||||
#include "svoxel.h"
|
||||
#include <vector>
|
||||
#include <vcg/space/index/grid_static_ptr.h>
|
||||
|
||||
//#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 VOX_TYPE, class SCALAR_TYPE=float>
|
||||
class Volume {
|
||||
public:
|
||||
typedef SCALAR_TYPE scalar;
|
||||
typedef SCALAR_TYPE scalar;
|
||||
typedef Point3<scalar> Point3x;
|
||||
typedef Box3<scalar> Box3x;
|
||||
|
||||
|
@ -172,7 +156,7 @@ bool Verbose; // se true stampa un sacco di info in piu su logfp;
|
|||
for(size_t i=0;i<rv.size();++i)
|
||||
rv[i].resize(0,VOX_TYPE::Zero());
|
||||
SetDim(bb);
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
@ -324,16 +308,16 @@ public:
|
|||
}
|
||||
|
||||
/*
|
||||
Data una posizione x,y,z restituisce true se tale posizione appartiene a un blocco gia' allocato
|
||||
In ogni caso mette in rpos la posizione del subbloc e in lpos la posizione all'interno del sottoblocco
|
||||
*/
|
||||
* Compute the offset <lpos> inside the subblock <rpos> 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.max[0] &&
|
||||
_y>=SubPartSafe.min[1] && _y<SubPartSafe.max[1] &&
|
||||
_z>=SubPartSafe.min[2] && _z<SubPartSafe.max[2]);
|
||||
_y>=SubPartSafe.min[1] && _y<SubPartSafe.max[1] &&
|
||||
_z>=SubPartSafe.min[2] && _z<SubPartSafe.max[2]);
|
||||
|
||||
// assert(x>=0 && x<sz[0] && y>=0 && y<sz[1] && z>=0 && z<sz[2]);
|
||||
|
||||
|
@ -1374,6 +1358,6 @@ class VolumeIterator
|
|||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* \ *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* 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. *
|
||||
|
@ -22,105 +22,120 @@
|
|||
****************************************************************************/
|
||||
#ifndef __VOXEL_H__
|
||||
#define __VOXEL_H__
|
||||
|
||||
namespace vcg {
|
||||
|
||||
// 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!!!
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
template<class SCALAR_TYPE=float>
|
||||
class Voxel
|
||||
{
|
||||
public:
|
||||
typedef SCALAR_TYPE scalar;
|
||||
public:
|
||||
typedef SCALAR_TYPE scalar;
|
||||
|
||||
Voxel(SCALAR_TYPE vv, bool bb, Point3<scalar> nn, short _cnt) {v=vv;b=bb;n=nn;cnt=_cnt;}
|
||||
Voxel(SCALAR_TYPE vv, Point3<scalar> nn, scalar qq) {v=vv;b=true;n=nn;cnt=0;q=qq;}
|
||||
Voxel(SCALAR_TYPE vv, bool bb, Point3<scalar> nn, short _cnt) {v=vv;b=bb;n=nn;cnt=_cnt;}
|
||||
Voxel(SCALAR_TYPE vv, Point3<scalar> 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<scalar> &N() const { return n; }
|
||||
|
||||
const Point3<scalar> &N() const { return n; }
|
||||
void SetN(const Point3<scalar> &nn) { n=nn; }
|
||||
const scalar &V() const { return v; }
|
||||
|
||||
void SetN(const Point3<scalar> &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(cnt<thr)
|
||||
{
|
||||
assert(cnt>0);
|
||||
assert(!B());
|
||||
if(cnt<thr)
|
||||
{
|
||||
(*this) = Zero();
|
||||
return false;
|
||||
}
|
||||
v/=cnt;
|
||||
q/=cnt;
|
||||
n/=cnt;
|
||||
cnt=0;
|
||||
b=true;
|
||||
(*this) = Zero();
|
||||
return false;
|
||||
}
|
||||
v/=cnt;
|
||||
q/=cnt;
|
||||
n/=cnt;
|
||||
cnt=0;
|
||||
b=true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static const Voxel &Zero() {
|
||||
static Voxel tt(0,false,Point3f(0,0,0),0);
|
||||
return tt;
|
||||
}
|
||||
void Merge(const Voxel &VOX)
|
||||
{
|
||||
v=(v*q+VOX.Q()*VOX.v)/(q+VOX.Q());
|
||||
n=(n*q+VOX.n*VOX.Q())/(q+VOX.Q());
|
||||
q=q+VOX.Q();
|
||||
}
|
||||
static const Voxel &Zero() {
|
||||
static Voxel tt(0,false,Point3f(0,0,0),0);
|
||||
return tt;
|
||||
}
|
||||
void Merge(const Voxel &VOX)
|
||||
{
|
||||
v=(v*q+VOX.Q()*VOX.v)/(q+VOX.Q());
|
||||
n=(n*q+VOX.n*VOX.Q())/(q+VOX.Q());
|
||||
q=q+VOX.Q();
|
||||
}
|
||||
|
||||
void Set(const Voxel &VOX)
|
||||
{
|
||||
v=VOX.v;
|
||||
n=VOX.n;
|
||||
q=VOX.q;
|
||||
}
|
||||
void Set(const Voxel &VOX)
|
||||
{
|
||||
v=VOX.v;
|
||||
n=VOX.n;
|
||||
q=VOX.q;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool b;
|
||||
short cnt;
|
||||
scalar v;
|
||||
scalar q;
|
||||
Point3<SCALAR_TYPE> n;
|
||||
protected:
|
||||
bool b;
|
||||
short cnt;
|
||||
scalar v;
|
||||
scalar q;
|
||||
Point3<SCALAR_TYPE> n;
|
||||
|
||||
};
|
||||
|
||||
|
@ -129,67 +144,67 @@ class Voxelfc :public Voxel<float>
|
|||
{
|
||||
public:
|
||||
|
||||
Voxelfc(float vv, bool bb, Point3f nn, short _cnt) :Voxel<float>(vv,bb,nn,_cnt){}
|
||||
Voxelfc(float vv, Point3f nn, scalar qq) :Voxel<float>(vv,nn,qq) {}
|
||||
Voxelfc(float vv, Point3f nn, scalar qq,Color4b cc) :Voxel<float>(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<float>(vv,bb,nn,_cnt){}
|
||||
Voxelfc(float vv, Point3f nn, scalar qq) :Voxel<float>(vv,nn,qq) {}
|
||||
Voxelfc(float vv, Point3f nn, scalar qq,Color4b cc) :Voxel<float>(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<float>::Normalize(thr);
|
||||
}
|
||||
inline bool Normalize(int thr)
|
||||
{
|
||||
if(cnt>=thr) c/=cnt;
|
||||
return Voxel<float>::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<float>::Merge(VOX);
|
||||
}
|
||||
void Merge(const Voxelfc &VOX)
|
||||
{
|
||||
c=( c*q + VOX.C()*VOX.Q() )/(q+VOX.Q());
|
||||
Voxel<float>::Merge(VOX);
|
||||
}
|
||||
|
||||
void Set(const Voxelfc &VOX)
|
||||
{
|
||||
Voxel<float>::Set(VOX);
|
||||
c=VOX.c;
|
||||
}
|
||||
void Set(const Voxelfc &VOX)
|
||||
{
|
||||
Voxel<float>::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<float>::operator +=(vx);
|
||||
if(cnt==1) c =vx.c;
|
||||
else c+=vx.c;
|
||||
return *this;
|
||||
}
|
||||
inline Voxelfc & operator += ( Voxelfc const & vx)
|
||||
{
|
||||
Voxel<float>::operator +=(vx);
|
||||
if(cnt==1) c =vx.c;
|
||||
else c+=vx.c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Point3f c;
|
||||
Point3f c;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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 MESH> 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<FaceType> PosType;
|
||||
typedef typename MESH::ScalarType ScalarType;
|
||||
typedef typename MESH::CoordType CoordType;
|
||||
|
||||
typedef typename face::Pos<FaceType> 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 <triangle>
|
||||
// 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 MESH>
|
||||
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<ScalarType> Box3Type;
|
||||
typedef typename face::Pos<FaceType> 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<ScalarType> Box3Type;
|
||||
typedef typename face::Pos<FaceType> PosType;
|
||||
|
||||
public:
|
||||
|
||||
class Info
|
||||
{
|
||||
public:
|
||||
Info(){}
|
||||
Info(PosType const &pHole, int const pHoleSize, Box3<ScalarType> &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<ScalarType> &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->v0<e.v0;
|
||||
return this->v1<e.v1;
|
||||
}
|
||||
};
|
||||
/// Main Single Hole Filling Function
|
||||
/// Given a specific hole (identified by the Info h) it fills it
|
||||
/// It also update a vector of face pointers
|
||||
/// It uses an heap to choose the best ear to be closed
|
||||
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()
|
||||
{
|
||||
if(!p.IsBorder())
|
||||
return false;
|
||||
PosType ip=p;ip.NextB();
|
||||
for(;ip!=p;ip.NextB())
|
||||
{
|
||||
if(!ip.IsBorder())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** FillHoleEar
|
||||
* Main Single Hole Filling Function
|
||||
* Given a specific hole (identified by the Info h) it fills it
|
||||
* It also update a vector of face pointers
|
||||
* It uses a priority queue to choose the best ear to be closed
|
||||
*/
|
||||
|
||||
template<class EAR>
|
||||
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<FacePointer *> &facePointersToBeUpdated)
|
||||
{
|
||||
//Aggiungo le facce e aggiorno il puntatore alla faccia!
|
||||
FaceIterator f = tri::Allocator<MESH>::AddFaces(m, h.size-2, facePointersToBeUpdated);
|
||||
|
||||
assert(tri::IsValidPointer(m,p.f));
|
||||
assert(p.IsBorder());
|
||||
int holeSize = EAR::InitNonManifoldBitOnHoleBoundary(p);
|
||||
FaceIterator f = tri::Allocator<MESH>::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<MESH>::DeleteFace(m,*f);
|
||||
f++;
|
||||
}
|
||||
|
||||
VertexType::DeleteBitFlag(nmBit); // non manifoldness bit
|
||||
}
|
||||
|
||||
template<class EAR>
|
||||
|
@ -504,7 +543,7 @@ template<class EAR>
|
|||
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<class EAR>
|
|||
for(fpi=EAR::AdjacencyRing().begin();fpi!=EAR::AdjacencyRing().end();++fpi)
|
||||
facePtrToBeUpdated.push_back( &*fpi );
|
||||
|
||||
FillHoleEar<EAR >(m, *ith,facePtrToBeUpdated);
|
||||
FillHoleEar<EAR >(m, ith->p,facePtrToBeUpdated);
|
||||
EAR::AdjacencyRing().clear();
|
||||
}
|
||||
}
|
||||
|
@ -671,207 +710,207 @@ template<class EAR>
|
|||
return false;
|
||||
}
|
||||
|
||||
static Weight computeWeight( int i, int j, int k,
|
||||
std::vector<PosType > pv,
|
||||
std::vector< std::vector< int > > v)
|
||||
static Weight computeWeight( int i, int j, int k,
|
||||
std::vector<PosType > 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<float>(angle , ComputeDihedralAngle(pi.v->P(), pj.v->P(), pk.v->P(), px.v->P()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = std::max<float>( 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<float>(angle , ComputeDihedralAngle(pj.v->P(), pk.v->P(), pi.v->P(), px.v->P()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = std::max<float>( 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<float>(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<PosType > 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<Weight>( nv, Weight() ) );
|
||||
|
||||
vi.resize( nv, std::vector<int>( 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<float>(angle , ComputeDihedralAngle(pi.v->P(), pj.v->P(), pk.v->P(), px.v->P()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = std::max<float>( 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<float>(angle , ComputeDihedralAngle(pj.v->P(), pk.v->P(), pi.v->P(), px.v->P()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = std::max<float>( 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<float>(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<PosType > 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<Weight>( nv, Weight() ) );
|
||||
|
||||
vi.resize( nv, std::vector<int>( 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<int> > vi, std::vector<PosType > 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<PosType > vvi;
|
||||
std::vector<FacePointer * > vfp;
|
||||
|
||||
std::vector<Info > vinfo;
|
||||
typename std::vector<Info >::iterator VIT;
|
||||
GetInfo(m, Selected,vinfo);
|
||||
|
||||
for(VIT = vinfo.begin(); VIT != vinfo.end();++VIT)
|
||||
{
|
||||
vvi.push_back(VIT->p);
|
||||
}
|
||||
|
||||
typename std::vector<PosType >::iterator ith;
|
||||
typename std::vector<PosType >::iterator ithn;
|
||||
typename std::vector<VertexPointer >::iterator itf;
|
||||
|
||||
std::vector<PosType > app;
|
||||
PosType ps;
|
||||
std::vector<FaceType > tr;
|
||||
std::vector<VertexPointer > 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<PosType >::iterator itP;
|
||||
std::vector<FacePointer *> 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<MESH>::AddFaces(m, (app.size()-2) , vfp);
|
||||
|
||||
calculateMinimumWeightTriangulation(m,f, app);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void getBoundHole (PosType sp,std::vector<PosType >&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<PosType > vvi;
|
||||
std::vector<FacePointer * > vfp;
|
||||
|
||||
std::vector<Info > vinfo;
|
||||
typename std::vector<Info >::iterator VIT;
|
||||
GetInfo(m, Selected,vinfo);
|
||||
|
||||
for(VIT = vinfo.begin(); VIT != vinfo.end();++VIT)
|
||||
{
|
||||
vvi.push_back(VIT->p);
|
||||
}
|
||||
|
||||
typename std::vector<PosType >::iterator ith;
|
||||
typename std::vector<PosType >::iterator ithn;
|
||||
typename std::vector<VertexPointer >::iterator itf;
|
||||
|
||||
std::vector<PosType > app;
|
||||
PosType ps;
|
||||
std::vector<FaceType > tr;
|
||||
std::vector<VertexPointer > 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<PosType >::iterator itP;
|
||||
std::vector<FacePointer *> 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<MESH>::AddFaces(m, (app.size()-2) , vfp);
|
||||
|
||||
calculateMinimumWeightTriangulation(m,f, app);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void getBoundHole (PosType sp,std::vector<PosType >&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
|
||||
|
|
|
@ -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<vector>
|
||||
#include<algorithm>
|
||||
#include<time.h>
|
||||
#include<math.h>
|
||||
#include<vcg/complex/complex.h>
|
||||
|
||||
#include <vcg/complex/complex.h>
|
||||
#include <time.h>
|
||||
namespace vcg{
|
||||
// Base class for Parameters
|
||||
// all parameters must be derived from this.
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace tri{
|
|||
static void Init(){}
|
||||
static math::Quadric<double> &Qd(VERTEX_TYPE &v) {return v.Qd();}
|
||||
static math::Quadric<double> &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*/){}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -666,7 +666,7 @@ public:
|
|||
CoordType dirR=vcg::tri::CrossField<MeshType>::Rotate(f0,f1,dir0);
|
||||
///then get the closest upf to K*PI/2 rotations
|
||||
CoordType dir1=f1.cPD1();
|
||||
CoordType ret=vcg::tri::CrossField<MeshType>::K_PI(dirR,dir1,f1.cN());
|
||||
CoordType ret=vcg::tri::CrossField<MeshType>::K_PI(dir1,dirR,f1.cN());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,11 @@ private:
|
|||
std::vector<CoordType> *sampleVec;
|
||||
bool vectorOwner;
|
||||
public:
|
||||
|
||||
std::vector<CoordType> &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 <float> angleSumH = tri::Allocator<MeshType>:: template GetPerVertexAttribute<float> (m);
|
||||
typename MeshType::template PerVertexAttributeHandle<float> angleSumH = tri::Allocator<MeshType>:: template GetPerVertexAttribute<float> (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 <class MeshType>
|
||||
void PoissonPruning(MeshType &m, // the mesh that has to be pruned
|
||||
std::vector<Point3f> &poissonSamples, // the vector that will contain the chosen set of points
|
||||
std::vector<typename MeshType::CoordType> &poissonSamples, // the vector that will contain the chosen set of points
|
||||
float radius, unsigned int randSeed=0)
|
||||
{
|
||||
std::vector<typename MeshType::VertexPointer> poissonSamplesVP;
|
||||
PoissonPruning(m,poissonSamplesVP,radius,randSeed);
|
||||
poissonSamples.resize(poissonSamplesVP.size());
|
||||
for(size_t i=0;i<poissonSamplesVP.size();++i)
|
||||
for(size_t i=0;i<poissonSamplesVP.size();++i)
|
||||
poissonSamples[i]=poissonSamplesVP[i]->P();
|
||||
}
|
||||
|
||||
|
|
|
@ -676,6 +676,8 @@ public:
|
|||
UV.clear();
|
||||
Pmesh.Clear();
|
||||
|
||||
vcg::tri::UpdateTopology<TriMesh>::FaceFace(Tmesh);
|
||||
|
||||
TestIsProper(Tmesh);
|
||||
|
||||
RoundInitial(Tmesh);
|
||||
|
|
|
@ -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<vcg/complex/algorithms/voronoi_volume_sampling.h>
|
||||
|
||||
namespace vcg
|
||||
{
|
||||
namespace tri
|
||||
{
|
||||
|
||||
template <class MeshType>
|
||||
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<MeshType> 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<MeshType>::VertexConstant(vvs.psd.poissonSurfaceMesh,0);
|
||||
std::vector<VertexPointer> 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<unsigned int> indVec;
|
||||
std::vector<ScalarType> sqDistVec;
|
||||
|
||||
this->vvs.psd.surfTree->doQueryDist( vi->P(), minDist*distThr,indVec,sqDistVec);
|
||||
if(indVec.size()>1)
|
||||
{
|
||||
for(size_t i=0;i<indVec.size();++i)
|
||||
{
|
||||
VertexPointer vp = &vvs.psd.poissonSurfaceMesh.vert[indVec[i]];
|
||||
//ScalarType dist = math::Sqrt(sqDistVec[i]);
|
||||
if(vp->Q() < 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<MeshType>::VertexClearV(vvs.montecarloVolumeMesh);
|
||||
for(size_t i=0;i<medialSrc.size();++i)
|
||||
medialSrc[i]->SetV();
|
||||
for(VertexIterator vi=vvs.montecarloVolumeMesh.vert.begin(); vi!=vvs.montecarloVolumeMesh.vert.end(); ++vi)
|
||||
if(vi->IsV()) tri::Allocator<MeshType>::AddVertex(*skelM,vi->P());
|
||||
printf("Generated a medial surf of %i vertexes\n",skelM->vn);
|
||||
}
|
||||
|
||||
|
||||
tri::Smooth<MeshType>::PointCloudQualityMedian(vvs.psd.poissonSurfaceMesh);
|
||||
tri::Smooth<MeshType>::PointCloudQualityAverage(vvs.psd.poissonSurfaceMesh,smoothSize,smoothIter);
|
||||
tri::UpdateColor<MeshType>::PerVertexQualityRamp(vvs.psd.poissonSurfaceMesh);
|
||||
tri::RedetailSampler<MeshType> rs;
|
||||
rs.init(&vvs.psd.poissonSurfaceMesh);
|
||||
rs.dist_upper_bound = vvs.psd.poissonSurfaceMesh.bbox.Diag()*0.05 ;
|
||||
rs.qualityFlag = true;
|
||||
tri::SurfaceSampling<MeshType, RedetailSampler<MeshType> >::VertexUniform(vvs.baseMesh, rs, vvs.baseMesh.vn, false);
|
||||
}
|
||||
|
||||
void RefineSkeletonVolume(MeshType &skelMesh)
|
||||
{
|
||||
CoordType closestP;
|
||||
int trialNum=0;
|
||||
for(int i=0;i<skelMesh.vn;++i)
|
||||
{
|
||||
CoordType point = math::GeneratePointInBox3Uniform(vvs.rng,vvs.baseMesh.bbox);
|
||||
trialNum++;
|
||||
ScalarType d = this->DistanceFromSurface(point, closestP);
|
||||
if(d<0){
|
||||
vcg::tri::Allocator<MeshType>::AddVertex(vvs.montecarloVolumeMesh,point);
|
||||
vvs.montecarloVolumeMesh.vert.back().Q() = fabs(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}; // end class
|
||||
|
||||
|
||||
} // end namespace vcg
|
||||
} // end namespace vcg
|
||||
#endif // VCG__SKELETON_H
|
|
@ -226,6 +226,7 @@ public:
|
|||
//create the sphere
|
||||
vcg::tri::Sphere<TriMeshType>(*sphere,SubDirections);
|
||||
vcg::tri::UpdateBounding<TriMeshType>::Box(*sphere);
|
||||
sphere->face.EnableMark();
|
||||
|
||||
///initialize grid
|
||||
GridSph.Set(sphere->face.begin(),sphere->face.end());
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<MeshType>::FaceClearV(m);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<VertexType *> &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<VertexType *> &vertToFixVec)
|
||||
static void MarkVertexVectorAsFixed(MeshType &m, std::vector<VertexType *> &vertToFixVec)
|
||||
{
|
||||
typename MeshType::template PerVertexAttributeHandle<bool> fixed;
|
||||
fixed = tri::Allocator<MeshType>:: template GetPerVertexAttribute<bool> (m,"fixed");
|
||||
|
|
|
@ -33,6 +33,103 @@ namespace vcg
|
|||
namespace tri
|
||||
{
|
||||
|
||||
template <class MeshType>
|
||||
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<typename MeshType::FaceType, ScalarType> GridType;
|
||||
|
||||
typedef SimpleVolume<SimpleVoxel<ScalarType> > VVSVolume;
|
||||
typedef typename vcg::tri::TrivialWalker<MeshType,VVSVolume> VVSWalker;
|
||||
typedef typename vcg::tri::MarchingCubes<MeshType, VVSWalker> VVSMarchingCubes;
|
||||
|
||||
PointSampledDistance(MeshType &_baseMesh)
|
||||
:surfTree(0),baseMesh(_baseMesh) {}
|
||||
typename KdTree<ScalarType>::PriorityQueue pq;
|
||||
GridType surfGrid; // used for fast inside query
|
||||
typedef FaceTmark<MeshType> MarkerFace;
|
||||
MarkerFace mf;
|
||||
vcg::face::PointDistanceBaseFunctor<ScalarType> PDistFunct;
|
||||
KdTree<ScalarType> *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<MeshType>::ComputeMeshArea(baseMesh);
|
||||
int MontecarloSurfSampleNum = 10 * meshArea / (poissonRadiusSurface*poissonRadiusSurface);
|
||||
tri::MeshSampler<MeshType> sampler(montecarloSurfaceMesh);
|
||||
tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::Montecarlo(baseMesh, sampler, MontecarloSurfSampleNum);
|
||||
montecarloSurfaceMesh.bbox = baseMesh.bbox; // we want the same bounding box
|
||||
poissonSurfaceMesh.Clear();
|
||||
tri::MeshSampler<MeshType> mps(poissonSurfaceMesh);
|
||||
typename tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::PoissonDiskParam pp;
|
||||
pp.geodesicDistanceFlag=false;
|
||||
|
||||
tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::PoissonDiskPruning(mps, montecarloSurfaceMesh, poissonRadiusSurface,pp);
|
||||
vcg::tri::UpdateBounding<MeshType>::Box(poissonSurfaceMesh);
|
||||
|
||||
printf("Surface Sampling radius %f - montecarlo %ivn - Poisson %ivn\n",poissonRadiusSurface,montecarloSurfaceMesh.vn,poissonSurfaceMesh.vn);
|
||||
VertexConstDataWrapper<MeshType> ww(poissonSurfaceMesh);
|
||||
if(surfTree) delete surfTree;
|
||||
surfTree = new KdTree<ScalarType>(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<MeshType>::Box(baseMesh);
|
||||
tri::UpdateNormal<MeshType>::PerFaceNormalized(baseMesh);
|
||||
}
|
||||
|
||||
KdTree<ScalarType> *surfTree; // used for fast inside query
|
||||
KdTree<ScalarType> *seedTree; // used to accumulate barycenter in relaxation
|
||||
KdTree<ScalarType> *seedDomainTree; // used to accumulate barycenter in relaxation
|
||||
|
||||
typename KdTree<ScalarType>::PriorityQueue pq;
|
||||
GridType surfGrid; // used for fast inside query
|
||||
typedef FaceTmark<MeshType> MarkerFace;
|
||||
MarkerFace mf;
|
||||
vcg::face::PointDistanceBaseFunctor<ScalarType> 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<MeshType> 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<MeshType>::ComputeMeshArea(baseMesh);
|
||||
int MontecarloSurfSampleNum = 10 * meshArea / (poissonRadiusSurface*poissonRadiusSurface);
|
||||
tri::MeshSampler<MeshType> sampler(montecarloSurfaceMesh);
|
||||
psd.Init(_poissonRadiusSurface);
|
||||
tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::SamplingRandomGenerator()=rng;
|
||||
tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::Montecarlo(baseMesh, sampler, MontecarloSurfSampleNum);
|
||||
montecarloSurfaceMesh.bbox = baseMesh.bbox; // we want the same bounding box
|
||||
poissonSurfaceMesh.Clear();
|
||||
tri::MeshSampler<MeshType> mps(poissonSurfaceMesh);
|
||||
typename tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::PoissonDiskParam pp;
|
||||
pp.geodesicDistanceFlag=false;
|
||||
|
||||
tri::SurfaceSampling<MeshType,tri::MeshSampler<MeshType> >::PoissonDiskPruning(mps, montecarloSurfaceMesh, poissonRadiusSurface,pp);
|
||||
vcg::tri::UpdateBounding<MeshType>::Box(poissonSurfaceMesh);
|
||||
|
||||
printf("Surface Sampling radius %f - montecarlo %ivn - Poisson %ivn\n",poissonRadiusSurface,montecarloSurfaceMesh.vn,poissonSurfaceMesh.vn);
|
||||
VertexConstDataWrapper<MeshType> ww(poissonSurfaceMesh);
|
||||
if(surfTree) delete surfTree;
|
||||
surfTree = new KdTree<ScalarType>(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 <radius> 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<MeshType>::VertexConstant(poissonSurfaceMesh,0);
|
||||
std::vector<VertexPointer> 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<unsigned int> indVec;
|
||||
std::vector<ScalarType> sqDistVec;
|
||||
|
||||
this->surfTree->doQueryDist( vi->P(), minDist*distThr,indVec,sqDistVec);
|
||||
if(indVec.size()>1)
|
||||
{
|
||||
for(size_t i=0;i<indVec.size();++i)
|
||||
{
|
||||
VertexPointer vp = &poissonSurfaceMesh.vert[indVec[i]];
|
||||
//ScalarType dist = math::Sqrt(sqDistVec[i]);
|
||||
if(vp->Q() < 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<MeshType>::VertexClearV(montecarloVolumeMesh);
|
||||
for(size_t i=0;i<medialSrc.size();++i)
|
||||
medialSrc[i]->SetV();
|
||||
for(VertexIterator vi=montecarloVolumeMesh.vert.begin(); vi!=montecarloVolumeMesh.vert.end(); ++vi)
|
||||
if(vi->IsV()) tri::Allocator<MeshType>::AddVertex(*skelM,vi->P());
|
||||
printf("Generated a medial surf of %i vertexes\n",skelM->vn);
|
||||
}
|
||||
|
||||
|
||||
tri::Smooth<MeshType>::PointCloudQualityMedian(poissonSurfaceMesh);
|
||||
tri::Smooth<MeshType>::PointCloudQualityAverage(poissonSurfaceMesh,smoothSize,smoothIter);
|
||||
tri::UpdateColor<MeshType>::PerVertexQualityRamp(poissonSurfaceMesh);
|
||||
tri::RedetailSampler<MeshType> rs;
|
||||
rs.init(&poissonSurfaceMesh);
|
||||
rs.dist_upper_bound = poissonSurfaceMesh.bbox.Diag()*0.05 ;
|
||||
rs.qualityFlag = true;
|
||||
tri::SurfaceSampling<MeshType, RedetailSampler<MeshType> >::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;i<skelMesh.vn;++i)
|
||||
{
|
||||
CoordType point = math::GeneratePointInBox3Uniform(rng,baseMesh.bbox);
|
||||
trialNum++;
|
||||
ScalarType d = this->DistanceFromSurface(point, closestP);
|
||||
if(d<0){
|
||||
vcg::tri::Allocator<MeshType>::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<MeshType>::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<MeshType,MeshType>::MeshCopy(seedDomainMesh,montecarloVolumeMesh);
|
||||
|
||||
vector<VertexPointer> 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<CoordType> seedPts(pruningVec.size());
|
||||
for(size_t i=0;i<pruningVec.size();++i)
|
||||
seedPts[i]=pruningVec[i]->P();
|
||||
std::vector<CoordType> seedPts;
|
||||
tri::PoissonPruning(seedDomainMesh,seedPts,poissonRadius,randSeed);
|
||||
tri::BuildMeshFromCoordVector(this->seedMesh,seedPts);
|
||||
|
||||
// Kdtree must be rebuilt at the end of each step;
|
||||
VertexConstDataWrapper<MeshType> vdw(seedMesh);
|
||||
if(seedTree) delete seedTree;
|
||||
|
|
|
@ -52,13 +52,13 @@ template<class MeshType>
|
|||
size_t Index(MeshType &m, const typename MeshType::HEdgeType* h) {return h-&*m.hedge.begin();}
|
||||
|
||||
template<class MeshType>
|
||||
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<class MeshType>
|
||||
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<class MeshType>
|
||||
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<class MeshType>
|
||||
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 <class MeshType, class ATTR_CONT>
|
||||
void ReorderAttribute(ATTR_CONT &c, std::vector<size_t> & 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 <class ATTR_TYPE>
|
||||
|
|
|
@ -89,6 +89,7 @@ Edited Comments and GPL license
|
|||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -196,7 +197,7 @@ template<class T> int IsNAN(T t) { return _isnan(t) || (!_finite(t)); }
|
|||
#elif defined(__MINGW32__) // GCC
|
||||
template<class T> int IsNAN(T t) { return std::isnan(t) || std::isinf(t); }
|
||||
#elif defined(__GNUC__) // GCC
|
||||
template<class T> int IsNAN(T t) { return isnan(t) || isinf(t); }
|
||||
template<class T> int IsNAN(T t) { return std::isnan(t) || std::isinf(t); }
|
||||
#else // generic
|
||||
|
||||
template<class T> int IsNAN(T t)
|
||||
|
|
|
@ -138,12 +138,18 @@ public:
|
|||
void operator*=( const T k );
|
||||
|
||||
template <class Matrix44Type>
|
||||
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 <class Matrix44Type>
|
||||
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 <class EigenMatrix44Type>
|
||||
void ToEigenMatrix(EigenMatrix44Type & m) const {
|
||||
|
|
|
@ -412,18 +412,6 @@ public:
|
|||
return (((double)generate()) + 0.5)*(1.0/4294967296.0);
|
||||
}
|
||||
|
||||
/// Generate a random triple of baricentric coords
|
||||
template <class PointType>
|
||||
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
|
||||
|
|
|
@ -75,7 +75,7 @@ public:
|
|||
RotoType rot; // rotation
|
||||
Point3<S> tra; // viewpoint
|
||||
public:
|
||||
ReferenceFrame(){}
|
||||
ReferenceFrame():rot(),tra(){}
|
||||
|
||||
void SetIdentity(){ rot.SetIdentity(); tra = Point3<S>(0.0,0.0,0.0);}
|
||||
void SetTra(const Point3<S> & tr) {tra = tr;}
|
||||
|
@ -87,18 +87,21 @@ public:
|
|||
Camera<S> Intrinsics; // the camera that made the shot
|
||||
ReferenceFrame<S,RotationType> Extrinsics; // the position and orientation of the camera
|
||||
Shot(const Camera<S> &i, const ReferenceFrame<S,RotationType> &e)
|
||||
:Intrinsics(),Extrinsics()
|
||||
{
|
||||
Intrinsics = i;
|
||||
Extrinsics = e;
|
||||
}
|
||||
|
||||
Shot(const Camera<S> &c)
|
||||
:Intrinsics(),Extrinsics()
|
||||
{
|
||||
Intrinsics = c;
|
||||
Extrinsics.SetIdentity();
|
||||
}
|
||||
|
||||
Shot()
|
||||
:Intrinsics(),Extrinsics()
|
||||
{
|
||||
Extrinsics.SetIdentity();
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ inline bool IsEdgeBorder(EdgeType const & e, const int j )
|
|||
}
|
||||
|
||||
template <class VertexType>
|
||||
void VVStarVE(VertexType* vp, std::vector<VertexType *> &starVec)
|
||||
void VVStarVE(const VertexType* vp, std::vector<VertexType *> &starVec)
|
||||
{
|
||||
starVec.clear();
|
||||
edge::VEIterator<typename VertexType::EdgeType> vei(vp);
|
||||
|
|
|
@ -321,8 +321,8 @@ void FFAttach(FaceType * &f, int z1, FaceType *&f2, int z2)
|
|||
template <class FaceType>
|
||||
void FFAttachManifold(FaceType * &f1, int z1, FaceType *&f2, int z2)
|
||||
{
|
||||
assert(IsBorder<FaceType>(*f1,z1));
|
||||
assert(IsBorder<FaceType>(*f2,z2));
|
||||
assert(IsBorder<FaceType>(*f1,z1) || f1->FFp(z1)==0);
|
||||
assert(IsBorder<FaceType>(*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 <CODE>CheckFlipFace()</CODE>.
|
||||
* Check for topological correctness first using <CODE>CheckFlipEdge()</CODE>.
|
||||
* \param f pointer to the face
|
||||
* \param z the edge index
|
||||
*
|
||||
* Note: For <em>edge flip</em> we intend the swap of the diagonal of the rectangle
|
||||
* Note: For <em>edge flip</em> 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 <class FaceType>
|
||||
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<FaceType>(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 <class FaceType>
|
||||
|
|
|
@ -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 <class RightVertexType>
|
||||
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; }
|
||||
};
|
||||
|
|
|
@ -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<BoxScalarType> (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<BoxScalarType> & delta )
|
||||
{
|
||||
min -= delta;
|
||||
max += delta;
|
||||
}
|
||||
/// Initializing the bounding box
|
||||
/// Initializing the bounding box
|
||||
void Set( const Point3<BoxScalarType> & 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<BoxScalarType> 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<BoxScalarType> & 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<BoxScalarType> & p, const BoxScalarType radius )
|
||||
{
|
||||
|
@ -154,19 +151,22 @@ void Add( const Point3<BoxScalarType> & 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<BoxScalarType> &m, const Box3<BoxScalarType> & b )
|
||||
{
|
||||
const Point3<BoxScalarType> &mn= b.min;
|
||||
const Point3<BoxScalarType> &mx= b.max;
|
||||
if(b.IsNull()) return; // Adding a null bbox should do nothing
|
||||
|
||||
const Point3<BoxScalarType> &mn= b.min;
|
||||
const Point3<BoxScalarType> &mx= b.max;
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mn[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mn[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mx[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mx[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mn[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mn[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mx[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mx[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mn[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mx[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mx[1],mn[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mn[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mx[0],mn[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(mn[0],mx[1],mx[2])));
|
||||
Add(m*(Point3<BoxScalarType>(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<BoxScalarType> & 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<BoxScalarType> const & p ) const
|
||||
{
|
||||
|
@ -203,9 +201,8 @@ void Add( const Point3<BoxScalarType> & 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<BoxScalarType> const & p ) const
|
||||
{
|
||||
|
@ -234,15 +231,14 @@ void Add( const Point3<BoxScalarType> & p, const BoxScalarType radius )
|
|||
b.min.Y()<max.Y() && b.max.Y()>min.Y() &&
|
||||
b.min.Z()<max.Z() && b.max.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<BoxScalarType> & p, const BoxScalarType radius )
|
|||
{
|
||||
return SquaredDistance(min,max);
|
||||
}
|
||||
/// Calcola il centro del bounding box.
|
||||
/// Return the center of the box.
|
||||
Point3<BoxScalarType> Center() const
|
||||
{
|
||||
return (min+max)/2;
|
||||
|
@ -277,7 +273,7 @@ void Add( const Point3<BoxScalarType> & 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());
|
||||
|
|
|
@ -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<float>::Import(const Color4<unsigned char> &b)
|
|||
(*this)[3]=b[3]/255.0f;
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
inline void Color4<double>::Import(const Color4<unsigned char> &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<unsigned char>::Import(const Color4<float> &b)
|
||||
{
|
||||
|
@ -297,6 +308,15 @@ inline void Color4<unsigned char>::Import(const Point4<float> &b)
|
|||
(*this)[3]=(unsigned char)(b[3]*255.0f);
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
inline void Color4<unsigned char>::Import(const Point4<double> &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<unsigned char> Color4<unsigned char>::Construct( const Color4<float> & b )
|
||||
{
|
||||
|
@ -317,6 +337,16 @@ inline Color4<float> Color4<float>::Construct( const Color4<unsigned char> & b )
|
|||
(float)(b[3])/255.0f);
|
||||
}
|
||||
|
||||
template <> template <>
|
||||
inline Color4<double> Color4<double>::Construct( const Color4<unsigned char> & b )
|
||||
{
|
||||
return Color4<double>(
|
||||
(double)(b[0])/255.0,
|
||||
(double)(b[1])/255.0,
|
||||
(double)(b[2])/255.0,
|
||||
(double)(b[3])/255.0);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Color4<unsigned char>::Color4(Color4<unsigned char>::ColorConstant cc)
|
||||
{
|
||||
|
@ -329,6 +359,12 @@ inline Color4<float>::Color4(Color4<float>::ColorConstant cc)
|
|||
Import(Color4<unsigned char>((Color4<unsigned char>::ColorConstant)cc));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Color4<double>::Color4(Color4<double>::ColorConstant cc)
|
||||
{
|
||||
Import(Color4<unsigned char>((Color4<unsigned char>::ColorConstant)cc));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Color4<unsigned char>::Color4(unsigned int cc)
|
||||
{
|
||||
|
@ -341,6 +377,12 @@ inline Color4<float>::Color4(unsigned int cc)
|
|||
Import(Color4<unsigned char>(cc));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Color4<double>::Color4(unsigned int cc)
|
||||
{
|
||||
Import(Color4<unsigned char>(cc));
|
||||
}
|
||||
|
||||
inline Color4<float> Clamp(Color4<float> &c)
|
||||
{
|
||||
c[0]=math::Clamp(c[0],0.0f,1.0f);
|
||||
|
@ -350,6 +392,15 @@ inline Color4<float> Clamp(Color4<float> &c)
|
|||
return c;
|
||||
}
|
||||
|
||||
inline Color4<double> Clamp(Color4<double> &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<unsigned char> Color4<unsigned char>::operator + ( const Color4<unsigned char> & p) const
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -35,481 +35,498 @@
|
|||
|
||||
namespace vcg {
|
||||
|
||||
template<typename _DataType>
|
||||
class ConstDataWrapper
|
||||
template<typename _DataType>
|
||||
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<const unsigned char*>(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<const unsigned char*>(pData)), mStride(stride), mSize(size)
|
||||
{}
|
||||
inline const DataType& operator[] (int i) const
|
||||
{
|
||||
return *reinterpret_cast<const DataType*>(mpData + i*mStride);
|
||||
}
|
||||
inline size_t size() const { return mSize; }
|
||||
protected:
|
||||
const unsigned char* mpData;
|
||||
int64_t mStride;
|
||||
size_t mSize;
|
||||
};
|
||||
return *reinterpret_cast<const DataType*>(mpData + i*mStride);
|
||||
}
|
||||
inline size_t size() const { return mSize; }
|
||||
protected:
|
||||
const unsigned char* mpData;
|
||||
int64_t mStride;
|
||||
size_t mSize;
|
||||
};
|
||||
|
||||
template<class StdVectorType>
|
||||
class VectorConstDataWrapper :public ConstDataWrapper<typename StdVectorType::value_type>
|
||||
template<class StdVectorType>
|
||||
class VectorConstDataWrapper :public ConstDataWrapper<typename StdVectorType::value_type>
|
||||
{
|
||||
public:
|
||||
inline VectorConstDataWrapper(StdVectorType &vec) :
|
||||
ConstDataWrapper<typename StdVectorType::value_type>(&(vec[0]), vec.size(), sizeof(typename StdVectorType::value_type))
|
||||
{}
|
||||
};
|
||||
|
||||
template<class MeshType>
|
||||
class VertexConstDataWrapper :public ConstDataWrapper<typename MeshType::CoordType>
|
||||
{
|
||||
public:
|
||||
inline VertexConstDataWrapper(MeshType &m) :
|
||||
ConstDataWrapper<typename MeshType::CoordType>(&(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<typename _Scalar>
|
||||
class KdTree
|
||||
{
|
||||
public:
|
||||
|
||||
typedef _Scalar Scalar;
|
||||
typedef vcg::Point3<Scalar> VectorType;
|
||||
typedef vcg::Box3<Scalar> AxisAlignedBoxType;
|
||||
|
||||
typedef HeapMaxPriorityQueue<int, Scalar> PriorityQueue;
|
||||
|
||||
struct Node
|
||||
{
|
||||
public:
|
||||
inline VectorConstDataWrapper(StdVectorType &vec):
|
||||
ConstDataWrapper<typename StdVectorType::value_type> ( &(vec[0]), vec.size(), sizeof(typename StdVectorType::value_type))
|
||||
{}
|
||||
};
|
||||
|
||||
template<class MeshType>
|
||||
class VertexConstDataWrapper :public ConstDataWrapper<typename MeshType::CoordType>
|
||||
{
|
||||
public:
|
||||
inline VertexConstDataWrapper(MeshType &m):
|
||||
ConstDataWrapper<typename MeshType::CoordType> ( &(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<typename _Scalar>
|
||||
class KdTree
|
||||
{
|
||||
public:
|
||||
|
||||
typedef _Scalar Scalar;
|
||||
typedef vcg::Point3<Scalar> VectorType;
|
||||
typedef vcg::Box3<Scalar> AxisAlignedBoxType;
|
||||
|
||||
typedef HeapMaxPriorityQueue<int, Scalar> 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<Node> NodeList;
|
||||
|
||||
// return the protected members which store the nodes and the points list
|
||||
inline const NodeList& _getNodes(void) { return mNodes; }
|
||||
inline const std::vector<VectorType>& _getPoints(void) { return mPoints; }
|
||||
|
||||
public:
|
||||
|
||||
KdTree(const ConstDataWrapper<VectorType>& 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<unsigned int>& points, std::vector<Scalar>& 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<Node> 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<VectorType>& _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<VectorType>& points, unsigned int nofPointsPerCell = 16, unsigned int maxDepth = 64, bool balanced = false);
|
||||
|
||||
AxisAlignedBoxType mAABB; //BoundingBox
|
||||
NodeList mNodes; //kd-tree nodes
|
||||
std::vector<VectorType> mPoints; //points read from the input DataWrapper
|
||||
std::vector<unsigned int> mIndices; //points indices
|
||||
~KdTree();
|
||||
|
||||
void doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue);
|
||||
|
||||
void doQueryDist(const VectorType& queryPoint, float dist, std::vector<unsigned int>& points, std::vector<Scalar>& 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<typename Scalar>
|
||||
KdTree<Scalar>::KdTree(const ConstDataWrapper<VectorType>& 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.size() ; ++i)
|
||||
{
|
||||
mPoints[i] = points[i];
|
||||
mIndices[i] = i;
|
||||
mAABB.Add(mPoints[i]);
|
||||
}
|
||||
// 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);
|
||||
|
||||
mNodes.reserve(4*mPoints.size()/nofPointsPerCell);
|
||||
int createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level);
|
||||
|
||||
protected:
|
||||
|
||||
AxisAlignedBoxType mAABB; //BoundingBox
|
||||
NodeList mNodes; //kd-tree nodes
|
||||
std::vector<VectorType> mPoints; //points read from the input DataWrapper
|
||||
std::vector<unsigned int> 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<typename Scalar>
|
||||
KdTree<Scalar>::KdTree(const ConstDataWrapper<VectorType>& 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<typename Scalar>
|
||||
KdTree<Scalar>::~KdTree()
|
||||
template<typename Scalar>
|
||||
KdTree<Scalar>::~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<typename Scalar>
|
||||
void KdTree<Scalar>::doQueryK(const VectorType& queryPoint, int k, PriorityQueue& mNeighborQueue)
|
||||
{
|
||||
mNeighborQueue.setMaxSize(k);
|
||||
mNeighborQueue.init();
|
||||
|
||||
std::vector<QueryNode> 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<typename Scalar>
|
||||
void KdTree<Scalar>::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<end ; ++i)
|
||||
mNeighborQueue.insert(mIndices[i], vcg::SquaredNorm(queryPoint - mPoints[i]));
|
||||
}
|
||||
//otherwise, if we're not on a leaf
|
||||
else
|
||||
{
|
||||
// the new offset is the distance between the searched point and the actual split coordinate
|
||||
float new_off = queryPoint[node.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;
|
||||
}
|
||||
//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 < end; ++i)
|
||||
mNeighborQueue.insert(mIndices[i], vcg::SquaredNorm(queryPoint - mPoints[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 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<typename Scalar>
|
||||
void KdTree<Scalar>::doQueryDist(const VectorType& queryPoint, float dist, std::vector<unsigned int>& points, std::vector<Scalar>& 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<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<typename Scalar>
|
||||
void KdTree<Scalar>::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<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<typename Scalar>
|
||||
unsigned int KdTree<Scalar>::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<typename Scalar>
|
||||
int KdTree<Scalar>::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<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;
|
||||
//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<typename Scalar>
|
||||
void KdTree<Scalar>::doQueryDist(const VectorType& queryPoint, float dist, std::vector<unsigned int>& points, std::vector<Scalar>& sqrareDists)
|
||||
{
|
||||
std::vector<QueryNode> 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<typename Scalar>
|
||||
void KdTree<Scalar>::doQueryClosest(const VectorType& queryPoint, unsigned int& index, Scalar& dist)
|
||||
{
|
||||
std::vector<QueryNode> 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<typename Scalar>
|
||||
unsigned int KdTree<Scalar>::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<typename Scalar>
|
||||
int KdTree<Scalar>::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<Scalar> 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
|
||||
|
|
|
@ -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 <vector>
|
||||
#include <vcg/space/distance3.h>
|
||||
|
||||
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 MeshType>
|
||||
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<FacePointer> list;
|
||||
};
|
||||
typedef std::vector<Node> NodeListPointer;
|
||||
|
||||
public:
|
||||
|
||||
KdTreeFace(MeshType& mesh, unsigned int maxObjPerCell = 64, unsigned int maxDepth = 64) : epsilon(std::numeric_limits<Scalar>::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 <class ObjectMarker> FacePointer doQueryClosest(const VectorType& queryPoint, VectorType& narestPoint, Scalar& dist, ObjectMarker& marker, Scalar maxDist = std::numeric_limits<Scalar>::max())
|
||||
{
|
||||
if (maxDist < std::numeric_limits<Scalar>::max() && !mNodes[0].aabb.IsIn(queryPoint) && vcg::PointFilledBoxDistance(queryPoint, mNodes[0].aabb) >= maxDist)
|
||||
{
|
||||
dist = maxDist;
|
||||
return NULL;
|
||||
}
|
||||
std::vector<QueryNode> 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
|
|
@ -240,6 +240,148 @@ static bool PackOccupancyMulti(const std::vector<Box2x > & 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<vcg::Point2i> & sizes, // the sizes of the rect to be packed
|
||||
const vcg::Point2i & max_size, // the size of the container
|
||||
std::vector<vcg::Point2i> & 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<int> 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<int> perm(n);
|
||||
for (i = 0; i<n; i++) perm[i] = i;
|
||||
ComparisonFunctor cmp(sizes);
|
||||
sort(perm.begin(), perm.end(), cmp);
|
||||
|
||||
if (sizes[perm[0]][0]>max_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<global_size[1]; y++)
|
||||
for (x = 0; x<global_size[0]; x++)
|
||||
{
|
||||
assert(x >= 0 && x<max_size[0]);
|
||||
assert(y >= 0 && y<max_size[1]);
|
||||
grid[x + y*max_size[0]] = j + 1;
|
||||
}
|
||||
|
||||
// Posiziono tutti gli altri
|
||||
for (i = 1; i<n; ++i)
|
||||
{
|
||||
j = perm[i];
|
||||
assert(j >= 0 && j<n);
|
||||
assert(posiz[j][0] == -1);
|
||||
|
||||
int bestx, besty, bestsx, bestsy, bestArea;
|
||||
|
||||
bestArea = -1;
|
||||
|
||||
int sx = sizes[j][0]; // Pe comodita' mi copio la dimensione
|
||||
int sy = sizes[j][1];
|
||||
assert(sx>0 && 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<x + sx; px++)
|
||||
{
|
||||
c = Grid(px, y);
|
||||
if (c) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c) // Salto il rettangolo
|
||||
{
|
||||
--c; // we store id+1...
|
||||
assert(c >= 0 && c<n);
|
||||
assert(posiz[c][0] != -1);
|
||||
x = posiz[c][0] + sizes[c][0];
|
||||
}
|
||||
else // x,y are an admissible position where we can put the rectangle
|
||||
{
|
||||
int nsx = std::max(global_size[0], x + sx);
|
||||
int nsy = std::max(global_size[1], y + sy);
|
||||
int area = nsx*nsy;
|
||||
|
||||
if (bestArea == -1 || bestArea>area)
|
||||
{
|
||||
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<posiz[j][1] + sy; y++)
|
||||
for (x = posiz[j][0]; x<posiz[j][0] + sx; x++)
|
||||
{
|
||||
assert(x >= 0 && x<max_size[0]);
|
||||
assert(y >= 0 && y<max_size[1]);
|
||||
grid[x + y*max_size[0]] = j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef Grid
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
@ -260,148 +402,6 @@ public:
|
|||
};
|
||||
|
||||
|
||||
/* 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<vcg::Point2i> & sizes, // the sizes of the rect to be packed
|
||||
const vcg::Point2i & max_size, // the size of the container
|
||||
std::vector<vcg::Point2i> & 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<int> 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<int> perm(n);
|
||||
for(i=0;i<n;i++) perm[i] = i;
|
||||
ComparisonFunctor cmp(sizes);
|
||||
sort(perm.begin(),perm.end(),cmp);
|
||||
|
||||
if(sizes[perm[0]][0]>max_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<global_size[1];y++)
|
||||
for(x=0;x<global_size[0];x++)
|
||||
{
|
||||
assert(x>=0 && x<max_size[0]);
|
||||
assert(y>=0 && y<max_size[1]);
|
||||
grid[x+y*max_size[0]] = j+1;
|
||||
}
|
||||
|
||||
// Posiziono tutti gli altri
|
||||
for(i=1;i<n;++i)
|
||||
{
|
||||
j = perm[i];
|
||||
assert(j>=0 && j<n);
|
||||
assert(posiz[j][0]==-1);
|
||||
|
||||
int bestx,besty,bestsx,bestsy,bestArea;
|
||||
|
||||
bestArea = -1;
|
||||
|
||||
int sx = sizes[j][0]; // Pe comodita' mi copio la dimensione
|
||||
int sy = sizes[j][1];
|
||||
assert(sx>0 && 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<x+sx;px++)
|
||||
{
|
||||
c = Grid(px,y);
|
||||
if(c) break;
|
||||
}
|
||||
}
|
||||
|
||||
if(c) // Salto il rettangolo
|
||||
{
|
||||
--c; // we store id+1...
|
||||
assert(c>=0 && c<n);
|
||||
assert(posiz[c][0]!=-1);
|
||||
x = posiz[c][0] + sizes[c][0];
|
||||
}
|
||||
else // x,y are an admissible position where we can put the rectangle
|
||||
{
|
||||
int nsx = std::max(global_size[0],x+sx);
|
||||
int nsy = std::max(global_size[1],y+sy);
|
||||
int area = nsx*nsy;
|
||||
|
||||
if(bestArea==-1 || bestArea>area)
|
||||
{
|
||||
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<posiz[j][1]+sy;y++)
|
||||
for(x=posiz[j][0];x<posiz[j][0]+sx;x++)
|
||||
{
|
||||
assert(x>=0 && x<max_size[0]);
|
||||
assert(y>=0 && y<max_size[1]);
|
||||
grid[x+y*max_size[0]] = j+1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef Grid
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Versione multitexture
|
||||
static bool PackIntMulti( const std::vector<Point2i> & sizes,
|
||||
const int ntexture,
|
||||
|
|
|
@ -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_NAMES_DERIVED_CLASS>& 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<ATT_NAMES_DERIVED_CLASS> unionSet(const RenderingAtts<ATT_NAMES_DERIVED_CLASS>& a,const RenderingAtts<ATT_NAMES_DERIVED_CLASS>& b)
|
||||
|
@ -288,6 +288,7 @@ namespace vcg
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
struct INT_ATT_NAMES : public ATT_NAMES
|
||||
{
|
||||
|
|
|
@ -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<GLuint>& textid = std::vector<GLuint>()) 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<size_t> vpatlas;
|
||||
std::vector<GLuint> 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<GLuint>& textid = std::vector<GLuint>()) 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<GLuint>& textureindex = std::vector<GLuint>()) 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<GLuint>& textureindex = std::vector<GLuint>()) 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(nz<pf->VN());
|
||||
|
||||
_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<InternalRendAtts> _meaningfulattsperprimitive;
|
||||
GLuint _tmpbuffer;
|
||||
GLuint _tmpind;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <typename T>
|
||||
class GL_TYPE_NM
|
||||
{public:
|
||||
static GLenum SCALAR() { assert(0); return 0;}
|
||||
};
|
||||
template <> class GL_TYPE_NM<float>
|
||||
{ public:
|
||||
typedef GLfloat ScalarType;
|
||||
static GLenum SCALAR() { return GL_FLOAT; }
|
||||
};
|
||||
template <> class GL_TYPE_NM<double>
|
||||
{public:
|
||||
typedef GLdouble ScalarType;
|
||||
static GLenum SCALAR() { return GL_DOUBLE; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "gl_type_name.h"
|
||||
|
||||
namespace vcg{
|
||||
|
||||
template <class MESH_TYPE>
|
||||
|
@ -153,7 +155,7 @@ public:
|
|||
|
||||
Box3<ScalarType> 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<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]);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,26 +34,10 @@
|
|||
#include <wrap/gl/space.h>
|
||||
#include <wrap/gl/math.h>
|
||||
#include <vcg/space/color4.h>
|
||||
#include <wrap/gl/gl_type_name.h>
|
||||
|
||||
|
||||
namespace vcg {
|
||||
|
||||
template <typename T>
|
||||
class GL_TYPE_NM
|
||||
{public:
|
||||
static GLenum SCALAR() { assert(0); return 0;}
|
||||
};
|
||||
template <> class GL_TYPE_NM<float>
|
||||
{ public:
|
||||
typedef GLfloat ScalarType;
|
||||
static GLenum SCALAR() { return GL_FLOAT; }
|
||||
};
|
||||
template <> class GL_TYPE_NM<double>
|
||||
{public:
|
||||
typedef GLdouble ScalarType;
|
||||
static GLenum SCALAR() { return GL_DOUBLE; }
|
||||
};
|
||||
|
||||
namespace vcg
|
||||
{
|
||||
//template <> GL_TYPE::SCALAR<double>() { return GL_DOUBLE; }
|
||||
|
||||
// classe base di glwrap usata solo per poter usare i vari drawmode, normalmode senza dover
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;k<fp->VN()*2;++k)
|
||||
for(int k=0;k<fp->VN();++k)
|
||||
fprintf(fpout,"%f %f "
|
||||
,fp->WT(k).u()
|
||||
,fp->WT(k).v()
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<numEdges; ++i)
|
||||
{
|
||||
ObjEdge & e = ev[i];
|
||||
EdgeType & edge = m.edge[i];
|
||||
|
||||
assert(e.v0 >= 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<indexedFaces[i].v.size();++j)
|
||||
{
|
||||
{
|
||||
int vertInd = indexedFaces[i].v[j];
|
||||
assert(vertInd >=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<Color4b> *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<length);
|
||||
} // end TokenizeNextLine
|
||||
|
||||
// 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(const std::string & token, int & vId, int & nId, int & tId, int mask)
|
||||
{
|
||||
static const char delimiter = '/';
|
||||
|
@ -813,150 +826,17 @@ namespace vcg {
|
|||
const bool hasNormal = (secondSep != std::string::npos) || (mask & Mask::IOM_WEDGNORMAL) || (mask & Mask::IOM_VERTNORMAL);
|
||||
|
||||
if (hasPosition) vId = atoi(token.substr(0, firstSep).c_str()) - 1;
|
||||
if (hasTexcoord) tId = atoi(token.substr(firstSep + 1, secondSep - firstSep - 1).c_str()) - 1;
|
||||
if (hasTexcoord) tId = atoi(token.substr(firstSep + 1, secondSep - firstSep - 1).c_str()) - 1;
|
||||
if (hasNormal)
|
||||
nId = atoi(token.substr(secondSep + 1).c_str()) - 1;
|
||||
// qDebug("%s -> %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<length && ((c = token[to]) !='/'))
|
||||
{
|
||||
vertex.push_back(c);
|
||||
++to;
|
||||
}
|
||||
++to;
|
||||
while (to<length && ((c = token[to]) !=' '))
|
||||
{
|
||||
texcoord.push_back(c);
|
||||
++to;
|
||||
}
|
||||
}
|
||||
} // end of SplitVVTToken
|
||||
|
||||
inline static void SplitVVNToken(std::string token, std::string &vertex, std::string &normal)
|
||||
{
|
||||
vertex.clear();
|
||||
normal.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!=length && ((c = token[to]) !='/'))
|
||||
{
|
||||
vertex.push_back(c);
|
||||
++to;
|
||||
}
|
||||
++to;
|
||||
++to; // should be the second '/'
|
||||
while (to!=length && ((c = token[to]) !=' '))
|
||||
{
|
||||
normal.push_back(c);
|
||||
++to;
|
||||
}
|
||||
}
|
||||
} // end of SplitVVNToken
|
||||
|
||||
inline static void SplitVVTVNToken(std::string token, std::string &vertex, std::string &texcoord, std::string &normal)
|
||||
{
|
||||
vertex.clear();
|
||||
texcoord.clear();
|
||||
normal.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!=length && ((c = token[to]) !='/'))
|
||||
{
|
||||
vertex.push_back(c);
|
||||
++to;
|
||||
}
|
||||
++to;
|
||||
while (to!=length && ((c = token[to]) !='/'))
|
||||
{
|
||||
texcoord.push_back(c);
|
||||
++to;
|
||||
}
|
||||
++to;
|
||||
while (to!=length && ((c = token[to]) !=' '))
|
||||
{
|
||||
normal.push_back(c);
|
||||
++to;
|
||||
}
|
||||
}
|
||||
} // end of SplitVVTVNToken
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Retrieves infos about kind of data stored into the file and fills a mask appropriately
|
||||
* \param filename The name of the file to open
|
||||
* \param mask A mask which will be filled according to type of data found in the object
|
||||
* \param oi A structure which will be filled with infos about the object to be opened
|
||||
* \param mask A mask which will be filled according to type of data found in the object
|
||||
* \param oi A structure which will be filled with infos about the object to be opened
|
||||
*/
|
||||
|
||||
static bool LoadMask(const char * filename, Info &oi)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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<n;++j)
|
||||
{
|
||||
if(pi.cb && (j%1000)==0) pi.cb(j*50/n,"Vertex Loading");
|
||||
va.a = 255;
|
||||
if( pf.Read( (void *)&(va) )==-1 )
|
||||
{
|
||||
pi.status = PlyInfo::E_SHORTFILE;
|
||||
|
@ -737,6 +741,7 @@ static int Open( OpenMeshType &m, const char * filename, PlyInfo &pi )
|
|||
int k;
|
||||
|
||||
if(pi.cb && (j%1000)==0) pi.cb(50+j*50/n,"Face Loading");
|
||||
fa.a = 255;
|
||||
if( pf.Read(&fa)==-1 )
|
||||
{
|
||||
pi.status = PlyInfo::E_SHORTFILE;
|
||||
|
@ -768,7 +773,7 @@ static int Open( OpenMeshType &m, const char * filename, PlyInfo &pi )
|
|||
(*fi).C()[0] = fa.r;
|
||||
(*fi).C()[1] = fa.g;
|
||||
(*fi).C()[2] = fa.b;
|
||||
(*fi).C()[3] = 255;
|
||||
(*fi).C()[3] = fa.a;
|
||||
}
|
||||
|
||||
if( pi.mask & Mask::IOM_WEDGTEXCOORD )
|
||||
|
@ -944,7 +949,7 @@ static int Open( OpenMeshType &m, const char * filename, PlyInfo &pi )
|
|||
}
|
||||
}
|
||||
//qDebug("Completed the reading of %i indexes",RangeGridAuxVec.size());
|
||||
tri::FaceGrid(m, RangeGridAuxVec, RangeGridCols,RangeGridRows);
|
||||
tri::SparseFaceGrid(m, RangeGridAuxVec, RangeGridCols,RangeGridRows);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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<ElementDescriptor*> meshDescr;
|
||||
meshDescr.push_back(&cameraDescr);
|
||||
meshDescr.push_back(&vertexDescr);
|
||||
|
|
|
@ -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<int, int, 1>(std::string("materialId"), nanoply::NNP_INT32, NULL);
|
||||
customAttrib.AddFaceAttribDescriptor<vcg::Point3f, float, 3>(std::string("barycenter"), nanoply::NNP_LIST_UINT8_FLOAT32, NULL);
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("kd"), nanoply::NNP_FLOAT32, mesh.material()[0].kd.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("ks"), nanoply::NNP_FLOAT32, mesh.material()[0].ks.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 1>(std::string("material"), std::string("rho"), nanoply::NNP_FLOAT32, &mesh.material()[0].rho);
|
||||
if (count > 0)
|
||||
{
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("kd"), nanoply::NNP_FLOAT32, mesh.material()[0].kd.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("ks"), nanoply::NNP_FLOAT32, mesh.material()[0].ks.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 1>(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<MyMesh>::IO_FACENORMAL;
|
||||
mask |= nanoply::NanoPlyWrapper<MyMesh>::IO_FACEATTRIB;
|
||||
mask |= nanoply::NanoPlyWrapper<MyMesh>::IO_MESHATTRIB;
|
||||
return (nanoply::NanoPlyWrapper<MyMesh>::LoadModel(filename, mesh, mask, customAttrib) != 0);
|
||||
return (nanoply::NanoPlyWrapper<MyMesh>::LoadModel(filename, mesh, mask, customAttrib) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -113,10 +116,13 @@ bool Save(const char* filename, MyMesh& mesh, bool binary)
|
|||
nanoply::NanoPlyWrapper<MyMesh>::CustomAttributeDescriptor customAttrib;
|
||||
customAttrib.AddVertexAttribDescriptor<int, int, 1>(std::string("materialId"), nanoply::NNP_INT32, &mesh.vertexMaterial[0]);
|
||||
customAttrib.AddFaceAttribDescriptor<vcg::Point3f, float, 3>(std::string("barycenter"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.faceBarycenter[0].V());
|
||||
customAttrib.AddMeshAttrib(std::string("material"), 2);
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("kd"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].kd.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("ks"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].ks.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 1>(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<Material, float, 3>(std::string("material"), std::string("kd"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].kd.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 3>(std::string("material"), std::string("ks"), nanoply::NNP_LIST_UINT8_FLOAT32, mesh.material()[0].ks.V());
|
||||
customAttrib.AddMeshAttribDescriptor<Material, float, 1>(std::string("material"), std::string("rho"), nanoply::NNP_FLOAT32, &mesh.material()[0].rho);
|
||||
}
|
||||
|
||||
//Save the ply file
|
||||
unsigned int mask = 0;
|
||||
|
|
|
@ -56,12 +56,14 @@ Cleaning of the automatic bbox caching support for ply files. First working vers
|
|||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <vcg/space/box3.h>
|
||||
#include <wrap/ply/plylib.h>
|
||||
using namespace vcg;
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace vcg
|
|||
void meshAttributesUpdated(bool hasmeshconnectivitychanged,const GLMeshAttributesInfo::RendAtts& changedrendatts)
|
||||
{
|
||||
QWriteLocker locker(&_lock);
|
||||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::meshAttributesUpdated(hasmeshconnectivitychanged,changedrendatts);
|
||||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::meshAttributesUpdated(hasmeshconnectivitychanged, changedrendatts);
|
||||
}
|
||||
|
||||
bool getPerViewInfo(UNIQUE_VIEW_ID_TYPE viewid,PerViewData<GL_OPTIONS_DERIVED_TYPE>& dt) const
|
||||
|
@ -62,6 +62,12 @@ namespace vcg
|
|||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::setPerViewInfo(viewid,dt);
|
||||
}
|
||||
|
||||
void setPerAllViewsInfo(const PerViewData<GL_OPTIONS_DERIVED_TYPE>& dt)
|
||||
{
|
||||
QWriteLocker locker(&_lock);
|
||||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE, UNIQUE_VIEW_ID_TYPE, GL_OPTIONS_DERIVED_TYPE>::setPerAllViewsInfo(dt);
|
||||
}
|
||||
|
||||
void removeView(UNIQUE_VIEW_ID_TYPE viewid)
|
||||
{
|
||||
QWriteLocker locker(&_lock);
|
||||
|
@ -74,6 +80,13 @@ namespace vcg
|
|||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::draw(viewid,_textids.textId());
|
||||
}
|
||||
|
||||
void drawAllocatedAttributesSubset(UNIQUE_VIEW_ID_TYPE viewid,const PerViewData<GL_OPTIONS_DERIVED_TYPE>& dt) const
|
||||
{
|
||||
QReadLocker locker(&_lock);
|
||||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::drawAllocatedAttributesSubset(viewid,dt,_textids.textId());
|
||||
}
|
||||
|
||||
|
||||
bool isBORenderingAvailable() const
|
||||
{
|
||||
QReadLocker locker(&_lock);
|
||||
|
@ -113,7 +126,7 @@ namespace vcg
|
|||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::setDebugMode(activatedebugmodality);
|
||||
}
|
||||
|
||||
void getLog(vcg::GLMeshAttributesInfo::DebugInfo& info)
|
||||
void getLog(GLMeshAttributesInfo::DebugInfo& info)
|
||||
{
|
||||
QWriteLocker locker(&_lock);
|
||||
vcg::NotThreadSafeGLMeshAttributesMultiViewerBOManager<MESH_TYPE,UNIQUE_VIEW_ID_TYPE,GL_OPTIONS_DERIVED_TYPE>::getLog(info);
|
||||
|
|
|
@ -30,6 +30,7 @@ template <class ShotType>
|
|||
shot.Extrinsics.SetRot(rot);
|
||||
|
||||
vcg::Camera<ScalarType> &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 <class ShotType>
|
|||
|
||||
const vcg::Camera<ScalarType> &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]);
|
||||
|
|
Loading…
Reference in New Issue