Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Використання 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)

Код, який розміщується посередині, може бути будь-яким. Нижче ми наведемо приклад коду для створення фігур, починаючи з прямокутника.

Генерація прямокутника

Оскільки ми використовуємо Mesh.PRIMITIVE_TRIANGLES для рендерингу, ми побудуємо прямокутник з трикутниками.

Прямокутник утворений двома трикутниками, що мають чотири спільні вершини. Для нашого прикладу ми створимо прямокутник з верхньою лівою точкою в (0, 0, 0), шириною та довжиною, що дорівнюють одиниці, як показано нижче:

Прямокутник, складений з двох трикутників, що мають чотири спільні вершини.

Щоб намалювати цей прямокутник, визначте координати кожної вершини в масиві verts.

verts = PackedVector3Array([
        Vector3(0, 0, 0),
        Vector3(0, 0, 1),
        Vector3(1, 0, 0),
        Vector3(1, 0, 1),
    ])

Масив uvs допомагає описати, де частини текстури повинні розміщуватися на сітці. Значення варіюються від 0 до 1. Залежно від вашої текстури, ви можете захотіти змінити ці значення.

uvs = PackedVector2Array([
        Vector2(0, 0),
        Vector2(1, 0),
        Vector2(0, 1),
        Vector2(1, 1),
    ])

Масив normals використовується для опису напрямку, куди спрямовані вершини, та використовується в розрахунках освітлення. У цьому прикладі ми за замовчуванням використовуватимемо напрямок Vector3.UP.

normals = PackedVector3Array([
        Vector3.UP,
        Vector3.UP,
        Vector3.UP,
        Vector3.UP,
    ])

Масив indices визначає порядок, у якому малюються вершини. Godot відтворює зображення за годинниковою стрілкою, тобто ми повинні вказати вершини трикутника, які хочемо намалювати, за годинниковою стрілкою.

Наприклад, щоб намалювати перший трикутник, нам потрібно намалювати вершини (0, 0, 0), (1, 0, 0) та (0, 0, 1) у такому порядку. Це те саме, що й малювання vert[0], vert[2] та vert[1], тобто індексів 0, 2 та 1, у масиві verts. Ці значення індексів визначає масив indices.

Індекс

verts[Index]

uvs[Index]

normals[Index]

0

(0, 0, 0)

(0, 0)

Vector3.UP

1

(0, 0, 1)

(1, 0)

Vector3.UP

2

(1, 0, 0)

(0, 1)

Vector3.UP

3

(1, 0, 1)

(1, 1)

Vector3.UP

indices = PackedInt32Array([
        0, 2, 1, # Draw the first triangle.
        2, 3, 1, # Draw the second triangle.
    ])

У сукупності код генерації прямокутника виглядає так:

extends MeshInstance3D

func _ready():

  # Insert setting up the PackedVector**Arrays here.

  verts = PackedVector3Array([
          Vector3(0, 0, 0),
          Vector3(0, 0, 1),
          Vector3(1, 0, 0),
          Vector3(1, 0, 1),
      ])

  uvs = PackedVector2Array([
          Vector2(0, 0),
          Vector2(1, 0),
          Vector2(0, 1),
          Vector2(1, 1),
      ])

  normals = PackedVector3Array([
          Vector3.UP,
          Vector3.UP,
          Vector3.UP,
          Vector3.UP,
      ])

  indices = PackedInt32Array([
          0, 2, 1,
          2, 3, 1,
      ])

  # Insert committing to the ArrayMesh here.

Для більш складного прикладу дивіться розділ про генерацію сфер нижче.

Генерація сфери

Ось зразок коду для створення сфери. Хоча код представлений в 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)