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时, 它会计算ArrayMeshes中没有的网格数据, 如面和边, 这些数据对于某些网格算法来说是必要的. 如果你不需要这些额外的信息, 那么使用 ArrayMesh 可能会更好.

备注

MeshDataTool 只能用于使用 Mesh.PRIMITIVE_TRIANGLES PrimitiveType 的网格。

我们通过调用 create_from_surface() 来使用 ArrayMesh 初始化 MeshDataTool。如果该 MeshDataTool 中已经有初始化的数据了,调用 create_from_surface() 会为你将其清除。或者你可以在重用 MeshDataTool 之前自己调用 clear()

下面的例子中,假定已经创建了一个名叫 mesh 的 ArrayMesh。网格生成的示例见 ArrayMesh 教程

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 教程

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)