三维网格数据结构
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); }
相关文章推荐
- 约瑟夫环问题的几种解法
- SDUT 数据结构实验之排序六:希尔排序
- SPL常用数据结构
- 树状数组 BIT (Binary Indexed Tree)
- 南邮数据结构试验---各种内排序算法实现及比较
- -------第一讲----第一节-------------- 1 基本概念-----------------什么是数据结构--------------
- 数据结构实验之排序七:选课名单
- 数据结构实验之查找五:平方之哈希表
- SDUT 数据结构实验之排序二:交换排序
- 数据结构实验之查找七:线性之哈希表 151 268
- 数据结构实验之查找一:二叉排序树
- 数据结构实训——图书管理系统
- 数据结构实验之排序六:希尔排序
- 算法设计题2.41~2.42-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 算法设计题2.39~2.40-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 算法设计题2.38-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 算法设计题2.37-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 算法设计题2.34~2.36-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 算法设计题2.33-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- 算法设计题2.32-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版