mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-04 14:11:02 +03:00
Improve clarity in procedural generation tutorials (#6004)
This commit is contained in:
@@ -3,53 +3,84 @@
|
||||
Using the ArrayMesh
|
||||
===================
|
||||
|
||||
This tutorial will present the basics of using an :ref:`ArrayMesh <class_arraymesh>`
|
||||
This tutorial will present the basics of using an :ref:`ArrayMesh <class_arraymesh>`.
|
||||
|
||||
To do so, we will use the function :ref:`add_surface_from_arrays() <class_ArrayMesh_method_add_surface_from_arrays>`,
|
||||
which takes up to four parameters. The first two are required, while the second two are optional.
|
||||
|
||||
The first is the ``PrimitiveType``, this is an OpenGL concept that instructs the GPU
|
||||
how to arrange the primitive based on the vertices given whether it is triangles,
|
||||
lines, points, etc. A complete list can be found under the :ref:`Mesh <class_mesh>`
|
||||
class reference page.
|
||||
The first parameter is the ``PrimitiveType``, an OpenGL concept that instructs the GPU
|
||||
how to arrange the primitive based on the vertices given, i.e. whether they represent triangles,
|
||||
lines, points, etc. See :ref:`Mesh.PrimitiveType <enum_Mesh_PrimitiveType>` for the options available.
|
||||
|
||||
The second 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 ``Pool**Array`` (e.g. PoolVector3Array,
|
||||
PoolIntArray, etc.) for each type of information.
|
||||
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 ``Pool**Array``
|
||||
(e.g. PoolVector3Array, PoolIntArray, etc.) for each type of information that will be used to build the surface.
|
||||
|
||||
- ``ARRAY_VERTEX`` = 0 | PoolVector3Array or PoolVector2Array
|
||||
- ``ARRAY_NORMAL`` = 1 | PoolVector3Array
|
||||
- ``ARRAY_TANGENT`` = 2 | PoolRealArray of groups of 4 floats. First 3 floats determine the tangent, and
|
||||
the last the binormal direction as -1 or 1.
|
||||
- ``ARRAY_COLOR`` = 3 | PoolColorArray
|
||||
- ``ARRAY_TEX_UV`` = 4 | PoolVector2Array or PoolVector3Array
|
||||
- ``ARRAY_TEX_UV2`` = 5 | PoolVector2Array or PoolVector3Array
|
||||
- ``ARRAY_BONES`` = 6 | PoolRealArray of groups of 4 floats or PoolIntArray of groups of 4 ints. Each group lists indexes of 4 bones that affects a given vertex.
|
||||
- ``ARRAY_WEIGHTS`` = 7 | PoolRealArray of groups of 4 floats. Each float lists the amount of weight an determined bone on ``ARRAY_BONES`` has on a given vertex.
|
||||
- ``ARRAY_INDEX`` = 8 | PoolIntArray
|
||||
The possible elements of ``arrays`` are listed below, together with the position they must have within ``arrays``.
|
||||
See also :ref:`Mesh.ArrayType <enum_Mesh_ArrayType>`.
|
||||
|
||||
The Array of vertices is always required. All the others are optional and will only be used if included.
|
||||
|
||||
Each array needs to have the same number of elements as the vertex array except for the index array.
|
||||
For arrays like tangents, an element is a group of 4 floats. So the array size will be four times
|
||||
the size of the vertex array size, but they will have the same number of elements
|
||||
.. list-table::
|
||||
:class: wrap-normal
|
||||
:width: 100%
|
||||
:widths: auto
|
||||
:header-rows: 1
|
||||
|
||||
The index array is unique.
|
||||
* - Index
|
||||
- Mesh.ArrayType Enum
|
||||
- Array type
|
||||
|
||||
The third parameter is an array of blendshapes for the Mesh to use. While this tutorial does not cover
|
||||
using blendshapes, it is possible to specify them when creating a surface from arrays.
|
||||
* - 0
|
||||
- ``ARRAY_VERTEX``
|
||||
- :ref:`PoolVector3Array <class_PoolVector3Array>` or :ref:`PoolVector2Array <class_PoolVector2Array>`
|
||||
|
||||
The last parameter is the compress flags which specifies which arrays to store with half as many bits. The
|
||||
values can be found in the classref for :ref:`VisualServer <class_visualserver>` under :ref:`ArrayFormat <enum_visualserver_arrayformat>`.
|
||||
* - 1
|
||||
- ``ARRAY_NORMAL``
|
||||
- :ref:`PoolVector3Array <class_PoolVector3Array>`
|
||||
|
||||
For normal usage you will find it is best to leave the last two parameters empty.
|
||||
* - 2
|
||||
- ``ARRAY_TANGENT``
|
||||
- :ref:`PoolRealArray <class_PoolRealArray>` of groups of 4 floats. First 3 floats determine the tangent, and
|
||||
the last the binormal direction as -1 or 1.
|
||||
|
||||
* - 3
|
||||
- ``ARRAY_COLOR``
|
||||
- :ref:`PoolColorArray <class_PoolColorArray>`
|
||||
|
||||
* - 4
|
||||
- ``ARRAY_TEX_UV``
|
||||
- :ref:`PoolVector2Array <class_PoolVector2Array>` or :ref:`PoolVector3Array <class_PoolVector3Array>`
|
||||
|
||||
* - 5
|
||||
- ``ARRAY_TEX_UV2``
|
||||
- :ref:`PoolVector2Array <class_PoolVector2Array>` or :ref:`PoolVector3Array <class_PoolVector3Array>`
|
||||
|
||||
* - 6
|
||||
- ``ARRAY_BONES``
|
||||
- :ref:`PoolRealArray <class_PoolRealArray>` of groups of 4 floats or :ref:`PoolIntArray <class_PoolIntArray>` of groups of 4 ints. Each group lists indexes of 4 bones that affects a given vertex.
|
||||
|
||||
* - 7
|
||||
- ``ARRAY_WEIGHTS``
|
||||
- :ref:`PoolRealArray <class_PoolRealArray>` of groups of 4 floats. Each float lists the amount of weight an determined bone on ``ARRAY_BONES`` has on a given vertex.
|
||||
|
||||
* - 8
|
||||
- ``ARRAY_INDEX``
|
||||
- :ref:`PoolIntArray <class_PoolIntArray>`
|
||||
|
||||
The array of vertices (at index 0) is always required. The index array is optional and will only be used if included. We won't use it in this tutorial.
|
||||
|
||||
All the other arrays carry information about the vertices. They are also 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 two parameters in :ref:`add_surface_from_arrays() <class_arraymesh_method_add_surface_from_arrays>` are typically left empty.
|
||||
|
||||
ArrayMesh
|
||||
---------
|
||||
|
||||
Add an :ref:`ArrayMesh <class_arraymesh>` to a MeshInstance. Normally, adding an ArrayMesh in
|
||||
the editor is not useful, but in this case it allows as to access the ArrayMesh from code
|
||||
without creating one.
|
||||
In the editor, create a :ref:`MeshInstance <class_meshinstance>` and add an :ref:`ArrayMesh <class_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 MeshInstance.
|
||||
|
||||
@@ -58,17 +89,17 @@ Under ``_ready()``, create a new Array.
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var arr = []
|
||||
var surface_array = []
|
||||
|
||||
This will be the array that we keep our surface information in, it will hold
|
||||
This will be the array that we keep our surface information in - it will hold
|
||||
all the arrays of data that the surface needs. Godot will expect it to be of
|
||||
size ``Mesh.ARRAY_MAX``, so resize it accordingly.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var arr = []
|
||||
arr.resize(Mesh.ARRAY_MAX)
|
||||
var surface_array = []
|
||||
surface_array.resize(Mesh.ARRAY_MAX)
|
||||
|
||||
Next create the arrays for each data type you will use.
|
||||
|
||||
@@ -86,17 +117,17 @@ by adding each array to ``surface_array`` and then committing to the mesh.
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
arr[Mesh.ARRAY_VERTEX] = verts
|
||||
arr[Mesh.ARRAY_TEX_UV] = uvs
|
||||
arr[Mesh.ARRAY_NORMAL] = normals
|
||||
arr[Mesh.ARRAY_INDEX] = indices
|
||||
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
|
||||
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arr) # No blendshapes or compression used.
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.
|
||||
|
||||
.. note:: In this example, we used ``Mesh.PRIMITIVE_TRIANGLES``, but you can use any primitive type
|
||||
available from mesh.
|
||||
|
||||
Put together the full code looks like:
|
||||
Put together, the full code looks like:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
@@ -104,10 +135,10 @@ Put together the full code looks like:
|
||||
extends MeshInstance
|
||||
|
||||
func _ready():
|
||||
var arr = []
|
||||
arr.resize(Mesh.ARRAY_MAX)
|
||||
var surface_array= []
|
||||
surface_array.resize(Mesh.ARRAY_MAX)
|
||||
|
||||
# PoolVectorXXArrays for mesh construction.
|
||||
# PoolVector**Arrays for mesh construction.
|
||||
var verts = PoolVector3Array()
|
||||
var uvs = PoolVector2Array()
|
||||
var normals = PoolVector3Array()
|
||||
@@ -118,17 +149,17 @@ Put together the full code looks like:
|
||||
#######################################
|
||||
|
||||
# Assign arrays to mesh array.
|
||||
arr[Mesh.ARRAY_VERTEX] = verts
|
||||
arr[Mesh.ARRAY_TEX_UV] = uvs
|
||||
arr[Mesh.ARRAY_NORMAL] = normals
|
||||
arr[Mesh.ARRAY_INDEX] = indices
|
||||
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.
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arr) # No blendshapes or compression used.
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) # No blendshapes or compression used.
|
||||
|
||||
|
||||
The code that goes in the middle can be whatever you want. Below we will present some example code that
|
||||
could go in the middle.
|
||||
The code that goes in the middle can be whatever you want. Below we will present some example code
|
||||
for generating a sphere.
|
||||
|
||||
Generating geometry
|
||||
-------------------
|
||||
@@ -152,7 +183,7 @@ that you find online.
|
||||
|
||||
func _ready():
|
||||
|
||||
# Set up the PoolVectorXArrays.
|
||||
# Insert setting up the PoolVector**Arrays here.
|
||||
|
||||
# Vertex indices.
|
||||
var thisrow = 0
|
||||
@@ -198,21 +229,16 @@ that you find online.
|
||||
prevrow = thisrow
|
||||
thisrow = point
|
||||
|
||||
# Commit to the ArrayMesh.
|
||||
|
||||
Combined with the code above, this code will generate a sphere.
|
||||
|
||||
When it comes to generating geometry with the ArrayMesh you need to understand what goes
|
||||
in each array and then you can follow tutorials for any language/engine and convert it into Godot.
|
||||
# Insert committing to the ArrayMesh here.
|
||||
|
||||
Saving
|
||||
------
|
||||
|
||||
Finally, Godot provides a single method to save ArrayMeshes using the :ref:`ResourceSaver <class_resourcesaver>`
|
||||
class. This is useful when you want to generate a mesh and then use it later without having to re-generate.
|
||||
Finally, we can use the :ref:`ResourceSaver <class_resourcesaver>` class to save the ArrayMesh.
|
||||
This is useful when you want to generate a mesh and then use it later without having to re-generate it.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Saves mesh to a .tres file with compression enabled.
|
||||
ResourceSaver.save("res://sphere.tres", mesh, 32)
|
||||
ResourceSaver.save("res://sphere.tres", mesh, ResourceSaver.FLAG_COMPRESS)
|
||||
|
||||
@@ -36,11 +36,11 @@ to draw it in different parts of your scene with different materials or transfor
|
||||
rotation, position etc.).
|
||||
|
||||
If you are going to draw the same object many times, it can be helpful to use a MultiMesh with
|
||||
a MultiMeshInstance. The MultiMeshInstance draws meshes thousands of times very
|
||||
cheaply. It takes advantage of hardware instancing in order to do so. The drawback with
|
||||
using a MultiMeshInstance is that you are limited to one material for all instances. It uses an
|
||||
instance array to store different colors and transformations for each instance, but all the
|
||||
instances use the same material.
|
||||
a MultiMeshInstance. MultiMeshInstances draw meshes thousands of times very
|
||||
cheaply by taking advantage of hardware instancing. The drawback with
|
||||
using a MultiMeshInstance is that each of your mesh's surfaces are limited to one material for
|
||||
all instances. It uses an instance array to store different colors and transformations for each
|
||||
instance, but all the instances of each surface use the same material.
|
||||
|
||||
What a Mesh is
|
||||
--------------
|
||||
@@ -54,7 +54,7 @@ Surfaces
|
||||
^^^^^^^^
|
||||
|
||||
Each surface has its own material. Alternatively, you can override the material for all surfaces
|
||||
in the Mesh when you use a MeshInstance using ``MeshInstance.override_material``.
|
||||
in the Mesh when you use a MeshInstance using the :ref:`material_override <class_GeometryInstance_property_material_override>` property.
|
||||
|
||||
Surface array
|
||||
^^^^^^^^^^^^^
|
||||
@@ -62,6 +62,7 @@ Surface array
|
||||
The surface array is an array of length ``ArrayMesh.ARRAY_MAX``. Each position in the array is
|
||||
filled with a sub-array containing per-vertex information. For example, the array located at
|
||||
``ArrayMesh.ARRAY_NORMAL`` is a :ref:`PoolVector3Array <class_PoolVector3Array>` of vertex normals.
|
||||
See :ref:`Mesh.ArrayType <enum_Mesh_ArrayType>` for more information.
|
||||
|
||||
The surface array can be indexed or non-indexed. Creating a non-indexed array is as easy as not assigning
|
||||
an array at the index ``ArrayMesh.ARRAY_INDEX``. A non-indexed array stores unique vertex information for
|
||||
@@ -80,7 +81,7 @@ be provided in the following tutorials.
|
||||
ArrayMesh
|
||||
^^^^^^^^^
|
||||
|
||||
The ArrayMesh resource extends Mesh to add a few different quality of life functions, and most
|
||||
The ArrayMesh resource extends Mesh to add a few different quality of life functions and, most
|
||||
importantly, the ability to construct a Mesh surface through scripting.
|
||||
|
||||
For more information about the ArrayMesh, please see the :ref:`ArrayMesh tutorial <doc_arraymesh>`.
|
||||
@@ -105,11 +106,11 @@ ImmediateGeometry
|
||||
|
||||
ImmediateGeometry is a node that uses an immediate mode style interface (like SurfaceTool) to draw objects. The
|
||||
difference between ImmediateGeometry and the SurfaceTool is that ImmediateGeometry is a node itself that can be
|
||||
added to the scene tree and is drawn directly from the code. The SurfaceTool generates a Mesh that needs to be added
|
||||
a MeshInstance to be seen.
|
||||
added to the scene tree and is drawn directly from the code, while the SurfaceTool generates a Mesh that needs to be added
|
||||
to a MeshInstance to be seen.
|
||||
|
||||
ImmediateGeometry is useful for prototyping because of the straightforward API, but it is slow because the geometry
|
||||
is rebuilt every frame. It is most useful for quickly adding simple geometry to debug visually (e.g. by drawing lines to
|
||||
ImmediateGeometry is useful for prototyping because of its straightforward API, but it is slow because the geometry
|
||||
is rebuilt every frame. It is most useful for adding simple geometry for visual debugging (e.g. by drawing lines to
|
||||
visualize physics raycasts etc.).
|
||||
|
||||
For more information about ImmediateGeometry, please see the :ref:`ImmediateGeometry tutorial <doc_immediategeometry>`.
|
||||
@@ -117,7 +118,7 @@ For more information about ImmediateGeometry, please see the :ref:`ImmediateGeom
|
||||
Which one should I use?
|
||||
-----------------------
|
||||
|
||||
Which method you use depends on what you are trying to do and what kind of procedure you are comfortable with.
|
||||
Which approach you use depends on what you are trying to do and what kind of procedure you are comfortable with.
|
||||
|
||||
Both SurfaceTool and ArrayMesh are excellent for generating static geometry (meshes) that don't change over time.
|
||||
|
||||
@@ -130,5 +131,5 @@ an ArrayMesh every frame.
|
||||
|
||||
The MeshDataTool is not fast, but it gives you access to all kinds of properties of the mesh that you don't get with the others
|
||||
(edges, faces, etc.). It is incredibly useful when you need that sort of data to transform the mesh, but it is not a good idea
|
||||
to use it if that information is not needed. The MeshDataTool is best used if you are going to be using an algorithm that requires
|
||||
to use it if that extra information is not needed. The MeshDataTool is best used if you are going to be using an algorithm that requires
|
||||
access to the face or edge array.
|
||||
|
||||
@@ -13,21 +13,19 @@ for certain mesh algorithms. If you do not need this extra information then it m
|
||||
|
||||
.. note:: MeshDataTool can only be used on Meshes that use the PrimitiveType ``Mesh.PRIMITIVE_TRIANGLES``.
|
||||
|
||||
As an example, let's walk through the process of deforming the mesh generated in the :ref:`ArrayMesh tutorial <doc_arraymesh>`.
|
||||
We initialize the MeshDataTool from an ArrayMesh by calling ``create_from_surface()``. If there is already data initialized in the MeshDataTool,
|
||||
calling ``create_from_surface()`` will clear it for you. Alternatively, you can call ``clear()`` yourself before re-using the MeshDataTool.
|
||||
|
||||
Assume the mesh is stored in an ArrayMesh named ``mesh``. We then initialize the MeshDataTool from
|
||||
``mesh`` by calling ``create_from_surface()``. If there is already data initialized in the MeshDataTool
|
||||
calling ``create_from_surface()`` will clear it for you. Alternatively, you can call ``clear()`` yourself
|
||||
before re-using the MeshDataTool
|
||||
In the examples below, assume an ArrayMesh called ``mesh`` has already been created. See :ref:`ArrayMesh tutorial <doc_arraymesh>` for an example of mesh generation.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var mdt = MeshDataTool.new()
|
||||
mdt.create_from_surface(mesh)
|
||||
mdt.create_from_surface(mesh, 0)
|
||||
|
||||
``create_from_surface()`` uses the vertex arrays from the ArrayMesh to calculate two additional arrays,
|
||||
one for edges and one for faces.
|
||||
one for edges and one for faces, for a total of three arrays.
|
||||
|
||||
An edge is a connection between any two vertices. Each edge in the edge array contains a reference to
|
||||
the two vertices it is composed of, and up to two faces that it is contained within.
|
||||
@@ -35,7 +33,7 @@ the two vertices it is composed of, and up to two faces that it is contained wit
|
||||
A face is a triangle made up of three vertices and three corresponding edges. Each face in the face array contains
|
||||
a reference to the three vertices and three edges it is composed of.
|
||||
|
||||
The vertex array contains edges, faces, normals, color, tangent, uv, uv2, bones, and weight information connected
|
||||
The vertex array contains edge, face, normal, color, tangent, uv, uv2, bone, and weight information connected
|
||||
with each vertex.
|
||||
|
||||
To access information from these arrays you use a function of the form ``get_****()``:
|
||||
@@ -45,7 +43,7 @@ To access information from these arrays you use a function of the form ``get_***
|
||||
|
||||
mdt.get_vertex_count() # Returns number of vertices in vertex array.
|
||||
mdt.get_vertex_faces(0) # Returns array of faces that contain vertex[0].
|
||||
mdt.get_face_normal(1) # Calculates and returns face normal.
|
||||
mdt.get_face_normal(1) # Calculates and returns face normal of the second face.
|
||||
mdt.get_edge_vertex(10, 1) # Returns the second vertex comprising the edge at index 10.
|
||||
|
||||
What you choose to do with these functions is up to you. A common use case is to iterate over all vertices
|
||||
@@ -59,8 +57,8 @@ and transform them in some way:
|
||||
vert *= 2.0 # Scales the vertex by doubling size.
|
||||
mdt.set_vertex(i, vert)
|
||||
|
||||
Finally, ``commit_to_surface()`` adds a new surface to the ArrayMesh. So if you are dynamically
|
||||
updating an existing ArrayMesh, first delete the existing surface before adding a new one.
|
||||
These modifications are not done in place on the ArrayMesh. If you are dynamically updating an existing ArrayMesh,
|
||||
first delete the existing surface before adding a new one using :ref:`commit_to_surface() <class_meshdatatool_method_commit_to_surface>`:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
@@ -68,7 +66,8 @@ updating an existing ArrayMesh, first delete the existing surface before adding
|
||||
mesh.surface_remove(0) # Deletes the first surface of the mesh.
|
||||
mdt.commit_to_surface(mesh)
|
||||
|
||||
Below is a complete example that creates a pulsing blob complete with new normals and vertex colors.
|
||||
Below is a complete example that turns a spherical mesh called ``mesh`` into a randomly deformed blob complete with updated normals and vertex colors.
|
||||
See :ref:`ArrayMesh tutorial <doc_arraymesh>` for how to generate the base mesh.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
Reference in New Issue
Block a user