Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
Использование ArrayMesh¶
В этом уроке будут представлены основы использования ArrayMesh.
To do so, we will use the function add_surface_from_arrays(), which takes up to five parameters. The first two are required, while the last three are optional.
Первым параметром является PrimitiveType
, концепция OpenGL, которая указывает GPU, как расположить примитив на основе заданных вершин, т.е. представляют ли они треугольники, линии, точки и т.д. Доступные варианты см. в Mesh.PrimitiveType.
The second parameter, arrays
, is the actual Array that stores the mesh information. The array is a normal Godot array that
is constructed with empty brackets []
. It stores a Packed**Array
(e.g. PackedVector3Array,
PackedInt32Array, etc.) for each type of information that will be used to build the surface.
Common elements of arrays
are listed below, together with the position they must have within arrays
.
See Mesh.ArrayType for a full list.
Индекс |
Mesh.ArrayType - перечисление |
Тип массива |
---|---|---|
0 |
|
|
1 |
|
|
2 |
|
PackedFloat32Array or PackedFloat64Array of groups of 4 floats. The first 3 floats determine the tangent, and the last float the binormal direction as -1 or 1. |
3 |
|
|
4 |
|
|
5 |
|
|
10 |
|
PackedFloat32Array of groups of 4 floats or PackedInt32Array of groups of 4 ints. Each group lists indexes of 4 bones that affects a given vertex. |
11 |
|
PackedFloat32Array or PackedFloat64Array of groups of 4 floats. Each float lists the amount of weight the corresponding bone in |
12 |
|
In most cases when creating a mesh, we define it by its vertex positions. So usually, the array of vertices (at index 0) is required, while the index array (at index 12) is optional and will only be used if included. It is also possible to create a mesh with only the index array and no vertex array, but that's beyond the scope of this tutorial. In fact, we won't use the index array at all.
All the other arrays carry information about the vertices. They are optional and will only be used if included. Some of these arrays (e.g. ARRAY_COLOR
)
use one entry per vertex to provide extra information about vertices. They must have the same size as the vertex array. Other arrays (e.g. ARRAY_TANGENT
) use
four entries to describe a single vertex. These must be exactly four times larger than the vertex array.
For normal usage, the last three parameters in add_surface_from_arrays() are typically left empty.
Setting up the ArrayMesh¶
In the editor, create a MeshInstance3D and add an ArrayMesh to it in the Inspector. Normally, adding an ArrayMesh in the editor is not useful, but in this case it allows us to access the ArrayMesh from code without creating one.
Next, add a script to the MeshInstance3D.
Ниже _ready()
создайте новый массив.
var surface_array = []
var surfaceArray = new Godot.Collections.Array();
Это будет массив, в котором мы храним нашу информацию о поверхности, он будет содержать все массивы данных, которые нужны поверхности. Godot ожидает, что он будет иметь размер Mesh.ARRAY_MAX
, поэтому измените его размер соответствующим образом.
var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)
var surfaceArray = new Godot.Collections.Array();
surfaceArray.Resize((int)Mesh.ArrayType.Max);
Затем создайте массивы для каждого типа данных, который вы будете использовать.
var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()
var verts = new List<Vector3>();
var uvs = new List<Vector2>();
var normals = new List<Vector3>();
var indices = new List<int>();
После того, как вы заполнили свои массивы данных своей геометрией, вы можете создать сетку, добавив каждый массив в surface_array, а затем зафиксировав его в сетке.
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
# No blendshapes, lods, or compression used.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
var arrMesh = Mesh as ArrayMesh;
if (arrMesh != null)
{
// No blendshapes, lods, or compression used.
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
}
Примечание
В этом примере мы использовали Mesh.PRIMITIVE_TRIANGLES, но вы можете использовать любой примитивный тип, доступный из сетки.
В совокупности полный код выглядит следующим образом:
extends MeshInstance3D
func _ready():
var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)
# PackedVector**Arrays for mesh construction.
var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()
#######################################
## Insert code here to generate mesh ##
#######################################
# Assign arrays to surface array.
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
# Create mesh surface from mesh array.
# No blendshapes, lods, or compression used.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
public partial class MyMeshInstance3D : MeshInstance3D
{
public override void _Ready()
{
var surfaceArray = new Godot.Collections.Array();
surfaceArray.Resize((int)Mesh.ArrayType.Max);
// C# arrays cannot be resized or expanded, so use Lists to create geometry.
var verts = new List<Vector3>();
var uvs = new List<Vector2>();
var normals = new List<Vector3>();
var indices = new List<int>();
/***********************************
* Insert code here to generate mesh.
* *********************************/
// Convert Lists to arrays and assign to surface array
surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
var arrMesh = Mesh as ArrayMesh;
if (arrMesh != null)
{
// Create mesh surface from mesh array
// No blendshapes, lods, or compression used.
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
}
}
}
Код, который находится в середине, может быть любым, каким вы захотите. Ниже мы приведем пример кода для генерации сферы.
Создаём геометрию¶
Здесь приведен пример кода для генерации сферы. Хотя код представлен на GDScript, в подходе к генерации сферы нет ничего специфичного для Godot. Эта реализация не имеет ничего общего с ArrayMeshes и является просто общим подходом к генерации сферы. Если у вас возникли трудности с пониманием или вы хотите узнать больше о процедурной геометрии в целом, вы можете воспользоваться любым учебником, который найдете в Интернете.
extends MeshInstance3D
var rings = 50
var radial_segments = 50
var radius = 1
func _ready():
# Insert setting up the PackedVector**Arrays here.
# Vertex indices.
var thisrow = 0
var prevrow = 0
var point = 0
# Loop over rings.
for i in range(rings + 1):
var v = float(i) / rings
var w = sin(PI * v)
var y = cos(PI * v)
# Loop over segments in ring.
for j in range(radial_segments + 1):
var u = float(j) / radial_segments
var x = sin(u * PI * 2.0)
var z = cos(u * PI * 2.0)
var vert = Vector3(x * radius * w, y * radius, z * radius * w)
verts.append(vert)
normals.append(vert.normalized())
uvs.append(Vector2(u, v))
point += 1
# Create triangles in ring using indices.
if i > 0 and j > 0:
indices.append(prevrow + j - 1)
indices.append(prevrow + j)
indices.append(thisrow + j - 1)
indices.append(prevrow + j)
indices.append(thisrow + j)
indices.append(thisrow + j - 1)
prevrow = thisrow
thisrow = point
# Insert committing to the ArrayMesh here.
public partial class MyMeshInstance3D : MeshInstance3D
{
private int _rings = 50;
private int _radialSegments = 50;
private float _radius = 1;
public override void _Ready()
{
// Insert setting up the surface array and lists here.
// Vertex indices.
var thisRow = 0;
var prevRow = 0;
var point = 0;
// Loop over rings.
for (var i = 0; i < _rings + 1; i++)
{
var v = ((float)i) / _rings;
var w = Mathf.Sin(Mathf.Pi * v);
var y = Mathf.Cos(Mathf.Pi * v);
// Loop over segments in ring.
for (var j = 0; j < _radialSegments + 1; j++)
{
var u = ((float)j) / _radialSegments;
var x = Mathf.Sin(u * Mathf.Pi * 2);
var z = Mathf.Cos(u * Mathf.Pi * 2);
var vert = new Vector3(x * _radius * w, y * _radius, z * _radius * w);
verts.Add(vert);
normals.Add(vert.Normalized());
uvs.Add(new Vector2(u, v));
point += 1;
// Create triangles in ring using indices.
if (i > 0 && j > 0)
{
indices.Add(prevRow + j - 1);
indices.Add(prevRow + j);
indices.Add(thisRow + j - 1);
indices.Add(prevRow + j);
indices.Add(thisRow + j);
indices.Add(thisRow + j - 1);
}
}
prevRow = thisRow;
thisRow = point;
}
// Insert committing to the ArrayMesh here.
}
}
Сохранение¶
Наконец, мы можем использовать класс ResourceSaver для сохранения ArrayMesh. Это полезно, когда вы хотите сгенерировать сетку, а затем использовать ее позже без необходимости повторной генерации.
# Saves mesh to a .tres file with compression enabled.
ResourceSaver.save(mesh, "res://sphere.tres", ResourceSaver.FLAG_COMPRESS)
// Saves mesh to a .tres file with compression enabled.
ResourceSaver.Save(Mesh, "res://sphere.tres", ResourceSaver.SaverFlags.Compress);