使用 ArrayMesh

本教程将介绍使用 ArrayMesh 的基础知识。

为此,我们将使用函数 add_surface_from_arrays() ,它最多需要五个参数。前两个参数是必须的,后三个参数是可选的。

第一个参数是 PrimitiveType(图元类型),这是 OpenGL 中的概念,用于指示 GPU 如何根据给定的顶点来安排图元,即它们表示的是三角形、线、还是点等等。可选项见 Mesh.PrimitiveType

第二个参数 arrays 是存储网格信息的实际 Array。该数组是一个普通的 Godot 数组,用空括号 [] 构造。它为每一种类型的信息存储一个 Packed**Array(如 PackedVector3Array、PackedInt32Array等),用于构建表面。

arrays 可能包含下列元素,另外还必须在 arrays 中包含位置信息。有关完整列表,另请参阅 Mesh.ArrayType

索引

Mesh.ArrayType 枚举

数组类型

0

ARRAY_VERTEX

PackedVector3ArrayPackedVector2Array

1

ARRAY_NORMAL

PackedVector3Array

2

ARRAY_TANGENT

PackedFloat32ArrayPackedFloat64Array 4 个浮点数组。 前 3 个浮点数确定切线,最后一个浮点数确定副法线方向,即 -1 或 1。

3

ARRAY_COLOR

PackedColorArray

4

ARRAY_TEX_UV

PackedVector2ArrayPackedVector3Array

5

ARRAY_TEX_UV2

PackedVector2ArrayPackedVector3Array

10

ARRAY_BONES

4 个 float 一组的 PackedFloat32Array 或 4 个 int 一组的 PackedInt32Array。每一组都列出了影响某个特定顶点的 4 根骨骼。

11

ARRAY_WEIGHTS

4 个 float 一组的 PackedFloat32ArrayPackedFloat64Array。每个 float 都列出了给定顶点对 ARRAY_BONES 中特定骨骼的权重。

12

ARRAY_INDEX

PackedInt32Array

在大多数情况下,创建网格时,我们通过顶点位置来定义网格。因此,顶点数组(位于索引 0 处)通常是必需的,而索引数组(位于索引 12 处)是可选的,只有在它被包含时才会使用。也可以只创建索引数组而不创建顶点数组,但这超出了本教程的范围。事实上,我们根本不会使用索引数组。

其他所有数组包含的都是关于顶点的信息。它们也是可选的,只有在包含时才会用到。有些数组(例如 ARRAY_COLOR`)用每个顶点一个元素的形式来提供额外的顶点信息。它们的大小必须与顶点数组一致。另一些数组(例如 ARRAY_TANGENT)用四个元素来描述一个顶点。它们必须正好是顶点数组的四倍。

正常的使用场景下,add_surface_from_arrays() 的最后三个参数通常都是留空的。

设置 ArrayMesh

在编辑器中,创建一个 MeshInstance3D ,并在检查器中为其添加一个 ArrayMesh。通常,在编辑器里添加 ArrayMesh 没什么用,但这里可以让我们免去用代码创建的麻烦,直接使用这个 ArrayMesh。

接下来,在 MeshInstance3D 上添加一个脚本。

_ready() 下创建一个新的数组。

GDScriptC#

  1. var surface_array = []
  1. var surfaceArray = new Godot.Collections.Array();

这将是保存表面信息的数组——将保存表面需要的所有数据数组。Godot 希望它的大小是 Mesh.ARRAY_MAX,所以要相应地调整。

GDScriptC#

  1. var surface_array = []
  2. surface_array.resize(Mesh.ARRAY_MAX)
  1. var surfaceArray = new Godot.Collections.Array();
  2. surfaceArray.Resize((int)Mesh.ArrayType.Max);

接下来, 为你将使用的每种数据类型创建数组.

GDScriptC#

  1. var verts = PackedVector3Array()
  2. var uvs = PackedVector2Array()
  3. var normals = PackedVector3Array()
  4. var indices = PackedInt32Array()
  1. var verts = new List<Vector3>();
  2. var uvs = new List<Vector2>();
  3. var normals = new List<Vector3>();
  4. var indices = new List<int>();

一旦你用几何体填充了你的数据数组, 就可以通过将每个数组添加到 surface_array , 然后提交到网格中来创建网格.

GDScriptC#

  1. surface_array[Mesh.ARRAY_VERTEX] = verts
  2. surface_array[Mesh.ARRAY_TEX_UV] = uvs
  3. surface_array[Mesh.ARRAY_NORMAL] = normals
  4. surface_array[Mesh.ARRAY_INDEX] = indices
  5. # No blendshapes, lods, or compression used.
  6. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
  1. surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
  2. surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
  3. surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
  4. surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
  5. var arrMesh = Mesh as ArrayMesh;
  6. if (arrMesh != null)
  7. {
  8. // No blendshapes, lods, or compression used.
  9. arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
  10. }

备注

在这个例子中,使用了 Mesh.PRIMITIVE_TRIANGLES,但你也可以使用网格所提供的任何图元类型。

把这些放到一起,完整的代码是这样的:

GDScriptC#

  1. extends MeshInstance3D
  2. func _ready():
  3. var surface_array = []
  4. surface_array.resize(Mesh.ARRAY_MAX)
  5. # PackedVector**Arrays for mesh construction.
  6. var verts = PackedVector3Array()
  7. var uvs = PackedVector2Array()
  8. var normals = PackedVector3Array()
  9. var indices = PackedInt32Array()
  10. #######################################
  11. ## Insert code here to generate mesh ##
  12. #######################################
  13. # Assign arrays to surface array.
  14. surface_array[Mesh.ARRAY_VERTEX] = verts
  15. surface_array[Mesh.ARRAY_TEX_UV] = uvs
  16. surface_array[Mesh.ARRAY_NORMAL] = normals
  17. surface_array[Mesh.ARRAY_INDEX] = indices
  18. # Create mesh surface from mesh array.
  19. # No blendshapes, lods, or compression used.
  20. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
  1. public partial class MyMeshInstance3D : MeshInstance3D
  2. {
  3. public override void _Ready()
  4. {
  5. var surfaceArray = new Godot.Collections.Array();
  6. surfaceArray.Resize((int)Mesh.ArrayType.Max);
  7. // C# arrays cannot be resized or expanded, so use Lists to create geometry.
  8. var verts = new List<Vector3>();
  9. var uvs = new List<Vector2>();
  10. var normals = new List<Vector3>();
  11. var indices = new List<int>();
  12. /***********************************
  13. * Insert code here to generate mesh.
  14. * *********************************/
  15. // Convert Lists to arrays and assign to surface array
  16. surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
  17. surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
  18. surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
  19. surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
  20. var arrMesh = Mesh as ArrayMesh;
  21. if (arrMesh != null)
  22. {
  23. // Create mesh surface from mesh array
  24. // No blendshapes, lods, or compression used.
  25. arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
  26. }
  27. }
  28. }

中间可以放你想要的任何代码。下面我们会给出一些示例代码,用于生成球体。

生成几何体

这是生成球体的示例代码。尽管代码是用 GDScript 编写的,但是 Godot 并没有指定用特定的方式来实现它。这种实现方式与 ArrayMesh 无关,仅仅是一种通用的生成球体的方式。如果你觉得这比较难以理解,或者想更全面地了解程序式几何体,可以在网上寻找相关的教程进行学习。

GDScriptC#

  1. extends MeshInstance3D
  2. var rings = 50
  3. var radial_segments = 50
  4. var radius = 1
  5. func _ready():
  6. # Insert setting up the PackedVector**Arrays here.
  7. # Vertex indices.
  8. var thisrow = 0
  9. var prevrow = 0
  10. var point = 0
  11. # Loop over rings.
  12. for i in range(rings + 1):
  13. var v = float(i) / rings
  14. var w = sin(PI * v)
  15. var y = cos(PI * v)
  16. # Loop over segments in ring.
  17. for j in range(radial_segments):
  18. var u = float(j) / radial_segments
  19. var x = sin(u * PI * 2.0)
  20. var z = cos(u * PI * 2.0)
  21. var vert = Vector3(x * radius * w, y * radius, z * radius * w)
  22. verts.append(vert)
  23. normals.append(vert.normalized())
  24. uvs.append(Vector2(u, v))
  25. point += 1
  26. # Create triangles in ring using indices.
  27. if i > 0 and j > 0:
  28. indices.append(prevrow + j - 1)
  29. indices.append(prevrow + j)
  30. indices.append(thisrow + j - 1)
  31. indices.append(prevrow + j)
  32. indices.append(thisrow + j)
  33. indices.append(thisrow + j - 1)
  34. if i > 0:
  35. indices.append(prevrow + radial_segments - 1)
  36. indices.append(prevrow)
  37. indices.append(thisrow + radial_segments - 1)
  38. indices.append(prevrow)
  39. indices.append(prevrow + radial_segments)
  40. indices.append(thisrow + radial_segments - 1)
  41. prevrow = thisrow
  42. thisrow = point
  43. # Insert committing to the ArrayMesh here.
  1. public partial class MyMeshInstance3D : MeshInstance3D
  2. {
  3. private int _rings = 50;
  4. private int _radialSegments = 50;
  5. private float _radius = 1;
  6. public override void _Ready()
  7. {
  8. // Insert setting up the surface array and lists here.
  9. // Vertex indices.
  10. var thisRow = 0;
  11. var prevRow = 0;
  12. var point = 0;
  13. // Loop over rings.
  14. for (var i = 0; i < _rings + 1; i++)
  15. {
  16. var v = ((float)i) / _rings;
  17. var w = Mathf.Sin(Mathf.Pi * v);
  18. var y = Mathf.Cos(Mathf.Pi * v);
  19. // Loop over segments in ring.
  20. for (var j = 0; j < _radialSegments; j++)
  21. {
  22. var u = ((float)j) / _radialSegments;
  23. var x = Mathf.Sin(u * Mathf.Pi * 2);
  24. var z = Mathf.Cos(u * Mathf.Pi * 2);
  25. var vert = new Vector3(x * _radius * w, y * _radius, z * _radius * w);
  26. verts.Add(vert);
  27. normals.Add(vert.Normalized());
  28. uvs.Add(new Vector2(u, v));
  29. point += 1;
  30. // Create triangles in ring using indices.
  31. if (i > 0 && j > 0)
  32. {
  33. indices.Add(prevRow + j - 1);
  34. indices.Add(prevRow + j);
  35. indices.Add(thisRow + j - 1);
  36. indices.Add(prevRow + j);
  37. indices.Add(thisRow + j);
  38. indices.Add(thisRow + j - 1);
  39. }
  40. }
  41. if (i > 0)
  42. {
  43. indices.Add(prevRow + _radialSegments - 1);
  44. indices.Add(prevRow);
  45. indices.Add(thisRow + _radialSegments - 1);
  46. indices.Add(prevRow);
  47. indices.Add(prevRow + _radialSegments);
  48. indices.Add(thisRow + _radialSegments - 1);
  49. }
  50. prevRow = thisRow;
  51. thisRow = point;
  52. }
  53. // Insert committing to the ArrayMesh here.
  54. }
  55. }

保存

最后,我们可以使用 ResourceSaver 类来保存该 ArrayMesh。当你想生成一个网格,然后在以后使用它而不需要重新生成时,这个方法很有用。

GDScriptC#

  1. # Saves mesh to a .tres file with compression enabled.
  2. ResourceSaver.save(mesh, "res://sphere.tres", ResourceSaver.FLAG_COMPRESS)
  1. // Saves mesh to a .tres file with compression enabled.
  2. ResourceSaver.Save(Mesh, "res://sphere.tres", ResourceSaver.SaverFlags.Compress);

Previous Next


© 版权所有 2014-present Juan Linietsky, Ariel Manzur and the Godot community (CC BY 3.0). Revision b1c660f7.

Built with Sphinx using a theme provided by Read the Docs.