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)

The code that goes in the middle can be whatever you want. Below we will present some example code for generating shapes, starting with a rectangle.

Generating a rectangle

Since we are using Mesh.PRIMITIVE_TRIANGLES to render, we will construct a rectangle with triangles.

A rectangle is formed by two triangles sharing four vertices. For our example, we will create a rectangle with its top left point at (0, 0, 0) with a width and length of one as shown below:

A rectangle made of two triangles sharing four vertices.

To draw this rectangle, define the coordinates of each vertex in the verts array.

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

The uvs array helps describe where parts of a texture should go onto the mesh. The values range from 0 to 1. Depending on your texture, you may want to change these values.

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

The normals array is used to describe the direction the vertices face and is used in lighting calculations. For this example, we will default to the Vector3.UP direction.

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

The indices array defines the order vertices are drawn. Godot renders in a clockwise direction, meaning that we must specify the vertices of a triangle we want to draw in clockwise order.

For example, to draw the first triangle, we will want to draw the vertices (0, 0, 0), (1, 0, 0), and (0, 0, 1) in that order. This is the same as drawing vert[0], vert[2], and vert[1], i.e., indices 0, 2, and 1, in the verts array. These index values are what the indices array defines.

Índice

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.
    ])

Put together, the rectangle generation code looks like:

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.

For a more complex example, see the sphere generation section below.

Generating a sphere

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)