Usando o MeshDataTool

O MeshDataTool não é usado para gerar geometria. Mas ele é útil para alterar dinamicamente a geometria, por exemplo, se você quiser escrever um script para tesselar, simplificar ou deformar malhas.

O MeshDataTool não é tão rápido quanto alterar os arrays diretamente usando o ArrayMesh. No entanto, ele oferece mais informações e ferramentas para trabalhar com malhas do que o ArrayMesh. Quando o MeshDataTool é usado, ele calcula dados da malha que não estão disponíveis em ArrayMeshes, como faces e arestas, que são necessários para certos algoritmos de malha. Se você não precisa dessas informações extras, pode ser melhor usar um ArrayMesh.

Nota

O MeshDataTool só pode ser usado em malhas que utilizam o PrimitiveType Mesh.PRIMITIVE_TRIANGLES.

Inicializamos o MeshDataTool a partir de um ArrayMesh chamando create_from_surface(). Se já houver dados inicializados no MeshDataTool, ao chamar create_from_surface() eles serão limpos automaticamente. Alternativamente, você pode chamar clear() manualmente antes de reutilizar o MeshDataTool.

Nos exemplos abaixo, assuma que um ArrayMesh chamado mesh já foi criado. Veja o tutorial de ArrayMesh para um exemplo de geração de malha.

var mdt = MeshDataTool.new()
mdt.create_from_surface(mesh, 0)

O create_from_surface() usa os arrays de vértices do ArrayMesh para calcular dois arrays adicionais, um para as arestas e outro para as faces, totalizando três arrays.

Uma aresta é uma conexão entre dois vértices quaisquer. Cada aresta no array de arestas contém uma referência aos dois vértices que a compõem e até duas faces nas quais ela está contida.

Uma face é um triângulo composto por três vértices e três arestas correspondentes. Cada face no array de faces contém uma referência aos três vértices e às três arestas que a compõem.

O array de vértices contém informações de arestas, faces, normal, cor, tangente, uv, uv2, osso e peso associadas a cada vértice.

Para acessar informações desses arrays, você usa uma função no formato get_****():

mdt.get_vertex_count() # Returns number of vertices in vertex array.
mdt.get_vertex_faces(0) # Returns array of faces that contain vertex[0].
mdt.get_face_normal(1) # Calculates and returns face normal of the second face.
mdt.get_edge_vertex(10, 1) # Returns the second vertex comprising the edge at index 10.

O que você decide fazer com essas funções fica a seu critério. Um caso de uso comum é iterar sobre todos os vértices e transformá-los de alguma forma:

for i in range(get_vertex_count):
    var vert = mdt.get_vertex(i)
    vert *= 2.0 # Scales the vertex by doubling size.
    mdt.set_vertex(i, vert)

Essas modificações não são feitas diretamente no ArrayMesh. Se você estiver atualizando dinamicamente um ArrayMesh existente, primeiro exclua a superfície atual antes de adicionar uma nova usando commit_to_surface():

mesh.clear_surfaces() # Deletes all of the mesh's surfaces.
mdt.commit_to_surface(mesh)

Abaixo está um exemplo completo que transforma uma malha esférica chamada mesh em uma bolha deformada aleatoriamente, com normais e cores de vértice atualizadas. Veja o tutorial de ArrayMesh para saber como gerar a malha base.

extends MeshInstance3D

var fnl = FastNoiseLite.new()
var mdt = MeshDataTool.new()

func _ready():
    fnl.frequency = 0.7

    mdt.create_from_surface(mesh, 0)

    for i in range(mdt.get_vertex_count()):
        var vertex = mdt.get_vertex(i).normalized()
        # Push out vertex by noise.
        vertex = vertex * (fnl.get_noise_3dv(vertex) * 0.5 + 0.75)
        mdt.set_vertex(i, vertex)

    # Calculate vertex normals, face-by-face.
    for i in range(mdt.get_face_count()):
        # Get the index in the vertex array.
        var a = mdt.get_face_vertex(i, 0)
        var b = mdt.get_face_vertex(i, 1)
        var c = mdt.get_face_vertex(i, 2)
        # Get vertex position using vertex index.
        var ap = mdt.get_vertex(a)
        var bp = mdt.get_vertex(b)
        var cp = mdt.get_vertex(c)
        # Calculate face normal.
        var n = (bp - cp).cross(ap - bp).normalized()
        # Add face normal to current vertex normal.
        # This will not result in perfect normals, but it will be close.
        mdt.set_vertex_normal(a, n + mdt.get_vertex_normal(a))
        mdt.set_vertex_normal(b, n + mdt.get_vertex_normal(b))
        mdt.set_vertex_normal(c, n + mdt.get_vertex_normal(c))

    # Run through vertices one last time to normalize normals and
    # set color to normal.
    for i in range(mdt.get_vertex_count()):
        var v = mdt.get_vertex_normal(i).normalized()
        mdt.set_vertex_normal(i, v)
        mdt.set_vertex_color(i, Color(v.x, v.y, v.z))

    mesh.clear_surfaces()
    mdt.commit_to_surface(mesh)