virtualx-engine-docs/_sources/tutorials/3d/merge_groups.rst.txt
2024-10-23 09:41:33 -07:00

273 lines
11 KiB
ReStructuredText

.. _doc_merge_groups:
MergeGroups
===========
The ``MergeGroup`` node allows the user to merge similar meshes so that they can
be grouped into a smaller number of nodes, and render using a single draw call
(per :ref:`Material <class_Material>`). This can greatly increase performance when
used with care.
Explanation
-----------
Usually individual meshes in Godot are represented by their own
:ref:`VisualInstance <class_VisualInstance>` in the
:ref:`SceneTree <class_SceneTree>`, such as :ref:`MeshInstance <class_MeshInstance>`.
These:
- Move independently
- Cull independently
- Render individually
In terms of performance however, each ``VisualInstance`` adds a cost, both in
terms of management, and in terms of rendering. Each mesh (and each surface on a
``MeshInstance`` is rendered as an individual mesh) will require an expensive
draw call to the graphics API, and often incur expensive rendering state changes.
If instead we can identify groups of meshes that could be potentially be
rendered together, and merge them ahead of time (either at design time, or
during level load), this can potentially lighten the load on the CPU and GPU,
and increase frame rates.
The merging trade-off
^^^^^^^^^^^^^^^^^^^^^
Lowering draw calls and reducing state changes tends to increase performance.
However, merged meshes are culled as a unit, and therefore if any part of the
merged mesh is visible in the view frustum, the cost of rendering all parts of
the mesh are incurred. For this reason it is recommended that you evaluate
performance both before and after merging, in order to find the best balance in
each case.
.. tip::
Remember that the bottlenecks may be different on different hardware.
Be sure to test on the lowest-end platforms you're targeting for your
project, especially mobile devices and integrated graphics.
Requirements
------------
There are some requirements for any group of meshes to be merged.
The most important requirement is that the source meshes must not move in
relation to each other. They must be either be completely static (non-moving)
or move together only as a single unit. This is because once several meshes are
merged into one, the relative transforms of the source meshes cannot change,
the transforms are baked into the new geometry.
The simplest candidate for merging is static level geometry, such as walls,
floors, tables etc. But groups of meshes that move "as one" are also good
candidates. Examples might be a pirate ship, or spacecraft.
Using MergeGroups
-----------------
After adding a ``MergeGroup`` node into your ``SceneTree``, move all the
VisualInstances you want to be merged so that they are parented by the
``MergeGroup`` (either as children, or grandchildren). By default, if you now
run the scene, the engine will automatically merge suitable meshes as the
``MergeGroup`` enters the ``SceneTree``.
.. image:: img/merge_group_house.webp
You can change the transform of the ``MergeGroup`` at runtime, and the whole
group will move as a unit (``MergeGroup`` is derived from
:ref:`Spatial <class_Spatial>`, so can be manipulated in all the regular ways,
such as changing transform or visibility).
Mesh similarity
---------------
It is important to note that not all meshes (or more accurately, mesh surfaces,
because merging takes place at the level of mesh surfaces) can be merged
together. There are strict limitations for the mesh surfaces to match.
All the following properties must match for merging to take place:
- Material
- Shadow casting settings
- Visibility
- Layer mask
- Portal settings
- Cull margin
- Lightmap settings
Usually this means only multiple instances of the same mesh will be merged (such
as a group of walls), but similar variations of mesh (e.g. different sized
tables) can sometimes be merged. Additionally, if you reuse materials (using
e.g. a "wood" material in several different meshes), these can often be merged.
Exceptions
----------
When you place a hierarchy of nodes underneath a ``MergeGroup``, such as a
pirate ship, there may be exceptions - meshes that you *do not* want merged.
This may be because, for instance, you may want them to move independently of
the ``MergeGroup``. An example might be a steering wheel, or sail.
Godot allows you fine control over exactly which nodes should be considered for
merging, via the ``merging_mode`` property which is present in the inspector
for every ``Spatial``, inside the ``Misc`` tab.
Merging modes:
- ``Inherit`` - Inherit the setting from the parent. The default for the scene root node is ``On``.
- ``On`` - Change the mode of the node (and any inheriting children) to allow merging.
- ``Off`` - Change the mode of the node (and any inheriting children) to disallow merging.
This means that if you, e.g. set the mode of a steering wheel to ``Off``, it
will not be considered for merging, and neither will any children or
grandchildren of the steering wheel (unless one of them explicitly reactivates
merging with an ``On`` ``merge_mode``).
Ways to use MergeGroups
-----------------------
There are three ways to use MergeGroups:
AutoMerge
^^^^^^^^^
The ``MergeGroup`` will attempt to merge any descendent merges as it enters the
``SceneTree``.
This is the simplest method, and the best introduction to merging.
Baking in the Editor
^^^^^^^^^^^^^^^^^^^^
When the ``MergeGroup`` is selected in the editor, a new ``bake`` button should
appear in the toolbar. This allows you to bake the entire merged scene out to a
file (.tscn (text scene) or .scn (binary scene)) at *design time* rather than at
*runtime*. This can be used creatively to build sensible composite objects which
are later used to compose a game level (this general approach is often known as
"kitbashing").
Additionally, baking in advance offers a great way to preview what will happen
when merging at runtime. It allows you to open the merged scene, and see which
meshes were successfully merged, and which were problematic.
.. figure:: img/bake_merge_group.webp
:align: center
Pros
~~~~
- Allows you easily see the results of merging
- Allows you to further build levels using these merged scenes
- No time is taken by the merging process at runtime
Cons
~~~~
- Merged scenes typically require substantially more storage than the original meshes
- The size of your exported game will likely increase
- Larger merged scenes may take longer to load
- Larger merged scenes may take up more RAM at runtime, especially on the GPU
If you merge, e.g. 10 boxes, the merged scene will have to store 10 times as
much geometry data, as the polygons are duplicated. More storage means your
exported game will be larger, the data will take longer to load, and consume
more RAM at runtime. For this reason, baking in advance tends to be more
practical with low poly meshes and art styles.
.. tip::
Due to the increased storage requirements, it is recommended that, wherever possible,
you bake scenes in binary format (``.scn``) rather than text (``.tscn``).
This is because binary scenes are much more compact in terms of storage,
while also being faster to load and save.
Manually at runtime
^^^^^^^^^^^^^^^^^^^
If the ``automerge`` property of a ``MergeGroup`` is disabled, then the node
will do nothing at runtime until you call its ``merge_meshes()`` function.
Triggering merging manually in this way has two major use cases:
1. Procedural levels. If you place objects (e.g. trees, boxes) at runtime using
script, rather than placing them at design time, then you want a way to *delay*
merging until after your placement is complete. Manually calling
``merge_meshes()`` allows this.
2. Merging parameters. These can be set via the ``MergeGroup::set_param()`` and
``MergeGroup::set_param_enabled()`` functions, prior to calling
``merge_meshes()``.
Merging parameters
------------------
Although the default operation of the ``MergeGroup`` works well in many
circumstances, there are a number of parameters which can be altered prior to
merging in order to access more advanced features. These can be set via the
``MergeGroup::set_param()`` functions, however the easiest way to visualize them
is via the ``bake`` method, which displays a dialog allowing you to modify
parameters. These are described in detail in the documentation for
``MergeGroup``.
Merging by locality - grouping and splitting
--------------------------------------------
When merging large numbers of meshes across a large map, sometimes the merging
goes too far, and results in a huge mesh that is too difficult to cull (as part
is always in the view frustum).
For example if you merge every tree in a forest, regardless of your viewpoint,
the whole forest will be rendered. Although rendering each tree individually
would be inefficient, rendering the whole forest in all cases is also
inefficient, but in a different way.
You may think instead to create several ``MergeGroups`` spread across the
forest, and only merge the trees in the local area. This would create an ideal
balance between reduced drawcalls, but still allowing broad scale culling to
take effect. The downside is that this kind of thing could be extremely labour
intensive in terms of scene design.
For this reason, ``MergeGroup`` has built in functionality for helping deal with
this problem automatically.
.. note::
Grouping and splitting is considered advanced, so is only available via the
manual method (setting parameters and calling ``merge_meshes()`` explicitly),
or via the bake method.
Grouping
^^^^^^^^
One of the simplest ways to get the advantages of merging without the
disadvantages of loss of culling resolution is grouping. The ``group_size``
parameter defaults to zero, which indicates that all suitable meshes be merged.
But if you set ``group_size`` to e.g. 2, then only the closest pairs of meshes
will be merged.
If you have 10 trees, it will try to merge 5 pairs of trees, with each pair
being as close together as possible (so that they can be culled efficiently).
This same principle works even in a forest of 1,000 trees.
Splitting
^^^^^^^^^
The alternative approach to grouping is splitting. Splitting takes place as a
post-step, *after* all the meshes have been merged.
Following our forest example, if we assume our 1,000 tree forest has been merged
into 1 huge mesh, splitting allows us to specify a 3D grid (with a horizontal
number of splits and vertical number of splits) and the ``MergeGroup`` will
attempt to split the large mesh into smaller ones more suitable for culling.
For example, with 5 horizontal splits and 1 vertical split we should get 25
final meshes (5 x 5 x 1) which should give us enough resolution for some decent
culling for our forest.
.. note::
The reason for allowing separate horizontal and vertical splits is that
many games are based on flat ground, where horizontal splits may be more
important than vertical. Increasing vertical splits may be counter-productive.
.. tip::
Splitting can make a lot of sense if you are building a world with voxels.
Other functionality
^^^^^^^^^^^^^^^^^^^
Also via the ``MergeGroup`` parameters, ``CSG`` nodes (e.g. :ref:`CSGBox <class_CSGBox>`)
and :ref:`GridMap <class_GridMap>` nodes can optionally be converted to a regular
:ref:`MeshInstance <class_MeshInstance>`. This allows them to be merged like any other mesh.