Використання ArrayMesh

Цей урок дасть основи використання ArrayMesh.

Для цього ми використаємо функцію add_surface_from_arrays(), яка приймає до п’яти параметрів. Перші два обов’язкові, а останні три необов’язкові.

Перший параметр - PrimitiveType, це концепція OpenGL, яка інструктує графічний процесор, як розташувати примітив на основі вершин, враховуючи, чи це трикутники, лінії, точки тощо. Повний список варіантів дивіться в Mesh.PrimitiveType.

Другий параметр, arrays, є фактичним масивом, який зберігає інформацію про сітку. Масив є звичайним масивом Godot, який складається з порожніх дужок []. Він зберігає Packed**Array (наприклад, PackedVector3Array, PackedInt32Array тощо) для кожного типу інформації, яка використовуватиметься для створення поверхні.

Загальні елементи масивів перераховані нижче разом із позицією, яку вони повинні мати в масивах. Див. Mesh.ArrayType для повного списку.

Індекс

Сітка.ТипМасиву.Enum

Тип масиву

0

ARRAY_VERTEX

PackedVector3Array або PackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

PackedFloat32Array або PackedFloat64Array груп із 4 плаваючих елементів. Перші 3 числа з плаваючою точкою визначають тангенс, а останні з плаваючою точкою — бінормальний напрямок як -1 або 1.

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2Array або PackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2Array або PackedVector3Array

10

ARRAY_BONES

PackedFloat32Array груп з 4 чисел або PackedInt32Array груп з 4 int. Кожна група містить індекси 4 кісток, які впливають на дану вершину.

11

ARRAY_WEIGHTS

PackedFloat32Array або PackedFloat64Array груп із 4 плаваючих елементів. Кожен 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)