Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Utilizzo di ArrayMesh

Questo tutorial presenterà le basi dell'utilizzo di un ArrayMesh.

Per farlo, useremo la funzione add_surface_from_arrays(), che accetta fino a cinque parametri. I primi due sono obbligatori, mentre gli ultimi tre sono facoltativi.

Il primo parametro è PrimitiveType, un concetto OpenGL che indica alla GPU come disporre la primitiva in base ai vertici forniti, ovvero se rappresentano triangoli, linee, punti, ecc. Consultare Mesh.PrimitiveType per le opzioni disponibili.

Il secondo parametro, arrays, è l'array vero e proprio che memorizza le informazioni sulla mesh. L'array è un tipico array di Godot costruito con parentesi quadre vuote []. Memorizza un Packed**Array (ad esempio PackedVector3Array, PackedInt32Array, ecc.) per ogni tipo di informazione che servirà per costruire la superficie.

Di seguito sono elencati gli elementi comuni di array, insieme alla posizione che devono avere all'interno di array. Consulta Mesh.ArrayType per un elenco completo.

Indice

Enum Mesh.ArrayType

Tipo di array

0

ARRAY_VERTEX

PackedVector3Array o PackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

PackedFloat32Array o PackedFloat64Array in gruppi di 4 float. I primi 3 float determinano la tangente, mentre l'ultimo float determina la direzione binormale, come -1 o 1.

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2Array o PackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2Array o PackedVector3Array

10

ARRAY_BONES

PackedFloat32Array in gruppi di 4 float o PackedInt32Array in gruppi di 4 int. Ogni gruppo elenca gli indici di 4 ossa che influenzano un determinato vertice.

11

ARRAY_WEIGHTS

PackedFloat32Array o PackedFloat64Array in gruppi di 4 float. Ogni float elenca la quantità di peso che l'osso corrispondente in ARRAY_BONES ha su un determinato vertice.

12

ARRAY_INDEX

PackedInt32Array

Nella maggior parte dei casi, quando si crea una mesh, la si definisce tramite le posizioni dei suoi vertici. Quindi, solitamente, l'array dei vertici (all'indice 0) è obbligatorio, mentre l'array degli indici (all'indice 12) è facoltativo e verrà utilizzato solo se incluso. È anche possibile creare una mesh tramite solo l'array degli indici e senza l'array dei vertici, ma questo è fuori dallo scopo di questo tutorial.

Tutti gli altri array contengono informazioni sui vertici. Sono facoltativi e verranno utilizzati solo se inclusi. Alcuni di questi array (ad esempio ARRAY_COLOR) utilizzano una voce per vertice per fornire informazioni in più sui vertici. Devono avere le stesse dimensioni dell'array dei vertici. Altri array (ad esempio ARRAY_TANGENT) utilizzano quattro voci per descrivere un singolo vertice. Questi devono essere esattamente quattro volte più grandi dell'array dei vertici.

Per un utilizzo comune, gli ultimi tre parametri in add_surface_from_arrays() sono in genere lasciati vuoti.

Preparazione dell'ArrayMesh

Nell'editor, crea un MeshInstance3D e aggiungi un ArrayMesh ad esso nell'Ispettore. Normalmente, aggiungere un ArrayMesh nell'editor non è utile, ma in questo caso ci permette di accedere all'ArrayMesh da codice senza doverlo creare.

Successivamente, aggiungi uno script al MeshInstance3D.

Sotto _ready(), crea un nuovo Array.

var surface_array = []

Questo sarà l'array in cui memorizzeremo le informazioni sulla superficie: conterrà tutti gli array di dati necessari alla superficie. Godot si aspetta che abbia una dimensione di Mesh.ARRAY_MAX, quindi ridimensionalo appropriatamente.

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

Poi crea gli array per ogni tipo di dati che utilizzerai.

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

Una volta riempiti gli array di dati con la tua geometria, puoi creare una mesh aggiungendo ciascun array a surface_array e poi confermando la mesh.

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

In questo esempio, abbiamo utilizzato Mesh.PRIMITIVE_TRIANGLES, ma è possibile utilizzare qualsiasi tipo di primitiva disponibile da mesh.

Messo assieme, il codice complete apparirà così:

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)

Il codice che va inserito in mezzo può essere qualsiasi cosa tu voglia. Di seguito presenteremo alcuni esempi di codice per generare forme, partendo da un rettangolo.

Generare un rettangolo

Poiché utilizziamo Mesh.PRIMITIVE_TRIANGLES per renderizzare, costruiremo un rettangolo con dei triangoli.

Un rettangolo è formato da due triangoli che condividono quattro vertici. Nel nostro esempio, creeremo un rettangolo con il suo vertice superiore sinistro in (0, 0, 0) e con una larghezza e lunghezza di uno, come mostrato di seguito:

Un rettangolo formato da due triangoli che condividono quattro vertici.

Per disegnare questo rettangolo, definisci le coordinate di ciascun vertice nell'array verts.

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

L'array uvs aiuta a descrivere dove le parti di una texture devono andare sulla mesh. I valori vanno da 0 a 1. A seconda della texture, potrebbe essere desiderato modificare questi valori.

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

L'array normals serve per descrivere la direzione verso cui puntano i vertici ed è utilizzato nei calcoli di illuminazione. Per questo esempio, lasceremo la direzione al valore predefinito di Vector3.UP.

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

L'array indices definisce l'ordine in cui sono disegnati i vertici. Godot renderizza in senso orario, il che significa che dobbiamo specificare i vertici di un triangolo che vogliamo disegnare in senso orario.

Ad esempio, per disegnare il primo triangolo, dovremo disegnare i vertici (0, 0, 0), (1, 0, 0) e (0, 0, 1) in quest'ordine. Questo equivale a disegnare vert[0], vert[2] e vert[1], ovvero gli indici 0, 2 e 1 nell'array verts. Questi sono i valori di indice definiti dall'array indices.

Indice

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

Messo assieme, il codice per la generazione del rettangolo appare così:

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.

Per un esempio più complesso, consulta la sezione sulla generazione di una sfera riportata di seguito.

Generare una sfera

Ecco un esempio di codice per generare una sfera. Sebbene il codice sia presentato in GDScript, non c'è nulla di specifico di Godot su come è stato generato. Questa implementazione non ha nulla a che fare in particolare con gli ArrayMesh ed è semplicemente un approccio generico per generare una sfera. Se hai difficoltà a comprenderlo o vuoi saperne di più sulla geometria procedurale in generale, potresti consultare qualsiasi tutorial che trovi online.

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.

Salvare

Infine, possiamo utilizzare la classe ResourceSaver per salvare l'ArrayMesh. Questo è utile quando si desidera generare una mesh e poi utilizzarla in seguito senza doverla rigenerare.

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