使用标准属性
本例主要说明以下问题:
1.如何添加和移除标准属性
2.如何获取和设置标准属性值
前面我们已经知道,我们可以绑定额外的数据实体到mesh上,作为mesh的属性。OpenMesh提供一系列所谓标准属性。与自定义属性不同,这些属性拥有特殊的特性和不同的接口,这正是本文关注的事情。
下表列出了可用的标准属性及其适用对象(节点、面、边、半边等)。
|
Vertex |
Face |
Edge |
Halfedge |
Color |
X |
X |
X |
|
Normal |
X |
X |
|
X |
Position (*) |
X |
|
|
|
Status |
X |
X |
X |
X |
TexCoord |
X |
|
|
X |
给实体添加标准属性,可简单地通过查询方法,例如:request_face_normals()。唯一的例外是位置属性,它不能被添加,因为这个属性应当是始终存在的,根本不能被移除。
本例中,我们:
1.为mesh对象添加节点法线属性
2.加载文件
3.检查文件数据是否提供了节点法线数据,如果未提供则计算法线。
4.将每个节点沿着法线方向移动一个单位长度
5.通过std::cout方法将最终位置输出
现在,我们开始为mesh添加节点法线属性。
mesh.request_vertex_normals();
用类似的方法,我们可以查询其他标准属性,如面法线属性:
mesh.request_face_normals();
if (!mesh.has_vertex_normals())
{
std::cerr << "ERROR:Standard vertex property 'Normals' not available!\n";
return 1;
}
如果文件数据中未包含法线数据,我们可以利用update_normals()函数计算。
但是,即使文件提供了法线数据,我们也可以利用标准属性做更多的事情,比如,修改它,也可以在用完后移除它。
mesh.release_vertex_normals();
但是,上面的代码运行后,发生了什么呢?当再次查询法线属性后就会发现,其实什么都没做,法线属性依然存在。但第二次移除后就会真的移除掉。标准属性拥有一个引用计数器,用于保存法线被引用的次数,每次引用它,都会被递增,而移除时则会被递减。如果引用计数器递减为0,则属性会被真正从内存中移除。
从上表看到,有9个动态可查询属性(9个?可能不包括status?)。查询函数定义在OpenMesh::Concepts::KernelT中,包括:
· request_edge_status()
· request_edge_colors()
· request_face_colors()
· request_face_normals()
· request_face_status()
· request_face_texture_index()
· request_halfedge_status()
· request_halfedge_normals()
· request_halfedge_texcoords1D()
· request_halfedge_texcoords2D()
· request_halfedge_texcoords3D()
· request_vertex_colors()
· request_vertex_normals()
· request_vertex_status()
· request_vertex_texcoords1D()
· request_vertex_texcoords2D()
· request_vertex_texcoords3D()
已添加的属性可通过下列函数移除:
· release_edge_status()
· release_edge_colors()
· release_face_colors()
· release_face_normals()
· release_face_status()
· release_face_texture_index()
· release_halfedge_status()
· release_halfedge_normals()
· release_halfedge_texcoords1D()
· release_halfedge_texcoords2D()
· release_halfedge_texcoords3D()
· release_vertex_colors()
· release_vertex_normals()
· release_vertex_status()
· release_vertex_texcoords1D()
· release_vertex_texcoords2D()
· release_vertex_texcoords3D()
属性的存在性可通过下列函数测试:
(译者注:区别于request,这是测试是否有数据,request是查询是否有属性)
· has_edge_status()
· has_edge_colors()
· has_face_colors()
· has_face_normals()
· has_face_status()
· has_face_texture_index()
· has_halfedge_status()
· has_halfedge_normals()
· has_halfedge_texcoords1D()
· has_halfedge_texcoords2D()
· has_halfedge_texcoords3D()
· has_vertex_colors()
· has_vertex_normals()
· has_vertex_status()
· has_vertex_texcoords1D()
· has_vertex_texcoords2D()
· has_vertex_texcoords3D()
当属性查询(译者注:request_)可用时,上述函数(译者注:has_)返回true表示属性数据存在。
状态(status)属性用于标记几何元素状态,如:选定、删除等,参考: Deletinggeometry elements 获取更多信息。
现在,我们知道了如何添加和移除标准属性,但是如何访问他们的数据呢?与自定义属性通过成员函数property()访问接口不同,每一个标准属性提供了get和set函数。前面的例子中,我们已经使用针对节点位置属性的get/set函数对,读取了节点位置数据并计算更新了它。这里,我们要将所有的节点沿着其法线方向移动一个单位长度。
for (MyMesh::VertexIter v_it =mesh.vertices_begin();
v_it != mesh.vertices_end(); ++v_it)
{
mesh.set_point( *v_it,mesh.point(*v_it)+mesh.normal(*v_it) );
}
get函数需要实体句柄作为参数,返回该实体的这个属性值,set函数需要额外的参数,用于传递新属性值给指定的实体。按照表中给出的,并非所有实体都具有get/set函数对,例如,表面实体有法线属性,但是没有纹理坐标属性,所以,调用mesh.textcoord2D(_face_handle),将在编译时产生错误。
我们已经了解了如何添加和移除标准属性,但还有一个问题:属性中保存的数据是什么类型?它还有什么其他的隐藏的秘密?下一节(Using meshattributes and traits),我们给出问题答案。
完整代码如下:
#include
// --------------------
#include
#include
typedef OpenMesh::TriMesh_ArrayKernelT<>MyMesh;
int main(int argc, char **argv)
{
MyMesh mesh;
if (argc!=2)
{
std::cerr << "Usage:"<< argv[0] << " \n";
return 1;
}
// request vertex normals, so the meshreader can use normal information
// if available
mesh.request_vertex_normals();
// assure we have vertex normals
if (!mesh.has_vertex_normals())
{
std::cerr << "ERROR:Standard vertex property 'Normals' not available!\n";
return 1;
}
OpenMesh::IO::Optionsopt;
if ( ! OpenMesh::IO::read_mesh(mesh,argv[1],opt))
{
std::cerr << "Errorloading mesh from file " << argv[1] << std::endl;
return 1;
}
// If the file did not provide vertexnormals, then calculate them
if ( !opt.check( OpenMesh::IO::Options::VertexNormal) )
{
// we need face normals to update thevertex normals
mesh.request_face_normals();
// let the mesh update the normals
mesh.update_normals();
// dispose the face normals, as we don'tneed them anymore
mesh.release_face_normals();
}
// move all vertices one unit lengthalong it's normal direction
for (MyMesh::VertexIter v_it =mesh.vertices_begin();
v_it != mesh.vertices_end(); ++v_it)
{
std::cout << "Vertex#"<< *v_it << ": " << mesh.point( *v_it );
mesh.set_point( *v_it,mesh.point(*v_it)+mesh.normal(*v_it) );
std::cout << " moved to"<< mesh.point( *v_it ) << std::endl;
}
// don't need the normals anymore? Removethem!
mesh.release_vertex_normals();
// just check if it really works
if (mesh.has_vertex_normals())
{
std::cerr << "Ouch!ERROR! Shouldn't have any vertex normals anymore!\n";
return 1;
}
return 0;
}