mirror of
https://github.com/hawkeye-stan/msfs-popout-panel-manager.git
synced 2024-12-03 11:10:10 +00:00
Added dynamic lod with auto fps
This commit is contained in:
parent
b0a6079800
commit
c68ec210d1
28 changed files with 1066 additions and 1166 deletions
|
@ -11,9 +11,9 @@
|
|||
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
|
||||
<RootNamespace>MSFSPopoutPanelManager.DomainModel</RootNamespace>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>4.1.0.4</Version>
|
||||
<AssemblyVersion>4.1.0.4</AssemblyVersion>
|
||||
<FileVersion>4.1.0.4</FileVersion>
|
||||
<Version>4.1.1.0</Version>
|
||||
<AssemblyVersion>4.1.1.0</AssemblyVersion>
|
||||
<FileVersion>4.1.1.0</FileVersion>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
|
|
38
DomainModel/DynamicLod/DynamicLodSimData.cs
Normal file
38
DomainModel/DynamicLod/DynamicLodSimData.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using MSFSPopoutPanelManager.Shared;
|
||||
|
||||
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
|
||||
{
|
||||
public class DynamicLodSimData : ObservableObject
|
||||
{
|
||||
public int Fps { get; set; }
|
||||
|
||||
public int Tlod { get; set; }
|
||||
|
||||
public int Olod { get; set; }
|
||||
|
||||
public double Agl { get; set; }
|
||||
|
||||
public string CloudQuality { get; set; } = "N/A";
|
||||
|
||||
public double AltAboveGround { get; set; }
|
||||
|
||||
public double AltAboveGroundMinusCg { get; set; }
|
||||
|
||||
public double GroundVelocity { get; set; }
|
||||
|
||||
public bool PlaneOnGround { get; set; } = true;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Fps = 0;
|
||||
Tlod = 0;
|
||||
Olod = 0;
|
||||
Agl = 0;
|
||||
CloudQuality = "N/A";
|
||||
AltAboveGround = 0;
|
||||
AltAboveGroundMinusCg = 0;
|
||||
GroundVelocity = 0;
|
||||
PlaneOnGround = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using MSFSPopoutPanelManager.Shared;
|
||||
|
||||
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
|
||||
{
|
||||
public class LodConfig : ObservableObject
|
||||
{
|
||||
public int Agl { get; set; }
|
||||
|
||||
public int Lod { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
|
||||
{
|
||||
public class ObservableLodConfigLinkedList : LinkedList<LodConfig>, INotifyCollectionChanged
|
||||
{
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, LodConfig item)
|
||||
{
|
||||
if (CollectionChanged == null)
|
||||
return;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public new void AddFirst(LodConfig item)
|
||||
{
|
||||
base.AddFirst(item);
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
|
||||
}
|
||||
|
||||
public new void AddLast(LodConfig item)
|
||||
{
|
||||
base.AddLast(item);
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
|
||||
}
|
||||
|
||||
public new void AddBefore(LinkedListNode<LodConfig> node, LinkedListNode<LodConfig> newNode)
|
||||
{
|
||||
base.AddBefore(node, newNode);
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value);
|
||||
}
|
||||
|
||||
public new void AddAfter(LinkedListNode<LodConfig> node, LinkedListNode<LodConfig> newNode)
|
||||
{
|
||||
base.AddAfter(node, newNode);
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value);
|
||||
}
|
||||
|
||||
public new void Remove(LodConfig item)
|
||||
{
|
||||
base.Remove(item);
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item);
|
||||
}
|
||||
|
||||
public new void RemoveFirst()
|
||||
{
|
||||
if (First == null)
|
||||
return;
|
||||
|
||||
base.RemoveFirst();
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, First.Value);
|
||||
}
|
||||
|
||||
public new void RemoveLast()
|
||||
{
|
||||
if (Last == null)
|
||||
return;
|
||||
|
||||
base.RemoveLast();
|
||||
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, Last.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,4 @@
|
|||
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
|
||||
using MSFSPopoutPanelManager.Shared;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.Serialization;
|
||||
using MSFSPopoutPanelManager.Shared;
|
||||
|
||||
namespace MSFSPopoutPanelManager.DomainModel.Setting
|
||||
{
|
||||
|
@ -11,31 +7,13 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
|
|||
public DynamicLodSetting()
|
||||
{
|
||||
InitializeChildPropertyChangeBinding();
|
||||
|
||||
TlodConfigs.CollectionChanged += (_, e) =>
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if (e.NewItems?[0] is LodConfig item)
|
||||
item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
|
||||
}
|
||||
};
|
||||
|
||||
OlodConfigs.CollectionChanged += (_, e) =>
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if (e.NewItems?[0] is LodConfig item)
|
||||
item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsEnabled { get; set; } = false;
|
||||
|
||||
public ObservableLodConfigLinkedList TlodConfigs { get; set; } = new();
|
||||
public bool PauseWhenMsfsLoseFocus { get; set; } = true;
|
||||
|
||||
public ObservableLodConfigLinkedList OlodConfigs { get; set; } = new();
|
||||
public bool PauseOutsideCockpitView { get; set; } = true;
|
||||
|
||||
public bool ResetEnabled { get; set; } = false;
|
||||
|
||||
|
@ -43,29 +21,28 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
|
|||
|
||||
public int ResetOlod { get; set; } = 100;
|
||||
|
||||
public void AddDefaultTLodConfigs()
|
||||
{
|
||||
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 0, Lod = 100 }));
|
||||
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 5000, Lod = 200 }));
|
||||
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 10000, Lod = 300 }));
|
||||
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 20000, Lod = 400 }));
|
||||
}
|
||||
public int TargetedFps { get; set; } = 60;
|
||||
|
||||
public void AddDefaultOLodConfigs()
|
||||
{
|
||||
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 0, Lod = 200 }));
|
||||
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 1000, Lod = 150 }));
|
||||
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 5000, Lod = 100 }));
|
||||
}
|
||||
public int FpsTolerance { get; set; } = 5;
|
||||
|
||||
[OnDeserialized]
|
||||
private void OnDeserialization(StreamingContext context)
|
||||
{
|
||||
foreach (var item in TlodConfigs)
|
||||
item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
|
||||
public bool TlodMinOnGround { get; set; } = true;
|
||||
|
||||
foreach (var item in OlodConfigs)
|
||||
item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
|
||||
}
|
||||
public int AltTlodBase { get; set; } = 1000;
|
||||
|
||||
public int TlodMin { get; set; } = 50;
|
||||
|
||||
public int TlodMax { get; set; } = 400;
|
||||
|
||||
public int CloudRecoveryTlod { get; set; } = 100;
|
||||
|
||||
public bool DecreaseCloudQuality { get; set; } = true;
|
||||
|
||||
public int OlodTop { get; set; } = 20;
|
||||
|
||||
public int OlodBase { get; set; } = 200;
|
||||
|
||||
public int AltOlodBase { get; set; } = 1000;
|
||||
|
||||
public int AltOlodTop { get; set; } = 10000;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,10 +53,11 @@ namespace MSFSPopoutPanelManager.MainApp
|
|||
|
||||
services.AddSingleton(s => new AppOrchestrator(SharedStorage, s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<HelpOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
|
||||
services.AddSingleton(_ => new ProfileOrchestrator(SharedStorage));
|
||||
services.AddSingleton(_ => new DynamicLodOrchestrator(SharedStorage));
|
||||
services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
|
||||
services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
|
||||
services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
|
||||
services.AddSingleton(_ => new FlightSimOrchestrator(SharedStorage));
|
||||
services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage, s.GetRequiredService<DynamicLodOrchestrator>()));
|
||||
services.AddSingleton(_ => new KeyboardOrchestrator(SharedStorage));
|
||||
services.AddSingleton(_ => new HelpOrchestrator());
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog.AddProfileDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
|
||||
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
|
||||
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
|
||||
Width="400"
|
||||
Height="210"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
|
||||
|
|
|
@ -3,26 +3,33 @@
|
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mainApp="clr-namespace:MSFSPopoutPanelManager.MainApp"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
||||
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
|
||||
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<system:Double x:Key="IconSize">22</system:Double>
|
||||
<system:Double x:Key="ButtonSize">22</system:Double>
|
||||
<Style
|
||||
x:Key="TextBlockHeading"
|
||||
BasedOn="{StaticResource {x:Type TextBlock}}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
<Setter Property="Width" Value="Auto" />
|
||||
<Setter Property="Margin" Value="0,5,0,0" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="TextBlockLabel"
|
||||
BasedOn="{StaticResource {x:Type TextBlock}}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
<Setter Property="Width" Value="650" />
|
||||
<Setter Property="Margin" Value="5,0,0,0" />
|
||||
<Setter Property="Margin" Value="0,7,0,0" />
|
||||
<Setter Property="LineHeight" Value="20" />
|
||||
</Style>
|
||||
<Style
|
||||
|
@ -33,522 +40,238 @@
|
|||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Grid d:DataContext="{d:DesignInstance viewModel:ApplicationViewModel}">
|
||||
<WrapPanel Margin="30,10,0,0">
|
||||
<!-- Terrain level of detail -->
|
||||
<WrapPanel Margin="20,0,0,0" Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="Bold">
|
||||
Terrain Level of Detail (TLOD)
|
||||
</TextBlock>
|
||||
<DataGrid
|
||||
Name="TlodGrid"
|
||||
Width="272"
|
||||
Height="248"
|
||||
<WrapPanel Orientation="Vertical">
|
||||
<WrapPanel Margin="120,10,0,0">
|
||||
<GroupBox
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
GridLinesVisibility="None"
|
||||
HeadersVisibility="Column"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
ItemsSource="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs}"
|
||||
SelectionUnit="FullRow"
|
||||
VerticalGridLinesBrush="#B9B9B9">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="Background" Value="{x:Null}" />
|
||||
<Setter Property="BorderBrush" Value="{x:Null}" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.ColumnHeaderStyle>
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
Text="{Binding}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="Height" Value="30" />
|
||||
<Setter Property="Background" Value="#FF576573" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
|
||||
</Style>
|
||||
</DataGrid.ColumnHeaderStyle>
|
||||
<DataGrid.CellStyle>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center" />
|
||||
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Style>
|
||||
</DataGrid.CellStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="100" Header="AGL">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
Path="Agl"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:AglValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="100" Header="LOD">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
Path="Lod"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:LodValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="70" Header="">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button
|
||||
Width="{StaticResource ButtonSize}"
|
||||
Height="{StaticResource ButtonSize}"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
Click="TLodDelete_Click"
|
||||
KeyboardNavigation.AcceptsReturn="False"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="Delete TLOD configuration">
|
||||
<materialDesign:PackIcon
|
||||
Width="{StaticResource IconSize}"
|
||||
Height="{StaticResource IconSize}"
|
||||
Kind="Delete" />
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<WrapPanel Margin="0,5,0,0">
|
||||
<DataGrid
|
||||
Name="AddTlodGrid"
|
||||
Width="272"
|
||||
Height="50"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="0"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
|
||||
GridLinesVisibility="None"
|
||||
HeadersVisibility="None"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
ItemsSource="{Binding AddTlodConfigs}"
|
||||
SelectionUnit="FullRow"
|
||||
VerticalGridLinesBrush="#B9B9B9"
|
||||
VerticalScrollBarVisibility="Disabled">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="Background" Value="{x:Null}" />
|
||||
<Setter Property="BorderBrush" Value="{x:Null}" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.CellStyle>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center" />
|
||||
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Style>
|
||||
</DataGrid.CellStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="100" Header="AGL">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:HintAssist.Hint="New AGL"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
SourceUpdated="AddTlod_SourceUpdated">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
NotifyOnSourceUpdated="True"
|
||||
Path="Agl"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:AglValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="100" Header="LOD">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:HintAssist.Hint="New LOD"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
SourceUpdated="AddTlod_SourceUpdated">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
NotifyOnSourceUpdated="True"
|
||||
Path="Lod"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:LodValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="70" Header="">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<materialDesign:PackIcon
|
||||
Width="{StaticResource IconSize}"
|
||||
Height="{StaticResource IconSize}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="White"
|
||||
Kind="Add" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</WrapPanel>
|
||||
Padding="5,2,5,2"
|
||||
Header="Sim Values"
|
||||
Style="{StaticResource MaterialDesignGroupBox}">
|
||||
<GroupBox.HeaderTemplate>
|
||||
<HierarchicalDataTemplate>
|
||||
<Label
|
||||
Height="12"
|
||||
Margin="10,1,1,1"
|
||||
Padding="0"
|
||||
FontSize="12">
|
||||
Sim Values
|
||||
</Label>
|
||||
</HierarchicalDataTemplate>
|
||||
</GroupBox.HeaderTemplate>
|
||||
<WrapPanel Margin="3">
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="20,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="FPS"
|
||||
IsHitTestVisible="False"
|
||||
IsReadOnly="True"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding FlightSimData.DynamicLodSimData.Fps, StringFormat='{}{0:0}'}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="20,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="TLOD"
|
||||
IsHitTestVisible="False"
|
||||
IsReadOnly="True"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding FlightSimData.DynamicLodSimData.Tlod}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="20,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="OLOD"
|
||||
IsHitTestVisible="False"
|
||||
IsReadOnly="True"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding FlightSimData.DynamicLodSimData.Olod}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="20,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="AGL"
|
||||
IsHitTestVisible="False"
|
||||
IsReadOnly="True"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding FlightSimData.DynamicLodSimData.Agl, StringFormat='{}{0:0}'}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="20,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="Clouds"
|
||||
IsHitTestVisible="False"
|
||||
IsReadOnly="True"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding FlightSimData.DynamicLodSimData.CloudQuality}" />
|
||||
</WrapPanel>
|
||||
</GroupBox>
|
||||
</WrapPanel>
|
||||
|
||||
<!-- Object level of detail -->
|
||||
<WrapPanel Margin="50,0,0,0" Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="Bold">
|
||||
Object Level of Detail (OLOD)
|
||||
</TextBlock>
|
||||
<DataGrid
|
||||
Name="OlodGrid"
|
||||
Width="272"
|
||||
Height="248"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
GridLinesVisibility="None"
|
||||
HeadersVisibility="Column"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
ItemsSource="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs}"
|
||||
SelectionUnit="FullRow"
|
||||
VerticalGridLinesBrush="#B9B9B9">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="Background" Value="{x:Null}" />
|
||||
<Setter Property="BorderBrush" Value="{x:Null}" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.ColumnHeaderStyle>
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
Text="{Binding}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="Height" Value="30" />
|
||||
<Setter Property="Background" Value="#FF576573" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
|
||||
</Style>
|
||||
</DataGrid.ColumnHeaderStyle>
|
||||
<DataGrid.CellStyle>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center" />
|
||||
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Style>
|
||||
</DataGrid.CellStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="100" Header="AGL">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
Path="Agl"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:AglValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="100" Header="LOD">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
Path="Lod"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:LodValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="70" Header="">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button
|
||||
Width="{StaticResource ButtonSize}"
|
||||
Height="{StaticResource ButtonSize}"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
Click="OLodDelete_Click"
|
||||
KeyboardNavigation.AcceptsReturn="False"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="Delete OLOD configuration">
|
||||
<materialDesign:PackIcon
|
||||
Width="{StaticResource IconSize}"
|
||||
Height="{StaticResource IconSize}"
|
||||
Kind="Delete" />
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<WrapPanel Margin="0,5,0,0">
|
||||
<DataGrid
|
||||
Name="AddOlodGrid"
|
||||
Width="272"
|
||||
Height="50"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="0"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
|
||||
GridLinesVisibility="None"
|
||||
HeadersVisibility="None"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
ItemsSource="{Binding AddOlodConfigs}"
|
||||
SelectionUnit="FullRow"
|
||||
VerticalGridLinesBrush="#B9B9B9"
|
||||
VerticalScrollBarVisibility="Disabled">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="Background" Value="{x:Null}" />
|
||||
<Setter Property="BorderBrush" Value="{x:Null}" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.CellStyle>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center" />
|
||||
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Style>
|
||||
</DataGrid.CellStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="100" Header="AGL">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:HintAssist.Hint="New AGL"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
SourceUpdated="AddOlod_SourceUpdated">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
NotifyOnSourceUpdated="True"
|
||||
Path="Agl"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:AglValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="100" Header="LOD">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox
|
||||
Width="100"
|
||||
materialDesign:HintAssist.Hint="New LOD"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
SourceUpdated="AddOlod_SourceUpdated">
|
||||
<TextBox.Text>
|
||||
<Binding
|
||||
Mode="TwoWay"
|
||||
NotifyOnSourceUpdated="True"
|
||||
Path="Lod"
|
||||
UpdateSourceTrigger="LostFocus">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:LodValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Width="70" Header="">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<materialDesign:PackIcon
|
||||
Width="{StaticResource IconSize}"
|
||||
Height="{StaticResource IconSize}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="White"
|
||||
Kind="Add" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="0,20,0,0" Orientation="Horizontal">
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">
|
||||
Enable reset of TLOD and OLOD to the following values when flight session ends.
|
||||
</TextBlock>
|
||||
<WrapPanel IsEnabled="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled}" Orientation="Horizontal">
|
||||
<WrapPanel Margin="47,10,0,0" Orientation="Vertical">
|
||||
<WrapPanel Margin="0,0,0,0">
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">General Options</TextBlock>
|
||||
<Line
|
||||
Stretch="Fill"
|
||||
Stroke="Gray"
|
||||
X2="1" />
|
||||
<TextBox
|
||||
Width="100"
|
||||
Height="40"
|
||||
Margin="45,5,0,0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
materialDesign:HintAssist.Hint="Reset TLOD"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="1"
|
||||
FontSize="14"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}">
|
||||
<TextBox.Text>
|
||||
<Binding Mode="TwoWay" Path="AppSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:LodValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
Margin="0,0,0,0"
|
||||
Padding="5,5,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Foreground="White"
|
||||
materialDesign:HintAssist.Hint="Targeted FPS"
|
||||
Background="#607D8B"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TargetedFps, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="FPS Tolerance"
|
||||
materialDesign:TextFieldAssist.SuffixText="%"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.FpsTolerance, Mode=TwoWay}" />
|
||||
<WrapPanel Margin="40,10,0,0">
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.PauseWhenMsfsLoseFocus, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">Pause when MSFS loses focus</TextBlock>
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="20,10,0,0">
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">Pause when not in cockpit view</TextBlock>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="0,15,0,0">
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">TLOD Options</TextBlock>
|
||||
<Line
|
||||
Stretch="Fill"
|
||||
Stroke="Gray"
|
||||
X2="1" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="0,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="TLOD Minimum"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodMin, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="TLOD Maximum"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodMax, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="TLOD Base Alt"
|
||||
materialDesign:TextFieldAssist.SuffixText="ft"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.AltTlodBase, Mode=TwoWay}" />
|
||||
<WrapPanel Margin="40,10,0,0">
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodMinOnGround, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">TLOD minimum on ground/TLOD Base Altitude</TextBlock>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="0,15,0,0">
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">OLOD Options</TextBlock>
|
||||
<Line
|
||||
Stretch="Fill"
|
||||
Stroke="Gray"
|
||||
X2="1" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="0,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="OLOD at Base Alt"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodBase, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="OLOD at Top Alt"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodTop, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="OLOD Base Alt"
|
||||
materialDesign:TextFieldAssist.SuffixText="ft"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.AltOlodBase, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="OLOD Top Alt"
|
||||
materialDesign:TextFieldAssist.SuffixText="ft"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.AltOlodTop, Mode=TwoWay}" />
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="0,15,0,0">
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">Cloud Options</TextBlock>
|
||||
<Line
|
||||
Stretch="Fill"
|
||||
Stroke="Gray"
|
||||
X2="1" />
|
||||
<TextBox
|
||||
Width="100"
|
||||
Height="40"
|
||||
Margin="20,5,0,0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
materialDesign:HintAssist.Hint="Reset OLOD"
|
||||
materialDesign:ValidationAssist.HorizontalAlignment="Center"
|
||||
BorderThickness="1"
|
||||
FontSize="14"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}">
|
||||
<TextBox.Text>
|
||||
<Binding Mode="TwoWay" Path="AppSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod">
|
||||
<Binding.ValidationRules>
|
||||
<appUserControl:LodValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
Margin="0,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="Cloud Adjust at TLOD"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.CloudRecoveryTlod, Mode=TwoWay}" />
|
||||
<WrapPanel Margin="20,10,0,0">
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.DecreaseCloudQuality, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">Decrease cloud quality by one level to archive desired FPS</TextBlock>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="0,15,0,0">
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">Reset TLOD and OLOD on Exit</TextBlock>
|
||||
<Line
|
||||
Stretch="Fill"
|
||||
Stroke="Gray"
|
||||
X2="1" />
|
||||
<WrapPanel Margin="0,0,0,0">
|
||||
<WrapPanel Margin="0,7,0,0">
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">Reset on Exit</TextBlock>
|
||||
</WrapPanel>
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="TLOD"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="75"
|
||||
Margin="40,0,0,0"
|
||||
materialDesign:HintAssist.FloatingScale="0.75"
|
||||
materialDesign:HintAssist.Hint="OLOD"
|
||||
PreviewTextInput="TxtBox_NumbersOnly"
|
||||
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
|
||||
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod, Mode=TwoWay}" />
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using MSFSPopoutPanelManager.MainApp.ViewModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
|
||||
using MSFSPopoutPanelManager.MainApp.ViewModel;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
|
||||
{
|
||||
public partial class DynamicLodPreference
|
||||
{
|
||||
private ObservableLodConfigLinkedList _tlodConfigs;
|
||||
private ObservableLodConfigLinkedList _olodConfigs;
|
||||
|
||||
public DynamicLodPreference()
|
||||
{
|
||||
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
|
||||
|
@ -24,146 +16,21 @@ namespace MSFSPopoutPanelManager.MainApp.AppUserControl
|
|||
InitializeComponent();
|
||||
return;
|
||||
}
|
||||
|
||||
AddTlodConfigs = new ObservableCollection<TempLodConfig>() { new () };
|
||||
AddOlodConfigs = new ObservableCollection<TempLodConfig>() { new () };
|
||||
|
||||
|
||||
Loaded += (_, _) =>
|
||||
{
|
||||
InitializeComponent();
|
||||
var dataContext = DataContext as ApplicationViewModel;
|
||||
_tlodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs;
|
||||
_olodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs;
|
||||
};
|
||||
}
|
||||
|
||||
public ObservableCollection<TempLodConfig> AddTlodConfigs { get; set; }
|
||||
|
||||
public ObservableCollection<TempLodConfig> AddOlodConfigs { get; set; }
|
||||
|
||||
private void AddTlod_SourceUpdated(object sender, DataTransferEventArgs e)
|
||||
private void TxtBox_NumbersOnly(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
var textBox = sender as TextBox;
|
||||
|
||||
var lodConfig = textBox?.DataContext as TempLodConfig;
|
||||
|
||||
if (lodConfig?.Agl == null || lodConfig.Lod == null)
|
||||
return;
|
||||
|
||||
if (UpdateTlodDuplicate(lodConfig))
|
||||
{
|
||||
RebindTLodGrid();
|
||||
return;
|
||||
}
|
||||
|
||||
var targetLodConfig = _tlodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl);
|
||||
var newLodConfig = new LodConfig { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod };
|
||||
|
||||
if (targetLodConfig == null)
|
||||
_tlodConfigs.AddFirst(newLodConfig);
|
||||
else
|
||||
_tlodConfigs.AddAfter(_tlodConfigs.Find(targetLodConfig), new LinkedListNode<LodConfig>(newLodConfig));
|
||||
|
||||
RebindTLodGrid();
|
||||
}
|
||||
|
||||
private void TLodDelete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var button = e.Source as Button;
|
||||
|
||||
if(button?.DataContext is not LodConfig lodConfig)
|
||||
return;
|
||||
|
||||
_tlodConfigs.Remove(lodConfig);
|
||||
|
||||
RebindTLodGrid();
|
||||
}
|
||||
|
||||
private void RebindTLodGrid()
|
||||
{
|
||||
this.TlodGrid.ItemsSource = null;
|
||||
this.TlodGrid.ItemsSource = _tlodConfigs.ToList();
|
||||
|
||||
AddTlodConfigs.Clear();
|
||||
AddTlodConfigs.Add(new TempLodConfig());
|
||||
}
|
||||
|
||||
private bool UpdateTlodDuplicate(TempLodConfig lodConfig)
|
||||
{
|
||||
var tlodConfig = _tlodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl);
|
||||
|
||||
if(tlodConfig == null)
|
||||
return false;
|
||||
|
||||
tlodConfig.Lod = Convert.ToInt32(lodConfig.Lod);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddOlod_SourceUpdated(object sender, DataTransferEventArgs e)
|
||||
{
|
||||
var textBox = sender as TextBox;
|
||||
|
||||
var lodConfig = textBox?.DataContext as TempLodConfig;
|
||||
|
||||
if (lodConfig?.Agl == null || lodConfig.Lod == null)
|
||||
return;
|
||||
|
||||
if (UpdateOlodDuplicate(lodConfig))
|
||||
{
|
||||
RebindOLodGrid();
|
||||
return;
|
||||
}
|
||||
|
||||
var targetLodConfig = _olodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl);
|
||||
var newLodConfig = new LodConfig() { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod };
|
||||
|
||||
if (targetLodConfig == null)
|
||||
_olodConfigs.AddFirst(newLodConfig);
|
||||
else
|
||||
_olodConfigs.AddAfter(_olodConfigs.Find(targetLodConfig), new LinkedListNode<LodConfig>(newLodConfig));
|
||||
|
||||
RebindOLodGrid();
|
||||
}
|
||||
|
||||
private void OLodDelete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var button = e.Source as Button;
|
||||
|
||||
if (button?.DataContext is not LodConfig lodConfig)
|
||||
return;
|
||||
|
||||
_olodConfigs.Remove(lodConfig);
|
||||
|
||||
RebindOLodGrid();
|
||||
}
|
||||
|
||||
private void RebindOLodGrid()
|
||||
{
|
||||
this.OlodGrid.ItemsSource = null;
|
||||
this.OlodGrid.ItemsSource = _olodConfigs.ToList();
|
||||
|
||||
AddOlodConfigs.Clear();
|
||||
AddOlodConfigs.Add(new TempLodConfig());
|
||||
}
|
||||
|
||||
private bool UpdateOlodDuplicate(TempLodConfig lodConfig)
|
||||
{
|
||||
var olodConfig = _olodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl);
|
||||
|
||||
if (olodConfig == null)
|
||||
return false;
|
||||
|
||||
olodConfig.Lod = Convert.ToInt32(lodConfig.Lod);
|
||||
return true;
|
||||
e.Handled = !(int.TryParse(e.Text, out _) || (e.Text.Trim() == "-"));
|
||||
}
|
||||
}
|
||||
|
||||
public class TempLodConfig
|
||||
{
|
||||
public int? Agl { get; set; }
|
||||
|
||||
public int? Lod { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class AglValidationRule : ValidationRule
|
||||
{
|
||||
|
|
|
@ -633,21 +633,23 @@
|
|||
<WrapPanel Visibility="{c:Binding LocalCompileOnly}">
|
||||
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryDynamicLodSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
|
||||
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">Dynamically adjust Terrain and Object Level of Details (Not Supported)</TextBlock>
|
||||
<WrapPanel>
|
||||
<TextBlock
|
||||
Padding="0,0,5,0"
|
||||
Foreground="Red"
|
||||
Style="{StaticResource TextBlockHeading}">
|
||||
(Unsupported Feature)
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource TextBlockHeading}">Dynamically adjust Terrain and Object Level of Details</TextBlock>
|
||||
</WrapPanel>
|
||||
<Line
|
||||
Stretch="Fill"
|
||||
Stroke="Gray"
|
||||
X2="1" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,10"
|
||||
Foreground="Red"
|
||||
Style="{StaticResource TextBlockLabel}">
|
||||
*** Experimental and not supported. Use at your own risk since this feature directly modifies MSFS memory. ***
|
||||
</TextBlock>
|
||||
<WrapPanel>
|
||||
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
|
||||
<TextBlock Style="{StaticResource TextBlockLabel}">
|
||||
Enable automatic adjustment of terrain level of detail (TLOD) and object level of detail (OLOD) based on current aircraft's above ground level (in feet).
|
||||
Enable automatic adjustments of TLOD and OLOD to get the desired targeted frame rate (FPS).
|
||||
</TextBlock>
|
||||
</WrapPanel>
|
||||
<WrapPanel Visibility="{Binding Path=AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
<RootNamespace>MSFSPopoutPanelManager.MainApp</RootNamespace>
|
||||
<ApplicationIcon>logo.ico</ApplicationIcon>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>4.1.0.4</Version>
|
||||
<AssemblyVersion>4.1.0.4</AssemblyVersion>
|
||||
<FileVersion>4.1.0.4</FileVersion>
|
||||
<Version>4.1.1.0</Version>
|
||||
<AssemblyVersion>4.1.1.0</AssemblyVersion>
|
||||
<FileVersion>4.1.1.0</FileVersion>
|
||||
<DebugType>embedded</DebugType>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<!-- Publishing options -->
|
||||
|
|
|
@ -33,15 +33,6 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
|
||||
public void Initialize()
|
||||
{
|
||||
// Add default dynamic LOD configs
|
||||
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled &&
|
||||
AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Count == 0 &&
|
||||
AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Count == 0)
|
||||
{
|
||||
AppSettingData.ApplicationSetting.DynamicLodSetting.AddDefaultTLodConfigs();
|
||||
AppSettingData.ApplicationSetting.DynamicLodSetting.AddDefaultOLodConfigs();
|
||||
}
|
||||
|
||||
if (AppSettingData.ApplicationSetting.GeneralSetting.CheckForUpdate)
|
||||
CheckForAutoUpdate();
|
||||
|
||||
|
|
|
@ -38,17 +38,6 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
break;
|
||||
}
|
||||
};
|
||||
|
||||
ApplicationSetting.DynamicLodSetting.TlodConfigs.CollectionChanged += (_, e) =>
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||
AppSettingDataManager.WriteAppSetting(ApplicationSetting);
|
||||
};
|
||||
|
||||
ApplicationSetting.DynamicLodSetting.OlodConfigs.CollectionChanged += (_, _) =>
|
||||
{
|
||||
AppSettingDataManager.WriteAppSetting(ApplicationSetting);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
|
||||
using MSFSPopoutPanelManager.WindowsAgent;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MSFSPopoutPanelManager.Orchestration
|
||||
{
|
||||
public class DynamicLodManager
|
||||
{
|
||||
private const string SIMMODULE_NAME = "WwiseLibPCx64P.dll";
|
||||
private const int PROCESS_VM_OPERATION = 0x0008;
|
||||
private const int PROCESS_VM_READ = 0x0010;
|
||||
private const int PROCESS_VM_WRITE = 0x0020;
|
||||
private const long OFFSET_MODULE_BASE =0x004B2368;
|
||||
private const long OFFSET_POINTER_MAIN = 0x3D0;
|
||||
private const long OFFSET_POINTER_TLOD_VR = 0x114;
|
||||
private const long OFFSET_POINTER_TLOD = 0xC;
|
||||
private const long OFFSET_POINTER_OLOD = 0xC;
|
||||
|
||||
private static bool _isActive;
|
||||
private static WindowProcess _process;
|
||||
private static IntPtr _processHandle;
|
||||
private static long _processModuleAddress;
|
||||
|
||||
private static long _addressMain;
|
||||
private static long _addressTlod;
|
||||
private static long _addressOlod;
|
||||
private static long _addressTlodVr;
|
||||
private static long _addressOlodVr;
|
||||
|
||||
private static FlightSimData _flightSimData;
|
||||
private static AppSettingData _appSettingData;
|
||||
private static LinkedListNode<LodConfig> _nextTlod;
|
||||
private static LinkedListNode<LodConfig> _nextOlod;
|
||||
private static LinkedListNode<LodConfig> _currentTlod;
|
||||
private static LinkedListNode<LodConfig> _currentOlod;
|
||||
|
||||
public static void Attach(FlightSimData flightSimData, AppSettingData appSettingData)
|
||||
{
|
||||
if (appSettingData == null || _isActive)
|
||||
return;
|
||||
|
||||
_flightSimData = flightSimData;
|
||||
_appSettingData = appSettingData;
|
||||
_flightSimData.OnAltAboveGroundChanged -= HandleOnAltAboveGroundChanged;
|
||||
_flightSimData.OnAltAboveGroundChanged += HandleOnAltAboveGroundChanged;
|
||||
|
||||
_process = WindowProcessManager.SimulatorProcess;
|
||||
_processModuleAddress = GetSimModuleAddress();
|
||||
|
||||
if (_process == null || _processModuleAddress == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
_processHandle = PInvoke.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, _process.ProcessId);
|
||||
|
||||
if (_processHandle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
_addressMain = ReadMemory<long>(_processModuleAddress + OFFSET_MODULE_BASE) + OFFSET_POINTER_MAIN;
|
||||
_addressTlod = ReadMemory<long>(_addressMain) + OFFSET_POINTER_TLOD;
|
||||
_addressTlodVr = ReadMemory<long>(_addressMain) + OFFSET_POINTER_TLOD_VR;
|
||||
_addressOlod = _addressTlod + OFFSET_POINTER_OLOD;
|
||||
_addressOlodVr = _addressTlodVr + OFFSET_POINTER_OLOD;
|
||||
|
||||
// Set initial LOD if already in cockpit
|
||||
InitializeData();
|
||||
|
||||
_isActive = true;
|
||||
}
|
||||
|
||||
public static void Detach()
|
||||
{
|
||||
if (_appSettingData == null)
|
||||
return;
|
||||
|
||||
if (_appSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled)
|
||||
{
|
||||
WriteTlod(_appSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod);
|
||||
WriteOlod(_appSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod);
|
||||
}
|
||||
|
||||
_isActive = false;
|
||||
|
||||
Debug.WriteLine($"Reset to custom LOD: TLOD: {_appSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod}, OLOD: {_appSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod}");
|
||||
}
|
||||
|
||||
private static void HandleOnAltAboveGroundChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!_flightSimData.IsFlightStarted)
|
||||
return;
|
||||
|
||||
var agl = _flightSimData.PlaneAltAboveGround < 0 ? 0 : _flightSimData.PlaneAltAboveGround;
|
||||
|
||||
if (_nextTlod != null && _nextTlod.Value.Agl <= agl)
|
||||
{
|
||||
_currentTlod = _nextTlod;
|
||||
_nextTlod = _currentTlod.Next;
|
||||
DynamicLodManager.WriteTlod(_currentTlod.Value.Lod);
|
||||
}
|
||||
else if (_currentTlod is { Previous: not null } && _currentTlod.Value.Agl > agl)
|
||||
{
|
||||
_currentTlod = _currentTlod.Previous;
|
||||
|
||||
if (_currentTlod != null)
|
||||
{
|
||||
_nextTlod = _currentTlod.Next;
|
||||
DynamicLodManager.WriteTlod(_currentTlod.Value.Lod);
|
||||
}
|
||||
}
|
||||
|
||||
if (_nextOlod != null && _nextOlod.Value.Agl <= agl)
|
||||
{
|
||||
_currentOlod = _nextOlod;
|
||||
_nextOlod = _currentOlod.Next;
|
||||
DynamicLodManager.WriteOlod(_currentOlod.Value.Lod);
|
||||
}
|
||||
else if (_currentOlod is { Previous: not null } && _currentOlod.Value.Agl > agl)
|
||||
{
|
||||
_currentOlod = _currentOlod.Previous;
|
||||
|
||||
if (_currentOlod != null)
|
||||
{
|
||||
_nextOlod = _currentOlod.Next;
|
||||
DynamicLodManager.WriteOlod(_currentOlod.Value.Lod);
|
||||
}
|
||||
}
|
||||
|
||||
if(_currentTlod != null && _currentOlod != null)
|
||||
Debug.WriteLine($"Altitude: {agl}, TLOD: {_currentTlod.Value.Lod}, OLOD: {_currentOlod.Value.Lod}");
|
||||
}
|
||||
|
||||
private static void WriteTlod(int value, bool isVr = false)
|
||||
{
|
||||
WriteMemory(isVr ? _addressTlodVr : _addressTlod, value / 100.0f);
|
||||
}
|
||||
|
||||
private static void WriteOlod(int value, bool isVr = false)
|
||||
{
|
||||
WriteMemory(isVr ? _addressOlodVr : _addressOlod, value / 100.0f);
|
||||
}
|
||||
|
||||
private static long GetSimModuleAddress()
|
||||
{
|
||||
if (_process == null)
|
||||
return -1;
|
||||
|
||||
foreach (ProcessModule processModule in _process.Modules)
|
||||
{
|
||||
if (processModule.ModuleName == SIMMODULE_NAME)
|
||||
return processModule.BaseAddress;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void WriteMemory(long address, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var buffer = StructureToByteArray(value);
|
||||
PInvoke.NtWriteVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static T ReadMemory<T>(long address) where T : struct
|
||||
{
|
||||
try
|
||||
{
|
||||
var byteSize = Marshal.SizeOf(typeof(T));
|
||||
var buffer = new byte[byteSize];
|
||||
PInvoke.NtReadVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
|
||||
return ByteArrayToStructure<T>(buffer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
private static byte[] StructureToByteArray(object obj)
|
||||
{
|
||||
var length = Marshal.SizeOf(obj);
|
||||
var array = new byte[length];
|
||||
var pointer = Marshal.AllocHGlobal(length);
|
||||
|
||||
Marshal.StructureToPtr(obj, pointer, true);
|
||||
Marshal.Copy(pointer, array, 0, length);
|
||||
Marshal.FreeHGlobal(pointer);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
|
||||
{
|
||||
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
|
||||
try
|
||||
{
|
||||
var result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||
|
||||
if(result != null)
|
||||
return (T)result;
|
||||
|
||||
return default(T);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeData()
|
||||
{
|
||||
var agl = _flightSimData.PlaneAltAboveGround < 0 ? 0 : _flightSimData.PlaneAltAboveGround;
|
||||
|
||||
var tlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.FirstOrDefault(x => x.Agl > agl);
|
||||
_nextTlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Find(tlod);
|
||||
|
||||
tlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.LastOrDefault(x => x.Agl <= agl);
|
||||
_currentTlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Find(tlod) ?? _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Last;
|
||||
|
||||
if (_currentTlod != null)
|
||||
DynamicLodManager.WriteTlod(_currentTlod.Value.Lod);
|
||||
|
||||
var olod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.FirstOrDefault(x => x.Agl > agl);
|
||||
_nextOlod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Find(olod);
|
||||
|
||||
olod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.LastOrDefault(x => x.Agl <= agl);
|
||||
_currentOlod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Find(olod) ?? _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Last;
|
||||
|
||||
if (_currentOlod != null)
|
||||
DynamicLodManager.WriteOlod(_currentOlod.Value.Lod);
|
||||
|
||||
if (_currentTlod != null && _currentOlod != null)
|
||||
Debug.WriteLine($"Initialize Altitude: {agl}, TLOD: {_currentTlod.Value.Lod}, OLOD: {_currentOlod.Value.Lod}");
|
||||
}
|
||||
|
||||
//private static int ReadTlod(bool isVr = false)
|
||||
//{
|
||||
// return Convert.ToInt32(ReadMemory<float>(isVr ? _addressTlodVr : _addressTlod) * 100.0f);
|
||||
//}
|
||||
|
||||
//private static int ReadOlod(bool isVr = false)
|
||||
//{
|
||||
// return Convert.ToInt32(ReadMemory<float>(isVr ? _addressOlodVr : _addressOlod) * 100.0f);
|
||||
//}
|
||||
}
|
||||
}
|
336
Orchestration/DynamicLodOrchestrator.cs
Normal file
336
Orchestration/DynamicLodOrchestrator.cs
Normal file
|
@ -0,0 +1,336 @@
|
|||
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
|
||||
using MSFSPopoutPanelManager.DomainModel.Setting;
|
||||
using MSFSPopoutPanelManager.Shared;
|
||||
using MSFSPopoutPanelManager.SimConnectAgent;
|
||||
using MSFSPopoutPanelManager.WindowsAgent;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MSFSPopoutPanelManager.Orchestration
|
||||
{
|
||||
public class DynamicLodOrchestrator : BaseOrchestrator
|
||||
{
|
||||
private const string SIMMODULE_NAME = "WwiseLibPCx64P.dll";
|
||||
private const int PROCESS_VM_OPERATION = 0x0008;
|
||||
private const int PROCESS_VM_READ = 0x0010;
|
||||
private const int PROCESS_VM_WRITE = 0x0020;
|
||||
private const long OFFSET_MODULE_BASE =0x004B2368;
|
||||
private const long OFFSET_POINTER_MAIN = 0x3D0;
|
||||
private const long OFFSET_POINTER_TLOD_VR = 0x114;
|
||||
private const long OFFSET_POINTER_TLOD = 0xC;
|
||||
private const long OFFSET_POINTER_OLOD = 0xC;
|
||||
private const long OFFSET_POINTER_CLOUDQ = 0x44;
|
||||
private const long OFFSET_POINTER_CLOUDQ_VR = 0x108;
|
||||
private const long OFFSET_POINTER_VR_MODE = 0x1C;
|
||||
private const long OFFSET_POINTER_FG_MODE = 0x4A;
|
||||
private const long OFFSET_POINTER_ANSIO_FILTER = -0x18;
|
||||
private const long OFFSET_POINTER_WATER_WAVES = 0x3C;
|
||||
|
||||
private bool _isActive;
|
||||
private WindowProcess _process;
|
||||
private IntPtr _processHandle;
|
||||
private long _processModuleAddress;
|
||||
|
||||
private long _addressTlod;
|
||||
private long _addressOlod;
|
||||
private long _addressTlodVr;
|
||||
private long _addressOlodVr;
|
||||
private long _addressCloudQ;
|
||||
private long _addressCloudQVr;
|
||||
private long _addressVrMode;
|
||||
private long _addressFgMode;
|
||||
|
||||
private DynamicLodSetting DynamicLodSetting => AppSettingData.ApplicationSetting.DynamicLodSetting;
|
||||
|
||||
private DynamicLodSimData SimData => FlightSimData.DynamicLodSimData;
|
||||
|
||||
private DateTime _lastLodUpdateTime = DateTime.Now;
|
||||
private bool _isDecreasedCloudQualityActive = false;
|
||||
|
||||
public DynamicLodOrchestrator(SharedStorage sharedStorage) : base(sharedStorage) {}
|
||||
|
||||
public void Attach()
|
||||
{
|
||||
if (AppSettingData == null || _isActive)
|
||||
return;
|
||||
|
||||
_process = WindowProcessManager.SimulatorProcess;
|
||||
_processModuleAddress = GetSimModuleAddress();
|
||||
|
||||
if (_process == null || _processModuleAddress == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
_processHandle = PInvoke.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, _process.ProcessId);
|
||||
|
||||
if (_processHandle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
_addressTlod = ReadMemory<long>(_processModuleAddress + OFFSET_MODULE_BASE) + OFFSET_POINTER_MAIN;
|
||||
|
||||
if (_addressTlod > 0)
|
||||
{
|
||||
_addressTlodVr = ReadMemory<long>(_addressTlod) + OFFSET_POINTER_TLOD_VR;
|
||||
_addressTlod = ReadMemory<long>(_addressTlod) + OFFSET_POINTER_TLOD;
|
||||
_addressOlod = _addressTlod + OFFSET_POINTER_OLOD;
|
||||
_addressOlodVr = _addressTlodVr + OFFSET_POINTER_OLOD;
|
||||
_addressCloudQ = _addressTlod + OFFSET_POINTER_CLOUDQ;
|
||||
_addressCloudQVr = _addressCloudQ + OFFSET_POINTER_CLOUDQ_VR;
|
||||
_addressVrMode = _addressTlod - OFFSET_POINTER_VR_MODE;
|
||||
_addressFgMode = _addressTlod - OFFSET_POINTER_FG_MODE;
|
||||
|
||||
if (!MemoryBoundaryTest())
|
||||
{
|
||||
FileLogger.WriteLog("Unable to validate memory space for Dynamic LOD", StatusMessageType.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_isActive = true;
|
||||
|
||||
_lastLodUpdateTime = DateTime.Now;
|
||||
_isDecreasedCloudQualityActive = false;
|
||||
}
|
||||
|
||||
public void Detach()
|
||||
{
|
||||
if (DynamicLodSetting == null)
|
||||
return;
|
||||
|
||||
if (DynamicLodSetting.ResetEnabled)
|
||||
{
|
||||
var isVr = ReadIsVr();
|
||||
WriteMemory(isVr ? _addressTlodVr : _addressTlod, DynamicLodSetting.ResetTlod / 100.0f);
|
||||
WriteMemory(isVr ? _addressOlodVr : _addressOlod, DynamicLodSetting.ResetOlod / 100.0f);
|
||||
}
|
||||
|
||||
_isActive = false;
|
||||
|
||||
Debug.WriteLine($"Reset to custom LOD: TLOD: {DynamicLodSetting.ResetTlod}, OLOD: {DynamicLodSetting.ResetOlod}");
|
||||
}
|
||||
|
||||
public int ReadTlod(bool isVr = false)
|
||||
{
|
||||
return Convert.ToInt32(ReadMemory<float>(isVr ? _addressTlodVr : _addressTlod) * 100.0f);
|
||||
}
|
||||
|
||||
public int ReadOlod(bool isVr = false)
|
||||
{
|
||||
return Convert.ToInt32(ReadMemory<float>(isVr ? _addressOlodVr : _addressOlod) * 100.0f);
|
||||
}
|
||||
|
||||
public string ReadCloudQuality(bool isVr = false)
|
||||
{
|
||||
return ReadCloudQualitySimValue(isVr) switch
|
||||
{
|
||||
0 => "Low",
|
||||
1 => "Medium",
|
||||
2 => "High",
|
||||
3 => "Ultra",
|
||||
_ => "N/A"
|
||||
};
|
||||
}
|
||||
|
||||
public int ReadCloudQualitySimValue(bool isVr = false)
|
||||
{
|
||||
return Convert.ToInt32(ReadMemory<int>(isVr ? _addressCloudQVr : _addressCloudQ));
|
||||
}
|
||||
|
||||
public bool ReadIsVr()
|
||||
{
|
||||
return ReadMemory<int>(_addressVrMode) == 1;
|
||||
}
|
||||
|
||||
public bool ReadIsFg(bool isVr)
|
||||
{
|
||||
if (isVr)
|
||||
return false;
|
||||
|
||||
return ReadMemory<byte>(_addressFgMode) == 1;
|
||||
}
|
||||
|
||||
public void UpdateLod(bool isVr)
|
||||
{
|
||||
if (DateTime.Now - _lastLodUpdateTime <= TimeSpan.FromSeconds(1))
|
||||
return;
|
||||
|
||||
if (!FlightSimData.IsFlightStarted || !FlightSimData.IsInCockpit || (DynamicLodSetting.PauseOutsideCockpitView && FlightSimData.CameraState != CameraState.Cockpit))
|
||||
return;
|
||||
|
||||
var deltaFps = SimData.Fps - DynamicLodSetting.TargetedFps;
|
||||
if (Math.Abs(deltaFps) < DynamicLodSetting.TargetedFps * DynamicLodSetting.FpsTolerance / 100.0) // within FPS tolerance
|
||||
return;
|
||||
|
||||
_lastLodUpdateTime = DateTime.Now;
|
||||
|
||||
SetTlod(deltaFps);
|
||||
SetOlod();
|
||||
}
|
||||
|
||||
private long GetSimModuleAddress()
|
||||
{
|
||||
if (_process == null)
|
||||
return -1;
|
||||
|
||||
foreach (ProcessModule processModule in _process.Modules)
|
||||
{
|
||||
if (processModule.ModuleName == SIMMODULE_NAME)
|
||||
return processModule.BaseAddress;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void WriteMemory(long address, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var buffer = StructureToByteArray(value);
|
||||
PInvoke.NtWriteVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private T ReadMemory<T>(long address) where T : struct
|
||||
{
|
||||
try
|
||||
{
|
||||
var byteSize = Marshal.SizeOf(typeof(T));
|
||||
var buffer = new byte[byteSize];
|
||||
PInvoke.NtReadVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
|
||||
return ByteArrayToStructure<T>(buffer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
private byte[] StructureToByteArray(object obj)
|
||||
{
|
||||
var length = Marshal.SizeOf(obj);
|
||||
var array = new byte[length];
|
||||
var pointer = Marshal.AllocHGlobal(length);
|
||||
|
||||
Marshal.StructureToPtr(obj, pointer, true);
|
||||
Marshal.Copy(pointer, array, 0, length);
|
||||
Marshal.FreeHGlobal(pointer);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private T ByteArrayToStructure<T>(byte[] bytes) where T : struct
|
||||
{
|
||||
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
|
||||
try
|
||||
{
|
||||
var result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||
|
||||
if(result != null)
|
||||
return (T)result;
|
||||
|
||||
return default(T);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
private bool MemoryBoundaryTest()
|
||||
{
|
||||
// Boundary check a few known setting memory addresses to see if any fail which likely indicates MSFS memory map has changed
|
||||
if (ReadTlod() < 10 || ReadTlod() > 1000 || ReadTlod(true) < 10 || ReadTlod(true) > 1000
|
||||
|| ReadOlod() < 10 || ReadOlod() > 1000 || ReadOlod(true) < 10 || ReadOlod(true) > 1000
|
||||
|| ReadCloudQuality() == "N/A" || ReadCloudQuality(true) == "N/A"
|
||||
|| ReadMemory<int>(_addressVrMode) < 0 || ReadMemory<int>(_addressVrMode) > 1
|
||||
|| ReadMemory<int>(_addressTlod + OFFSET_POINTER_ANSIO_FILTER) < 1 || ReadMemory<int>(_addressTlod + OFFSET_POINTER_ANSIO_FILTER) > 16
|
||||
|| !(ReadMemory<int>(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 128 || ReadMemory<int>(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 256 || ReadMemory<int>(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 512))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetTlod(int deltaFps, bool isVr = false)
|
||||
{
|
||||
var tlodStep = Math.Max(5, Math.Abs(deltaFps / 2));
|
||||
var newTlod = SimData.Tlod + Math.Sign(deltaFps) * tlodStep;
|
||||
|
||||
if (DynamicLodSetting.TlodMinOnGround && SimData.AltAboveGround <= DynamicLodSetting.AltTlodBase)
|
||||
{
|
||||
newTlod = DynamicLodSetting.TlodMin;
|
||||
}
|
||||
else if (newTlod < DynamicLodSetting.TlodMin)
|
||||
{
|
||||
newTlod = DynamicLodSetting.TlodMin;
|
||||
}
|
||||
else if (newTlod > DynamicLodSetting.TlodMax)
|
||||
{
|
||||
newTlod = DynamicLodSetting.TlodMax;
|
||||
}
|
||||
|
||||
if (ReadTlod(isVr) == newTlod)
|
||||
return;
|
||||
|
||||
// Adjust cloud quality if applicable
|
||||
if (deltaFps < 0 && newTlod < DynamicLodSetting.CloudRecoveryTlod && !_isDecreasedCloudQualityActive)
|
||||
{
|
||||
_isDecreasedCloudQualityActive = true;
|
||||
WriteMemory(isVr ? _addressCloudQVr : _addressCloudQ, ReadCloudQualitySimValue(isVr) - 1); // High
|
||||
|
||||
_lastLodUpdateTime = _lastLodUpdateTime.AddSeconds(2); // Add extra delay for cloud setting to take effect
|
||||
Debug.WriteLine("New Cloud Quality written - 2.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (deltaFps > 0 && newTlod >= DynamicLodSetting.CloudRecoveryTlod && _isDecreasedCloudQualityActive)
|
||||
{
|
||||
_isDecreasedCloudQualityActive = false;
|
||||
WriteMemory(isVr ? _addressCloudQVr : _addressCloudQ, ReadCloudQualitySimValue(isVr) + 1); // Ultra
|
||||
|
||||
_lastLodUpdateTime = _lastLodUpdateTime.AddSeconds(2);
|
||||
Debug.WriteLine("New Cloud Quality written - 3.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"New TLOD written - {newTlod}.");
|
||||
WriteMemory(isVr ? _addressTlodVr : _addressTlod, newTlod / 100.0f);
|
||||
}
|
||||
|
||||
private void SetOlod(bool isVr = false)
|
||||
{
|
||||
int newOlod;
|
||||
|
||||
if (SimData.AltAboveGround < DynamicLodSetting.AltOlodBase)
|
||||
{
|
||||
newOlod = DynamicLodSetting.OlodBase;
|
||||
}
|
||||
else if (SimData.AltAboveGround > DynamicLodSetting.AltOlodTop)
|
||||
{
|
||||
newOlod = DynamicLodSetting.OlodTop;
|
||||
}
|
||||
else
|
||||
{
|
||||
newOlod = Convert.ToInt32(DynamicLodSetting.OlodBase - (DynamicLodSetting.OlodBase - DynamicLodSetting.OlodTop) * (SimData.AltAboveGround - DynamicLodSetting.AltOlodBase) / (DynamicLodSetting.AltOlodTop - DynamicLodSetting.AltOlodBase));
|
||||
}
|
||||
|
||||
if (ReadOlod(isVr) == newOlod)
|
||||
return;
|
||||
|
||||
Debug.WriteLine($"New OLOD written - {newOlod}.");
|
||||
WriteMemory(isVr ? _addressOlodVr : _addressOlod, newOlod / 100.0f);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using MSFSPopoutPanelManager.Shared;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
|
||||
using MSFSPopoutPanelManager.SimConnectAgent;
|
||||
|
||||
namespace MSFSPopoutPanelManager.Orchestration
|
||||
|
@ -15,8 +16,6 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
IsSimConnectActive = false;
|
||||
}
|
||||
|
||||
public event EventHandler OnAltAboveGroundChanged;
|
||||
|
||||
public bool IsSimConnectActive { get; set; }
|
||||
|
||||
public string AircraftName { get; set; }
|
||||
|
@ -43,8 +42,6 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
|
||||
public bool PlaneInParkingSpot { get; set; }
|
||||
|
||||
public int PlaneAltAboveGround { get; set; }
|
||||
|
||||
public bool IsSimulatorStarted { get; set; }
|
||||
|
||||
public bool IsSimConnectDataReceived { get; set; }
|
||||
|
@ -55,6 +52,8 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
|
||||
public IHudBarData HudBarData { get; set; }
|
||||
|
||||
public DynamicLodSimData DynamicLodSimData { get; set; } = new();
|
||||
|
||||
[IgnorePropertyChanged]
|
||||
internal ProfileData ProfileDataRef { get; set; }
|
||||
|
||||
|
@ -65,9 +64,6 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
// Automatic switching of active profile when SimConnect active aircraft change
|
||||
if (e.PropertyName == "AircraftName")
|
||||
ProfileDataRef.AutoSwitchProfile();
|
||||
|
||||
if(e.PropertyName == "PlaneAltAboveGround")
|
||||
OnAltAboveGroundChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
|
@ -85,7 +81,6 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
PlaneInParkingSpot = false;
|
||||
CameraState = CameraState.Unknown;
|
||||
IsSimulatorStarted = false;
|
||||
PlaneAltAboveGround = 0;
|
||||
CameraViewTypeAndIndex1Max = 0;
|
||||
CameraViewTypeAndIndex2Max = 0;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
private System.Timers.Timer _msfsGameExitDetectionTimer;
|
||||
private SimConnectProvider _simConnectProvider;
|
||||
|
||||
private DynamicLodOrchestrator _dynamicLodOrchestrator;
|
||||
private bool _isTurnedOnPower;
|
||||
private bool _isTurnedOnAvionics;
|
||||
|
||||
public FlightSimOrchestrator(SharedStorage sharedStorage) : base(sharedStorage)
|
||||
public FlightSimOrchestrator(SharedStorage sharedStorage, DynamicLodOrchestrator dynamicLodOrchestrator) : base(sharedStorage)
|
||||
{
|
||||
_dynamicLodOrchestrator = dynamicLodOrchestrator;
|
||||
_simConnectProvider = new SimConnectProvider();
|
||||
}
|
||||
|
||||
|
@ -40,9 +42,7 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
WindowProcessManager.GetSimulatorProcess(); // refresh simulator process
|
||||
DetectMsfsExit();
|
||||
|
||||
// Attach in memory override for Dynamic LOD
|
||||
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
|
||||
DynamicLodManager.Attach(FlightSimData, AppSettingData);
|
||||
StartDynamicLod();
|
||||
};
|
||||
|
||||
_simConnectProvider.OnDisconnected += (_, _) =>
|
||||
|
@ -61,61 +61,39 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
|
||||
_simConnectProvider.OnSimConnectDataRequiredRefreshed += (_, e) =>
|
||||
{
|
||||
var electricalMasterBattery = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.ElectricalMasterBattery).Value);
|
||||
if (electricalMasterBattery != FlightSimData.ElectricalMasterBatteryStatus)
|
||||
FlightSimData.ElectricalMasterBatteryStatus = electricalMasterBattery;
|
||||
|
||||
var avionicsMasterSwitch = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.AvionicsMasterSwitch).Value);
|
||||
if (avionicsMasterSwitch != FlightSimData.AvionicsMasterSwitchStatus)
|
||||
FlightSimData.AvionicsMasterSwitchStatus = avionicsMasterSwitch;
|
||||
|
||||
var trackIR = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.TrackIREnable).Value);
|
||||
if (trackIR != FlightSimData.TrackIRStatus)
|
||||
FlightSimData.TrackIRStatus = trackIR;
|
||||
|
||||
var cameraStateInt = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraState).Value);
|
||||
var result = Enum.TryParse<CameraState>(cameraStateInt.ToString(), out var cameraState);
|
||||
if (!result)
|
||||
cameraState = CameraState.Unknown;
|
||||
if (cameraState != FlightSimData.CameraState)
|
||||
FlightSimData.CameraState = cameraState;
|
||||
|
||||
var cockpitCameraZoom = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CockpitCameraZoom).Value);
|
||||
if (cockpitCameraZoom != FlightSimData.CockpitCameraZoom)
|
||||
FlightSimData.CockpitCameraZoom = cockpitCameraZoom;
|
||||
|
||||
var planeAltAboveGround = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.PlaneAltAboveGround).Value);
|
||||
if (planeAltAboveGround != FlightSimData.PlaneAltAboveGround)
|
||||
FlightSimData.PlaneAltAboveGround = planeAltAboveGround;
|
||||
|
||||
var cameraViewTypeAndIndex0 = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex0).Value);
|
||||
if (cameraViewTypeAndIndex0 != FlightSimData.CameraViewTypeAndIndex0)
|
||||
FlightSimData.CameraViewTypeAndIndex0 = cameraViewTypeAndIndex0;
|
||||
|
||||
var cameraViewTypeAndIndex1 = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1).Value);
|
||||
if (cameraViewTypeAndIndex1 != FlightSimData.CameraViewTypeAndIndex1)
|
||||
FlightSimData.CameraViewTypeAndIndex1 = cameraViewTypeAndIndex1;
|
||||
|
||||
var cameraViewTypeAndIndex1Max = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1Max).Value);
|
||||
if (cameraViewTypeAndIndex1Max != FlightSimData.CameraViewTypeAndIndex1Max)
|
||||
FlightSimData.CameraViewTypeAndIndex1Max = cameraViewTypeAndIndex1Max;
|
||||
|
||||
var cameraViewTypeAndIndex2Max = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex2Max).Value);
|
||||
if (cameraViewTypeAndIndex2Max != FlightSimData.CameraViewTypeAndIndex2Max)
|
||||
FlightSimData.CameraViewTypeAndIndex2Max = cameraViewTypeAndIndex2Max;
|
||||
|
||||
FlightSimData.IsSimConnectDataReceived = true;
|
||||
MapRequiredSimConnectData(e);
|
||||
};
|
||||
|
||||
_simConnectProvider.OnSimConnectDataHudBarRefreshed += (_, e) =>
|
||||
{
|
||||
if (ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled)
|
||||
MapHudBarSimConnectData(e);
|
||||
if (!ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled || !FlightSimData.IsFlightStarted)
|
||||
return;
|
||||
|
||||
MapHudBarSimConnectData(e);
|
||||
};
|
||||
|
||||
_simConnectProvider.OnSimConnectDataDynamicLodRefreshed += (_, e) =>
|
||||
{
|
||||
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled || !FlightSimData.IsFlightStarted || !WindowActionManager.IsMsfsInFocus())
|
||||
return;
|
||||
|
||||
var isVr = _dynamicLodOrchestrator.ReadIsVr();
|
||||
|
||||
MapDynamicLodSimConnectData(e, isVr);
|
||||
_dynamicLodOrchestrator.UpdateLod(isVr);
|
||||
};
|
||||
|
||||
_simConnectProvider.OnSimConnectDataEventFrameRefreshed += (_, e) =>
|
||||
{
|
||||
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled || !FlightSimData.IsFlightStarted)
|
||||
return;
|
||||
|
||||
MapEventFrameData(e);
|
||||
};
|
||||
|
||||
_simConnectProvider.OnActiveAircraftChanged += (_, e) =>
|
||||
{
|
||||
var aircraftName = String.IsNullOrEmpty(e) ? null : e;
|
||||
var aircraftName = string.IsNullOrEmpty(e) ? null : e;
|
||||
if (FlightSimData.AircraftName != aircraftName)
|
||||
{
|
||||
FlightSimData.AircraftName = aircraftName;
|
||||
|
@ -392,9 +370,7 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
|
||||
FlightSimData.IsFlightStarted = true;
|
||||
|
||||
// Attach in memory override for Dynamic LOD
|
||||
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
|
||||
DynamicLodManager.Attach(FlightSimData, AppSettingData);
|
||||
StartDynamicLod();
|
||||
}
|
||||
|
||||
private void HandleOnFlightStopped(object sender, EventArgs e)
|
||||
|
@ -409,9 +385,8 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
|
||||
FlightSimData.IsFlightStarted = false;
|
||||
|
||||
// Detach in memory override for Dynamic LOD
|
||||
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
|
||||
DynamicLodManager.Detach();
|
||||
StopDynamicLod();
|
||||
FlightSimData.DynamicLodSimData.Clear();
|
||||
}
|
||||
|
||||
private void DetectMsfsExit()
|
||||
|
@ -436,6 +411,50 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
};
|
||||
}
|
||||
|
||||
private void MapRequiredSimConnectData(List<SimDataItem> simData)
|
||||
{
|
||||
var electricalMasterBattery = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.ElectricalMasterBattery).Value);
|
||||
if (electricalMasterBattery != FlightSimData.ElectricalMasterBatteryStatus)
|
||||
FlightSimData.ElectricalMasterBatteryStatus = electricalMasterBattery;
|
||||
|
||||
var avionicsMasterSwitch = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.AvionicsMasterSwitch).Value);
|
||||
if (avionicsMasterSwitch != FlightSimData.AvionicsMasterSwitchStatus)
|
||||
FlightSimData.AvionicsMasterSwitchStatus = avionicsMasterSwitch;
|
||||
|
||||
var trackIR = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.TrackIREnable).Value);
|
||||
if (trackIR != FlightSimData.TrackIRStatus)
|
||||
FlightSimData.TrackIRStatus = trackIR;
|
||||
|
||||
var cameraStateInt = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraState).Value);
|
||||
var result = Enum.TryParse<CameraState>(cameraStateInt.ToString(), out var cameraState);
|
||||
if (!result)
|
||||
cameraState = CameraState.Unknown;
|
||||
if (cameraState != FlightSimData.CameraState)
|
||||
FlightSimData.CameraState = cameraState;
|
||||
|
||||
var cockpitCameraZoom = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CockpitCameraZoom).Value);
|
||||
if (cockpitCameraZoom != FlightSimData.CockpitCameraZoom)
|
||||
FlightSimData.CockpitCameraZoom = cockpitCameraZoom;
|
||||
|
||||
var cameraViewTypeAndIndex0 = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex0).Value);
|
||||
if (cameraViewTypeAndIndex0 != FlightSimData.CameraViewTypeAndIndex0)
|
||||
FlightSimData.CameraViewTypeAndIndex0 = cameraViewTypeAndIndex0;
|
||||
|
||||
var cameraViewTypeAndIndex1 = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1).Value);
|
||||
if (cameraViewTypeAndIndex1 != FlightSimData.CameraViewTypeAndIndex1)
|
||||
FlightSimData.CameraViewTypeAndIndex1 = cameraViewTypeAndIndex1;
|
||||
|
||||
var cameraViewTypeAndIndex1Max = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1Max).Value);
|
||||
if (cameraViewTypeAndIndex1Max != FlightSimData.CameraViewTypeAndIndex1Max)
|
||||
FlightSimData.CameraViewTypeAndIndex1Max = cameraViewTypeAndIndex1Max;
|
||||
|
||||
var cameraViewTypeAndIndex2Max = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex2Max).Value);
|
||||
if (cameraViewTypeAndIndex2Max != FlightSimData.CameraViewTypeAndIndex2Max)
|
||||
FlightSimData.CameraViewTypeAndIndex2Max = cameraViewTypeAndIndex2Max;
|
||||
|
||||
FlightSimData.IsSimConnectDataReceived = true;
|
||||
}
|
||||
|
||||
private void MapHudBarSimConnectData(List<SimDataItem> simData)
|
||||
{
|
||||
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.ElevatorTrim, FlightSimData.HudBarData.ElevatorTrim, out var newValue))
|
||||
|
@ -466,6 +485,49 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
FlightSimData.HudBarData.SimRate = newValue;
|
||||
}
|
||||
|
||||
private void MapDynamicLodSimConnectData(List<SimDataItem> simData, bool isVr)
|
||||
{
|
||||
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGround, FlightSimData.DynamicLodSimData.Agl, out var newValue))
|
||||
FlightSimData.DynamicLodSimData.Agl = newValue;
|
||||
|
||||
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGround, FlightSimData.DynamicLodSimData.AltAboveGround, out newValue))
|
||||
FlightSimData.DynamicLodSimData.AltAboveGround = newValue;
|
||||
|
||||
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGroundMinusCg, FlightSimData.DynamicLodSimData.AltAboveGroundMinusCg, out newValue))
|
||||
FlightSimData.DynamicLodSimData.AltAboveGroundMinusCg = newValue;
|
||||
|
||||
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.GroundVelocity, FlightSimData.DynamicLodSimData.GroundVelocity, out newValue))
|
||||
FlightSimData.DynamicLodSimData.GroundVelocity = newValue;
|
||||
|
||||
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.SimOnGround, 1.0f, out newValue))
|
||||
FlightSimData.DynamicLodSimData.PlaneOnGround = Convert.ToBoolean(newValue);
|
||||
|
||||
var tlod = _dynamicLodOrchestrator.ReadTlod(isVr);
|
||||
if (FlightSimData.DynamicLodSimData.Tlod != tlod)
|
||||
FlightSimData.DynamicLodSimData.Tlod = tlod;
|
||||
|
||||
var olod = _dynamicLodOrchestrator.ReadOlod(isVr);
|
||||
if (FlightSimData.DynamicLodSimData.Olod != olod)
|
||||
FlightSimData.DynamicLodSimData.Olod = olod;
|
||||
|
||||
var cloudQuality = _dynamicLodOrchestrator.ReadCloudQuality(isVr);
|
||||
if (FlightSimData.DynamicLodSimData.CloudQuality != cloudQuality)
|
||||
FlightSimData.DynamicLodSimData.CloudQuality = cloudQuality;
|
||||
|
||||
if (FlightSimData.IsFlightStarted && FlightSimData.IsInCockpit &&
|
||||
(!AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView || (AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView && FlightSimData.CameraState == CameraState.Cockpit)))
|
||||
FlightSimData.DynamicLodSimData.Fps = FpsCalc.GetAverageFps(_dynamicLodOrchestrator.ReadIsFg(isVr) ? _currentFps * 2 : _currentFps);
|
||||
}
|
||||
|
||||
private int _currentFps;
|
||||
private void MapEventFrameData(int fps)
|
||||
{
|
||||
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
|
||||
return;
|
||||
|
||||
_currentFps = fps;
|
||||
}
|
||||
|
||||
private bool CompareSimConnectData(List<SimDataItem> simData, string propName, double source, out double newValue)
|
||||
{
|
||||
var propData = simData.Find(d => d.PropertyName == propName);
|
||||
|
@ -486,5 +548,26 @@ namespace MSFSPopoutPanelManager.Orchestration
|
|||
newValue = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void StartDynamicLod()
|
||||
{
|
||||
if (_simConnectProvider != null)
|
||||
{
|
||||
// Attach in memory override for Dynamic LOD
|
||||
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
|
||||
_dynamicLodOrchestrator.Attach();
|
||||
|
||||
_simConnectProvider.StartDynamicLod();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopDynamicLod()
|
||||
{
|
||||
// Detach in memory override for Dynamic LOD
|
||||
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
|
||||
_dynamicLodOrchestrator.Detach();
|
||||
|
||||
_simConnectProvider.StopDynamicLod();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
|
||||
<RootNamespace>MSFSPopoutPanelManager.Orchestration</RootNamespace>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>4.1.0.4</Version>
|
||||
<AssemblyVersion>4.1.0.4</AssemblyVersion>
|
||||
<FileVersion>4.1.0.4</FileVersion>
|
||||
<Version>4.1.1.0</Version>
|
||||
<AssemblyVersion>4.1.1.0</AssemblyVersion>
|
||||
<FileVersion>4.1.1.0</FileVersion>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
|
||||
<RootNamespace>MSFSPopoutPanelManager.Shared</RootNamespace>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>4.1.0.4</Version>
|
||||
<AssemblyVersion>4.1.0.4</AssemblyVersion>
|
||||
<FileVersion>4.1.0.4</FileVersion>
|
||||
<Version>4.1.1.0</Version>
|
||||
<AssemblyVersion>4.1.1.0</AssemblyVersion>
|
||||
<FileVersion>4.1.1.0</FileVersion>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
{
|
||||
REQUIRED_DEFINITION = 0,
|
||||
HUDBAR_DEFINITION,
|
||||
DYNAMICLOD_DEFINITION,
|
||||
WRITABLE_TRACK_IR_DEFINITION,
|
||||
WRITABLE_COCKPIT_CAMERA_ZOOM_DEFINITION,
|
||||
WRITABLE_COCKPIT_CAMERA_STATE_DEFINITION,
|
||||
|
@ -17,6 +18,7 @@
|
|||
{
|
||||
REQUIRED_REQUEST = 0,
|
||||
HUDBAR_REQUEST,
|
||||
DYNAMICLOD_REQUEST,
|
||||
NA
|
||||
}
|
||||
|
||||
|
|
36
SimconnectAgent/FpsCalc.cs
Normal file
36
SimconnectAgent/FpsCalc.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace MSFSPopoutPanelManager.SimConnectAgent
|
||||
{
|
||||
public class FpsCalc
|
||||
{
|
||||
private const int FpsLen = 50;
|
||||
private static readonly float[] FpsStatistic = new float[FpsLen];
|
||||
private static int _fpsIndex = -1;
|
||||
|
||||
public static int GetAverageFps(int newValue)
|
||||
{
|
||||
if (_fpsIndex == -1)
|
||||
{
|
||||
for (var i = 0; i < FpsLen; i++)
|
||||
FpsStatistic[i] = newValue;
|
||||
|
||||
_fpsIndex = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
FpsStatistic[_fpsIndex] = newValue;
|
||||
_fpsIndex++;
|
||||
if (_fpsIndex >= FpsLen)
|
||||
_fpsIndex = 0;
|
||||
}
|
||||
|
||||
var fps = 0;
|
||||
if (_fpsIndex != -1)
|
||||
fps = Convert.ToInt32(FpsStatistic.Sum() / FpsLen);
|
||||
|
||||
return fps;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,15 +5,17 @@
|
|||
SIM_START,
|
||||
SIM_STOP,
|
||||
AIRCRAFT_LOADED,
|
||||
VIEW
|
||||
VIEW,
|
||||
FRAME
|
||||
}
|
||||
|
||||
public enum ActionEvent
|
||||
{
|
||||
DUMMY1, // must register 4 dummy events to reserve space for SimConnectEvent above
|
||||
DUMMY1, // must register 5 dummy events to reserve space for SimConnectEvent above
|
||||
DUMMY2,
|
||||
DUMMY3,
|
||||
DUMMY4,
|
||||
DUMMY5,
|
||||
MASTER_BATTERY_SET,
|
||||
AVIONICS_MASTER_SET,
|
||||
PAUSE_SET,
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
{
|
||||
private const int MSFS_DATA_REFRESH_TIMEOUT = 500;
|
||||
private const int MSFS_HUDBAR_DATA_REFRESH_TIMEOUT = 200;
|
||||
private const int MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT = 300;
|
||||
|
||||
private readonly SimConnector _simConnector;
|
||||
|
||||
|
@ -19,6 +20,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
|
||||
private System.Timers.Timer _requiredRequestDataTimer;
|
||||
private System.Timers.Timer _hudBarRequestDataTimer;
|
||||
private System.Timers.Timer _dynamicLodRequestDataTimer;
|
||||
private bool _isPowerOnForPopOut;
|
||||
private bool _isAvionicsOnForPopOut;
|
||||
private bool _isTrackIRManaged;
|
||||
|
@ -33,6 +35,8 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
public event EventHandler OnException;
|
||||
public event EventHandler<List<SimDataItem>> OnSimConnectDataRequiredRefreshed;
|
||||
public event EventHandler<List<SimDataItem>> OnSimConnectDataHudBarRefreshed;
|
||||
public event EventHandler<List<SimDataItem>> OnSimConnectDataDynamicLodRefreshed;
|
||||
public event EventHandler<int> OnSimConnectDataEventFrameRefreshed;
|
||||
public event EventHandler<string> OnActiveAircraftChanged;
|
||||
|
||||
public SimConnectProvider()
|
||||
|
@ -43,7 +47,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
_simConnector.OnException += HandleSimException;
|
||||
_simConnector.OnReceiveSystemEvent += HandleReceiveSystemEvent;
|
||||
_simConnector.OnReceivedRequiredData += HandleRequiredDataReceived;
|
||||
_simConnector.OnReceivedHudBarData += HandleHudBarDataReceived;
|
||||
_simConnector.OnReceivedHudBarData += (_, e) => OnSimConnectDataHudBarRefreshed?.Invoke(this, e);
|
||||
_simConnector.OnReceivedDynamicLodData += (_, e) => OnSimConnectDataDynamicLodRefreshed?.Invoke(this, e);
|
||||
_simConnector.OnReceivedEventFrameData += (_, e) => OnSimConnectDataEventFrameRefreshed?.Invoke(this, e);
|
||||
_simConnector.OnActiveAircraftChanged += (_, e) => OnActiveAircraftChanged?.Invoke(this, e);
|
||||
|
||||
_isHandlingCriticalError = false;
|
||||
|
@ -108,6 +114,29 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
_hudBarRequestDataTimer.Stop();
|
||||
}
|
||||
|
||||
public void StartDynamicLod()
|
||||
{
|
||||
if (_dynamicLodRequestDataTimer.Enabled)
|
||||
return;
|
||||
|
||||
// shut down data request and wait for the last request to be completed
|
||||
_dynamicLodRequestDataTimer.Stop();
|
||||
Thread.Sleep(MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT);
|
||||
|
||||
_simConnector.SetSimConnectDynamicLodDataDefinition();
|
||||
|
||||
_dynamicLodRequestDataTimer.Start();
|
||||
|
||||
_simConnector.StartReceiveFrameData();
|
||||
}
|
||||
|
||||
public void StopDynamicLod()
|
||||
{
|
||||
_dynamicLodRequestDataTimer.Stop();
|
||||
_simConnector.StopReceiveFrameData();
|
||||
}
|
||||
|
||||
|
||||
public void TurnOnPower(bool isRequiredForColdStart)
|
||||
{
|
||||
if (!isRequiredForColdStart || _requiredSimData == null)
|
||||
|
@ -295,10 +324,29 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
// ignored
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (_isHudBarDataActive)
|
||||
SetHudBarConfig(_activeHudBarType);
|
||||
|
||||
// Setup dynamic data request timer
|
||||
_dynamicLodRequestDataTimer = new()
|
||||
{
|
||||
Interval = MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT,
|
||||
};
|
||||
_dynamicLodRequestDataTimer.Stop();
|
||||
_dynamicLodRequestDataTimer.Elapsed += (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_simConnector.RequestDynamicLodData();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
OnConnected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
@ -306,6 +354,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
{
|
||||
_requiredRequestDataTimer.Stop();
|
||||
_hudBarRequestDataTimer.Stop();
|
||||
_dynamicLodRequestDataTimer.Stop();
|
||||
OnDisconnected?.Invoke(this, EventArgs.Empty);
|
||||
StopAndReconnect();
|
||||
}
|
||||
|
@ -316,6 +365,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
|
||||
_requiredRequestDataTimer.Stop();
|
||||
_hudBarRequestDataTimer.Stop();
|
||||
_dynamicLodRequestDataTimer.Stop();
|
||||
|
||||
if (!_isHandlingCriticalError)
|
||||
{
|
||||
|
@ -332,11 +382,6 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
OnSimConnectDataRequiredRefreshed?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void HandleHudBarDataReceived(object sender, List<SimDataItem> e)
|
||||
{
|
||||
OnSimConnectDataHudBarRefreshed?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private CameraState _currentCameraState = CameraState.Unknown;
|
||||
|
||||
private void DetectFlightStartedOrStopped(List<SimDataItem> simData)
|
||||
|
@ -375,6 +420,8 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
|
||||
_isHudBarDataActive = false;
|
||||
_hudBarRequestDataTimer.Stop();
|
||||
|
||||
_dynamicLodRequestDataTimer.Stop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -20,15 +20,18 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
private bool _isDisabledReconnect;
|
||||
|
||||
private readonly List<SimConnectDataDefinition> _simConnectRequiredDataDefinitions = SimDataDefinitions.GetRequiredDefinitions();
|
||||
private readonly List<SimConnectDataDefinition> _simConnectDynamicLodDataDefinitions = SimDataDefinitions.GetDynamicLodDefinitions();
|
||||
private List<SimConnectDataDefinition> _simConnectHudBarDataDefinitions;
|
||||
private readonly FieldInfo[] _simConnectStructFields = typeof(SimConnectStruct).GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
public event EventHandler<string> OnException;
|
||||
public event EventHandler<List<SimDataItem>> OnReceivedRequiredData;
|
||||
public event EventHandler<List<SimDataItem>> OnReceivedHudBarData;
|
||||
public event EventHandler<List<SimDataItem>> OnReceivedDynamicLodData;
|
||||
public event EventHandler OnConnected;
|
||||
public event EventHandler OnDisconnected;
|
||||
public event EventHandler<SimConnectEvent> OnReceiveSystemEvent;
|
||||
public event EventHandler<int> OnReceivedEventFrameData;
|
||||
public event EventHandler<string> OnActiveAircraftChanged;
|
||||
|
||||
public bool Connected { get; set; }
|
||||
|
@ -66,6 +69,11 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
AddHudBarDataDefinitions();
|
||||
}
|
||||
|
||||
public void SetSimConnectDynamicLodDataDefinition()
|
||||
{
|
||||
AddDynamicLodDataDefinitions();
|
||||
}
|
||||
|
||||
private void HandleOnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
|
||||
{
|
||||
ReceiveMessage();
|
||||
|
@ -124,6 +132,26 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
}
|
||||
}
|
||||
|
||||
public void RequestDynamicLodData()
|
||||
{
|
||||
if (_simConnect == null || !Connected)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (_simConnectDynamicLodDataDefinitions != null)
|
||||
_simConnect.RequestDataOnSimObjectType(DataRequest.DYNAMICLOD_REQUEST, DataDefinition.DYNAMICLOD_DEFINITION, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (!_isDisabledReconnect)
|
||||
_isDisabledReconnect = true;
|
||||
|
||||
OnException?.Invoke(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveMessage()
|
||||
{
|
||||
if (_simConnect == null)
|
||||
|
@ -208,6 +236,25 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
}
|
||||
}
|
||||
|
||||
public void StartReceiveFrameData()
|
||||
{
|
||||
if (_simConnect == null)
|
||||
return;
|
||||
|
||||
_simConnect.OnRecvEventFrame -= HandleOnRecvEventFrame;
|
||||
_simConnect.OnRecvEventFrame += HandleOnRecvEventFrame;
|
||||
|
||||
_simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.FRAME);
|
||||
_simConnect.SubscribeToSystemEvent(SimConnectEvent.FRAME, "Frame");
|
||||
}
|
||||
|
||||
public void StopReceiveFrameData()
|
||||
{
|
||||
_simConnect.OnRecvEventFrame -= HandleOnRecvEventFrame;
|
||||
|
||||
_simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.FRAME);
|
||||
}
|
||||
|
||||
private void InitializeSimConnect()
|
||||
{
|
||||
Debug.WriteLine("Trying to start simConnect");
|
||||
|
@ -234,8 +281,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
_simConnect.SubscribeToSystemEvent(SimConnectEvent.VIEW, "View");
|
||||
_simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.AIRCRAFT_LOADED);
|
||||
_simConnect.SubscribeToSystemEvent(SimConnectEvent.AIRCRAFT_LOADED, "AircraftLoaded");
|
||||
|
||||
|
||||
|
||||
AddRequiredDataDefinitions();
|
||||
SetupActionEvents();
|
||||
|
||||
|
@ -256,6 +302,14 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
Connected = true;
|
||||
}
|
||||
|
||||
private void HandleOnRecvEventFrame(SimConnect sender, SIMCONNECT_RECV_EVENT_FRAME data)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
OnReceivedEventFrameData?.Invoke(this, Convert.ToInt32(data.fFrameRate));
|
||||
}
|
||||
|
||||
private void HandleOnRecvSystemState(SimConnect sender, SIMCONNECT_RECV_SYSTEM_STATE data)
|
||||
{
|
||||
switch ((SystemStateRequestId)Enum.Parse(typeof(SystemStateRequestId), data.dwRequestID.ToString()))
|
||||
|
@ -361,6 +415,41 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
_simConnect.RegisterDataDefineStruct<SimConnectStruct>(DataDefinition.HUDBAR_DEFINITION);
|
||||
}
|
||||
|
||||
private void AddDynamicLodDataDefinitions()
|
||||
{
|
||||
if (_simConnect == null)
|
||||
return;
|
||||
|
||||
_simConnect.ClearDataDefinition(DataDefinition.DYNAMICLOD_DEFINITION);
|
||||
|
||||
if (_simConnectDynamicLodDataDefinitions == null)
|
||||
return;
|
||||
|
||||
foreach (var definition in _simConnectDynamicLodDataDefinitions)
|
||||
{
|
||||
if (definition.DefinitionId != DataDefinition.DYNAMICLOD_DEFINITION ||
|
||||
definition.DataDefinitionType != DataDefinitionType.SimConnect) continue;
|
||||
|
||||
SIMCONNECT_DATATYPE simConnectDataType;
|
||||
switch (definition.DataType)
|
||||
{
|
||||
case DataType.String:
|
||||
simConnectDataType = SIMCONNECT_DATATYPE.STRING256;
|
||||
break;
|
||||
case DataType.Float64:
|
||||
simConnectDataType = SIMCONNECT_DATATYPE.FLOAT64;
|
||||
break;
|
||||
default:
|
||||
simConnectDataType = SIMCONNECT_DATATYPE.FLOAT64;
|
||||
break;
|
||||
}
|
||||
|
||||
_simConnect.AddToDataDefinition(definition.DefinitionId, definition.VariableName, definition.SimConnectUnit, simConnectDataType, 0.0f, SimConnect.SIMCONNECT_UNUSED);
|
||||
}
|
||||
|
||||
_simConnect.RegisterDataDefineStruct<SimConnectStruct>(DataDefinition.DYNAMICLOD_DEFINITION);
|
||||
}
|
||||
|
||||
private void HandleOnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
|
||||
{
|
||||
Stop();
|
||||
|
@ -404,6 +493,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
case (int)DataRequest.HUDBAR_REQUEST:
|
||||
ParseHudBarReceivedSimData(data);
|
||||
break;
|
||||
case (int)DataRequest.DYNAMICLOD_REQUEST:
|
||||
ParseDynamicLodReceivedSimData(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -483,6 +575,46 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void ParseDynamicLodReceivedSimData(SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_simConnectDynamicLodDataDefinitions == null)
|
||||
return;
|
||||
|
||||
var simData = new List<SimDataItem>();
|
||||
var simDataStruct = (SimConnectStruct)data.dwData[0];
|
||||
|
||||
var i = 0;
|
||||
lock (_simConnectDynamicLodDataDefinitions)
|
||||
{
|
||||
foreach (var definition in _simConnectDynamicLodDataDefinitions)
|
||||
{
|
||||
if (definition.DataDefinitionType != DataDefinitionType.SimConnect)
|
||||
continue;
|
||||
|
||||
var dataValue = _simConnectStructFields[i].GetValue(simDataStruct);
|
||||
var simDataItem = new SimDataItem
|
||||
{
|
||||
PropertyName = definition.PropName,
|
||||
Value = dataValue == null ? 0 : (double)dataValue
|
||||
};
|
||||
|
||||
simData.Add(simDataItem);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
OnReceivedDynamicLodData?.Invoke(this, simData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FileLogger.WriteException($"SimConnector: SimConnect received dynamic lod data exception - {ex.Message}", ex);
|
||||
StopAndReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetActiveAircraftTitle(string aircraftFilePath)
|
||||
{
|
||||
var filePathToken = aircraftFilePath.Split(@"\");
|
||||
|
|
|
@ -14,7 +14,6 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneInParkingSpot, VariableName = "ATC ON PARKING SPOT", SimConnectUnit = "Bool", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraState, VariableName = "CAMERA STATE", SimConnectUnit = "Number", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CockpitCameraZoom, VariableName = "COCKPIT CAMERA ZOOM", SimConnectUnit = "Percentage", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGround, VariableName = "PLANE ALT ABOVE GROUND", SimConnectUnit = "Feet", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex0, VariableName = "CAMERA VIEW TYPE AND INDEX:0", SimConnectUnit = "Enum", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1, VariableName = "CAMERA VIEW TYPE AND INDEX:1", SimConnectUnit = "Enum", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1Max, VariableName = "CAMERA VIEW TYPE AND INDEX MAX:1", SimConnectUnit = "Number", DataType = DataType.Float64 },
|
||||
|
@ -44,6 +43,18 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
}
|
||||
}
|
||||
|
||||
public static List<SimConnectDataDefinition> GetDynamicLodDefinitions()
|
||||
{
|
||||
var definitions = new List<SimConnectDataDefinition>
|
||||
{
|
||||
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGround, VariableName = "PLANE ALT ABOVE GROUND", SimConnectUnit = "feet", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGroundMinusCg, VariableName = "PLANE ALT ABOVE GROUND MINUS CG", SimConnectUnit = "feet", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.SimOnGround, VariableName = "SIM ON GROUND", SimConnectUnit = "Bool", DataType = DataType.Float64 },
|
||||
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.GroundVelocity, VariableName = "GROUND VELOCITY", SimConnectUnit = "knots", DataType = DataType.Float64 }
|
||||
};
|
||||
return definitions;
|
||||
}
|
||||
|
||||
private static List<SimConnectDataDefinition> GetSharedHudBarDefinitions()
|
||||
{
|
||||
var definitions = new List<SimConnectDataDefinition>
|
||||
|
@ -80,7 +91,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
};
|
||||
return definitions;
|
||||
}
|
||||
|
||||
|
||||
public static class PropName
|
||||
{
|
||||
public static string ElectricalMasterBattery = "ElectricalMasterBattery";
|
||||
|
@ -96,6 +107,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
|
|||
|
||||
// Dynamic LOD
|
||||
public static string PlaneAltAboveGround = "PlaneAltAboveGround";
|
||||
public static string PlaneAltAboveGroundMinusCg = "PlaneAltAboveGroundMinusCg";
|
||||
public static string SimOnGround = "SimOnGround";
|
||||
public static string GroundVelocity = "GroundVelocity";
|
||||
|
||||
// Hud Bar data
|
||||
public static string ElevatorTrim = "ElevatorTrim";
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
|
||||
<RootNamespace>MSFSPopoutPanelManager.SimConnectAgent</RootNamespace>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>4.1.0.4</Version>
|
||||
<AssemblyVersion>4.1.0.4</AssemblyVersion>
|
||||
<FileVersion>4.1.0.4</FileVersion>
|
||||
<Version>4.1.1.0</Version>
|
||||
<AssemblyVersion>4.1.1.0</AssemblyVersion>
|
||||
<FileVersion>4.1.1.0</FileVersion>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
|
|
|
@ -293,7 +293,12 @@ namespace MSFSPopoutPanelManager.WindowsAgent
|
|||
var bottomEdge = rect.Y + rect.Height;
|
||||
|
||||
return point.X >= rect.X && point.X <= rightEdge && point.Y >= rect.Y && point.Y <= bottomEdge;
|
||||
}
|
||||
|
||||
public static bool IsMsfsInFocus()
|
||||
{
|
||||
var handle = PInvoke.GetForegroundWindow();
|
||||
return PInvoke.GetWindowText(handle).Substring(0, 26).Equals("Microsoft Flight Simulator", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
|
||||
<RootNamespace>MSFSPopoutPanelManager.WindowsAgent</RootNamespace>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>4.1.0.4</Version>
|
||||
<AssemblyVersion>4.1.0.4</AssemblyVersion>
|
||||
<FileVersion>4.1.0.4</FileVersion>
|
||||
<Version>4.1.1.0</Version>
|
||||
<AssemblyVersion>4.1.1.0</AssemblyVersion>
|
||||
<FileVersion>4.1.1.0</FileVersion>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
|
|
Loading…
Reference in a new issue