Usando ArrayMesh

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

Para ello, utilizaremos la función add_surface_from_arrays(), que toma hasta cuatro parámetros. Los dos primeros son obligatorios, mientras que los dos segundos son opcionales.

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.

El segundo es el Array actual que almacena la información del mesh. El array es un array normal de Godot que se construye con los corchetes vacíos []. Almacena un Pool**Array (por ejemplo, PoolVector3Array, PoolIntArray, etc.) para cada tipo de información que se usará para contruir la superficie.

Los posibles elementos de "arrays" se enumeran a continuación, junto con la posición que deben tener dentro de "arrays". Consulta también Mesh.ArrayType.

Index

Mesh.ArrayType Enum

Tipo de array

0

ARRAY_VERTEX

PoolVector3Array o PoolVector2Array

1

ARRAY_NORMAL

PoolVector3Array

2

ARRAY_TANGENT

Un PoolRealArray de grupos de 4 números en coma flotante. Los primeros 3 números determinan la tangente y el último número indica la dirección de la binormal como -1 o 1.

3

ARRAY_COLOR

PoolColorArray

4

ARRAY_TEX_UV

PoolVector2Array o PoolVector3Array

5

ARRAY_TEX_UV2

PoolVector2Array o PoolVector3Array

6

ARRAY_BONES

PoolRealArray de grupos de 4 floats o PoolIntArray de grupos de 4 ints. Cada grupo lista los índices de 4 huesos que afectan a un vértice dado.

7

ARRAY_WEIGHTS

Un PoolRealArray de grupos de 4 números en coma flotante. Cada número representa el peso que un hueso determinado en "ARRAY_BONES" tiene en un vértice dado.

8

ARRAY_INDEX

PoolIntArray

El arreglo de vértices (en el índice 0) siempre es requerido. El arreglo de índices es opcional y solo se utilizará si se incluye. En este tutorial no lo utilizaremos.

Todos los demás arreglos contienen información sobre los vértices. También son opcionales y solo se utilizarán si se incluyen. Algunos de estos arreglos (por ejemplo, ARRAY_COLOR) utilizan una entrada por vértice para proporcionar información adicional sobre los vértices. Deben tener el mismo tamaño que el arreglo de vértices. Otros arreglos (por ejemplo, ARRAY_TANGENT) utilizan cuatro entradas para describir un solo vértice. Estos deben ser exactamente cuatro veces más grandes que el arreglo de vértices.

Para un uso normal, los últimos dos parámetros en add_surface_from_arrays() generalmente se dejan vacíos.

ArrayMesh

En el editor, crea un MeshInstance y agrega un ArrayMesh a través del Inspector. Normalmente, agregar un ArrayMesh en el editor no es útil, pero en este caso nos permite acceder al ArrayMesh desde el código sin crear uno.

A continuación, agrega el script al MeshInstance.

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 = PoolVector3Array()
var uvs = PoolVector2Array()
var normals = PoolVector3Array()
var indices = PoolIntArray()

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

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

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

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

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("res://sphere.tres", mesh, ResourceSaver.FLAG_COMPRESS)