arraymesh.rst 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. .. _doc_arraymesh:
  2. Using the ArrayMesh
  3. ===================
  4. This tutorial will present the basics of using an :ref:`ArrayMesh <class_arraymesh>`.
  5. To do so, we will use the function :ref:`add_surface_from_arrays() <class_ArrayMesh_method_add_surface_from_arrays>`,
  6. which takes up to five parameters. The first two are required, while the last three are optional.
  7. The first parameter is the ``PrimitiveType``, an OpenGL concept that instructs the GPU
  8. how to arrange the primitive based on the vertices given, i.e. whether they represent triangles,
  9. lines, points, etc. See :ref:`Mesh.PrimitiveType <enum_Mesh_PrimitiveType>` for the options available.
  10. The second parameter, ``arrays``, is the actual Array that stores the mesh information. The array is a normal Godot array that
  11. is constructed with empty brackets ``[]``. It stores a ``Packed**Array`` (e.g. PackedVector3Array,
  12. PackedInt32Array, etc.) for each type of information that will be used to build the surface.
  13. Common elements of ``arrays`` are listed below, together with the position they must have within ``arrays``.
  14. See :ref:`Mesh.ArrayType <enum_Mesh_ArrayType>` for a full list.
  15. .. list-table::
  16. :class: wrap-normal
  17. :width: 100%
  18. :widths: auto
  19. :header-rows: 1
  20. * - Index
  21. - Mesh.ArrayType Enum
  22. - Array type
  23. * - 0
  24. - ``ARRAY_VERTEX``
  25. - :ref:`PackedVector3Array <class_PackedVector3Array>` or :ref:`PackedVector2Array <class_PackedVector2Array>`
  26. * - 1
  27. - ``ARRAY_NORMAL``
  28. - :ref:`PackedVector3Array <class_PackedVector3Array>`
  29. * - 2
  30. - ``ARRAY_TANGENT``
  31. - :ref:`PackedFloat32Array <class_PackedFloat32Array>` or :ref:`PackedFloat64Array <class_PackedFloat64Array>` of groups of 4 floats. The first 3 floats determine the tangent, and the last float the binormal
  32. direction as -1 or 1.
  33. * - 3
  34. - ``ARRAY_COLOR``
  35. - :ref:`PackedColorArray <class_PackedColorArray>`
  36. * - 4
  37. - ``ARRAY_TEX_UV``
  38. - :ref:`PackedVector2Array <class_PackedVector2Array>` or :ref:`PackedVector3Array <class_PackedVector3Array>`
  39. * - 5
  40. - ``ARRAY_TEX_UV2``
  41. - :ref:`PackedVector2Array <class_PackedVector2Array>` or :ref:`PackedVector3Array <class_PackedVector3Array>`
  42. * - 10
  43. - ``ARRAY_BONES``
  44. - :ref:`PackedFloat32Array <class_PackedFloat32Array>` of groups of 4 floats or :ref:`PackedInt32Array <class_PackedInt32Array>` of groups of 4 ints. Each group lists indexes of 4 bones that affects a given vertex.
  45. * - 11
  46. - ``ARRAY_WEIGHTS``
  47. - :ref:`PackedFloat32Array <class_PackedFloat32Array>` or :ref:`PackedFloat64Array <class_PackedFloat64Array>` of groups of 4 floats. Each float lists the amount of weight the corresponding bone in ``ARRAY_BONES`` has on a given vertex.
  48. * - 12
  49. - ``ARRAY_INDEX``
  50. - :ref:`PackedInt32Array <class_PackedInt32Array>`
  51. 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
  52. 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.
  53. 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``)
  54. 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
  55. four entries to describe a single vertex. These must be exactly four times larger than the vertex array.
  56. For normal usage, the last three parameters in :ref:`add_surface_from_arrays() <class_arraymesh_method_add_surface_from_arrays>` are typically left empty.
  57. Setting up the ArrayMesh
  58. ------------------------
  59. In the editor, create a :ref:`MeshInstance3D <class_meshinstance3d>` and add an :ref:`ArrayMesh <class_arraymesh>` to it in the Inspector.
  60. Normally, adding an ArrayMesh in the editor is not useful, but in this case it allows us to access the ArrayMesh
  61. from code without creating one.
  62. Next, add a script to the MeshInstance3D.
  63. Under ``_ready()``, create a new Array.
  64. .. tabs::
  65. .. code-tab:: gdscript GDScript
  66. var surface_array = []
  67. .. code-tab:: csharp C#
  68. var surfaceArray = new Godot.Collections.Array();
  69. This will be the array that we keep our surface information in - it will hold
  70. all the arrays of data that the surface needs. Godot will expect it to be of
  71. size ``Mesh.ARRAY_MAX``, so resize it accordingly.
  72. .. tabs::
  73. .. code-tab:: gdscript GDScript
  74. var surface_array = []
  75. surface_array.resize(Mesh.ARRAY_MAX)
  76. .. code-tab:: csharp C#
  77. var surfaceArray = new Godot.Collections.Array();
  78. surfaceArray.Resize((int)Mesh.ArrayType.Max);
  79. Next create the arrays for each data type you will use.
  80. .. tabs::
  81. .. code-tab:: gdscript GDScript
  82. var verts = PackedVector3Array()
  83. var uvs = PackedVector2Array()
  84. var normals = PackedVector3Array()
  85. var indices = PackedInt32Array()
  86. .. code-tab:: csharp C#
  87. var verts = new List<Vector3>();
  88. var uvs = new List<Vector2>();
  89. var normals = new List<Vector3>();
  90. var indices = new List<int>();
  91. Once you have filled your data arrays with your geometry you can create a mesh
  92. by adding each array to ``surface_array`` and then committing to the mesh.
  93. .. tabs::
  94. .. code-tab:: gdscript GDScript
  95. surface_array[Mesh.ARRAY_VERTEX] = verts
  96. surface_array[Mesh.ARRAY_TEX_UV] = uvs
  97. surface_array[Mesh.ARRAY_NORMAL] = normals
  98. surface_array[Mesh.ARRAY_INDEX] = indices
  99. # No blendshapes, lods, or compression used.
  100. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
  101. .. code-tab:: csharp C#
  102. surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
  103. surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
  104. surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
  105. surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
  106. var arrMesh = Mesh as ArrayMesh;
  107. if (arrMesh != null)
  108. {
  109. // No blendshapes, lods, or compression used.
  110. arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
  111. }
  112. .. note:: In this example, we used ``Mesh.PRIMITIVE_TRIANGLES``, but you can use any primitive type
  113. available from mesh.
  114. Put together, the full code looks like:
  115. .. tabs::
  116. .. code-tab:: gdscript GDScript
  117. extends MeshInstance3D
  118. func _ready():
  119. var surface_array = []
  120. surface_array.resize(Mesh.ARRAY_MAX)
  121. # PackedVector**Arrays for mesh construction.
  122. var verts = PackedVector3Array()
  123. var uvs = PackedVector2Array()
  124. var normals = PackedVector3Array()
  125. var indices = PackedInt32Array()
  126. #######################################
  127. ## Insert code here to generate mesh ##
  128. #######################################
  129. # Assign arrays to surface array.
  130. surface_array[Mesh.ARRAY_VERTEX] = verts
  131. surface_array[Mesh.ARRAY_TEX_UV] = uvs
  132. surface_array[Mesh.ARRAY_NORMAL] = normals
  133. surface_array[Mesh.ARRAY_INDEX] = indices
  134. # Create mesh surface from mesh array.
  135. # No blendshapes, lods, or compression used.
  136. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
  137. .. code-tab:: csharp C#
  138. public partial class MyMeshInstance3D : MeshInstance3D
  139. {
  140. public override void _Ready()
  141. {
  142. var surfaceArray = new Godot.Collections.Array();
  143. surfaceArray.Resize((int)Mesh.ArrayType.Max);
  144. // C# arrays cannot be resized or expanded, so use Lists to create geometry.
  145. var verts = new List<Vector3>();
  146. var uvs = new List<Vector2>();
  147. var normals = new List<Vector3>();
  148. var indices = new List<int>();
  149. /***********************************
  150. * Insert code here to generate mesh.
  151. * *********************************/
  152. // Convert Lists to arrays and assign to surface array
  153. surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
  154. surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
  155. surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
  156. surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
  157. var arrMesh = Mesh as ArrayMesh;
  158. if (arrMesh != null)
  159. {
  160. // Create mesh surface from mesh array
  161. // No blendshapes, lods, or compression used.
  162. arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
  163. }
  164. }
  165. }
  166. The code that goes in the middle can be whatever you want. Below we will present some
  167. example code for generating a sphere.
  168. Generating geometry
  169. -------------------
  170. Here is sample code for generating a sphere. Although the code is presented in
  171. GDScript, there is nothing Godot specific about the approach to generating it.
  172. This implementation has nothing in particular to do with ArrayMeshes and is just a
  173. generic approach to generating a sphere. If you are having trouble understanding it
  174. or want to learn more about procedural geometry in general, you can use any tutorial
  175. that you find online.
  176. .. tabs::
  177. .. code-tab:: gdscript GDScript
  178. extends MeshInstance3D
  179. var rings = 50
  180. var radial_segments = 50
  181. var radius = 1
  182. func _ready():
  183. # Insert setting up the PackedVector**Arrays here.
  184. # Vertex indices.
  185. var thisrow = 0
  186. var prevrow = 0
  187. var point = 0
  188. # Loop over rings.
  189. for i in range(rings + 1):
  190. var v = float(i) / rings
  191. var w = sin(PI * v)
  192. var y = cos(PI * v)
  193. # Loop over segments in ring.
  194. for j in range(radial_segments + 1):
  195. var u = float(j) / radial_segments
  196. var x = sin(u * PI * 2.0)
  197. var z = cos(u * PI * 2.0)
  198. var vert = Vector3(x * radius * w, y * radius, z * radius * w)
  199. verts.append(vert)
  200. normals.append(vert.normalized())
  201. uvs.append(Vector2(u, v))
  202. point += 1
  203. # Create triangles in ring using indices.
  204. if i > 0 and j > 0:
  205. indices.append(prevrow + j - 1)
  206. indices.append(prevrow + j)
  207. indices.append(thisrow + j - 1)
  208. indices.append(prevrow + j)
  209. indices.append(thisrow + j)
  210. indices.append(thisrow + j - 1)
  211. prevrow = thisrow
  212. thisrow = point
  213. # Insert committing to the ArrayMesh here.
  214. .. code-tab:: csharp C#
  215. public partial class MyMeshInstance3D : MeshInstance3D
  216. {
  217. private int _rings = 50;
  218. private int _radialSegments = 50;
  219. private float _radius = 1;
  220. public override void _Ready()
  221. {
  222. // Insert setting up the surface array and lists here.
  223. // Vertex indices.
  224. var thisRow = 0;
  225. var prevRow = 0;
  226. var point = 0;
  227. // Loop over rings.
  228. for (var i = 0; i < _rings + 1; i++)
  229. {
  230. var v = ((float)i) / _rings;
  231. var w = Mathf.Sin(Mathf.Pi * v);
  232. var y = Mathf.Cos(Mathf.Pi * v);
  233. // Loop over segments in ring.
  234. for (var j = 0; j < _radialSegments + 1; j++)
  235. {
  236. var u = ((float)j) / _radialSegments;
  237. var x = Mathf.Sin(u * Mathf.Pi * 2);
  238. var z = Mathf.Cos(u * Mathf.Pi * 2);
  239. var vert = new Vector3(x * _radius * w, y * _radius, z * _radius * w);
  240. verts.Add(vert);
  241. normals.Add(vert.Normalized());
  242. uvs.Add(new Vector2(u, v));
  243. point += 1;
  244. // Create triangles in ring using indices.
  245. if (i > 0 && j > 0)
  246. {
  247. indices.Add(prevRow + j - 1);
  248. indices.Add(prevRow + j);
  249. indices.Add(thisRow + j - 1);
  250. indices.Add(prevRow + j);
  251. indices.Add(thisRow + j);
  252. indices.Add(thisRow + j - 1);
  253. }
  254. }
  255. prevRow = thisRow;
  256. thisRow = point;
  257. }
  258. // Insert committing to the ArrayMesh here.
  259. }
  260. }
  261. Saving
  262. ------
  263. Finally, we can use the :ref:`ResourceSaver <class_resourcesaver>` class to save the ArrayMesh.
  264. This is useful when you want to generate a mesh and then use it later without having to re-generate it.
  265. .. tabs::
  266. .. code-tab:: gdscript GDScript
  267. # Saves mesh to a .tres file with compression enabled.
  268. ResourceSaver.save(mesh, "res://sphere.tres", ResourceSaver.FLAG_COMPRESS)
  269. .. code-tab:: csharp C#
  270. // Saves mesh to a .tres file with compression enabled.
  271. ResourceSaver.Save(Mesh, "res://sphere.tres", ResourceSaver.SaverFlags.Compress);