Up to date

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

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

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

MeshDataTool работает не так быстро, как изменение массивов непосредственно с помощью ArrayMesh. Однако он предоставляет больше информации и инструментов для работы с сетками, чем ArrayMesh. Когда используется MeshDataTool, он вычисляет данные сетки, которые недоступны в массивах, таких как грани и ребра, которые необходимы для определенных алгоритмов сетки. Если вам не нужна эта дополнительная информация, то, возможно, лучше использовать ArrayMesh.

Примечание

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

Предположим, что сетка хранится в ArrayMesh с именем mesh. Затем мы инициализируем MeshDataTool из mesh вызовом create_from_surface(). Если в MeshDataTool уже есть инициализированные данные, вызов create_from_surface() очистит их. В качестве альтернативы, вы можете вызвать clear() самостоятельно перед повторным использованием 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 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.

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

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)

Эти модификации не выполняются на месте 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()
        # 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)