如何定义网格类型
VCG库提供了几种编码网格的方式,但最常用的一种是顶点向量容器和三角形向量容器(如用于三角网格的三角形和用于四边形网格的四边形)。以下是一个VCG网格类型的定义:
class MyMesh:
public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> , std::vector<MyEdge> > {
};
其中,vcg::tri::TriMesh
是定义三角网格的基类,基于如下模板:包含顶点的容器类型和包含面片的STL随机访问容器类型。
换句话说,网格的定义只需要从vcg::tri::TriMesh
中派生并提供个人编码网格的元素容器。下面的示例中我们还传递了一个std::vector<MyEdge>
,很快会看到,其代表的是边。
#include<vcg/complex/complex.h>
class MyVertex; class MyEdge; class MyFace;
struct MyUsedTypes : public vcg::UsedTypes<vcg::Use<MyVertex>::AsVertexType,
vcg::Use<MyEdge> ::AsEdgeType,
vcg::Use<MyFace> ::AsFaceType>{};
class MyVertex : public vcg::Vertex<MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::Normal3f, vcg::vertex::BitFlags >{};
class MyFace : public vcg::Face<MyUsedTypes, vcg::face::FFAdj, vcg::face::VertexRef, vcg::face::BitFlags > {};
class MyEdge : public vcg::Edge<MyUsedTypes> {};
class MyMesh : public vcg::tri::TriMesh<std::vector<MyVertex>, std::vector<MyFace> , std::vector<MyEdge> > {};
面片、边和顶点是理解VCG库的关键元素。一个顶点、一条边、一个面片以及一个四面体仅仅是用户定义的属性集合。例如,你可能希望MyVertex
包含顶点的坐标,那么在该顶点处的表面法线、颜色等要如何处理?VCG库给您提供了一种方式,使得您可以在每个顶点、边或者面片中存储想要的属性。例如,以下定义了三种顶点类型,复杂度逐渐增加:
class MyVertex0 : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::BitFlags >{};
class MyVertex1 : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::Normal3f, vcg::vertex::BitFlags >{};
class MyVertex2 : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::Color4b, vcg::vertex::CurvatureDirf,
vcg::vertex::Qualityf, vcg::vertex::Normal3f, vcg::vertex::BitFlags >{};
vcg::Vertex
是VCG中顶点的基类。vcg::UsedTypes
则声明了网格定义中涉及的类型。实际上,它是一个用户自定义实体类型的名称以及这些实体类型在网格定义中所起作用之间的映射。该映射是使用带语法的模板参数实现的。它看起来很繁琐,但对于使用来说非常方便。正如你所看到的,上述三种顶点定义的模板参数(或者成分)不同。这些参数指定了顶点类型中存储的值。
为了方便,VCG库中实现了许多参数,完整的参数列表可以在Vertex Components,Edge Components以及Face Components包中找到。你可以任意组合这些参数作为自定义顶点/边/面片类型的模板参数(顺序不重要)。现在,我们能够有效地定义MyMesh
类型:
#include<vcg/complex/complex.h>
class MyVertex; class MyEdge; class MyFace;
struct MyUsedTypes : public vcg::UsedTypes<vcg::Use<MyVertex> ::AsVertexType,
vcg::Use<MyEdge> ::AsEdgeType,
vcg::Use<MyFace> ::AsFaceType>{};
class MyVertex : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::Normal3f, vcg::vertex::BitFlags >{};
class MyFace : public vcg::Face< MyUsedTypes, vcg::face::FFAdj, vcg::face::VertexRef, vcg::face::BitFlags > {};
class MyEdge : public vcg::Edge< MyUsedTypes> {};
class MyMesh : public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> , std::vector<MyEdge> > {};
int main( int argc, char **argv )
{
if(argc<2)
{
printf("Usage trimesh_base <meshfilename.obj>\n");
return -1;
}
MyMesh m;
if(vcg::tri::io::ImporterOFF<MyMesh>::Open(m,argv[1])!=vcg::tri::io::ImporterOFF<MyMesh>::NoError)
{
printf("Error reading file %s\n",argv[1]);
exit(0);
}
vcg::tri::RequirePerVertexNormal(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalized(m);
printf("Input mesh vn:%i fn:%i\n",m.VN(),m.FN());
printf( "Mesh has %i vert and %i faces\n", m.VN(), m.FN() );
return 0;
}
注:vcg::face::VertexRef
是一个属性,该属性存储三个指向顶点类型的指针,因此实现了可索引的数据结构。这是一个为什么MyFace
类型需要知道MyVertex
类型的示例。
如何创建网格
当声明好网格类型后,你可能想要实例化一个对象并且使用顶点和三角形去填充该对象。经典的做法就是打开一些诸如上述代码中的文件。也许你能想到对顶点和面片容器进行push_back
操作。实际上,这是错误的方法,因为将元素添加到容器中可能会产生副作用。我们在Allocating and Deleting mesh elements页面中讨论这个问题并给出正确的添加网格元素的方法。
网格元素的flags
通常,对于网格的一个元素来说,我们为其分配一个小的bit向量容器,该容器包含顶点和面相关的位信息。例如,顶点的删除仅仅在该向量中标记为一个删除位。更多有关flags的细节请查看Bit Flags页面。
如何处理网格
在网格上执行某些操作的算法通常被编写为类的静态成员函数。 例如,下面插入的代码是UpdateNormals类的一部分,该类包含几种计算法线值的算法。
...
template <class ComputeMeshType>
class UpdateNormals{
...
// Calculates the face normal (if stored in the current face type)
static void PerFace(ComputeMeshType &m)
// Calculates the vertex normal. Without exploiting or touching face normals
// The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
static void PerVertex(ComputeMeshType &m)
// Calculates both vertex and face normals.
// The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
static void PerVertexPerFace(ComputeMeshType &m)
...
};
该类是名为UpdateValue类的核心部分,该类可计算顶点或面片属性的值,可以在文件夹vcg / complex / trimesh / update中找到。 例如,以下示例显示如何计算法线以及每个顶点的均值和高斯曲率值:
#include <vector>
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/vertex/component.h>
#include <vcg/simplex/face/base.h>
#include <vcg/simplex/face/component.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/update/normals.h> //class UpdateNormals
#include <vcg/complex/trimesh/update/curvature.h> //class UpdateCurvature
class MyVertex;
class MyFace;
class MyUsedTypes: public vcg::UsedTypes< vcg::Use<MyVertex>::AsVertexType>,
vcg::Use<MyFace>::AsFaceType>
class MyVertex: public vcg::Vertex<MyUsedTypes, vcg::vertex::BitFlags,vcg::vertex::Coord3d, vcg::vertex::Normal3f,vcg::vertex::Curvaturef>{};
class MyFace: public vcg::Face<MyUsedTypes, vcg::face::BitFlags,vcg::face::VertexRef>{};
class MyMesh: public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> > {};
int main()
{
MyMesh m;
// fill the mesh
...
// compute the normal per-vertex -> update the value of v.N() for all v (vcg::vertex::Normal3f)
vcg::tri::UpdateNormals<MyMesh>::PerVertexPerFace(m);
// compute the curvature per-vertex -> update the value of v.H() and v.K() (vcg::vertex::Curvaturef)
vcg::tri::UpdateCurvature<MyMesh>::MeanAndGaussian(m);
return 0;
}
除了更新网格属性值的算法之外,VCG库还提供了从另一个源创建网格的算法,例如从点集(通过Ball Pivoting方法)或从体数据集的等值面(通过Marching Cubes 算法)。这些算法可以在vcg / complex / trimesh / create /中找到。 最后,您可以在/ vcg / complex / trimesh下找到用于细分(中点,循环,蝶形…),平滑,封闭孔和其他当前未归类在任何特定标题下的算法。