Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Використання MeshDataTool
MeshDataTool не використовується для створення геометрії. Але він корисний для динамічної зміни геометрії, наприклад, якщо ви хочете написати скрипт для тесселяції, спрощення, або деформування, меша.
MeshDataTool не такий швидкий, як зміна масивів безпосередньо за допомогою ArrayMesh. Однак він надає більше інформації та інструментів для роботи з сітками, ніж ArrayMesh. Коли використовується MeshDataTool, він обчислює дані сітки, які недоступні в ArrayMesh, такі як грані та ребра, які необхідні для певних алгоритмів сітки. Якщо вам не потрібна ця додаткова інформація, можливо, краще використовувати ArrayMesh.
Примітка
MeshDataTool можна використовувати лише в Мешах, які використовують PrimitiveType Mesh.PRIMITIVE_TRIANGLES.
Ми ініціалізуємо MeshDataTool з ArrayMesh, викликаючи create_from_surface(). Якщо в MeshDataTool вже є ініціалізовані дані, виклик create_from_surface() очистить їх. Або ж ви можете самостійно викликати clear() перед повторним використанням MeshDataTool.
У наведених нижче прикладах припустимо, що ArrayMesh під назвою mesh вже створено. Перегляньте зразок генерації меша в посібнику ArrayMesh.
var mdt = MeshDataTool.new()
mdt.create_from_surface(mesh, 0)
var mdt = new MeshDataTool();
mdt.CreateFromSurface(mesh, 0);
create_from_surface() використовує масиви вершин з ArrayMesh для обчислення двох додаткових масивів, один для ребр і один для граней, в загальному виходить три масиви.
Ребро - це зв'язок між будь-якими двома вершинами. Кожне ребро в масиві ребр містить посилання на дві вершини, з яких воно складається, і на дві грані, в яких воно міститься.
Грань - це трикутник, що складається з трьох вершин і трьох відповідних ребр. Кожна грань в масиві граней містить посилання на три вершини і три ребра, з яких вона складається.
Масив вершин містить інформацію про ребро, грань, нормаль, колір, дотичну, uv, uv2, кістку та вагу, пов'язану з кожною вершиною.
Для доступу до інформації з цих масивів використовується функція форми get_****():
mdt.get_vertex_count() # Returns the number of vertices in the vertex array.
mdt.get_vertex_faces(0) # Returns an array of faces that contain vertex[0].
mdt.get_face_normal(1) # Calculates and returns the face normal of the second face.
mdt.get_edge_vertex(10, 1) # Returns the second vertex comprising the edge at index 10.
mdt.GetVertexCount(); // Returns the number of vertices in the vertex array.
mdt.GetVertexFaces(0); // Returns an array of faces that contain vertex[0].
mdt.GetFaceNormal(1); // Calculates and returns the face normal of the second face.
mdt.GetEdgeVertex(10, 1); // Returns the second vertex comprising the edge at index 10.
Те, що ви вирішите зробити за допомогою цих функцій, залежить від вас. Поширений випадок використання полягає в тому, щоб ітерувати всі вершини і перетворити їх певним чином:
for i in range(mdt.get_vertex_count()):
var vert = mdt.get_vertex(i)
vert *= 2.0 # Scales the vertex by doubling its size.
mdt.set_vertex(i, vert)
for (var i = 0; i < mdt.GetVertexCount(); i++)
{
Vector3 vert = mdt.GetVertex(i);
vert *= 2.0f; // Scales the vertex by doubling its size.
mdt.SetVertex(i, vert);
}
Ці зміни не виконуються зразу на ArrayMesh. Якщо ви динамічно оновлюєте наявний ArrayMesh, то, перш ніж додавати нову поверхню, спочатку видаліть існуючу використовуючи commit_to_surface():
mesh.clear_surfaces() # Deletes all of the mesh's surfaces.
mdt.commit_to_surface(mesh)
mesh.ClearSurfaces(); // Deletes all of the mesh's surfaces.
mdt.CommitToSurface(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()
# Scale the vertices using noise.
vertex = vertex * (fnl.get_noise_3dv(vertex) * 0.5 + 0.75)
mdt.set_vertex(i, vertex)
# Calculate the 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 the vertex position using the vertex index.
var ap = mdt.get_vertex(a)
var bp = mdt.get_vertex(b)
var cp = mdt.get_vertex(c)
# Calculate the normal of the face.
var n = (bp - cp).cross(ap - bp).normalized()
# Add this face normal to the current vertex normals.
# 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 the vertices one last time to normalize their normals and
# set the vertex colors to these new normals.
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)
using Godot;
public partial class MyMeshInstance3D : MeshInstance3D
{
MeshDataTool mdt = new MeshDataTool();
FastNoiseLite fnl = new FastNoiseLite();
public override void _Ready()
{
fnl.Frequency = 0.7f;
ArrayMesh mesh = Mesh as ArrayMesh; // The mesh assigned the MeshInstance3D needs to be an ArrayMesh.
mdt.CreateFromSurface(mesh, 0);
for (var i = 0; i < mdt.GetVertexCount(); i++)
{
Vector3 vertex = mdt.GetVertex(i).Normalized();
// Scale the vertices using noise.
vertex = vertex * (fnl.GetNoise3Dv(vertex) * 0.5f + 0.75f);
mdt.SetVertex(i, vertex);
}
// Calculate the vertex normals, face-by-face.
for (var i = 0; i < mdt.GetFaceCount(); i++)
{
// Get the index in the vertex array.
var a = mdt.GetFaceVertex(i, 0);
var b = mdt.GetFaceVertex(i, 1);
var c = mdt.GetFaceVertex(i, 2);
// Get the vertex position using the vertex index.
var ap = mdt.GetVertex(a);
var bp = mdt.GetVertex(b);
var cp = mdt.GetVertex(c);
// Calculate the normal of the face.
var n = (bp - cp).Cross(ap - bp).Normalized();
// Add this face normal to the current vertex normals.
// This will not result in perfect normals, but it will be close.
mdt.SetVertexNormal(a, n + mdt.GetVertexNormal(a));
mdt.SetVertexNormal(b, n + mdt.GetVertexNormal(b));
mdt.SetVertexNormal(c, n + mdt.GetVertexNormal(c));
}
// Run through the vertices one last time to normalize their normals and
// set the vertex colors to these new normals.
for (var i = 0; i < mdt.GetVertexCount(); i++)
{
var v = mdt.GetVertexNormal(i).Normalized();
mdt.SetVertexNormal(i, v);
mdt.SetVertexColor(i, new Color(v.X, v.Y, v.Z));
}
mesh.ClearSurfaces();
mdt.CommitToSurface(mesh);
}
}