VCG库之分配和删除网格元素
文章目录
- vcg::tri::Allocator
- 元素分配
- 元素销毁
- 复制网格
vcg::tri::Allocator
由VCG管理的模型中,顶点和面片元素通常是以vector形式保存的。vector这种C++序列容器在添加和删除元素时很容易带来迭代器失效的问题。因此,VCG提供了专门的类模板vcg::tri::Allocator来管理网格元素的添加或者删除。使用该类模板可以为一个网格安全的添加或者删除元素。
元素分配
VCG的元素分配函数是由vcg::tri::Allocator类模板提供的,它们都是静态成员函数。
AddVertex:为模型添加单个顶点(至少要提供顶点坐标,但也可以同时包含法线或者颜色),返回指向该顶点的迭代器。AddVertices:为模型添加多个顶点(至少要提供新增顶点的数目),返回指向第一个添加的顶点的迭代器。该函数进行了容器扩容,虽然为新顶点分配了内存,但并没有确定诸如坐标、法线等顶点属性。AddFace:为模型添加一个面片(需要提供三个顶点的指针、索引或者坐标),返回指向该面片的迭代器。AddFaces:为模型添加多个面片(至少要提供新增面片的数目),返回指向第一个添加的面片的迭代器。该函数进行了容器扩容,虽然为新面片分配了内存,但并没有确定诸如顶点、法线等面片属性。
class MyMesh : public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> > {};int main()
{MyMesh m;MyMesh::VertexIterator vi = vcg::tri::Allocator<MyMesh>::AddVertices(m,3); //添加3个顶点MyMesh::FaceIterator fi = vcg::tri::Allocator<MyMesh>::AddFaces(m,1); //对应添加一个三角面片MyMesh::VertexPointer ivp[4]; //顶点指针数组ivp[0]=&*vi; vi->P()=MyMesh::CoordType ( 0.0, 0.0, 0.0); ++vi;ivp[1]=&*vi; vi->P()=MyMesh::CoordType ( 1.0, 0.0, 0.0); ++vi;ivp[2]=&*vi; vi->P()=MyMesh::CoordType ( 0.0, 1.0, 0.0); ++vi;fi->V(0)=ivp[0];fi->V(1)=ivp[1];fi->V(2)=ivp[2];ivp[3]= &*vcg::tri::Allocator<MyMesh>::AddVertex(m,MyMesh::CoordType ( 1.0, 1.0, 0.0)); //添加单个顶点vcg::tri::Allocator<MyMesh>::AddFace(m, ivp[1],ivp[2],ivp[3]); //添加单个面片(使用顶点指针指明面片对应的顶点)
}
上述代码的意思是:首先定义一个网格对象m,并向其中添加三个顶点和一个对应的面片,顶点的坐标分别为(0,0,0),(1,0,0),(0,1,0),构成一个三角形面片。然后再向m中添加一个顶点,该顶点与(1,0,0)和(0,0,0)构成第二个三角形面片。此时,m实际对应一个四边形。platnoic.h中有更多例子。
然而,上述代码中,vcg::tri::Allocator这句会报错。这是因为它是用了顶点指针ivp[1]和ivp[2],但其上一句代码是添加元素,这会导致这些指针失效。这种情况下,应该传递给vcg::tri::Allocator函数一个PointerUpdater来更新指针。
vcg::tri::Allocator<MyMesh>::PointerUpdater<MyMesh::VertexPointer> pu;ivp[3] = &*vcg::tri::Allocator<MyMesh>::AddVertices(m, 1, pu); //AddVertex本身没有传递`PointerUpdater`的能力,但它内部使用的是AddVertices,故此处使用AddVertices代替AddVertexivp[3]->P() = Point3f(1.0, 1.0, 0.0);if (pu.NeedUpdate()) {pu.Update(ivp[1]);pu.Update(ivp[2]);}vcg::tri::Allocator<MyMesh>::AddFace(m, ivp[1], ivp[3], ivp[2]);
元素销毁
VCG的元素销毁函数是由vcg::tri::Allocator类模板提供的,它们都是静态成员函数。
DeleteVertex:删除某个指定顶点,会更新模型的vn成员变量。DeleteFace:删除某个指定面片,会更新模型的fn成员变量。
VCG库采取的是“惰性删除策略”,即向量容器中那些被删除的元素仅仅被标记为“被删除”(通过BitFlags进行标记),实际上仍然存在。
vcg::tri::Allocator<MyMesh>::DeleteVertex(m,m.vert[0]);
上述代码删除了m中的第一个顶点,实际上是将其标记为“被删除”(m.vert[0].isD()==true)。此时,m.FN()为3,但m.vert.size()依旧为4,说明元素仍然存在于容器中。
由于可能会遇到已经删除的元素,因此当遍历顶点和面片容器时,应注意使用!IsD()检查:
MyMesh::CoordType b(0,0,0); for(fi = m.face.begin(); fi!=m.face.end(); ++fi ){if(!fi->IsD()){b += vcg::Barycenter(*fi); //将有效面片的重心之和保存在变量b中}}
在某些情况下,尤其是必须多次遍历而不删除/创建任何内容时,显式调用两个垃圾回收函数来避免已删除元素的影响是一个方便的选择:
vcg::tri::Allocator<MyMesh>::CompactFaceVector(m);
vcg::tri::Allocator<MyMesh>::CompactVertexVector(m);
在调用了这两个函数之后,可以不用检查每个元素的IsD()状态,并安全的使用。
注意:如果网格中没有被删除元素,则compactor函数会立即返回(它仅仅检查容器大小是否与元素数量相匹配),因此可以在冗长的操作之前安全地使用它。
复制网格
考虑到网格本身的复杂性,禁止任何将网格复制给简单对象的企图。如果要复制一个网格,则必须使用Append实用类:
MyMesh m2;
vcg::tri::Append<MyMesh,MyMesh>::MeshCopy(m2,m);
上述代码的意思是:将网格m整个复制给m2。在vcg::tri::Append实用类中,也拥有复制网格m的子集(类似于vector容器的区间复制)或者将网格m附加到m2中的函数。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
