Використання ArrayMesh
Цей урок дасть основи використання ArrayMesh.
Для цього ми використаємо функцію add_surface_from_arrays(), яка приймає до п’яти параметрів. Перші два обов’язкові, а останні три необов’язкові.
Перший параметр - PrimitiveType, це концепція OpenGL, яка інструктує графічний процесор, як розташувати примітив на основі вершин, враховуючи, чи це трикутники, лінії, точки тощо. Повний список варіантів дивіться в Mesh.PrimitiveType.
Другий параметр, arrays, є фактичним масивом, який зберігає інформацію про сітку. Масив є звичайним масивом Godot, який складається з порожніх дужок []. Він зберігає Packed**Array (наприклад, PackedVector3Array, PackedInt32Array тощо) для кожного типу інформації, яка використовуватиметься для створення поверхні.
Загальні елементи масивів перераховані нижче разом із позицією, яку вони повинні мати в масивах. Див. Mesh.ArrayType для повного списку.
Індекс |
Сітка.ТипМасиву.Enum |
Тип масиву |
|---|---|---|
0 |
|
|
1 |
|
|
2 |
|
PackedFloat32Array або PackedFloat64Array груп із 4 плаваючих елементів. Перші 3 числа з плаваючою точкою визначають тангенс, а останні з плаваючою точкою — бінормальний напрямок як -1 або 1. |
3 |
|
|
4 |
|
|
5 |
|
|
10 |
|
PackedFloat32Array груп з 4 чисел або PackedInt32Array груп з 4 int. Кожна група містить індекси 4 кісток, які впливають на дану вершину. |
11 |
|
PackedFloat32Array або PackedFloat64Array груп із 4 плаваючих елементів. Кожен float перераховує кількість ваги відповідної кістки в |
12 |
|
У більшості випадків при створенні сітки ми визначаємо її положенням вершин. Тому зазвичай масив вершин (з індексом 0) є обов’язковим, тоді як масив індексів (з індексом 12) необов’язковий і використовуватиметься, лише якщо він включений. Також можна створити сітку лише з масивом індексів і без масиву вершин, але це виходить за рамки цього підручника.
Всі інші масиви несуть інформацію про вершини. Вони є необов’язковими та використовуватимуться, лише якщо включені. Деякі з цих масивів (наприклад, ARRAY_COLOR) використовують один запис на вершину для надання додаткової інформації про вершини. Вони повинні мати той самий розмір, що й масив вершин Інші масиви (наприклад, ARRAY_TANGENT) використовують чотири записи для опису однієї вершини. Вони повинні бути точно в чотири рази більші за масив вершин.
Для нормального використання останні три параметри в add_surface_from_arrays() зазвичай залишаються порожніми.
Налаштування ArrayMesh
У редакторі створіть MeshInstance3D і додайте до нього ArrayMesh в інспекторі. Зазвичай додавання ArrayMesh у редакторі не є корисним, але в цьому випадку це дозволяє отримати доступ до ArrayMesh із коду, не створюючи його.
Далі додайте скрипт до MeshInstance3D.
Під _ready() створіть новий масив.
var surface_array = []
Godot.Collections.Array surfaceArray = [];
Це буде масив, в якому ми зберігаємо інформацію про нашу поверхню, він буде містити всі масиви даних, які потрібні поверхні. Godot очікує, що він буде розміром з Mesh.ARRAY_MAX, тому змініть його відповідно.
var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)
Godot.Collections.Array surfaceArray = [];
surfaceArray.Resize((int)Mesh.ArrayType.Max);
Далі створіть масиви для кожного типу даних, які ви будете використовувати.
var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()
List<Vector3> verts = [];
List<Vector2> uvs = [];
List<Vector3> normals = [];
List<int> indices = [];
Після того, як ви заповнили свої масиви даних геометрією, ви можете створити меш, додавши кожен масив до 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()
{
Godot.Collections.Array surfaceArray = [];
surfaceArray.Resize((int)Mesh.ArrayType.Max);
// C# arrays cannot be resized or expanded, so use Lists to create geometry.
List<Vector3> verts = [];
List<Vector2> uvs = [];
List<Vector3> normals = [];
List<int> indices = [];
/***********************************
* 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 в підході до його створення. Ця реалізація не має нічого спільного з ArrayMesh-ами і є лише загальним підходом до створення сфери. Якщо у вас виникли проблеми з розумінням, або ви хочете дізнатися більше про процедурну геометрію, ви можете використовувати будь-який урок, який знайдете в Інтернеті.
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);