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

ARRAY_VERTEX

PackedVector3ArrayPackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

每組包含 4 個浮點數的 PackedFloat32ArrayPackedFloat64Array。前 3 個浮點數決定了切線,而最後一個浮點數決定了副切線方向為 -1 或 1。

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2ArrayPackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2ArrayPackedVector3Array

10

ARRAY_BONES

PackedFloat32Array 的每組 4 個浮點數,或 PackedInt32Array 的每組 4 個整數。每組列出會影響到指定頂點的 4 個骨骼索引。

11

ARRAY_WEIGHTS

PackedFloat32ArrayPackedFloat64Array,每組 4 個浮點數。每個浮點數列出在給定頂點上,在 ARRAY_BONES 中對應骨骼所擁有的權重值。

12

ARRAY_INDEX

PackedInt32Array

在大多數情況下,建立 Mesh 時,我們透過其頂點位置定義它。所以通常,頂點陣列 (位於索引 0) 是必須的,而索引陣列 (位於索引 12) 是選用的,且只有在包含時才會被使用。也可能僅使用索引陣列而非頂點陣列來建立 Mesh,但這超出本教學的範圍。

其他所有陣列包含的都是關於頂點的資訊。他們也是可選的,包含時才會用到。有些陣列(例如 ARRAY_COLOR`)用每個頂點一個元素的形式來提供額外的頂點資訊。他們的大小必須與頂點陣列一致。另一些陣列(例如 ARRAY_TANGENT)用四個元素來描述一個頂點。他們必須正好是頂點陣列的四倍大小。

正常的使用場景下,add_surface_from_arrays() 的最後兩個參數通常都是留空的。

設定遊戲區域

在編輯器中建立一個 MeshInstance3D,並在屬性面板中加上一個 ArrayMesh。一般來說在編輯器加 ArrayMesh 沒什麼意義,但這裡這麼做可以讓我們直接從程式碼存取該 ArrayMesh 而不用自行建立。

將腳本附加到節點。

_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)