如何定义曲面网格类型
vcg 库可以用多种方式表达一个曲面网格, 通常的方式是一个顶点集合和一个三角形集合(例如, 由三角形组成的三角网格). 下面一行代码是定义曲面类型的例子:
class MyMesh : public vcg::tri::TriMesh<std::vector<MyVertex>, std::vector<MyFace>,std::vector<MyEdge>> {};
这里 vcg::TriMesh
是模板化的三角网格的基础类型:
- 包含顶点的容器, 通常使用
std::vector
- 包含随机访问面对容器
也就是说, 为了定义曲面网格, 只需继承 vcg::TriMesh
并提供你想要表达网格的容器元素的类型. 在第二个例子中, 我们仍然定义了边的容器类型. 注意, vcg::TriMesh
的模板类型参数是没有顺序之分的.
#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
包含顶点坐标 (x,y,z)
, 但是对于顶点对应的法向量又该如何呢? … 还有颜色? VCG 提供了一种漂亮的方式去定义任何你想要每一个面,每一个顶点,或者每一条边对应的属性. 下面的例子 展示了MyVertex
三种复杂度递增的定义.
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
声明了曲面定义中包含的类型. 这是一个你的实体类型(MyVertex, MyFace,..., 它们在曲面定义中扮演的类型
)之间的映射. 这个映射使用建立模板参数传递通过语法 当你看到可能会觉得令人讨厌但是…. . 注意看MyVertex0,1,2
定义之间模板参数的区别. 这些参数
指定了顶点值的类型.
MyVertex0
使用三个double类型存储顶点坐标, 三个 float 类型存储法向量.MyVertex1
通过4个byte存储颜色MyVertex2
储存了很多个不同的组件
许多其他的组件在库中的实现的单一的, 组件列表可以在 顶点组件, 边组件, 面组件. 你可以在你的类型定义中任意组合它们. 下面看一段完整的例子:
#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
到顶点或者面的容器里. 实际上, 这样添加元素的方式是不好的. 我们解释这个问题并给在开辟和删除曲面元素出添加元素的正确方式.
曲面元素的标记
通常对于曲面的每一个元素,我们都指定一个仅包含单个byte
的 vector<>
来储存一些标记信息. 例如, 删除一个顶点, 在它的 标记属性 vector 里标记为 Deletion
. (详细讨论参考开辟和删除曲面元素). 不同类型的标记位参见Bit Fags.
如何处理曲面
处理网格的算法通常是写成静态模板成员函数, 用 mesh type
作为模版参数. 下面的例子的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)
...
};
这个类的完整代码可以在 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 还提供了从其他源创建曲面的算法, 例如从点云数据或者从体数据. 这些算法可以在vcg/complex/trimesh/create
文件夹下找到. 最后, 在vcg/complex/trimesh
下, 还可以找到更多的算法, 包括 光滑, 孔洞填充等等.