Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

使用 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)