三维网格数据结构

{
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);
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值