使用 ArrayMesh¶
本教程将介绍使用 ArrayMesh 的基础知识。
为此, 我们将使用函数 add_surface_from_arrays() , 它最多需要四个参数. 前两个参数是必须的, 后两个参数是可选的.
第一个参数是 PrimitiveType
(图元类型),这是 OpenGL 中的概念,用于指示 GPU 如何根据给定的顶点来安排图元,即它们表示的是三角形、线、还是点等等。可选项见 Mesh.PrimitiveType。
第二各参数 arrays
是存储网格信息的实际 Array。该数组是一个普通的 Godot 数组,用空括号 []
构造。它为每一种类型的信息存储一个 Pool**Array
(如 PoolVector3Array、PoolIntArray 等),用于构建表面。
arrays
可能包含下列元素,另外还必须在 arrays
中包含位置信息。另请参阅 Mesh.ArrayType。
索引 |
Mesh.ArrayType 枚举 |
数组类型 |
---|---|---|
0 |
|
|
1 |
|
|
2 |
|
4 个浮点数一组的 PoolRealArray。前 3 个浮点数决定切线,最后一个决定双法线方向,为 -1 或 1。 |
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
4 个 float 一组的 PoolRealArray 或 4 个 int 一组的 PoolIntArray。每一组都列出了影响某个特定顶点的 4 根骨骼。 |
7 |
|
4 个 float 一组的 PoolRealArray。每个 float 都列出了给定顶点对 |
8 |
|
顶点数组(索引为 0)始终是必须的。索引数组是可选的,包含时才会用到。在这个教程中我们不会用到。
其他所有数组包含的都是关于顶点的信息。他们也是可选的,包含时才会用到。有些数组(例如 ARRAY_COLOR`)用每个顶点一个元素的形式来提供额外的顶点信息。他们的大小必须与顶点数组一致。另一些数组(例如 ARRAY_TANGENT
)用四个元素来描述一个顶点。他们必须正好是顶点数组的四倍大小。
正常的使用场景下,add_surface_from_arrays() 的最后两个参数通常都是留空的。
ArrayMesh¶
在编辑器中,创建一个 MeshInstance 并在检查器中为其添加一个 ArrayMesh。通常,在编辑器里添加 ArrayMesh 没什么用,但这里可以让我们免去用代码创建的麻烦,直接使用这个 ArrayMesh。
接下来,在 MeshInstance 上添加一个脚本。
在 _ready()
下创建一个新的数组。
var surface_array = []
这将是保存表面信息的数组——将保存表面需要的所有数据数组。Godot 希望它的大小是 Mesh.ARRAY_MAX
,所以要相应地调整。
var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)
接下来, 为您将使用的每种数据类型创建数组.
var verts = PoolVector3Array()
var uvs = PoolVector2Array()
var normals = PoolVector3Array()
var indices = PoolIntArray()
一旦你用几何体填充了你的数据数组, 就可以通过将每个数组添加到 surface_array
, 然后提交到网格中来创建网格.
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.
备注
在这个例子中,使用了 Mesh.PRIMITIVE_TRIANGLES
,但你也可以使用网格所提供的任何图元类型。
把这些放到一起,完整的代码是这样的:
extends MeshInstance
func _ready():
var surface_array= []
surface_array.resize(Mesh.ARRAY_MAX)
# PoolVector**Arrays for mesh construction.
var verts = PoolVector3Array()
var uvs = PoolVector2Array()
var normals = PoolVector3Array()
var indices = PoolIntArray()
#######################################
## Insert code here to generate mesh ##
#######################################
# Assign arrays to mesh array.
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
# Create mesh surface from mesh array.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.
中间可以放你想要的任何代码。下面我们会给出一些示例代码,用于生成球体。
生成几何体¶
这是生成球体的示例代码。尽管代码是用 GDScript 编写的,但是 Godot 并没有指定用特定的方式来实现它。这种实现方式与 ArrayMesh 无关,仅仅是一种通用的生成球体的方式。如果您觉得这比较难以理解,或者想更全面地了解程序式几何体,可以在网上寻找相关的教程进行学习。
extends MeshInstance
var rings = 50
var radial_segments = 50
var height = 1
var radius = 1
func _ready():
# Insert setting up the PoolVector**Arrays here.
# Vertex indices.
var thisrow = 0
var prevrow = 0
var point = 0
# Loop over rings.
for i in range(rings + 1):
var v = float(i) / rings
var w = sin(PI * v)
var y = cos(PI * v)
# Loop over segments in ring.
for j in range(radial_segments):
var u = float(j) / radial_segments
var x = sin(u * PI * 2.0)
var z = cos(u * PI * 2.0)
var vert = Vector3(x * radius * w, y, z * radius * w)
verts.append(vert)
normals.append(vert.normalized())
uvs.append(Vector2(u, v))
point += 1
# Create triangles in ring using indices.
if i > 0 and j > 0:
indices.append(prevrow + j - 1)
indices.append(prevrow + j)
indices.append(thisrow + j - 1)
indices.append(prevrow + j)
indices.append(thisrow + j)
indices.append(thisrow + j - 1)
if i > 0:
indices.append(prevrow + radial_segments - 1)
indices.append(prevrow)
indices.append(thisrow + radial_segments - 1)
indices.append(prevrow)
indices.append(prevrow + radial_segments)
indices.append(thisrow + radial_segments - 1)
prevrow = thisrow
thisrow = point
# Insert committing to the ArrayMesh here.
保存¶
最后,我们可以使用 ResourceSaver 类来保存该 ArrayMesh。当你想生成一个网格,然后在以后使用它而不需要重新生成时,这个方法很有用。
# Saves mesh to a .tres file with compression enabled.
ResourceSaver.save("res://sphere.tres", mesh, ResourceSaver.FLAG_COMPRESS)