Использование ArrayMesh

В этом уроке будут представлены основы использования ArrayMesh.

Для этого мы будем использовать функцию add_surface_from_arrays(), которая принимает до четырех параметров. Первые два являются обязательными, а вторые два - необязательными.

Первым параметром является PrimitiveType, концепция OpenGL, которая указывает GPU, как расположить примитив на основе заданных вершин, т.е. представляют ли они треугольники, линии, точки и т.д. Доступные варианты см. в Mesh.PrimitiveType.

Второй параметр, arrays, является фактическим массивом, который хранит информацию о сетке. Массив представляет собой обычный массив Godot, построенный с помощью пустых скобок []. Он хранит Pool**Array (например, PoolVector3Array, PoolIntArray и т.д.) для каждого типа информации, которая будет использоваться для построения поверхности.

Возможные элементы arrays перечислены ниже, вместе с позицией, которую они должны иметь в пределах arrays. См. также Mesh.ArrayType.

Индекс

Mesh.ArrayType - перечисление

Тип массива

0

ARRAY_VERTEX

PoolVector3Array или PoolVector2Array

1

ARRAY_NORMAL

PoolVector3Array

2

ARRAY_TANGENT

PoolRealArray группа из 4 вещественных чисел. Первые 3 числа определяют тангенс, а последний - бинормальное направление как -1 или 1.

3

ARRAY_COLOR

PoolColorArray

4

ARRAY_TEX_UV

PoolVector2Array или PoolVector3Array

5

ARRAY_TEX_UV2

PoolVector2Array или PoolVector3Array

6

ARRAY_BONES

PoolRealArray группа из 4 вещественных чисел или PoolIntArray групп из 4 целых чисел. Каждая группа перечисляет индексы 4 костей, которые влияют на данную вершину.

7

ARRAY_WEIGHTS

PoolRealArray группа из 4 вещественных числе. Каждое число перечисляет количество веса, которое определенная кость на ARRAY_BONES имеет на данную вершину.

8

ARRAY_INDEX

PoolIntArray

Массив вершин (с индексом 0) всегда обязателен. Массив индексов является необязательным и будет использоваться только в том случае, если он включен. Мы не будем использовать его в этом уроке.

Все остальные массивы несут информацию о вершинах. Они также являются необязательными и будут использоваться только в том случае, если они включены. Некоторые из этих массивов (например, ARRAY_COLOR) используют по одной записи на вершину, чтобы предоставить дополнительную информацию о вершинах. Они должны иметь тот же размер, что и массив вершин. Другие массивы (например, ARRAY_TANGENT) используют четыре записи для описания одной вершины. Они должны быть ровно в четыре раза больше, чем массив вершин.

Для обычного использования последние два параметра в add_surface_from_arrays() обычно оставляют пустыми.

ArrayMеsh

В редакторе создайте MeshInstance и добавьте к нему ArrayMesh в инспекторе. Обычно добавление ArrayMesh в редакторе не является полезным, но в данном случае это позволяет нам получить доступ к ArrayMesh из кода без его создания.

Затем добавьте скрипт в MeshInstance.

Ниже _ready() создайте новый массив.

var surface_array = []

Это будет массив, в котором мы храним нашу информацию о поверхности, он будет содержать все массивы данных, которые нужны поверхности. Godot ожидает, что он будет иметь размер Mesh.ARRAY_MAX, поэтому измените его размер соответствующим образом.

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

Затем создайте массивы для каждого типа данных, который вы будете использовать.

var verts = PoolVector3Array()
var uvs = PoolVector2Array()
var normals = PoolVector3Array()
var indices = PoolIntArray()

После того, как вы заполнили свои массивы данных своей геометрией, вы можете создать сетку, добавив каждый массив в 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

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

Примечание

В этом примере мы использовали Mesh.PRIMITIVE_TRIANGLES, но вы можете использовать любой примитивный тип, доступный из сетки.

В совокупности полный код выглядит следующим образом:

extends MeshInstance

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

    # PoolVector**Arrays for mesh construction.
    var verts = PoolVector3Array()
    var uvs = PoolVector2Array()
    var normals = PoolVector3Array()
    var indices = PoolIntArray()

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

    # Assign arrays to mesh 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.
    mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.

Код, который находится в середине, может быть любым, каким вы захотите. Ниже мы приведем пример кода для генерации сферы.

Создаём геометрию

Здесь приведен пример кода для генерации сферы. Хотя код представлен на GDScript, в подходе к генерации сферы нет ничего специфичного для Godot. Эта реализация не имеет ничего общего с ArrayMeshes и является просто общим подходом к генерации сферы. Если у вас возникли трудности с пониманием или вы хотите узнать больше о процедурной геометрии в целом, вы можете воспользоваться любым учебником, который найдете в Интернете.

extends MeshInstance

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

func _ready():

    # Insert setting up the PoolVector**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):
            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, 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)

        if i > 0:
            indices.append(prevrow + radial_segments - 1)
            indices.append(prevrow)
            indices.append(thisrow + radial_segments - 1)

            indices.append(prevrow)
            indices.append(prevrow + radial_segments)
            indices.append(thisrow + radial_segments - 1)

        prevrow = thisrow
        thisrow = point

  # Insert committing to the ArrayMesh here.

Сохранение

Наконец, мы можем использовать класс ResourceSaver для сохранения ArrayMesh. Это полезно, когда вы хотите сгенерировать сетку, а затем использовать ее позже без необходимости повторной генерации.

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