Использование MeshDataTool

Инструмент MeshDataTool не используется для создания геометрии. Но он полезен для динамического изменения геометрии, например, если вы хотите написать сценарий для тесселяции, упрощения или деформации сетки.

The MeshDataTool is not as fast as altering arrays directly using ArrayMesh. However, it provides more information and tools to work with meshes than the ArrayMesh does. When the MeshDataTool is used, it calculates mesh data that is not available in ArrayMeshes such as faces and edges, which are necessary for certain mesh algorithms. If you do not need this extra information then it may be better to use an ArrayMesh.

Примечание

MeshDataTool можно использовать только в сетках, использующих примитивный тип Mesh.PRIMITIVE_TRIANGLES.

We initialize the MeshDataTool from an ArrayMesh by calling create_from_surface(). If there is already data initialized in the MeshDataTool, calling create_from_surface() will clear it for you. Alternatively, you can call clear() yourself before re-using the MeshDataTool.

В приведенных ниже примерах предполагается, что сетка ArrayMesh под названием mesh уже создана. Пример создания сетки см. в ArrayMesh tutorial.

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

create_from_surface() использует массивы вершин из ArrayMesh для вычисления двух дополнительных массивов, одного для рёбер и одного для граней, всего три массива.

Ребро - это соединение между любыми двумя вершинами. Каждое ребро в массиве ребер содержит ссылку на две вершины, из которых оно состоит, и до двух граней, внутри которых оно содержится.

Грань - это треугольник, состоящий из трех вершин и трех соответствующих ребер. Каждая грань в массиве граней содержит ссылку на три вершины и три ребра, из которых она состоит.

Массив вершин содержит информацию о грани, лице, нормали, цвете, касательной, uv, uv2, кости и весе, связанную с каждой вершиной.

Для доступа к информации из этих массивов вы используете функцию вида get_****():

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

То, что вы решите делать с этими функциями, зависит от вас. Распространенным вариантом использования является перебор всех вершин и их преобразование каким-либо образом:

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

Эти модификации не выполняются на месте ArrayMesh. Если вы динамически обновляете существующий ArrayMesh, сначала удалите существующую поверхность, прежде чем добавлять новую, используя commit_to_surface():

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

Ниже приведен пример, который превращает сферическую сетку mesh в произвольно деформированный шар с обновленными нормалями и цветами вершин. Как сгенерировать базовую сетку смотрите в ArrayMesh tutorial.

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()
        # Scale the vertices using noise.
        vertex = vertex * (fnl.get_noise_3dv(vertex) * 0.5 + 0.75)
        mdt.set_vertex(i, vertex)

    # Calculate the 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 the vertex position using the vertex index.
        var ap = mdt.get_vertex(a)
        var bp = mdt.get_vertex(b)
        var cp = mdt.get_vertex(c)
        # Calculate the normal of the face.
        var n = (bp - cp).cross(ap - bp).normalized()
        # Add this face normal to the current vertex normals.
        # 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 the vertices one last time to normalize their normals and
    # set the vertex colors to these new normals.
    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)