Usando o ArrayMesh

Este tutorial apresentará o básico de uso de um ArrayMesh.

Para isso, usaremos a função add_surface_from_arrays(), que aceita até cinco parâmetros. Os dois primeiros são obrigatórios, enquanto os três últimos são opcionais.

O primeiro parâmetro é o PrimitiveType, um conceito do OpenGL que instrui a GPU sobre como organizar a primitiva com base nos vértices fornecidos, ou seja, se eles representam triângulos, linhas, pontos etc. Veja Mesh.PrimitiveType para as opções disponíveis.

O segundo parâmetro, arrays, é o Array propriamente dito que armazena as informações da malha. O array é um array normal do Godot, construído com colchetes vazios []. Ele armazena um Packed**Array (por exemplo, PackedVector3Array, PackedInt32Array etc.) para cada tipo de informação que será usada para construir a superfície.

Elementos comuns de arrays estão listados abaixo, junto com a posição que devem ocupar dentro de arrays. Veja Mesh.ArrayType para a lista completa.

Índice

Mesh.ArrayType Enum

Tipo de matriz

0

ARRAY_VERTEX

PackedVector3Array or PackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

PackedFloat32Array ou PackedFloat64Array de grupos de 4 números float. Os primeiros 3 floats determinam a tangente, e o último float determina a direção da binormal como -1 ou 1.

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2Array or PackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2Array or PackedVector3Array

10

ARRAY_BONES

PackedFloat32Array de grupos de 4 floats ou PackedInt32Array de grupos de 4 inteiros. Cada grupo lista os índices de 4 ossos que afetam um determinado vértice.

11

ARRAY_WEIGHTS

PackedFloat32Array ou PackedFloat64Array de grupos de 4 floats. Cada float indica a quantidade de influência que o osso correspondente em ARRAY_BONES tem sobre um determinado vértice.

12

ARRAY_INDEX

PackedInt32Array

Geralmente ao criar uma malha, ela é definida pelas posições dos vértices. Portanto, geralmente o array de vértices (no índice 0) é obrigatório, enquanto o array de índices (no índice 12) é opcional e só será usado se for incluído. Também é possível criar uma malha apenas com o array de índices e sem o array de vértices, mas isso está além do escopo deste tutorial.

Todos os outros arrays carregam informações sobre os vértices. Eles são opcionais e só serão usados se incluídos. Alguns desses arrays (por exemplo, ARRAY_COLOR) usam uma entrada por vértice para fornecer informações extras sobre os vértices. Eles devem ter o mesmo tamanho do array de vértices. Outros arrays (por exemplo, ARRAY_TANGENT) usam quatro entradas para descrever um único vértice. Esses devem ter exatamente quatro vezes o tamanho do array de vértices.

Para uso comum, os três últimos parâmetros de add_surface_from_arrays() são geralmente deixados em branco.

Setting up the ArrayMesh

No editor, crie um MeshInstance3D e adicione um ArrayMesh a ele no Inspetor. Normalmente, adicionar um ArrayMesh no editor não é útil, mas neste caso isso nos permite acessar o ArrayMesh pelo código sem precisar criar um.

Next, add a script to the MeshInstance3D.

Em ''_ready()'', crie uma nova Array.

var surface_array = []

Esta será a matriz na qual manteremos nossas informações de superfície - ela conterá todas as matrizes de dados que a superfície necessita. Godot espera que ela seja do tamanho Mesh.ARRAY_MAX, portanto, redimensione-a de acordo.

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

Em seguida, crie as matrizes para cada tipo de dado que você usará.

var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()

Uma vez que você tenha preenchido suas matrizes de dados com sua geometria, você pode criar uma malha adicionando cada matriz a surface_array e, em seguida, comitando para a malha.

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)

Nota

Neste exemplo, utilizamos Mesh.PRIMITIVE_TRIANGLES, mas você pode utilizar qualquer tipo primitivo disponível na malha.

Juntos, o código completo se parece com:

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)

O código que vai no meio pode ser o que você quiser. A seguir apresentaremos um exemplo de código para gerar uma esfera.

Gerando geometria

Aqui está uma amostra de código para gerar uma esfera. Embora o código seja apresentado no GDScript, não há nada de específico do Godot sobre a abordagem para gerá-lo. Esta implementação não tem nada em particular a ver com ArrayMeshes e é apenas uma abordagem genérica para a geração de uma esfera. Se você estiver tendo problemas para compreendê-la ou quiser aprender mais sobre a geometria de procedimentos em geral, você pode usar qualquer tutorial que encontrar on-line.

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.

Salvando

Finally, we can use the ResourceSaver class to save the ArrayMesh. This is useful when you want to generate a mesh and then use it later without having to re-generate it.

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