# Modular SimObject Merging Modular aircraft provide a way to have multiple components and merge them together, with a hierarchy of component files that can override each other based on several factors to create a final aircraft. This merging is done in two steps:   - ##### On Game Start The first merge happens when the aircraft package is mounted to the {{< glossterm >}}vfs{{< /glossterm >}} on game start, so that the aircraft variations can be listed correctly in the menus. This merging takes place in the following way: - The [Common](modular-simobject-project-structure/#common) folder is read as the "base" of the aircraft, onto which other things will be attached/merged. - Each of the [Presets](modular-simobject-project-structure/#presets) for the aircraft is checked and the information in the [`aircraft.cfg`](../cfg-files/aircraft.cfg/) is used to tell the simulation which variation of the aircraft each preset represents. - The [`attached_object.cfg`](../cfg-files/attached_objects.cfg/) for each preset is parsed and an internal list of available attachments is created. - The various configuration (CFG) files from the [Common](modular-simobject-project-structure/#common), [Attachments](modular-simobject-project-structure/#attachments) and [Presets](modular-simobject-project-structure/#presets) are merged. - Finally the different [Liveries](modular-simobject-project-structure/#liveries) are parsed and assigned as available to the appropriate presets. Once this has happened, each variation is ready to be shown in the user menu for selection, and for each aircraft, the available liveries will be shown in the liveries panel.   - ##### On Flight Start The second merge happens when a flight starts. At this moment, the aircraft itself is generated as follows: - The attachment models required by the preset are added to the common model. - The livery is applied. - XML, FLT, etc... files from the common, attachments, and presets are merged together. With all that done, the final aircraft is ready to fly.     ### Merge Rules When it comes to the merging of the different configuration files, the following rules will be applied:   - A version of the file must exist in at least one of the Common or Preset or Attachment folders for a merge to occur. For all CFG files with no file present in the Common folder, an "empty" file will be generated, and a merge will *always* occur between the rest of the CFG files present in the Preset and Attachment folders. For [checklist](aircraft/checklists/checklist-xml-properties/), [panel](aircraft/instruments/panel-xml-properties/), and [sound](../sounds/sound-xml/) XML files, a copy of the file is **required** in the Presets folder, even if this is an "empty" file with just a top level element, which must also have the [AutoMerge](modular-simobject-xml-properties/#AutoMerge) attribute, for example: ``` cpp ```   - To merge a *specific* file, components of this file **must share the same path from their respective root** (see the schema below).   - To be considered as an "overrider", an Attachment must have a unique **alias** in the [`attached_objects.cfg`](../cfg-files/attached_objects.cfg/) file. This alias will be used as an identifier each time something needs to be merged.   - If a CFG file parameter is considered as **required** then this must be present in the final merged files (and as such, in the SimObject editor any required parameter that is not defined will be flagged in red in the associated **presets**).   ##### Root Folder Schema ``` codeblock + Project_Name |---+ attachments | |---+ My_Company | |---+ Function_MyAttach // This is the root of the SimAttachment "Function_My_Attachment" | |---+ config | |---- aircraft.cfg | |---+ common // This is the root of the Common part | |---+ config | |---- aircraft.cfg | |---- systems.cfg | |---+ presets |---+ Company_Name |---+ My_Preset // This is the root of Preset "My_Preset" |---+ config |--- aircraft.cfg // Merge = Common + Function_My_Attachment + My_Preset |--- flight_model.cfg |--- systems.cfg // Merge = Common + My_Preset ```     ### CFG Merge Examples In this example we'll go through the merge process for CFG (and FLT) files. The example will use a schematic approach just to illustrate how the final files are created through the hierarchy of common, attachments and presets. The two examples below cover the two "modes" which can be used to merge files. These modes are set using the `[MODULAR_MERGE]` section, and if this section is not included as part of the CFG file, then the default behaviour is to **auto-merge**.   #### Auto-Merge The auto-merge process for the various CFG (and FLT) files is as follows:   1. The [Common](modular-simobject-project-structure/#common) files are read: these represent the base files for the aircraft. The common information about the aircraft which is not linked to any [Attachments](modular-simobject-project-structure/#attachments) should be defined in the common files, unless the parameters have a default value in which case it can be omitted. 2. Each SimAttachment in the [Attachments](modular-simobject-project-structure/#attachments) folder with an *alias*, and which has a version of the CFG file currently being parsed, will **override** the common CFG according to the following rules: - In the case of a SimAttachment referencing another SimAttachment using the [`attached_objects.cfg`](../cfg-files/attached_objects.cfg/), the child attachment CFG files will be parsed *before* the parent, and the parent will override any parameters that they share. - In the case of a SimAttachment referencing another SimAttachment using the [`attachment.cfg`](../cfg-files/attachment.cfg/), The *base* files will be parsed first, and then the parent files will be merged to extend the base CFG information. - If a **section** defined in a SimAttachment CFG is missing in the corresponding common file, this section will be added to the final CFG. - For any given **section** within a CFG file, the parameters from the SimAttachment which are *not* indexed will override the corresponding parameters in the common CFG file. - In case of an indexed parameter within a SimAttachment CFG, the parameter will be added at the end of the common parameters, and the index will be updated appropriately (see the example below). 3. Finally, the [Presets](modular-simobject-project-structure/#presets) files will be processed, following the same rules outlined above in point (2). This means that the preset files provide the final override for the aircraft variation that the preset represents.   Schema Example *before* auto-merging: ``` codeblock // From Common ///////////////////////////////////////////////////// [SECTION_1] a = 1 b = 2 c.0 = 123 c.1 = 456 c.2 = 789 ```` codeblock // From Attachments - Attach_1 ///////////////////////////////////// [SECTION_1] b = 26 c.0 = 123456 c.1 = 654321 [SECTION_2] azerty = TRUE ```` codeblock // From Attachment - Attach_2 ////////////////////////////////////// [SECTION_1] b = 54 ```` codeblock // From Preset ///////////////////////////////////////////////////// [MODULAR_MERGE] auto = TRUE [SECTION_1] c.0 = 123456789 ```   Schema Example *after* auto-merging: ``` codeblock // Merged CFG file //////////////////////////////////////////////// [SECTION_1] // from Preset (added by Common, then overriden by Attach_1, Attach_2, then Preset) a = 1 // from Common b = 54 // from Attach_2 (added by Common, then overriden by Attach_1, then Attach_2) c.0 = 123 // from Common c.1 = 456 // from Common c.2 = 789 // from Common c.3 = 123456 // from Attach_1 (originally c.0 but the index is changed to accommodate the other indexed values) c.4 = 654321 // from Attach_1 (originally c.1 but the index is changed to accommodate the other indexed values) c.5 = 123456789 // from Preset (originally c.0 but the index is changed to accommodate the other indexed values) [SECTION_2] // from Attach_1 azerty = TRUE // from Attach_1 ```     #### Manual-Merge If auto-merge is set to *false* in the `[MODULAR_MERGE]` section, then the process described above for auto-merging will still be performed, but there will be an additional step performed for each CFG that is parsed and merged:   - When the merged file is processed, every parameter *value* is inspected. If the value for any of the parameters is a string identifying a SimAttachment *alias*, then the value of the field will be replaced by the corresponding value from the attachment CFG. This string is formatted as follows: AttachmentRebind\#XXX For example: param = AttachmentRebind\#Attachment\_Alias   Schema Example *before* manual-merging: ``` codeblock // From Common ///////////////////////////////////////////////////// [SECTION_1] a = 1 b = 2 c.0 = 123 ```` codeblock // From Attachments - Attach_1 ///////////////////////////////////// [SECTION_1] b = 26 c.0 = 123456 c.1 = 456789 [SECTION_2] azerty = TRUE ```` codeblock // From Attachment - Attach_2 ////////////////////////////////////// [SECTION_1] b = 54 c.0 = 256 [SECTION_2] azerty = FALSE ```` codeblock // From Preset ///////////////////////////////////////////////////// [MODULAR_MERGE] auto = FALSE [SECTION_1] c.0 = AttachmentRebind#Attach_1 c.1 = 123456789 [SECTION_2] azerty = AttachmentRebind#Attach_1 ```   Schema Example *after* auto-merging but *before* manual-merging: ``` codeblock // Merged CFG file //////////////////////////////////////////////// [SECTION_1] a = 1 b = 54 c.0 = AttachmentRebind#Attach_1 c.1 = 456789 c.2 = 256 c.4 = 123456789 [SECTION_2] azerty = AttachmentRebind#Attach_1 ```   Schema Example *after* manual-merging: ``` codeblock // Merged CFG file //////////////////////////////////////////////// [SECTION_1] // from Preset (added by Common, then overriden by Attach_1, Attach_2, then Preset) a = 1 // from Common b = 54 // from Attach_2 (added by Common, then overriden by Attach_1, then Attach_2) c.0 = 123456 // from Attach_1 (added by Common, then overriden by Attach_1, Attach_2, then Preset) c.1 = 456789 // from Attach_1 c.2 = 123456789 // from Preset [SECTION_2] // from Preset (added by Attach_1, then overriden by Attach_2, then Preset) azerty = TRUE // from Attach_1 (added by Attach_1, then overriden by Attach_2, then Preset) ```     #### Merging CFG Dynamic Parameters SimAttachments can have *dynamic parameters* assigned to them by adding a `[DynamicParameters]` section to the file. These are parameters that get defined in the CFG file(s) for the attachment, and then get modified in the preset [`attached_objects.cfg`](../cfg-files/attached_objects.cfg/) file so that when the final file is generated, the parameter will have different values based on the preset (or the base values of the SimAttachment if the preset does not change them).   For example, let's look at how to change the position of the aircraft wheels based on the selected preset. To start with we are creating the dynamic parameters for the wheel contact points in the SimAttachment [`flight_model.cfg`](../cfg-files/flight_model.cfg/) file: ``` codeblock [DynamicParameters] param.0="s_pitch, 11" param.1="center_wheel_position,-2.3, 0, -4.18" param.2="left_wheel_position,-15.8, -6, -4.14" param.3="right_wheel_position,-15.8, 6, -4.14" [CONTACT_POINTS] static_pitch = [s_pitch] point.1 = Name:center_wheel[uid] #Properties:1, [center_wheel_position], 750, 0, 0.5, 22, 0.21, 3, 0.5, 0, 0, 0, 0, 0 point.2 = Name:left_wheel[uid] #Properties:1, [left_wheel_position], 2000, 1, 0.5, 0, 0.26, 3, 0.5, 0, 0, 2, 0, 0 point.3 = Name:right_wheel[uid] #Properties:1, [right_wheel_position], 2000, 2, 0.5, 0, 0.26, 3, 0.5, 0, 0, 3, 0, 0 ```   So, the dynamic parameter is simply a string of values, where the first one is always the identifier of the parameter. For single parameters, this is all you need to know as you then substitute the value of a CFG parameter with the dynamic parameter identifier and the merge process will "pull" the correct value for it from that. However it is important to note that if the parameter is an *indexed* parameter, then you will also need to have `[uid]` appended to it so that the merge process can later add the attachment *alias* to it for proper identification.   Once you have set up the attachment parameters, you can then edit the [`attached_objects.cfg`](../cfg-files/attached_objects.cfg/) for the appropriate preset(s) with the values you want to override with: ``` codeblock [SIM_ATTACHMENT.0] // (other params...) alias="Function_Wheels" cfg_parameter.0 = "s_pitch, 8" cfg_parameter.1 = "center_wheel_position,-2.5, 0, -4.25" ```   You do not need to provide override values for all the dynamic parameters you have defined, as the ones you don't supply will just use the initial values from the SimAttachment. Once you have set this up, when the files are merged as part of the build process, the [`flight_model.cfg`](../cfg-files/flight_model.cfg/) file will look like this: ``` codeblock [CONTACT_POINTS] static_pitch = 8 // (other params...) point.5=Name:center_wheel_Function_Wheels #Properties:1, -2.5, 0, -4.25, 750, 0, 0.5, 22, 0.21, 3, 0.5, 0, 0, 0, 0, 0 point.6=Name:left_wheel_Function_Wheels #Properties:1, -15.8, -6, -4.14, 2000, 1, 0.5, 0, 0.26, 3, 0.5, 0, 0, 2, 0, 0 point.7=Name:right_wheel_Function_Wheels #Properties:1, -15.8, 6, -4.14, 2000, 2, 0.5, 0, 0.26, 3, 0.5, 0, 0, 3, 0, 0 ```     ### XML Merge Examples When it comes to XML files, the merge process is very similar to that used by CFG files, following the path: common > attachments > Preset > auto-merge > manual-merge   Merging is controlled using the `AutoMerge` attribute, in the *root* node. This attribute can **only** exist in the [Presets](modular-simobject-project-structure/#presets) XML files, and when set to 1 (*true*) then files will use **auto-merge**, and if set to 0 (*false*) then they will use **manual-merge**. If this attribute is *not* included then then the default behaviour is to **auto-merge**.   #### Auto-Merge The auto-merge feature for XML files works as follows: 1. The [Common](modular-simobject-project-structure/#common) files are read: these represent the base files for the aircraft. The common information about the aircraft which is not linked to any [Attachments](modular-simobject-project-structure/#attachments) should be defined in the common files 2. Each SimAttachment in the [Attachments](modular-simobject-project-structure/#attachments) folder with an *alias*, and which has a version of the XML file currently being parsed, will **override** the common XML values and/or elements (including sub-elements), according to the following rules: - The first step is to try and match the attachment's XML architecture to the common one using elements, sub-elements, and attributes. An attachment element is considered as matching a Common element when they share the same *name* and every *attribute* (and value) from the attachment node is present in the Common element. - If an element has been found to match, then sub-elements within it will be processed. If there are no sub-elements, then the value of the matching element will be updated. - If no match is found, then a copy on the element and its children will be made in the merge file. 3. Finally, the [Presets](modular-simobject-project-structure/#presets) files will be processed, following the same rules outlined above in point (2). This means that the preset files provide the final override for the aircraft variation that the preset represents.   Schema example *before* auto-merging: ``` xml 26 azerty 123456789 qsdfg ```` xml 987654321 ```` xml 0 wxcvbn ```` xml 124 toto ```   Schema example *after* auto-merging: ``` xml 26 azerty 987654321 qsdfg 124 toto ```   #### Manual-Merge If auto-merge is set to 0 (*false*) in the `AutoMerge` attribute, then the process described above for auto-merging will still be performed, but there will be an additional step performed for each XML that is parsed and merged. In this additional step, the Preset files will be parsed for the `` and `` nodes, which will be used to perform the manual-merge of the file.   Schema example *before* auto-merging: ``` xml 26 azerty 123456789 qsdfg ```` xml 987654321 ```` xml 0 wxcvbn ```` xml 124 toto ```   Schema example *after* auto-merging but *before* manual-merging: ``` xml 26 azerty 987654321 qsdfg 124 toto ```   Schema example *after* manual-merging: ``` xml 124 toto 26 azerty ```   #### Merging XML Dynamic Parameters Dynamic parameters can be created in the model behaviour XML files by using the `` element, like this: ``` xml ``` TO BE FIXED       ### Using [uid] When an element in the XML or CFG files references an *indexed* parameter (for example a circuit index from the [`systems.cfg`](../cfg-files/systems.cfg/#ELECTRICAL)), then you should append `[uid]` to the end of the circuit name so that the correct one is referenced. This is done because indexed parameters may have different indices after the merge process has been completed, and so we rely on a combination of the CFG parameter *name* and the attachment *alias* to identify the indexed parameter correctly, since the index is no longer a reliable indication of target.   To give an example, let's say we have an attachment which requires the following circuit definition: ``` cpp circuit.1 = Type:CIRCUIT_PFD # ConsumerCfg:ConsumerPFD #Name:PFD ``` If the attachment is not meant to be instanced several times on the same host, you can use the circuit name as it is defined, using the `''_n` format, as shown below: ``` xml (A:CIRCUIT ON:'PFD'_n, percent) ```   However, if the attachment is supposed to be created several times, you can refer to it using a concatenated name syntax with the dynamic parameter `[uid]` created automatically if the sim-attachment is listed more than one time. So, if you have two attachments defined with two different aliases like this: ``` cpp // first time we use the same sim-attachment alias = "foo" // second time we use the same sim-attachment alias = "bar" ``` Then in the CFG file you would define the parameter like this: ``` cpp // before merge circuit.1 = Type:CIRCUIT_PFD # ConsumerCfg:ConsumerPFD #Name:PFD[uid] ``` And after merging the CFG will look like this: ``` cpp circuit.1 = Type:CIRCUIT_PFD # ConsumerCfg:ConsumerPFD #Name:PFD_foo circuit.2 = Type:CIRCUIT_PFD # ConsumerCfg:ConsumerPFD #Name:PFD_bar ``` And in the XML it would be referenced like this: ``` xml (A:CIRCUIT ON:'PFD[uid]'_n, percent) ``` This gives us 2 use cases : - The sim-attachment is instanced only once, CFGs do not conflict, so names won't be affected - `PFD[uid]` becomes simply `PFD` when instantiated - The sim-attachment is instanced several times, CFGs and XMLs are merged using the attachment alias when relying on the automatic merge - the first `PFD[uid]` becomes `PFD_foo` when instantiated - the second `PFD[uid]` becomes `PFD_bar` when instantiated     ### Merge Process For Liveries When it comes to merging livery into the final modular aircraft, the simulation goes over the list of **tags** for each attachment and then loads the livery parts according to the tags assigned to the various folders. Take the following livery folder as an example: ``` codeblock + Liveries |---+ Company_Name | |---+ Livery_One | |---+ model.wheels | | |---- wheels_LOD0.gltf | | |---- wheels_LOD1.gltf | | |---- | |---+ texture.wheels | | |---- a_texture_used_in_the_attachment_wheels_gltf.png // override a texture | | |---- a_texture_used_in_wheels_lod_gltf // a texture used in the added models | | |---- | |---+ panel.[TAG] | |---- panel.cfg // some VPaintings ```   When loading the wheels attachment, the attachment only has one tag: "wheels". The simulation will then check for `texture.wheels`, `model.wheels`, and `panel.wheels` folders in the livery root directory. Each of these folders will be treated as follows:   - ##### Texture Folders For each tag, if the `texture.[TAG]` folder exists, it will be used as a *fallback* first. Then, the simulation will prepend the attachment texture directories with the livery texture directory so that the livery textures will always be used *first* (if they exist). If there are `texture.cfg` files in the folder, these will be listed next in the fallbacks, as illustrated here: - livery folders: \- `texture.[tag]`: for each attachment tag - `texture.[texture_folder]`: if a texture folder is specified in the `attached_objects.cfg` - `texture.[model_entry_type]`: either "interior" or "exterior" - `texture` - attachment folders: - `texture.[texture_folder]`: if a texture folder is specified in the `attached_objects.cfg` - `texture` - `texture.[texture_folder]` and `texture` of inherited attachments from attachment to base - base model instance folders: - existing `texture` directories which weren't already listed as fallbacks This load order guarantees the override rules are respected. Note that the `texture.interior` and `texture.exterior` folders are special cases which are reserved for liveries targeting the textures from the [Common](modular-simobject-project-structure/#common) and [Presets](modular-simobject-project-structure/#presets) folders.   - ##### Model Folders Livery models are added to the list of extra models to merge which will be processed and merged automatically when loading the attachment. The merge is done {{< glossterm >}}lod{{< /glossterm >}} by {{< glossterm >}}lod{{< /glossterm >}}, and to simplify this process the {{< glossterm >}}gltf{{< /glossterm >}} names should follow the schema of `Livery_LOD[lod_number].gltf`. The `lod_number` starts at 0 and is restricted to the number of {{< glossterm >}}lod{{< /glossterm >}}s the model being merging into has defined in its model info. Note that you may alternatively supply a `livery.xml` file to re-use the same {{< glossterm >}}gltf{{< /glossterm >}} in multiple LOD definitions. If a [`livery.xml`](aircraft/liveries/livery-xml-properties/) is found it will be used instead of the schema matching method to populate the per-LOD {{< glossterm >}}gltf{{< /glossterm >}}s to merge, for example: ``` xml ``` This file may also be used when the {{< glossterm >}}lod{{< /glossterm >}} files do not follow the naming schema outlined above. {{< callout context="note" title="NOTE" icon="outline/bulb" >}} the `MinSize` attribute is *required* but is *ignored* by the simulation.Note that the `model.interior` and `model.exterior` are special cases, as they are reserved for liveries targeting the models from the [Common](modular-simobject-project-structure/#common) and [Presets](modular-simobject-project-structure/#presets) folders. {{< /callout >}}   - ##### Panel Folder For each tag, if the `panel.[tag]` folder exists and has a `panel.cfg`, then that CFG will be loaded. In the case of liveries, *only* the `[VPaintingN]` section is required (although you can include other sections like `[DynamicParameters]`, for example). When working with this file in a livery, it is important to note that *Vpaintings* **can only be added and cannot override existing ones**. To counteract this limitation, *Vpaintings* support dynamic parameter replacement using the `[Panel_DynamicParameters]` section of the `livery.cfg` file, or per-preset using the `vpainting_parameter.N` parameter in the `attached_objects.cfg`. The load order for modular aircraft panels guarantees merge rules are respected and looks like this: - **attachment panel folder**: loads both *VPaintings* and *VCockpits* - `panel.cfg` from the base to attach to. - `panel.cfg` of the attachment. - **livery panel folder**: limited to adding *VPaintings*. - `panel.[tag]` for each attachment tag.