Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Usando ArrayMesh

Este tutorial presentará los fundamentos del uso de un ArrayMesh.

To do so, we will use the function add_surface_from_arrays(), which takes up to five parameters. The first two are required, while the last three are optional.

El primer parámetro es el PrimitiveType, un concepto de OpenGL que instruye a la GPU cómo organizar el primitivo basado en los vértices proporcionados, es decir, si representan triángulos, líneas, puntos, etc. Consulta Mesh.PrimitiveType para ver las opciones disponibles.

The second parameter, arrays, is the actual Array that stores the mesh information. The array is a normal Godot array that is constructed with empty brackets []. It stores a Packed**Array (e.g. PackedVector3Array, PackedInt32Array, etc.) for each type of information that will be used to build the surface.

Common elements of arrays are listed below, together with the position they must have within arrays. See Mesh.ArrayType for a full list.

Index

Mesh.ArrayType Enum

Tipo de array

0

ARRAY_VERTEX

PackedVector3Array or PackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

PackedFloat32Array or PackedFloat64Array of groups of 4 floats. The first 3 floats determine the tangent, and the last float the binormal direction as -1 or 1.

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2Array or PackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2Array or PackedVector3Array

10

ARRAY_BONES

PackedFloat32Array of groups of 4 floats or PackedInt32Array of groups of 4 ints. Each group lists indexes of 4 bones that affects a given vertex.

11

ARRAY_WEIGHTS

PackedFloat32Array or PackedFloat64Array of groups of 4 floats. Each float lists the amount of weight the corresponding bone in ARRAY_BONES has on a given vertex.

12

ARRAY_INDEX

PackedInt32Array

In most cases when creating a mesh, we define it by its vertex positions. So usually, the array of vertices (at index 0) is required, while the index array (at index 12) is optional and will only be used if included. It is also possible to create a mesh with only the index array and no vertex array, but that's beyond the scope of this tutorial. In fact, we won't use the index array at all.

All the other arrays carry information about the vertices. They are optional and will only be used if included. Some of these arrays (e.g. ARRAY_COLOR) use one entry per vertex to provide extra information about vertices. They must have the same size as the vertex array. Other arrays (e.g. ARRAY_TANGENT) use four entries to describe a single vertex. These must be exactly four times larger than the vertex array.

For normal usage, the last three parameters in add_surface_from_arrays() are typically left empty.

Setting up the ArrayMesh

In the editor, create a MeshInstance3D and add an ArrayMesh to it in the Inspector. Normally, adding an ArrayMesh in the editor is not useful, but in this case it allows us to access the ArrayMesh from code without creating one.

Next, add a script to the MeshInstance3D.

Dentro de _ready(), crea un nuevo Array.

var surface_array = []

Este será el arreglo en el que guardaremos la información de la superficie; contendrá todos los arrays de datos que la superficie necesita. Godot espera que tenga un tamaño de Mesh.ARRAY_MAX, así que redimensiona el arreglo en consecuencia.

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

A continuación, crea los arreglos para cada tipo de datos que utilizarás.

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

Una vez que hayas llenado tus arreglos de datos con tu geometría, puedes crear una malla agregando cada arreglo a surface_array y luego comprometiéndolo a la malla.

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

En este ejemplo, utilizamos Mesh.PRIMITIVE_TRIANGLES, pero puedes utilizar cualquier tipo de primitiva disponible en la malla.

A continuación se muestra el código completo:

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)

El código que se coloca en el medio puede ser el que desees. A continuación, presentaremos un ejemplo de código para generar una esfera.

Generando geometría

Aquí tienes un ejemplo de código para generar una esfera. Aunque el código está presentado en GDScript, no hay nada específico de Godot en el enfoque para generarlo. Esta implementación no tiene nada en particular que ver con ArrayMeshes y es simplemente un enfoque genérico para generar una esfera. Si tienes dificultades para entenderlo o quieres aprender más sobre geometría procedural en general, puedes utilizar cualquier tutorial que encuentres en línea.

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.

Guardando

Finalmente, podemos utilizar la clase ResourceSaver para guardar la ArrayMesh. Esto es útil cuando queremos generar una malla y luego utilizarla posteriormente sin tener que volver a generarla.

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