123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- .. _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.
|