MODEL EXAMPLES
This section contains examples of the contents of the [model].xml
file for various different situations.
Complete Model XML Example
The example below shows a complete model definition for an aircraft in Microsoft Flight Simulator. After this we will show some shorter example code related to specific features from the model XML, some of which are not shown in this full sample (since not all the XML elements will be required by all models, and it will depend on what the purpose of the model is, ie: human, aircraft, vehicle, etc...):
<?xml version="1.0" encoding="utf-8"?>
<ModelInfo version="1.0" guid="{bbe2683e-2f6d-4d07-a6ec-e2cbcf6b56b2}">
<LODS>
<LOD minSize="80" ModelFile="aircraft_LOD00.gltf"/>
<LOD minSize="30" ModelFile="aircraft_LOD01.gltf"/>
<LOD minSize="20" ModelFile="aircraft_LOD02.gltf"/>
<LOD minSize="13" ModelFile="aircraft_LOD03.gltf"/>
<LOD minSize="8" ModelFile="aircraft_LOD04.gltf"/>
<LOD minSize="5" ModelFile="aircraft_LOD05.gltf"/>
<LOD minSize="1" ModelFile="aircraft_LOD06.gltf"/>
</LODS>
<Behaviors>
<Include ModelBehaviorFile="Asobo\Common.xml"/>
<Include ModelBehaviorFile="Asobo\Exterior.xml"/>
<Include ModelBehaviorFile="Asobo\Misc/Pl463.xml"/>
<Include ModelBehaviorFile="Asobo\Generic\FX.xml"/>
<Component ID="EXT_HANDLING">
<UseTemplate Name="ASOBO_HANDLING_Elevator_Template">
<ANIM_NAME>elevator_percent_key</ANIM_NAME>
</UseTemplate>
<UseTemplate Name="ASOBO_HANDLING_Aileron_Template">
<ANIM_NAME_LEFT>L_aileron_percent_key</ANIM_NAME_LEFT>
<ANIM_NAME_RIGHT>R_aileron_percent_key</ANIM_NAME_RIGHT>
</UseTemplate>
<UseTemplate Name="ASOBO_HANDLING_Rudder_Template">
<ANIM_NAME>rudder_percent_key</ANIM_NAME>
</UseTemplate>
</Component>
<Component ID="EXT_ENGINE">
<UseTemplate Name="ASOBO_ENGINE_Propeller_Template">
<ID>1</ID>
<ANIM_NODE_ID>Prop1</ANIM_NODE_ID>
<ANIM_NAME>prop1_anim</ANIM_NAME>
<STILL_NODE_ID>prop0_still</STILL_NODE_ID>
<FROSTED_STILL_NODE_ID>prop0_still_frost</FROSTED_STILL_NODE_ID>
<SLOW_NODE_ID>prop0_slow</SLOW_NODE_ID>
<PROP_BLURRED_NODE_ID>prop0_blurred</PROP_BLURRED_NODE_ID>
<PROP_SIDE_BLURRED_NODE_ID>PROP_SIDE_BLUR</PROP_SIDE_BLURRED_NODE_ID>
<NO_PROP_CONE_BLUR>True</NO_PROP_CONE_BLUR>
<MIN_RPM_FOR_SLOW>320</MIN_RPM_FOR_SLOW>
<MIN_RPM_FOR_BLUR>570</MIN_RPM_FOR_BLUR>
</UseTemplate>
</Component>
<Component ID="EXT_GEARS">
<UseTemplate Name="ASOBO_GEAR_Center_Tire_Template">
<ANIM_NAME>c_tire_anim</ANIM_NAME>
<NODE_ID_STILL>WHEEL_C_STILL_KEY</NODE_ID_STILL>
<NODE_ID_BLURRED>WHEEL_C_STILL_BLURRED_KEY</NODE_ID_BLURRED>
</UseTemplate>
<UseTemplate Name="ASOBO_GEAR_Left_Template">
<ANIM_NAME>l_gear</ANIM_NAME>
</UseTemplate>
<UseTemplate Name="ASOBO_GEAR_Left_Tire_Template">
<ANIM_NAME>l_tire_anim</ANIM_NAME>
<NODE_ID_STILL>FRONTWHEEL_L_STILL_KEY</NODE_ID_STILL>
<NODE_ID_BLURRED>FRONTWHEEL_L_BLURRED_KEY</NODE_ID_BLURRED>
</UseTemplate>
<UseTemplate Name="ASOBO_GEAR_Right_Template">
<ANIM_NAME>r_gear</ANIM_NAME>
</UseTemplate>
<UseTemplate Name="ASOBO_GEAR_Right_Tire_Template">
<ANIM_NAME>r_tire_anim</ANIM_NAME>
<NODE_ID_STILL>FRONTWHEEL_R_STILL_KEY</NODE_ID_STILL>
<NODE_ID_BLURRED>FRONTWHEEL_R_BLURRED_KEY</NODE_ID_BLURRED>
</UseTemplate>
<UseTemplate Name="ASOBO_GEAR_Steering_Template">
<ANIM_NAME>c_wheel</ANIM_NAME>
</UseTemplate>
</Component>
<Component ID="INT_HANDLING">
<UseTemplate Name="ASOBO_HANDLING_RudderPedals_Template">
<USE_BRAKE/>
<SPLIT_RUDDERS>True</SPLIT_RUDDERS>
<DIFFERENTIATE_BRAKE_ANIM_L_R>True</DIFFERENTIATE_BRAKE_ANIM_L_R>
<RUDDERPEDALS_TYPE>MERGED</RUDDERPEDALS_TYPE>
<NODE_ID_L_BRAKE>HANDLING_RudderPedals_1</NODE_ID_L_BRAKE>
<NODE_ID_R_BRAKE>HANDLING_RudderPedals_2</NODE_ID_R_BRAKE>
<NODE_ID_L_RUDDER>HANDLING_RudderPedals_1_Brake</NODE_ID_L_RUDDER>
<NODE_ID_R_RUDDER>HANDLING_RudderPedals_2_Brake</NODE_ID_R_RUDDER>
<BRAKE_ANIM_NAME_L>HANDLING_RudderPedals_Brake_L</BRAKE_ANIM_NAME_L>
<BRAKE_ANIM_NAME_R>HANDLING_RudderPedals_Brake_R</BRAKE_ANIM_NAME_R>
</UseTemplate>
<UseTemplate Name="ASOBO_HANDLING_Yoke_Template"/>
</Component>
<Component ID="LANDING_FX">
<UseTemplate Name="ASOBO_LANDING_FX"/>
</Component>
</Behaviors>
</ModelInfo>
LOD Example
The model XML file contains a list of LOD definitions, ordered from high quality to low quality. Every LOD defines a *.glTF
model and a minimum size for display selection. For the purposes of LOD selection, the current size on screen of a model, is the vertical size on screen of the model's bounding sphere.
The size on screen of a model is determined by the camera position, the camera's field of view and the actual size of the model. The size of the model is calculated by accumulating the bounding volume extremes of all LODs in their default non-animated state. From the extremes, a bounding sphere is calculated. Since the sphere encompasses the extremes, the actual size on screen of a model is usually slightly larger than one might expect.
The selection process is as follows: starting from LOD 0, going down, the first LOD with a minSize
smaller than the model's current size on screen will be selected for display. The selection will also take into account forced and disabled LODs as configured by the model options.
NOTE: The last lod of any given model will always be displayed, until smaller than a certain hard-coded size. This size is scaled by the object LOD graphics setting within the sim. As such, it is important for the last lod to be as light-weight as possible (i.e. a single draw call). This usually means one node, one mesh, one material and less than 100 triangles. See the section on model LODs for examples and more information.
Below you can see an example of a LOD definition:
<ModelInfo>
<LODS>
<LOD minSize="100" modelFile="TBM930_LOD00.gltf"/>
<LOD minSize="50" modelFile="TBM930_LOD01.gltf"/>
<LOD minSize="35" modelFile="TBM930_LOD02.gltf"/>
<LOD minSize="20" modelFile="TBM930_LOD03.gltf"/>
<LOD minSize="10" modelFile="TBM930_LOD04.gltf"/>
<LOD minSize="5" modelFile="TBM930_LOD05.gltf"/>
<LOD modelFile="TBM930_LOD06.gltf"/>
</LODS>
</ModelInfo>
Submodels And Animations
The following example shows how the <MergeModel>
element can be used to merge submodels on a LOD as well as reference one (or more) animation gLTF:
<ModelInfo>
<LODS>
<LOD ModelFile="FireTruck_LOD1_01.gltf">
<MergeModel>FireTruck_LOD1_02.gltf</MergeModel>
<MergeModel>FireTruck_LOD1_03.gltf</MergeModel>
<MergeModel>FireTruck_LOD1_04.gltf</MergeModel>
<MergeModel>FireTruck_LOD1_05.gltf</MergeModel>
<MergeModel>..\animation\FireTruck.gltf</MergeModel>
</LOD>
</LODS>
</ModelInfo>
When loading a LOD from the [model].xml
, the system will merge each of the given gLTF models one by one, starting with the base model specified in the <LOD />
element. In the above example, it first creates the base model using "FireTruck_01.gltf
", and then adds "FireTruck_02.gltf
", etc... before finally loading the animation file.
You can find more information on submodels here:
Model Attachments
Below is a simplified, schematic example of how we use the <AttachModel>
element to attach independent model XML files to the main model being defined:
<?xml version="1.0" encoding="utf-8"?>
<ModelInfo guid="{daba6ada-f090-4a4c-b0f0-f91f30932b4b}" version="1.1">
<LODS>
<LOD ModelFile="parent_LOD0.gltf" minSize="30">
<AttachModel id="child_model"/>
</LOD>
<LOD ModelFile="parent_LOD1.gltf" minSize="10">
<AttachModel id="child_model"/>
</LOD>
<LOD ModelFile="parent_LOD2.gltf" minSize="5">
<AttachModel id="child_model"/>
</LOD>
<LOD ModelFile="parent_LOD3.gltf" minSize="3"/>
</LODS>
<ModelAttachments>
<ModelAttachment id="child_model">
<AttachToNode>attachment_point</AttachToNode>
<Model>..\model.child\model.xml</Model>
</ModelAttachment>
</ModelAttachments> </ModelInfo>
The following is a "real world" example of how an actual XML file would look when working with multiple model attachments:
<?xml version="1.0" encoding="utf-8"?>
<ModelInfo>
<LODS>
<LOD minSize="150" ModelFile="DA62_LOD00.gltf">
<MergeModel>..\livery\DA62_LiveryFS_LOD00.gltf</MergeModel>
<AttachModel id="myInstrument"/>
<AttachModel id="chair1"/>
</LOD>
<LOD minSize="110" ModelFile="DA62_LOD01.gltf">
<AttachModel id="chair1"/>
</LOD>
<LOD minSize="75" ModelFile="DA62_LOD02.gltf"/>
</LODS>
<ModelAttachments>
<ModelAttachment id="myInstrument">
<AttachToNode>SomeNodeName</AttachToNode>
<Model>..\..\..\..\Instruments\SomeInstrument.xml</Model>
</ModelAttachment>
<ModelAttachment id="chair1">
<AttachToNode>chair1pos</AttachToNode>
<Model>..\..\Shared\Misc\FancyChair.xml</Model>
</ModelAttachment>
</ModelAttachments>
</ModelInfo>
You can find more information on model attachments here:
<NodeAnimation>
Example
Below is a short example of how we use the <NodeAnimation>
element to add wing-flex to the model:
<NodeAnimation type="WingFlex">
<Node>WING_BONE_LEFT_01</Node> <!-- the left wing bone closest to the center -->
<Node>WING_BONE_LEFT_02</Node> <!-- in between -->
<Node>WING_BONE_LEFT_03</Node> <!-- in between -->
<Node>WING_BONE_LEFT_04</Node> <!-- the left wing bone furthest from the center -->
<Node>WING_BONE_RIGHT_01</Node> <!-- the right wing bone closest to the center -->
<Node>WING_BONE_RIGHT_02</Node> <!-- in between -->
<Node>WING_BONE_RIGHT_03</Node> <!-- in between -->
<Node>WING_BONE_RIGHT_04</Node> <!-- the right wing bone furthest from the center -->
<Node>Engine_PIVOT_LEFT_1</Node> <!-- the left wing engine pivot node closest to the center -->
<Node>Engine_PIVOT_LEFT_2</Node> <!-- the left wing engine pivot node furthest from the center -->
<Node>Engine_PIVOT_RIGHT_1</Node> <!-- the right wing engine pivot node closest to the center -->
<Node>Engine_PIVOT_RIGHT_2</Node> <!-- the right wing engine pivot node furthest from the center -->
</NodeAnimation>
<BlendTreeState>
Example
Below is a short example of how we use <BlendTreeState>
element to blend various animations together based on the return <Value>
of an expression:
<BlendTreeState name="Stop">
<Animations>
<Animation guid="D1ACD09E-89A1-45B5-9B68-1D011F94A6F3" loop ="True" speed="1" threshold="0.0"/>
<Animation guid="074AE60A-26C4-4324-A7A6-2AD81700B03A" loop ="True" speed="1" threshold="0.25"/>
<Animation guid="4378ED30-CEA3-4AD1-A480-633FA294F8EA" loop ="True" speed="1" threshold="0.5"/>
<Animation guid="ABC3876F-7132-42DB-932B-B086D53BA893" loop ="True" speed="1" threshold="0.75"/>
</Animations>
<Value>1 (A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 5 / -</Value>
</BlendTreeState>
<Transition>
Example
The <Transition>
element is used to set the conditions under which two previously defined animations should be "merged" and transition from one to the other. The example below shows a sample of how this is setup in the XML, in this case it's part of the transition setup for a Marshaller:
<!-- Idle to StandBy 0m < Dir Z < 500m -->
<Transition name="Idle to StandBy" start="Idle" end ="StandBy" duration="0.5">
<Condition>
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 500 <
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 0 >
and
</Condition>
</Transition>
<!-- StandBy to Idle 0m > Dir Z || Dir Z > 500m -->
<Transition name="StandBy to Idle" start="StandBy" end ="Idle" duration="0.5">
<Condition>
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 500 >
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 0 <
or
</Condition>
</Transition>
<!-- StandBy to Facing 0m < Dir Z < 300m && abs(Dir X) < 50m && abs(Alpha) < 95° -->
<Transition name="StandBy to Facing" start="StandBy" end ="Facing" duration="0.5">
<Condition>
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 300 <
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION Z PARKINGSPACE, METERS) 0 >
(A:MARSHALLER AIRCRAFT DISTANCE DIRECTION X PARKINGSPACE, METERS) abs 50 <
(A:MARSHALLER AIRCRAFT HEADING PARKINGSPACE, DEGREES) abs 95 <
and
and
and
</Condition>
</Transition>
Jetway Example
The following snippet is an example of setting up the Jetway inverse kinematics (IK) using the <IKChain>
and <IKConstraint>
elements:
<IKChain Name="IK_MainHandle">
<Start>Rotation_Base</Start>
<End>Pivot</End>
<TranslationSpeed>0.4</TranslationSpeed> <!-- Meters per second for each Node compared to its parent -->
<RotationSpeed>3</RotationSpeed> <!-- Degrees per second for each Node compared to its parent -->
</IKChain>
<IKChain Name="IK_SecondaryHandle">
<Start>Bone05</Start>
<End>Contact</End>
<TranslationSpeed>0.4</TranslationSpeed>
<RotationSpeed>30</RotationSpeed>
</IKChain>
<IKChain Name="IK_WheelsGroundLock">
<Start>Bone07</Start>
<End>Bone09</End>
<TranslationSpeed>10</TranslationSpeed>
<RotationSpeed>90</RotationSpeed>
</IKChain>
<IKConstraint>
<Node>Rotation_Base</Node>
<Bank min="-12.6" max="9.3"/>
<Heading/>
</IKConstraint>
<IKConstraint>
<Node>Bone01</Node>
<X min="2.218" max="7.4"/>
</IKConstraint>
<IKConstraint>
<Node>Bone02</Node>
<X min="0" max="5.567"/>
</IKConstraint>
<IKConstraint>
<Node>Bone03</Node>
<X min="0" max="5.597"/>
</IKConstraint>
<IKConstraint>
<Node>Bone04</Node>
<X min="0" max="6.134"/>
</IKConstraint>
<IKConstraint>
<Node>Bone05</Node>
</IKConstraint>
<IKConstraint>
<Node>Bone07</Node>
</IKConstraint>
<IKConstraint>
<Node>Bone08</Node>
<X min="0.6" max="6"/>
</IKConstraint>
<IKConstraint>
<Node>Bone09</Node>
</IKConstraint>
Simple Animation Examples
Standard animations are not reliant on code or physics and can run on loop, useful for ambient animations, run cycles and actions (for example). These are easy to include and require a single line in the XML:
<Animation name="Ambient" guid="A6F1C5D0-BEF6-449e-BAF8-FB777F27808F" type="Standard" typeParam="AutoPlay,Random" />
The section of XML below is a simple example that triggers an animation when the flaps move inwards:
<AnimationEvent EventName="CAM_FLAPS_Backward" AnimationName="r_flap_percent_key">
<Time>3</Time>
<IsNormalPlayback>False</IsNormalPlayback>
</AnimationEvent>
For legacy animations that are for FSX SimObjects, you would need to accompany each animation with a <PartInfo>
, for example:
<Animation name="c_gear" guid="E2E339A6-FCBF-43a9-A780-3473E03AB54B" length="200" type="Sim" typeparam2="c_gear" typeparam="AutoPlay" />
<PartInfo>
<Name>c_gear</Name>
<AnimLength>200</AnimLength>
<Animation>
<Parameter>
<Sim>
<Variable>GEAR ANIMATION POSITION:0</Variable>
<Units>keyframe</Units>
</Sim>
</Parameter>
</Animation>
</PartInfo>
XML Program Examples
Within the model behavior files for models you can use "XML programs" to adapt the result of a component behavior or an input event depending on different factors, like a conditional check, or the current state of a value, etc... These programs are created using specific XML elements which are described on the following page:
The examples below show how some of these elements are used within a behavior component or input event.
<Condition>
Example
The example below shows a simple <Condition>
check that will do nothing if the test resolves toFalse
, or it will call the template "ASOBO_GT_Update
" if the test resolves as True
. The check itself is testing whether the parameter "STATE0_TIMER
" exists, and has a value greater than 0:
<Condition>
<Test>
<And>
<Arg Check="STATE0_TIMER"/>
<Greater>
<Value>STATE0_TIMER</Value>
<Number>0</Number>
</Greater>
</And>
</Test>
<True>
<UseTemplate Name="ASOBO_GT_Update">
<UPDATE_ONCE>True</UPDATE_ONCE>
<UPDATE_CODE>(#SWITCH_POSITION_TYPE#:#SWITCH_POSITION_VAR#) 0 == if{ 1 (>#SWITCH_POSITION_TYPE#:#SWITCH_POSITION_VAR#) }</UPDATE_CODE>
</UseTemplate>
</True>
</Condition>
<Switch>
Example
The example below shows a simple <Switch>
check that will go through each of the cases until the "case" check resolves as "True" - performing the actions within that case - or until the "default" case is met:
<Switch>
<Case Valid="ONLY_COMPRESSION">
<UseTemplate Name="ASOBO_GT_Anim">
<ANIM_CODE>(A:CONTACT POINT COMPRESSION:#ID#, Percent)</ANIM_CODE>
</UseTemplate>
</Case>
<Case Valid="EXTENSION_AND_COMPRESSION">
<UseTemplate Name="ASOBO_GT_Anim">
<ANIM_CODE>(A:CONTACT POINT POSITION:#ID#, Percent) (A:CONTACT POINT COMPRESSION:#ID#, Percent) + 0.5 *</ANIM_CODE>
</UseTemplate>
</Case>
<Default>
<UseTemplate Name="ASOBO_GT_Anim">
<ANIM_CODE>(A:CONTACT POINT POSITION:#ID#, Percent)</ANIM_CODE>
</UseTemplate>
</Default>
</Switch>
<Loop>
Example
This example shows a simple <Loop>
that creates a variable (AP_ID
), sets it to 1, then increments it by 1 each iteration through the loop until it reaches the value from the input parameter (#AP_COUNT#
), at which point the loop will end and perform the "then" action:
<Loop>
<Setup>
<Param>AP_ID</Param>
<From>1</From>
<Inc>1</Inc>
<To>#AP_COUNT#</To>
</Setup>
<Do>
<Parameters Type="Default" Lifetime="Loop">
<RESET_CODE_T/>
</Parameters>
<Parameters Type="Override" Lifetime="Loop">
<RESET_CODE_T>#RESET_CODE_T# #AP_ID# #ID# != if{ 0 (>L:XMLVAR_Autopilot_#AP_ID#_Status) }</RESET_CODE_T>
</Parameters>
</Do>
<Then>
<RESET_CODE>#RESET_CODE_T#</RESET_CODE>
</Then>
</Loop>
Input Event Example
The following example illustrates the <InputEvent>
element that is used to perform some form of calculation and return a value when any instrument input is detected. In this case, we're detecting any interaction with a cockpit control to change the volume of COM1. Note the use of Input Parameters and the general format of the XML, as this is the most basic way that an input event should be structured:
<InputEvent ID="ASOBO_GIE_Base">
<Presets>
<Preset ID="SOUND_COM_1_Volume">
<Tooltip>
<Icon>ROTATE</Icon>
<Interaction>PRIMARY_DOWN+X_AXIS</Interaction>
<InteractionLockable>X_AXIS</InteractionLockable>
<TTDescription RPN="True">1 (R:1:@TT_Package.AUDIOPANEL_KNOB_COM_VOLUME_ACTION) @sprintf</TTDescription>
<TTValue RPN="True">(B:SOUND_COM_1_Volume, percent) '%d%%' @sprintf</TTValue>
</Tooltip>
<Value>
<Units>percent</Units>
<Code>(O:MyValue)</Code>
<Init>(A:COM VOLUME:1, percent) (>O:MyValue)</Init>
<WatchVars>
<Simvar ID="COM VOLUME:1"/>
</WatchVars>
</Value>
<Inc>
<Code>(A:COM VOLUME:1, percent) p0 + (>B:SOUND_COM_1_Volume_Set)</Code>
<Parameters>
<Param Type="Float" RPN="True">p0 5 *</Param>
</Parameters>
</Inc>
<Dec>
<Code>(A:COM VOLUME:1, percent) p0 - (>B:SOUND_COM_1_Volume_Set)</Code>
<Parameters>
<Param Type="Float" RPN="True">p0 5 *</Param>
</Parameters>
</Dec>
<Set>
<Code>p0 0 max 100 min (>O:MyValue) (O:MyValue) (>K:COM1_VOLUME_SET)</Code>
<Parameters>
<Param Type="Float" RPN="True">p0</Param>
</Parameters>
</Set>
</Preset>
<Presets>
<InputEvent>
Parameter Function Example
The following example illustrates the creation of Parameter Functions. In this case it's a function that can be used in multiple different components to setup parameters for use with buttons:
<ParametersFn Name="AIRCRAFT_Push_Button_Get_Params_ID">
<Parameters Type="Default">
<IE_NAME_BASE>#IE_SOURCE#_Button</IE_NAME_BASE>
<BASE_VAR_NAME>XMLVAR_#IE_SOURCE#</BASE_VAR_NAME>
<HAS_COVER>True</HAS_COVER>
<IS_AIRLINER>True</IS_AIRLINER>
<TT_DESCRIPTION_ID>@TT_Aircraft.TT_PLACEHOLDER_DESC</TT_DESCRIPTION_ID>
<TOOLTIP_TITLE>@TT_Aircraft.TT_PLACEHOLDER_TITLE</TOOLTIP_TITLE>
</Parameters>
<Parameters Type="Override">
<Condition NotEmpty="ID">
<PREFIXED_ID Process="String">#ID# '%02d' @sprintf</PREFIXED_ID>
</Condition>
</Parameters>
<Parameters Type="Override">
<Condition NotEmpty="PREFIXED_ID">
<BASE_NAME>#BASE_NAME#_#PREFIXED_ID#</BASE_NAME>
<IE_NAME_BASE>#IE_NAME_BASE#_#PREFIXED_ID#</IE_NAME_BASE>
<BASE_VAR_NAME>#BASE_VAR_NAME#_#PREFIXED_ID#</BASE_VAR_NAME>
</Condition>
</Parameters>
<ReturnParameters>
<INTERACTION_TYPE>Push</INTERACTION_TYPE>
<IS_AIRLINER>#IS_AIRLINER#</IS_AIRLINER>
<IE_NAME>#IE_NAME_BASE#</IE_NAME>
<NODE_ID>#BASE_NAME#</NODE_ID>
<ANIM_NAME>#BASE_NAME#</ANIM_NAME>
<COVER_NODE_ID>#BASE_NAME#_Cover</COVER_NODE_ID>
<COVER_ANIM_NAME>#BASE_NAME#_Cover</COVER_ANIM_NAME>
<COVER_IE_NAME>#IE_NAME_BASE#_Cover</COVER_IE_NAME>
<SET_STATE_EXTERNAL>p0 (>L:#BASE_VAR_NAME#)</SET_STATE_EXTERNAL>
<GET_STATE_EXTERNAL>(L:#BASE_VAR_NAME#) sp0</GET_STATE_EXTERNAL>
<LOCAL_VAR_TO_WATCH_0>#BASE_VAR_NAME#</LOCAL_VAR_TO_WATCH_0>
<!-- TO BE REMOVED WHEN DOING EMISSIVES -->
<TT_DESCRIPTION_ID>#TT_DESCRIPTION_ID#</TT_DESCRIPTION_ID>
<TOOLTIP_TITLE>#TOOLTIP_TITLE#</TOOLTIP_TITLE>
<NO_SEQ1/>
<NO_SEQ2/>
</ReturnParameters>
</ParametersFn>
This function would then be used in behavior Components something like this:
<Component ID="Overhead">
<Component ID="EPU_Buttons">
<Loop>
<Setup>
<Param>ID</Param>
<From>1</From>
<Inc>1</Inc>
<To>18</To>
</Setup>
<Do>
<UseTemplate Name="ASOBO_ELECTRICAL_Base_Template">
<UseParametersFn Name="AIRCRAFT_Push_Button_Get_Params_ID">
<IE_SOURCE>EPU</IE_SOURCE>
<BASE_NAME>OVH_PUSH_EPU</BASE_NAME>
</UseParametersFn>
</UseTemplate>
</Do>
</Loop>
<Loop>
<Setup>
<Param>ID</Param>
<From>1</From>
<Inc>1</Inc>
<To>2</To>
</Setup>
<Do>
<UseTemplate Name="ASOBO_ELECTRICAL_Base_Template">
<UseParametersFn Name="AIRCRAFT_Push_Button_Get_Params_ID">
<IE_SOURCE>OPD_LV_BAT</IE_SOURCE>
<BASE_NAME>OVH_PUSH_OPD_LV_BAT</BASE_NAME>
</UseParametersFn>
</UseTemplate>
</Do>
</Loop>
<Loop>
<Setup>
<Param>ID</Param>
<From>1</From>
<Inc>1</Inc>
<To>2</To>
</Setup>
<Do>
<UseTemplate Name="ASOBO_ELECTRICAL_Base_Template">
<UseParametersFn Name="AIRCRAFT_Push_Button_Get_Params_ID">
<IE_SOURCE>OPD_LV_MAIN</IE_SOURCE>
<BASE_NAME>OVH_PUSH_OPD_LV_MAIN</BASE_NAME>
</UseParametersFn>
</UseTemplate>
</Do>
</Loop>
<Loop>
<Setup>
<Param>ID</Param>
<From>1</From>
<Inc>1</Inc>
<To>2</To>
</Setup>
<Do>
<UseTemplate Name="ASOBO_ELECTRICAL_Base_Template">
<UseParametersFn Name="AIRCRAFT_Push_Button_Get_Params_ID">
<IE_SOURCE>ESU</IE_SOURCE>
<BASE_NAME>OVH_PUSH_ESU</BASE_NAME>
</UseParametersFn>
</UseTemplate>
</Do>
</Loop>
</Component>
</Component>
Update Frequency Preset Example
Update presets are used to define how often a model behavior component updates based on the size of the SimObject on screen. This example shows a basic preset definition, and you can find a full explanation of the XML elements used from the page on Update Frequency Preset Definitions:
<UpdateFrequencyPresetList>
<UpdateFrequencyPreset>
<Name>My_Default_Update</Name>
<SizeOnScreenForMaxUpdateFrequency>.1</SizeOnScreenForMaxUpdateFrequency>
<SizeOnScreenForMinUpdateFrequency>.01</SizeOnScreenForMinUpdateFrequency>
<SizeOnScreenToStopUpdate>.01</SizeOnScreenToStopUpdate>
</UpdateFrequencyPreset>
</UpdateFrequencyPresetList>
Legacy Code Animations
The animation in the example below shows only if the N1 is rotating slowly and sets the correct position of the model animation for legacy FSX aircraft. The <Animation>
parameter outputs a value between 0 and 100, which is then divided by the <AnimLength>
- 100 to sample the model animation at the correct position. The model animation simply defines a rotation from 0 to 360 degrees:
<PartInfo>
<Name>N1_2_still</Name>
<Copy>prop_anim</Copy>
<AnimLength>100</AnimLength>
<Visibility>
<Parameter>
<Code>
(A:ENG N1 RPM:3, percent) 6.25 < if{ 1 } els{ 0 }
</Code>
</Parameter>
</Visibility>
<Animation>
<Parameter>
<Code>
(A:ENG N1 RPM:3, percent) 0.01 >
if{ (A:GENERAL ENG RPM:3,degrees per second) (E:ABSOLUTE TIME, second) * 360 % 3.6 / }
els{ 0 }
</Code>
</Parameter>
</Animation>
</PartInfo>
For more information on the coding language used for the conditional expressions, see the section on Reverse Polish Notation.
Legacy Simulation-Driven Material Animation Example
IMPORTANT! Material animations are an experimental feature and may undergo significant changes in the future.
Simulation-driven material animations currently only support animating the emissive parameter and should return the intensity of the light emitted. The value returned should be 0 or higher, where 0 is no emission and 1 is fully emissive. Values above 1 may be used to induce strong bloom effects. The <AnimLength>
parameter is not necessary for Material animations to function.
Below is a short example of this kind of animation:
<PartInfo>
<Name>switch_landing_light</Name>
<Materials>
<Emissive>
<Parameter>
<Code>
(A:LIGHT LANDING, bool) (A:LIGHT LANDING ON, bool) and 100 *
</Code>
</Parameter>
</Emissive>
</Materials>
</PartInfo>
Legacy User Interaction
User interaction is defined with the <MouseRect>
element. They may change simulation variables through key events, or local (L:
) variables directly. These simulation variables are used to animate the relevant parts. Many examples may be found in the plane packages provided with the game.
The below example animates an instrument that, when moved by the user, changes the rudder trim position:
<PartInfo>
<Name>btn_rudder</Name>
<AnimLength>100</AnimLength>
<PartID>BTN_RUDDER</PartID>
<CameraGUID>{7E10EDEC-E1BD-4221-87F3-C00775D5281D}</CameraGUID>
<Animation>
<Parameter>
<Code>
(L:LastPos) 0 == if{ 50 (>L:RudderTrimtabPos) }
(A:RUDDER TRIM PCT, percent) (L:RudderTrimtabPos) 50 - 30 / + (>L:NextValue, percent) (L:NextValue, percent) -100 > (L:NextValue, percent) 100 < and if{ (L:NextValue, percent) (>K:RUDDER_TRIM_SET) }
(L:RudderTrimtabPos)
</Code>
</Parameter>
<Lag>50</Lag>
</Animation>
<MouseRect>
<Cursor>Grab</Cursor>
<MouseFlags>WheelUp+WheelDown+LeftDrag+LeftSingle+MoveRepeat+LeftRelease+Leave</MouseFlags>
<CallbackCode>
(M:Event) 'LeftSingle' scmi 0 == if{ (M:X) (>L:LastPos) }
(M:Event) 'WheelUp' scmi 0 == if{ 0 (>K:RUDDER_TRIM_RIGHT) }
(M:Event) 'WheelDown' scmi 0 == if{ 0 (>K:RUDDER_TRIM_LEFT) }
(M:Event) 'LeftDrag' scmi 0 == if{
(M:X) (L:LastPos) - 5 > if{ (L:RudderTrimtabPos) 2 + (>L:RudderTrimtabPos) (M:X) (>L:LastPos) }
(M:X) (L:LastPos) - -5 < if{ (L:RudderTrimtabPos) 2 - (>L:RudderTrimtabPos) (M:X) (>L:LastPos) } }
(M:Event) 'LeftRelease' scmi 0 == if{ 50 (>L:RudderTrimtabPos) }
(M:Event) 'Leave' scmi 0 == if{ 50 (>L:RudderTrimtabPos) }
</CallbackCode>
</MouseRect>
</PartInfo>