Mesh类
Mesh类提供了一种加载,操纵和保存mesh(网格)的接口。
通过使用该类,您就能创建mesh,然后为其添加geometry(几何体)(比如添加box(盒体)型,plane(平面)型,capsule(胶囊)型或cylinder(缸体)型的表面),为其添加动画和骨骼(前提是您想要创建骨骼蒙皮),再之后使用其创建如下对象:
Mesh(网格)的特性列举如下:
- mesh的每个表面支持多个变形目标(Morph Targets)。
- 为获得更好的加载速度,每个表面都拥有基于四元数的相切基础。
- 支持8位顶点颜色(colors)。
- 表面的每个顶点都拥有2组索引:坐标索引和三角形索引。 这样不仅提高了加载速度,还减少了数据的重复。
- 表面的每个顶点都拥有2个UV集。
- 每个mesh的绑定姿势(bind pose)变换都会被存储(即便没有动画(animations)和动画帧(animation frames)也会如此)。 另请参阅:getBoneTransfroms()函数。
第1个动画帧不应包含绑定姿势。
顶点的索引
每个顶点都存储了包括自身位置,法线,副法线,切线和纹理坐标在内的信息。 这里存在2种独立的顶点缓冲区:
- CVertex型坐标缓冲区,该缓冲区只存储顶点坐标。
- TVertex型三角形缓冲区,该缓冲区存储了像法线,副法线,切线,颜色,UV坐标这样的顶点属性(attributes)。
在下图中,箭头用来表示法向(normals):
坐标缓冲区(coordinate buffer)就是坐标顶点的数组。 为了减少数据的重复,拥有相同坐标的表面的顶点只在缓冲区中被保存一次。 例如,上图中给出的表面,其坐标缓冲区为:
CB = [C0,C1,C2,C3]
三角形缓冲区(triangle buffer)就是三角形顶点的数组。 例如,上图中给出的表面,其三角形缓冲区为:
TB = [T0,T1,T2,T3,T4,T5]
- 法线(Normal)
- 副法线(Binormal)
- 切线(Tangent)
- 第1UV贴图纹理坐标(1st UV map texture coordinates)
- 第2UV贴图纹理坐标(2nd UV map texture coordinates)
- 颜色(Color)
坐标顶点和三角形顶点都拥有索引。 这里存在2种索引缓冲区,可用于获取mesh的每个三角形本身的cvertex型顶点数据和tvertex型顶点数据:
- CIndex型缓冲区 — 指坐标索引,也就是链接到cvertex型顶点的链接。
- TIndex型缓冲区 — 指三角形索引,也就是链接到tvertex型顶点的链接。
坐标索引
每个坐标顶点都拥有坐标索引。 坐标索引的数量等于3*三角形的数量。 对给定表面而言,坐标索引的数组为:
CIndices = [Ci0,Ci1,Ci3,Ci1,Ci2,Ci3]
- 前3个元素是第1个(底)三角形的坐标索引。
- 后3个元素是第2个(顶)三角形的坐标索引。
三角形索引
每个三角形顶点都拥有三角形索引。 三角形索引的数量等于3*三角形的数量。 对给定表面而言,三角形索引的数组为:
TIndices = [Ti0,Ti1,Ti5,Ti2,Ti3,Ti4]
- 前3个元素是第1个(底)三角形的三角形索引。
- 后3个元素是第2个(顶)三角形的三角形索引。
另请参阅
- Mesh(网格)文件格式章节。
- Mesh类函数的介绍。
将一个Mesh复制进另一Mesh
您可通过Mesh类将一个mesh复制进另一mesh。
在下面的例子中,我们会通过让Mesh类作为容器(container)的方式将ObjectMeshDynamic网格复制给新ObjectMeshDynamic类的实例。 其执行序列如下:
- 这里假设您已经拥有了一个要复制的ObjectMeshDynamic。 创建一个Mesh类的实例来作为ObjectMeshDynamic网格的容器。
- 通过getMesh()函数将ObjectMeshDynamic网格复制给Mesh。
- 通过ObjectMeshDynamic类的重载构造函数从Mesh创建新的ObjecteMeshDynamic实例。
/* ... */
// 创建ObjectMeshDynamic实例和Mesh实例
ObjectMeshDynamic dynamicMesh = new ObjectMeshDynamic();
Mesh firstMesh = new Mesh();
// 获取ObjectMeshDynamic并将其复制给Mesh类的实例
dynamicMesh.getMesh(firstMesh);
// 从firstMesh网格创建新的ObjectMeshDynamic实例
ObjectMeshDynamic dynamicMesh_2 = new ObjectMeshDynamic(firstMesh);
/* ... */
此外,您也可通过setMesh()函数复制mesh。 例如,在下面的代码中,我们会将mesh实例设置给已经创建了的ObjectMeshDynamic网格。 其执行序列如下:
- 这里假设您已经拥有了2个ObjectMeshDynamic类的实例。 创建一个Mesh类的实例来作为容器。
- 通过getMesh()函数将ObjectMeshDynamic复制给Mesh 。
- 通过setMesh()函数将mesh设置给第2个ObjectMeshDynamic实例。
/* ... */
// 创建ObjectMeshDynamic实例
ObjectMeshDynamic dynamicMesh = new ObjectMeshDynamic();
ObjectMeshDynamic dynamicMesh_2 = new ObjectMeshDynamic();
// 创建Mesh实例
Mesh firstMesh = new Mesh();
// 获取ObjectMeshDynamic并将其复制给Mesh类的实例
dynamicMesh.getMesh(firstMesh);
// 将secondMesh网格设置给dynamicMesh_2实例
dynamicMesh_2.setMesh(firstMesh);
/* ... */
您能通过构造函数可以很容易地将一个mesh实例复制给一个新的mesh实例,同时还可将mesh实例作为参数传递:
Mesh firstMesh = new Mesh();
Mesh secondMesh = new Mesh(firstMesh);
复制网格(Mesh)的表面
通过使用Mesh类,您就能将一个mesh的表面复制别的mesh。
在下面的例子中,我们会创建2个带有不同表面的网格。 第1个mesh拥有capsule型表面,第2个mesh拥有box型表面。 我们会将来自第2个mesh的box型表面复制给第一个mesh。 其执行序列如下:
- 创建2个Mesh类的实例,并为它们添加表面。
- 通过addMeshSurface()函数从mesh_2网格添加box型表面,并将其添加给mesh_1网格。
- 从mesh_1实例创建新ObjectMeshDynamic网格。
- 将动态网格添加给Editor,并为其设置材质,属性和名称。
// 创建2个mesh实例
Mesh mesh_1 = new Mesh();
Mesh mesh_2 = new Mesh();
// 为已添加的网格添加表面
mesh_1.addCapsuleSurface("capsule_surface", 1.0f, 2.0f, 200, 100);
mesh_2.addBoxSurface("box_surface", vec3(2.2));
// 将mesh_2的表面作为新表面添加给mesh_1
// 使用的名称为"new_box_surface"
mesh_1.addMeshSurface("new_box_surface", mesh_2, 0);
// 从mesh_1对象创建ObjectMeshDynamic
ObjectMeshDynamic dynamicMesh = new ObjectMeshDynamic(mesh_1);
// 将dynamicMesh作为Node对象添加给Editor
engine.editor.addNode(node_remove(dynamicMesh));
// 为mesh设置位置
dynamicMesh.setWorldTransform(translate(Vec3(10.0f,10.0f,10.0f)));
// 将材质设置给mesh
dynamicMesh.setMaterial("mesh_base","*");
// 将属性设置给mesh
dynamicMesh.setProperty("surface_base","*");
// 为mesh设置名称
dynamicMesh.setName("Dynamic Mesh");
这时ObjectMeshDynamic网格便会出现在Editor中:
添加顶点(Vertices)以及更改它们的属性(Attributes)
您可通过Mesh类更改顶点的属性(attributes)。
在下面的例子中,我们会通过将顶点添加给mesh来创建一个plane,并更改这些顶点中的一些的属性(attributes)。 让我们将其阐述如下:
- 创建Mesh类的实例并为其添加4个顶点。
- 添加6个索引来创建一个plane。
- 在那之后,通过函数createTangents()和createNormals()创建切线和法线。
- 2个顶点的法线的当前值会被显示在控制台中。 待更改完之后,新值也将被显示在控制台中。
- 切线会被显示在控制台中,之后它会被更改,并再次被显示在控制台中。
- 从mesh实例创建新ObjectMeshDynamic网格。
- 将动态网格添加给Editor,并为其设置材质,属性和名称。
// 创建一mesh实例
Mesh mesh = new Mesh();
// 添加一新表面
mesh.addSurface("surface_0");
// 为plane添加顶点
mesh.addVertex(vec3(0.0f,0.0f,0.0f),0);
mesh.addVertex(vec3(0.0f,0.0f,1.0f),0);
mesh.addVertex(vec3(0.0f,1.0f,0.0f),0);
mesh.addVertex(vec3(0.0f,1.0f,1.0f),0);
// 添加索引
mesh.addIndex(0,0);
mesh.addIndex(1,0);
mesh.addIndex(2,0);
mesh.addIndex(3,0);
mesh.addIndex(2,0);
mesh.addIndex(1,0);
// 创建切线
mesh.createTangents();
// 创建法线
mesh.createNormals(0);
// 在控制台中显示更改之前,有关第1条法线
// 和第2条法线的法线信息
log.message("the first normal is: %s and the second is: %s\n", typeinfo(mesh.getNormal(0, 0, 0)), typeinfo(mesh.getNormal(1, 0, 0)));
// 为法线设置新值
mesh.setNormal(0, vec3(1,1,0), 0, 0);
mesh.setNormal(1, vec3(1,1,1), 0, 0);
// 在控制台中显示法线值
log.message("the first normal is: %s and the second is: %s\n", typeinfo(mesh.getNormal(0, 0, 0)), typeinfo(mesh.getNormal(1, 0, 0)));
// 在控制台中显示切线值
log.message("tangent is: %s\n", typeinfo(mesh.getTangent(0,0,0)));
// 为切线设置新值
mesh.setTangent(0, quat(0.0,-0.0,-0.0,1), 0,0);
// 待更改完切线值之后显示其新值
log.message("tangent is: %s\n", typeinfo(mesh.getTangent(0,0,0)));
// 从Mesh对象创建ObjectMeshDynamic
ObjectMeshDynamic dynamicMesh = new ObjectMeshDynamic(mesh);
// 将dynamicMesh作为Node对象添加给Editor
engine.editor.addNode(node_remove(dynamicMesh));
// 为mesh设置位置
dynamicMesh.setWorldTransform(translate(Vec3(10.0f,10.0f,10.0f)));
// 将材质设置给mesh
dynamicMesh.setMaterial("mesh_base","*");
// 将属性设置给mesh
dynamicMesh.setProperty("surface_base","*");
// 为mesh设置名称
dynamicMesh.setName("new_mesh");
当您启动项目时,您就会在控制台中看到如下信息:
the first normal is:vec3: -1 0 0 and the second is:vec3: -1 0 0
the first normal is:vec3: 1 1 0 and the second is:vec3: 1 1 1
tangent is:quat: 0.5 -0.5 -0.5 0.5
tangent is:quat: 0 0 0 0.999962
待函数setTangent()和setNormal()被调用之后,法线值和切线值就被更改了。
让我们为如下这部分代码添加注释:
// 在控制台中显示切线值
log.message("tangent is: %s\n", typeinfo(mesh.getTangent(0,0,0)));
// 为切线设置新值
mesh.setTangent(0, quat(0.0,-0.0,-0.0,1), 0,0);
// 待更改完切线值之后显示其新值
log.message("tangent is: %s\n", typeinfo(mesh.getTangent(0,0,0)));