您的位置:首页 > 理论基础 > 数据结构算法

三维网格数据结构

2015-12-27 15:23 447 查看
{
public :
int P0Index;
int P1Index;
int P2Index;
Triangle(int p0index, int p1index, int p2index)
   {
      this->P0Index=p0index;
      this->P1Index=p1index;
      this->P2Index=p2index;
}
};


  注意,这个索引定义方式只有针对Mesh中的三角片才有意义,因为这个索引表示这个三角形的顶点在Mesh的点集数组中的位置。笔者比较推荐这种方式,之后文章的Mesh的三角形都是这样定义,原因下文有提到。

  综上Mesh类可以有类似下面两种定义,一种使用指针,一种使用索引:

class Mesh
{
public:
std::vector<Point3d*> Vertices;
std::vector<Triangle*> Faces;
Mesh();
~Mesh();
void AddVertex(Point3d* toAdd);
void AddFace(Triangle* tri);
};


class Mesh
{
public:
std::vector<Point3d> Vertices;
std::vector<Triangle> Faces;
Mesh();
~Mesh();
void AddVertex(Point3d& toAdd);
void AddFace(Triangle& tri);
};


这样最为简单的Mesh类型就定义完成。

  不难发现采用点地址实际上比采用索引要占更多的空间。因为在对于一个有效的Point3d地址来说,它必然位于堆上,而不能使用栈上的地址或者vector中的地址(使用vector中的地址必须保证vector不因为添加新点而自动执行内部扩容操作,实际应用中这点一般不能保证),因而基于点地址的Triangle结构实际上多存了指针的空间。而若采用索引,则占用的最小空间等于点集的空间加索引的空间,所以一般推荐采用索引表示的三角形来构成Mesh中的三角形集合。

  Mesh除了点集和三角形集合外,根据具体的应用还可以有很多附加的信息。比如三角形结构可以增加成员变量存储法向量。点结构也可以增加成员存放点法向量,当然这些结构也可以不存在相应的三角形和点中,可以放在Mesh中以数组形式来存,位置和三角形集合和点集合分别一一对应。

网格文件格式

  一个Mesh网格就是一个物体的模型,上文介绍了Mesh在内存中的形式。实际应用中,当一个物体的Mesh被计算出来后,往往需要把这个Mesh写成文件永久保存,这就涉及到了三维模型文件格式的问题。在业界,三维模型文件的格式有多种多样,同时也有不少的开源3d显示软件支持打开和保存这些文件。比较著名的一个三维模型编辑开源软件是VCG实验室的MeshLab。下图是这个软件的主界面的样子。





  MeshLab支持import和export 各种网格文件,可以从他支持的文件列表中看出三维网格文件的各种格式,比较常见的有stl、obj、ply、3ds、off等。每一种格式都有自己的特点。这里简单的讲讲本文这样定义的Mesh使用ply格式如何读写。无论什么文件格式,无非是使用一个表达方式把建立一个Mesh所必须的信息记录下来。PLY格式文件除了记录Mesh的点和三角形之外,还能支持很多其他方式表示的Mesh,比如使用点集和边集表示的Mesh。不过这些复杂深入的知识不在本文介绍,详细的PLY格式能以怎样的方式记录怎样的信息可以在维基百科上获得。

  下图是本文定义的Mesh写成ASCII文本形式的PLY文件的大概形式:

  


  将本文索引方式定义的Mesh,使用C++语言读写.ply文件的函数如下:

void ReadFile(Mesh& mesh,const char* fileName)
{
int vcount=0;
int fcount=0;
FILE * nfile = fopen(fileName,"r");
fscanf(nfile,"ply\nformat ascii 1.0\ncomment VCGLIB generated\nelement vertex %d\n",&vcount);
fscanf(nfile,"property float x\nproperty float y\nproperty float z\nproperty uchar red\nproperty uchar green\nproperty uchar blue\nelement face %d\n",&fcount);
fscanf(nfile,"property list int int vertex_indices\nend_header\n");
float v1=0,v2=0,v3=0;
int r=0,g=0,b=0;
int i1=0,i2=0,i3=0;
for(int i=0;i<vcount;i++)
{
fscanf(nfile,"%f %f %f %d %d %d\n",&v1,&v2,&v3,&r,&g,&b);
Point3d p3d(v1,v2,v3);
mesh.AddVertex(p3d);
}
for(int j=0;j<fcount;j++)
{
fscanf(nfile,"3 %d %d %d\n",&i1,&i2,&i3);
Triangle t(i1,i2,i3);
mesh.AddFace(t);
}
fclose(nfile);
}


void Output(Mesh& mesh,const char* filename)
{
FILE * nfile = fopen(filename,"wb");
fprintf(nfile,"ply\n");
fprintf(nfile,"format ascii 1.0\n");
fprintf(nfile,"comment VCGLIB generated\n");
fprintf(nfile,"element vertex %d\n",mesh.Vertices.size());
fprintf(nfile,"property float x\n");
fprintf(nfile,"property float y\n");
fprintf(nfile,"property float z\n");
fprintf(nfile,"property uchar red\n");
fprintf(nfile,"property uchar green\n");
fprintf(nfile,"property uchar blue\n");
fprintf(nfile,"element face %d\n",mesh.Faces.size());
fprintf(nfile,"property list int int vertex_indices\n");
fprintf(nfile,"end_header\n");
for(size_t i=0;i<mesh.Vertices.size();i++)
{
   fprintf(nfile,"%.2f %.2f %.2f %d %d %d\n",mesh.Vertices[i].Data[0],mesh.Vertices[i].Data[1],mesh.Vertices[i].Data[2],255,255,255);
}
for(size_t i=0;i<mesh.Faces.size();i++)
{
    fprintf(nfile,"%d %d %d %d\n",3,mesh.Faces[i].P0Index,mesh.Faces[i].P1Index,mesh.Faces[i].P2Index);
}
fclose(nfile);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: