使用 ArrayMesh

本教程将介绍使用 ArrayMesh 的基础知识。

为此,我们将使用函数 add_surface_from_arrays() ,它最多需要五个参数。前两个参数是必须的,后三个参数是可选的。

第一个参数是 PrimitiveType(图元类型),这是 OpenGL 中的概念,用于指示 GPU 如何根据给定的顶点来排列图元,即它们是否代表三角形、线条、点等。有关可用选项,请参阅 Mesh.PrimitiveType

第二个参数 arrays 是存储网格信息的实际 Array。该数组是一个普通的 Godot 数组,用空括号 [] 构造。它为每一种类型的信息存储一个 Packed**Array(如 PackedVector3Array、PackedInt32Array等),用于构建表面。

arrays 的常见元素列出如下,还有必须在 arrays 中包含位置信息。有关完整列表,另请参阅 Mesh.ArrayType

索引

Mesh.ArrayType 枚举

数组类型

0

ARRAY_VERTEX

PackedVector3ArrayPackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

PackedFloat32ArrayPackedFloat64Array 4 个浮点数组。 前 3 个浮点数确定切线,最后一个浮点数确定副法线方向,即 -1 或 1。

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2ArrayPackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2ArrayPackedVector3Array

10

ARRAY_BONES

4 个 float 一组的 PackedFloat32Array 或 4 个 int 一组的 PackedInt32Array。每一组都列出了影响给定顶点的 4 根骨骼的索引。

11

ARRAY_WEIGHTS

4 个 float 一组的 PackedFloat32ArrayPackedFloat64Array。每个 float 都列出了给定顶点对 ARRAY_BONES 中特定骨骼的权重。

12

ARRAY_INDEX

PackedInt32Array

在创建网格的大部分情况中,我们通过顶点位置来定义网格。因此(位于索引 0 处的)顶点数组通常是必需的。而(位于索引 12 处的)索引数组是可选的,只有在它被包含时才会使用。也可以只用索引数组而不用顶点数组来创建网格,但这不在本教程的内容范围之中。

其他所有数组包含的都是关于顶点的信息。它们也是可选的,只有在包含时才会用到。有些数组(例如 ARRAY_COLOR`)用每个顶点一个元素的形式来提供额外的顶点信息。它们的大小必须与顶点数组一致。另一些数组(例如 ARRAY_TANGENT)用四个元素来描述一个顶点。它们必须正好是顶点数组的四倍。

正常的使用场景下,add_surface_from_arrays() 的最后三个参数通常都是留空的。

设置 ArrayMesh

在编辑器中,创建一个 MeshInstance3D ,并在检查器中为其添加一个 ArrayMesh。通常,在编辑器里添加 ArrayMesh 没什么用,但这里可以让我们免去用代码创建的麻烦,直接使用这个 ArrayMesh。

接下来,在 MeshInstance3D 上添加一个脚本。

_ready() 下创建一个新的数组。

var surface_array = []

这将是保存表面信息的数组——将保存表面需要的所有数据数组。Godot 希望它的大小是 Mesh.ARRAY_MAX,所以要相应地调整。

var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)

接下来,为你将使用的每种数据类型创建数组.

var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()

一旦你用几何体填充了你的数据数组,就可以通过将每个数组添加到 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

# No blendshapes, lods, or compression used.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)

备注

在这个例子中,使用了 Mesh.PRIMITIVE_TRIANGLES,但你也可以使用网格所提供的任何图元类型。

把这些放到一起,完整的代码是这样的:

extends MeshInstance3D

func _ready():
	var surface_array = []
	surface_array.resize(Mesh.ARRAY_MAX)

	# PackedVector**Arrays for mesh construction.
	var verts = PackedVector3Array()
	var uvs = PackedVector2Array()
	var normals = PackedVector3Array()
	var indices = PackedInt32Array()

	#######################################
	## Insert code here to generate mesh ##
	#######################################

	# Assign arrays to 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

	# Create mesh surface from mesh array.
	# No blendshapes, lods, or compression used.
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)

中间可以放你想要的任何代码。下面我们会给出一些示例代码,用于生成球体。

生成几何体

这是生成球体的示例代码。尽管代码是用 GDScript 编写的,但是 Godot 并没有指定用特定的方式来实现它。这种实现方式与 ArrayMesh 无关,仅仅是一种通用的生成球体的方式。如果你觉得这比较难以理解,或者想更全面地了解程序式几何体,可以在网上寻找相关的教程进行学习。

extends MeshInstance3D

var rings = 50
var radial_segments = 50
var radius = 1

func _ready():

	# Insert setting up the PackedVector**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 + 1):
			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 * radius, 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)

		prevrow = thisrow
		thisrow = point

  # Insert committing to the ArrayMesh here.

保存

最后,我们可以使用 ResourceSaver 类来保存该 ArrayMesh。当你想生成一个网格,然后在以后使用它而不需要重新生成时,这个方法很有用。

# Saves mesh to a .tres file with compression enabled.
ResourceSaver.save(mesh, "res://sphere.tres", ResourceSaver.FLAG_COMPRESS)