ArrayMeshの使用
このチュートリアルでは、 ArrayMesh の使用の基本について説明します。
そのためには、最大5つのパラメータを取る関数 add_surface_from_arrays() を使用します。最初の2つは必須ですが、残りの3つはオプションです。
1つ目は PrimitiveType です。これは指定された頂点に基づいてプリミティブをどのように配置するか、つまり頂点が三角形、線、点など、どれを表すかを GPU に指示する OpenGL の概念です。使用可能なオプションについては Mesh.PrimitiveType を参照してください。
2番目のパラメータ arrays はメッシュ情報を格納する実際の配列です。配列は空の括弧 [] で構成された通常のGodot配列です。サーフェスの構築に使用される情報の種類ごとに、 Packed**Array (例: PackedVector3Array、PackedInt32Array など) を格納します。
arrays の共通要素と、それが arrays 内で指定する位置を以下に示します。完全なリストは Mesh.ArrayType を参照してください。
インデックス |
Mesh.ArrayType |
配列タイプ |
|---|---|---|
0 |
|
|
1 |
|
|
2 |
|
PackedFloat32Array または PackedFloat64Array の4つのfloatのグループ。最初の3つのfloatは接線を決定し、最後のfloatは従法線方向を -1 または 1 として指定します。 |
3 |
|
|
4 |
|
|
5 |
|
|
10 |
|
4つのfloatのグループの PackedFloat32Array 。または4つの int のグループの PackedInt32Array。各グループには特定の頂点に影響を与える4つのボーンのインデックスがリストされます。 |
11 |
|
PackedFloat32Array または PackedFloat64Array の4つのfloatのグループ。各floatには |
12 |
|
ほとんどのケースでメッシュを作成するときは頂点の位置を使ってメッシュを定義します。通常は頂点配列 (index=0) は必須ですが、インデックス配列 (index=12) はオプションであり、含まれている場合にのみ使用されます。インデックス配列のみで頂点配列を使用せずにメッシュを作成することも可能ですが、それはこのチュートリアルでは解説しません。
その他の全ての配列は、頂点に関する情報を保持します。これらもオプションであり、含まれている場合にのみ使用されます。これらの配列の一部 (例: ARRAY_COLOR) は、頂点ごとに1つのエントリを使用して、頂点に関する追加情報を提供します。これらは頂点配列と同じサイズである必要があります。その他の配列 (例: ARRAY_TANGENT) は、1つの頂点を記述するために4つのエントリを使用します。これらは、頂点配列のちょうど4倍の大きさである必要があります。
以上の配列を準備してメッシュを生成するには、関数 add_surface_from_arrays() を使用します。これは最大4つのパラメーターを指定します。始めの2つは必須ですが、残りの2つはオプションです。
ArrayMeshの設定
エディターで MeshInstance3D を作成し、インスペクターでそれに ArrayMesh を追加します。通常、エディターで 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.
}
}
保存
最後に、ArrayMesh を保存するために ResourceSaver クラスを使用できます。これは事前にメッシュを生成して保存しておき、後で使用したい場合に便利です。
# 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);