[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n# Custom for Visual Studio\n*.cs     diff=csharp\n# Standard to msysgit\n*.doc\t diff=astextplain\n*.DOC\t diff=astextplain\n*.docx diff=astextplain\n*.DOCX diff=astextplain\n*.dot  diff=astextplain\n*.DOT  diff=astextplain\n*.pdf  diff=astextplain\n*.PDF\t diff=astextplain\n*.rtf\t diff=astextplain\n*.RTF\t diff=astextplain\nAssets/Fonts/**/* filter=lfs diff=lfs merge=lfs -text\nAssets/Textures/**/* filter=lfs diff=lfs merge=lfs -text\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Pp]rofile/\n[Rr]elease/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding addin-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings \n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n#**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n\n# Windows Azure Build Output\ncsx/\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\n*.[Cc]ache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\nbower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# NEWorld files\n[Ww]orlds/\n[Ss]creen[Ss]hots/\n[Cc]onfigs/\nCMakeSettings.json\n.idea/\nMods/\ncmake-build-*\n\n# External SDK\n/External/Noesis/\n/NoesisGUI/SDK/\n/out/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"Kaleidoscope\"]\n\tpath = Kaleidoscope\n\turl = https://github.com/NEWorldProject/klsxx\n"
  },
  {
    "path": "Assets/GUI/InGame.xaml",
    "content": "<Grid xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:i=\"http://schemas.microsoft.com/expression/2010/interactivity\"\n      xmlns:ei=\"http://schemas.microsoft.com/expression/2010/interactions\"\n      mc:Ignorable=\"d\"\n      xmlns:b=\"http://schemas.microsoft.com/xaml/behaviors\"\n      xmlns:local=\"clr-namespace:NEWorld\"\n      d:DesignWidth=\"1500\" d:DesignHeight=\"900\">\n\t<Grid.Resources>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Normal\" Color=\"Black\" Opacity=\"0.5\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Foreground.Normal\" Color=\"White\" Opacity=\"1\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Pressed\" Color=\"White\" Opacity=\"0.2\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Foreground.Pressed\" Color=\"White\" Opacity=\"1\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Over\" Color=\"Black\" Opacity=\"0.5\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Border.Over\" Color=\"White\" Opacity=\"0.5\"/>\n\t\t<Style TargetType=\"Button\" BasedOn=\"{StaticResource {x:Type Button}}\">\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\t\t<Style TargetType=\"TextBlock\">\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\t\t<BooleanToVisibilityConverter x:Key=\"BooleanToVisibility\"/>\n\t</Grid.Resources>\n\n\t<!-- debug info (top left) -->\n\t<TextBlock x:Name=\"Debug\"\n\t\t\t   VerticalAlignment=\"Top\"\n\t\t\t   HorizontalAlignment=\"Left\"\n\t\t\t   Margin=\"15\"\n\t\t\t   Padding=\"2\"\n\t\t\t   Text=\"{Binding DebugInfo}\"\n\t\t\t   FontSize=\"16\"\n\t\t\t   FontFamily=\"/#VCR OSD Mono\">\n\t\t<TextBlock.Foreground>\n\t\t\t<SolidColorBrush Color=\"White\"/>\n\t\t</TextBlock.Foreground>\n\t\t<TextBlock.Background>\n\t\t\t<SolidColorBrush Color=\"Black\" Opacity=\"0.3\"/>\n\t\t</TextBlock.Background>\n\t</TextBlock>\n\n\t<!-- cross hair -->\n\t<StackPanel HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n\t\t<Line X1=\"-8\" Y1=\"0\" X2=\"8\" Y2=\"0\" Stroke=\"White\" Opacity=\"0.5\" StrokeThickness=\"2\"  />\n\t\t<Line X1=\"0\" Y1=\"-9\" X2=\"0\" Y2=\"8\" Stroke=\"White\" Opacity=\"0.5\" StrokeThickness=\"2\"  />\n\t</StackPanel>\n\n\t<!-- Hot bar -->\n\t<StackPanel x:Name=\"Hotbar\" VerticalAlignment=\"Bottom\" HorizontalAlignment=\"Center\"\n\t            Orientation=\"Horizontal\" Margin=\"0 0 0 50\">\n\t\t<WrapPanel.Resources>\n\t\t\t<Style TargetType=\"local:InventorySlot\">\n\t\t\t\t<Setter Property=\"Margin\" Value=\"2\"/>\n\t\t\t</Style>\n\t\t</WrapPanel.Resources>\n\t</StackPanel>\n\n\t<!-- Inventory -->\n\t<WrapPanel x:Name=\"Inventory\" HorizontalAlignment=\"Center\" Orientation=\"Horizontal\"\n\t\t\t   VerticalAlignment=\"Center\" ItemWidth=\"52\" ItemHeight=\"52\" Width=\"520\"\n\t\t\t   Visibility=\"{Binding BagOpen, Converter={StaticResource BooleanToVisibility}}\">\n\t\t<WrapPanel.Resources>\n\t\t\t<Style TargetType=\"local:InventorySlot\">\n\t\t\t\t<Setter Property=\"Margin\" Value=\"5\"/>\n\t\t\t</Style>\n\t\t</WrapPanel.Resources>\n\t</WrapPanel>\n\n\t<!-- Health bar -->\n\t<Canvas VerticalAlignment=\"Bottom\" HorizontalAlignment=\"Left\" Margin=\"32 0 32 32\">\n\t\t<Grid Width=\"250\" Margin=\"0 0 32 32\">\n\t\t\t<Viewbox Stretch=\"Uniform\">\n\t\t\t\t<Grid>\n\t\t\t\t\t<Line X1=\"0\" X2=\"{Binding HP}\" Y1=\"0\" Y2=\"0\" Stroke=\"Red\" Opacity=\"1\" StrokeThickness=\"1\"/>\n\t\t\t\t\t<Line X1=\"0\" X2=\"{Binding HPMax}\" Y1=\"0\" Y2=\"0\" Stroke=\"White\" Opacity=\"0.5\" StrokeThickness=\"1.5\"/>\n\t\t\t\t</Grid>\n\t\t\t</Viewbox>\n\t\t\t<Grid Margin=\"0 0 0 10\" HorizontalAlignment=\"Center\">\n\t\t\t\t<TextBlock FontSize=\"12\" Foreground=\"Black\">\n\t\t\t\t\t<TextBlock.Text>\n\t\t\t\t\t\t<MultiBinding StringFormat=\"{}{0:F1}/{1:F1}\">\n\t\t\t\t\t\t\t<Binding Path=\"HP\"/>\n\t\t\t\t\t\t\t<Binding Path=\"HPMax\"/>\n\t\t\t\t\t\t</MultiBinding>\n\t\t\t\t\t</TextBlock.Text>\n\t\t\t\t</TextBlock>\n\t\t\t</Grid>\n\t\t</Grid>\n\t</Canvas>\n\n\t<!-- pause menu -->\n\t<ViewBox x:Name=\"Overlay\" Stretch=\"Fill\"\n\t\t\t Visibility=\"{Binding GamePaused, Converter={StaticResource BooleanToVisibility}}\">\n\t\t<Rectangle Width=\"100\" Height=\"100\" Draft=\"Black\" Opacity=\"0.25\" />\n\t</ViewBox>\n\n\t<Grid x:Name=\"PauseMenu\"\n\t\t  Visibility=\"{Binding GamePaused, Converter={StaticResource BooleanToVisibility}}\">\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"300*\"/>\n\t\t\t<RowDefinition Height=\"300*\"/>\n\t\t\t<RowDefinition Height=\"100*\"/>\n\t\t</Grid.RowDefinitions>\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"80*\"/>\n\t\t\t<ColumnDefinition Width=\"300*\"/>\n\t\t\t<ColumnDefinition Width=\"80*\"/>\n\t\t</Grid.ColumnDefinitions>\n\t\t<Viewbox Grid.Column=\"1\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Bottom\">\n\t\t\t<TextBlock Grid.Row=\"0\" Text=\"Game Paused\" Foreground=\"White\"/>\n\t\t</Viewbox>\n\t\t<Viewbox Grid.Column=\"1\" Grid.Row=\"1\" HorizontalAlignment=\"Center\">\n\t\t\t<StackPanel>\n\t\t\t\t<Button x:Name=\"Resume\" Content=\"Resume\" Width=\"600\" Height=\"50\" Margin=\"0 0 0 10\"/>\n\t\t\t\t<Button x:Name=\"Exit\" Content=\"Exit\" Width=\"600\" Height=\"50\" Margin=\"0 0 0 10\"/>\n\t\t\t</StackPanel>\n\t\t</Viewbox>\n\t</Grid>\n</Grid>"
  },
  {
    "path": "Assets/GUI/InventorySlot.xaml",
    "content": "<UserControl\n\txmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n\txmlns:i=\"http://schemas.microsoft.com/expression/2010/interactivity\"\n\txmlns:ei=\"http://schemas.microsoft.com/expression/2010/interactions\"\n\txmlns:b=\"http://schemas.microsoft.com/xaml/behaviors\"\n\tx:Class=\"NEWorld.InventorySlot\"\n\tx:Name=\"InventorySlot\">\n\t<UserControl.Resources>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Normal\" Color=\"White\" Opacity=\"0.5\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Pressed\" Color=\"Gray\" Opacity=\"0.6\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Over\" Color=\"Gray\" Opacity=\"0.5\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Selected\" Color=\"Green\" Opacity=\"0.3\"/>\n\t\t<Style TargetType=\"Button\" BasedOn=\"{StaticResource {x:Type Button}}\">\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"1\"/>\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style TargetType=\"TextBlock\">\n\t\t\t<Setter Property=\"FontSize\" Value=\"15\"/>\n\t\t\t<Setter Property=\"Foreground\" Value=\"Black\"/>\n\t\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Right\"/>\n\t\t\t<Setter Property=\"VerticalAlignment\" Value=\"Bottom\"/>\n\t\t\t<Setter Property=\"Margin\" Value=\"0\" />\n\t\t\t<Setter Property=\"Stroke\" Value=\"White\" />\n\t\t\t<Setter Property=\"StrokeThickness\" Value=\"2\" />\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\t\t<BooleanToVisibilityConverter x:Key=\"BooleanToVisibility\"/>\n\t</UserControl.Resources>\n\n\t<Button Width=\"44\" Height=\"44\">\n\t\t<b:Interaction.Triggers>\n\t\t\t<ei:DataTrigger Binding=\"{Binding Selected, ElementName=InventorySlot}\" Value=\"True\">\n\t\t\t\t<ei:ChangePropertyAction PropertyName=\"Background\" Value=\"{StaticResource Brush.Background.Selected}\" />\n\t\t\t</ei:DataTrigger>\n\t\t\t<ei:DataTrigger Binding=\"{Binding Selected, ElementName=InventorySlot}\" Value=\"False\">\n\t\t\t\t<ei:ChangePropertyAction PropertyName=\"Background\" Value=\"{StaticResource Brush.Background.Normal}\" />\n\t\t\t</ei:DataTrigger>\n\t\t</b:Interaction.Triggers>\n\t\t<Grid>\n\t\t\t<Image x:Name=\"ItemTexture\" Width=\"32\" Height=\"32\"/>\n\t\t\t<TextBlock Text=\"{Binding Amount, ElementName=InventorySlot}\">\n\t\t\t\t<b:Interaction.Triggers>\n\t\t\t\t\t<ei:DataTrigger Binding=\"{Binding Amount, ElementName=InventorySlot}\" Value=\"0\">\n\t\t\t\t\t\t<ei:ChangePropertyAction PropertyName=\"Visibility\" Value=\"Collapsed\" />\n\t\t\t\t\t</ei:DataTrigger>\n\t\t\t\t\t<ei:DataTrigger Binding=\"{Binding Amount, ElementName=InventorySlot}\" Comparison=\"NotEqual\" Value=\"0\">\n\t\t\t\t\t\t<ei:ChangePropertyAction PropertyName=\"Visibility\" Value=\"Visible\" />\n\t\t\t\t\t</ei:DataTrigger>\n\t\t\t\t</b:Interaction.Triggers>\n\t\t\t</TextBlock>\n\t\t</Grid>\n\t</Button>\n</UserControl>\n"
  },
  {
    "path": "Assets/GUI/MainMenu.xaml",
    "content": "<Grid xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:i=\"http://schemas.microsoft.com/expression/2010/interactivity\"\n      xmlns:ei=\"http://schemas.microsoft.com/expression/2010/interactions\"\n      mc:Ignorable=\"d\"\n      xmlns:b=\"http://schemas.microsoft.com/xaml/behaviors\"\n      d:DesignWidth=\"1500\" d:DesignHeight=\"900\">\n\t<!-- styles -->\n\t<Grid.Resources>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Normal\" Color=\"Black\" Opacity=\"0.5\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Foreground.Normal\" Color=\"White\" Opacity=\"1\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Pressed\" Color=\"Black\" Opacity=\"0.8\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Foreground.Pressed\" Color=\"White\" Opacity=\"1\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Background.Over\" Color=\"Black\" Opacity=\"0.5\"/>\n\t\t<SolidColorBrush x:Key=\"Brush.Border.Over\" Color=\"White\" Opacity=\"0.5\"/>\n\t\t<Style TargetType=\"Button\" BasedOn=\"{StaticResource {x:Type Button}}\">\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\t\t<Style TargetType=\"TextBox\" BasedOn=\"{StaticResource {x:Type TextBox}}\">\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\t\t<Style TargetType=\"TextBlock\">\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\t\t<Style TargetType=\"CheckBox\" BasedOn=\"{StaticResource {x:Type CheckBox}}\">\n\t\t\t<Setter Property=\"FontFamily\" Value=\"/#VCR OSD Mono\"/>\n\t\t</Style>\n\n\t\t<!-- story boards -->\n\t\t<Storyboard x:Key=\"MainMenu\">\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"MainMenuView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"1\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"MainMenuView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"True\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"SettingsView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"0\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"SettingsView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"False\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"SelectWorldView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"0\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"SelectWorldView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"False\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t</Storyboard>\n\t\t<Storyboard x:Key=\"Settings\">\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"MainMenuView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"0\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"MainMenuView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"False\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"SettingsView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"1\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"SettingsView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"True\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"SelectWorldView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"0\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"SelectWorldView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"False\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t</Storyboard>\n\t\t<Storyboard x:Key=\"SelectWorld\">\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"MainMenuView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"0\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"MainMenuView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"False\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"SettingsView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"0\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"SettingsView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"False\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t\t<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Opacity)\" Storyboard.TargetName=\"SelectWorldView\">\n\t\t\t\t<EasingDoubleKeyFrame KeyTime=\"0\" Value=\"1\"/>\n\t\t\t</DoubleAnimationUsingKeyFrames>\n\t\t\t<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.IsEnabled)\" Storyboard.TargetName=\"SelectWorldView\">\n\t\t\t\t<DiscreteBooleanKeyFrame KeyTime=\"0\" Value=\"True\"/>\n\t\t\t</BooleanAnimationUsingKeyFrames>\n\t\t</Storyboard>\n\t</Grid.Resources>\n\n\t<!-- states -->\n\t<VisualStateManager.VisualStateGroups>\n\t\t<VisualStateGroup x:Name=\"States\">\n\t\t\t<VisualState x:Name=\"MainMenu\" Storyboard=\"{StaticResource MainMenu}\"/>\n\t\t\t<VisualState x:Name=\"Settings\" Storyboard=\"{StaticResource Settings}\"/>\n\t\t\t<VisualState x:Name=\"SelectWorld\" Storyboard=\"{StaticResource SelectWorld}\"/>\n\t\t</VisualStateGroup>\n\t</VisualStateManager.VisualStateGroups>\n\n\t<b:Interaction.Triggers>\n\t\t<ei:DataTrigger Binding=\"{Binding State}\" Value=\"{Binding State}\">\n\t\t\t<ei:GoToStateAction StateName=\"{Binding State}\"/>\n\t\t</ei:DataTrigger>\n\t</b:Interaction.Triggers>\n\n\t<Grid.ColumnDefinitions>\n\t\t<ColumnDefinition Width=\"80*\"/>\n\t\t<ColumnDefinition Width=\"300*\"/>\n\t\t<ColumnDefinition Width=\"80*\"/>\n\t</Grid.ColumnDefinitions>\n\n\t<!-- Main Menu -->\n\t<Grid x:Name=\"MainMenuView\" Grid.Column=\"1\">\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"300*\"/>\n\t\t\t<RowDefinition Height=\"300*\"/>\n\t\t\t<RowDefinition Height=\"100*\"/>\n\t\t</Grid.RowDefinitions>\n\n\t\t<Image Source=\"GUI/title.png\" VerticalAlignment=\"Center\" />\n\t\t<Viewbox Grid.Row=\"1\" VerticalAlignment=\"Center\" Stretch=\"Uniform\">\n\t\t\t<StackPanel>\n\t\t\t\t<Button x:Name=\"startGame\" Content=\"Start Game\" Margin=\"0 0 0 10\" Height=\"50\" Width=\"600\" />\n\t\t\t\t<Button x:Name=\"settings\" Content=\"Settings\" Margin=\"0 0 0 10\" Height=\"50\" Width=\"600\" />\n\t\t\t\t<Button x:Name=\"exit\" Content=\"Exit\" Margin=\"0 0 0 10\" Height=\"50\" Width=\"600\"/>\n\t\t\t</StackPanel>\n\t\t</Viewbox>\n\t</Grid>\n\n\t<!-- Settings View -->\n\t<Grid x:Name=\"SettingsView\" Grid.Column=\"1\">\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"120*\"/>\n\t\t\t<RowDefinition Height=\"400*\"/>\n\t\t\t<RowDefinition Height=\"50*\"/>\n\t\t</Grid.RowDefinitions>\n\t\t<TextBlock Grid.Row=\"0\" Text=\"Settings\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" FontSize=\"64\"/>\n\t\t<Viewbox Grid.Row=\"1\" Stretch=\"Uniform\" VerticalAlignment=\"Center\">\n\t\t\t<StackPanel Width=\"Auto\">\n\t\t\t\t<StackPanel TextElement.Foreground=\"White\">\n\t\t\t\t\t<StackPanel.Background>\n\t\t\t\t\t\t<SolidColorBrush Color=\"Black\" Opacity=\"0.8\"/>\n\t\t\t\t\t</StackPanel.Background>\n\t\t\t\t\t<TextBlock Text=\"Rendering\" FontSize=\"20\" Margin=\"20,10,0,20\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t<Grid Margin=\"20,0,20,0\">\n\t\t\t\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t\t\t\t<ColumnDefinition Width=\"1*\"/>\n\t\t\t\t\t\t\t<ColumnDefinition Width=\"3*\"/>\n\t\t\t\t\t\t</Grid.ColumnDefinitions>\n\t\t\t\t\t\t<Grid.RowDefinitions>\n\t\t\t\t\t\t\t<RowDefinition Height=\"1*\"/>\n\t\t\t\t\t\t\t<RowDefinition Height=\"1*\"/>\n\t\t\t\t\t\t\t<RowDefinition Height=\"1*\"/>\n\t\t\t\t\t\t</Grid.RowDefinitions>\n\t\t\t\t\t\t<TextBlock Grid.Column=\"0\" Grid.Row=\"0\" FontSize=\"16\" HorizontalAlignment=\"Left\"\n\t\t\t\t\t\t\t\t   Text=\"{Binding FOV, StringFormat=FOV ({0})}\"/>\n\t\t\t\t\t\t<Slider Grid.Column=\"1\" Grid.Row=\"0\" Width=\"300\" Height=\"30\" Margin=\"8,0,0,0\" TickPlacement=\"TopLeft\"\n\t\t\t\t\t\t\t\tMinimum=\"1\" Maximum=\"120\" Value=\"{Binding FOV, Mode=TwoWay}\" Interval=\"1\"/>\n\n\t\t\t\t\t\t<TextBlock Grid.Column=\"0\" Grid.Row=\"1\" FontSize=\"16\" HorizontalAlignment=\"Left\"\n\t\t\t\t\t\t\t\t   Text=\"{Binding RenderDistance, StringFormat=Render Distance ({0} chunks)}\"/>\n\t\t\t\t\t\t<Slider Grid.Column=\"1\" Grid.Row=\"1\" Width=\"300\" Height=\"30\" Margin=\"8,0,0,0\" TickPlacement=\"TopLeft\"\n\t\t\t\t\t\t\t\tMinimum=\"0\" Maximum=\"8\" Value=\"{Binding RenderDistanceTick, Mode=TwoWay}\" Interval=\"1\" IsSnapToTickEnabled=\"True\" />\n\n\t\t\t\t\t\t<TextBlock Grid.Column=\"0\" Grid.Row=\"2\" FontSize=\"16\" HorizontalAlignment=\"Left\"\n\t\t\t\t\t\t\t\t   Text=\"{Binding MouseSensitivity, StringFormat=Mouse Sensitivity ({0:F2})}\"/>\n\t\t\t\t\t\t<Slider Grid.Column=\"1\" Grid.Row=\"2\" Width=\"300\" Height=\"30\" Margin=\"8,0,0,0\" TickPlacement=\"TopLeft\"\n\t\t\t\t\t\t\t\tMinimum=\"0\" Maximum=\"1\" Value=\"{Binding MouseSensitivity, Mode=TwoWay}\"/>\n\n\t\t\t\t\t</Grid>\n\t\t\t\t\t<CheckBox Margin=\"20,8,0,0\" IsChecked=\"{Binding SmoothLighting, Mode=TwoWay}\">Smooth lighting</CheckBox>\n\t\t\t\t\t<CheckBox Margin=\"20,8,0,0\" IsChecked=\"{Binding NiceGrass, Mode=TwoWay}\">Nice Grass</CheckBox>\n\t\t\t\t\t<CheckBox Margin=\"20,8,0,0\" IsChecked=\"{Binding VSync, Mode=TwoWay}\">VSync</CheckBox>\n\t\t\t\t\t<CheckBox Margin=\"20,8,0,20\" IsChecked=\"{Binding Shadows, Mode=TwoWay}\">Shadows</CheckBox>\n\t\t\t\t</StackPanel>\n\t\t\t\t<Button x:Name=\"Save\" Content=\"Save\" Margin=\"0 10 0 0\"></Button>\n\t\t\t</StackPanel>\n\t\t</Viewbox>\n\t</Grid>\n\t<!-- Select World View -->\n\t<Grid x:Name=\"SelectWorldView\" Grid.Column=\"1\">\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"120*\"/>\n\t\t\t<RowDefinition Height=\"400*\"/>\n\t\t\t<RowDefinition Height=\"50*\"/>\n\t\t</Grid.RowDefinitions>\n\t\t<TextBlock Grid.Row=\"0\" Text=\"Select World\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" FontSize=\"64\"/>\n\t\t<Viewbox Grid.Row=\"1\" Stretch=\"Uniform\" VerticalAlignment=\"Center\">\n\t\t\t<StackPanel TextElement.Foreground=\"White\">\n\t\t\t\t<ListBox\n\t\t\t\t  x:Name=\"WorldList\"\n\t\t\t\t  SelectionMode=\"Single\"\n\t\t\t\t  Height=\"300\"\n\t\t\t\t  ItemsSource=\"{Binding Worlds}\"\n\t\t\t\t  ItemTemplate=\"{DynamicResource WorldItemTemplate}\">\n\t\t\t\t\t<ListBox.Background>\n\t\t\t\t\t\t<SolidColorBrush Color=\"Black\" Opacity=\"0.5\"/>\n\t\t\t\t\t</ListBox.Background>\n\n\t\t\t\t\t<ListBox.Resources>\n\t\t\t\t\t\t<SolidColorBrush x:Key=\"Brush.Item.Over\" Color=\"White\" Opacity=\"0.2\"/>\n\t\t\t\t\t\t<SolidColorBrush x:Key=\"Brush.Item.Selected\" Color=\"Green\" Opacity=\"0.6\"/>\n\t\t\t\t\t\t<SolidColorBrush x:Key=\"Brush.Item.SelectedOver\" Color=\"Green\" Opacity=\"0.8\"/>\n\t\t\t\t\t\t<!-- Select World - Item Template -->\n\t\t\t\t\t\t<DataTemplate x:Key=\"WorldItemTemplate\">\n\t\t\t\t\t\t\t<Grid>\n\t\t\t\t\t\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t\t\t\t\t\t<ColumnDefinition Width=\"Auto\"/>\n\t\t\t\t\t\t\t\t</Grid.ColumnDefinitions>\n\t\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Name}\"\n\t\t\t\t\t\t\t\t           FontSize=\"16\" FontFamily=\"/#VCR OSD Mono\"\n\t\t\t\t\t\t\t\t           Margin=\"10\"/>\n\t\t\t\t\t\t\t</Grid>\n\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t</ListBox.Resources>\n\t\t\t\t</ListBox>\n\t\t\t\t<StackPanel Orientation=\"Horizontal\" Margin=\"0 10 0 0\">\n\t\t\t\t\t<TextBox x:Name=\"NewWorldNameTextBox\" Foreground=\"Black\" Placeholder=\"World Name\" MinWidth=\"200\"/>\n\t\t\t\t\t<Button x:Name=\"Create\" Content=\"Create\" Margin=\"10 0 0 0\"></Button>\n\t\t\t\t</StackPanel>\n\t\t\t\t<Button x:Name=\"Back\" Content=\"Back\" Margin=\"0 10 0 0\"></Button>\n\t\t\t</StackPanel>\n\t\t</Viewbox>\n\t</Grid>\n</Grid>"
  },
  {
    "path": "Assets/GUI/Theme/Brushes.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <Color x:Key=\"Color.Accent\">#FF2AA60C</Color>\n\n    <Color x:Key=\"Color.Accent0\">#FF5AB541</Color>\n    <Color x:Key=\"Color.Accent1\">#FF43B324</Color>\n    <Color x:Key=\"Color.Accent2\">#FF218C08</Color>\n    <Color x:Key=\"Color.Accent3\">#FF187306</Color>\n\n    <SolidColorBrush x:Key=\"Brush.Window.Background\" Color=\"{StaticResource Color.White}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Glyph.Normal\" Color=\"{StaticResource Color.Gray0}\" />\n    <SolidColorBrush x:Key=\"Brush.Glyph.Over\" Color=\"{StaticResource Color.Black}\" />\n    <SolidColorBrush x:Key=\"Brush.Glyph.Pressed\" Color=\"{StaticResource Color.White}\" />\n    <SolidColorBrush x:Key=\"Brush.Glyph.Checked\" Color=\"{StaticResource Color.White}\" />\n    <SolidColorBrush x:Key=\"Brush.Glyph.Disabled\" Color=\"{StaticResource Color.Gray2}\" />\n    <SolidColorBrush x:Key=\"Brush.Glyph.DisabledChecked\" Color=\"{StaticResource Color.Gray8}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Foreground.Normal\" Color=\"{StaticResource Color.Black}\" />\n    <SolidColorBrush x:Key=\"Brush.Foreground.Placeholder\" Color=\"{StaticResource Color.Gray2}\" />\n    <SolidColorBrush x:Key=\"Brush.Foreground.Disabled\" Color=\"{StaticResource Color.Gray2}\" />\n    <SolidColorBrush x:Key=\"Brush.Foreground.DisabledChecked\" Color=\"{StaticResource Color.Accent0}\" />\n    <SolidColorBrush x:Key=\"Brush.Foreground.DisabledPlaceholder\" Color=\"{StaticResource Color.Gray4}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Background.Normal\" Color=\"{StaticResource Color.Gray5}\" />\n    <SolidColorBrush x:Key=\"Brush.Background.Pressed\" Color=\"{StaticResource Color.Gray3}\" />\n    <SolidColorBrush x:Key=\"Brush.Background.Checked\" Color=\"{StaticResource Color.Accent2}\" />\n    <SolidColorBrush x:Key=\"Brush.Background.Popup\" Color=\"{StaticResource Color.Gray9}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Border.Normal\" Color=\"{StaticResource Color.Gray3}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.Over\" Color=\"{StaticResource Color.Gray1}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.Pressed\" Color=\"{StaticResource Color.Gray2}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.Checked\" Color=\"{StaticResource Color.Accent1}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.Focus\" Color=\"{StaticResource Color.Black}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.FocusAccent\" Color=\"{StaticResource Color.Accent}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.Disabled\" Color=\"{StaticResource Color.Gray5}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.DisabledChecked\" Color=\"{StaticResource Color.Accent3}\" />\n    <SolidColorBrush x:Key=\"Brush.Border.Popup\" Color=\"{StaticResource Color.Gray6}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Track.Normal\" Color=\"{StaticResource Color.Gray7}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Thumb.Normal\" Color=\"{StaticResource Color.Gray3}\" />\n    <SolidColorBrush x:Key=\"Brush.Thumb.Over\" Color=\"{StaticResource Color.Gray1}\" />\n    <SolidColorBrush x:Key=\"Brush.Thumb.Pressed\" Color=\"{StaticResource Color.Gray1}\" />\n    <SolidColorBrush x:Key=\"Brush.Thumb.Disabled\" Color=\"{StaticResource Color.Gray5}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Header.Normal\" Color=\"{StaticResource Color.Gray7}\" />\n    <SolidColorBrush x:Key=\"Brush.Header.Over\" Color=\"{StaticResource Color.Gray6}\" />\n    <SolidColorBrush x:Key=\"Brush.Header.Pressed\" Color=\"{StaticResource Color.Gray5}\" />\n    <SolidColorBrush x:Key=\"Brush.Header.Checked\" Color=\"{StaticResource Color.Accent3}\" />\n    <SolidColorBrush x:Key=\"Brush.Header.CheckedOver\" Color=\"{StaticResource Color.Accent2}\" />\n    <SolidColorBrush x:Key=\"Brush.Header.CheckedPressed\" Color=\"{StaticResource Color.Accent}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Hyperlink.Normal\" Color=\"{StaticResource Color.Accent}\" />\n    <SolidColorBrush x:Key=\"Brush.Hyperlink.Over\" Color=\"{StaticResource Color.Black}\" />\n\n    <SolidColorBrush x:Key=\"Brush.TextBox.Normal\" Color=\"{StaticResource Color.Gray9}\" />\n    <SolidColorBrush x:Key=\"Brush.TextBox.Focused\" Color=\"{StaticResource Color.White}\" />\n\n    <SolidColorBrush x:Key=\"Brush.Item.Over\" Color=\"{StaticResource Color.Gray6}\" />\n    <SolidColorBrush x:Key=\"Brush.Item.Selected\" Color=\"{StaticResource Color.Accent3}\" />\n    <SolidColorBrush x:Key=\"Brush.Item.SelectedOver\" Color=\"{StaticResource Color.Accent2}\" />\n\n    <SolidColorBrush x:Key=\"Brush.ComboBox.Normal\" Color=\"{StaticResource Color.Gray9}\" />\n    <SolidColorBrush x:Key=\"Brush.ComboBox.Over\" Color=\"{StaticResource Color.Gray6}\" />\n    <SolidColorBrush x:Key=\"Brush.ComboBox.Pressed\" Color=\"{StaticResource Color.Gray5}\" />\n    <SolidColorBrush x:Key=\"Brush.ComboBox.Edit\" Color=\"{StaticResource Color.White}\" />\n\n    <SolidColorBrush x:Key=\"Brush.TabControl.Normal\" Color=\"{StaticResource Color.Gray8}\" />\n    <SolidColorBrush x:Key=\"Brush.TabControl.Over\" Color=\"{StaticResource Color.Gray9}\" />\n    <SolidColorBrush x:Key=\"Brush.TabControl.Selected\" Color=\"{StaticResource Color.Accent2}\" />\n\n    <SolidColorBrush x:Key=\"Brush.TreeView.Collapsed\" Color=\"{StaticResource Color.Gray1}\" />\n    <SolidColorBrush x:Key=\"Brush.TreeView.Expanded\" Color=\"{StaticResource Color.Black}\" />\n    <SolidColorBrush x:Key=\"Brush.TreeView.Over\" Color=\"{StaticResource Color.Accent}\" />\n    <SolidColorBrush x:Key=\"Brush.TreeView.SelectedOver\" Color=\"{StaticResource Color.Accent0}\" />\n\n    <SolidColorBrush x:Key=\"Brush.ToolBar.Normal\" Color=\"{StaticResource Color.Gray8}\" />\n    <SolidColorBrush x:Key=\"Brush.ToolBar.Over\" Color=\"{StaticResource Color.Gray6}\" />\n    <SolidColorBrush x:Key=\"Brush.ToolBar.Pressed\" Color=\"{StaticResource Color.Gray5}\" />\n    <SolidColorBrush x:Key=\"Brush.ToolBar.Checked\" Color=\"{StaticResource Color.Accent2}\" />\n\n</ResourceDictionary>"
  },
  {
    "path": "Assets/GUI/Theme/Colors.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <Color x:Key=\"Color.Black\">#20282f</Color>\n\n    <Color x:Key=\"Color.Gray0\">#41484e</Color>\n    <Color x:Key=\"Color.Gray1\">#5d6469</Color>\n    <Color x:Key=\"Color.Gray2\">#7b8085</Color>\n    <Color x:Key=\"Color.Gray3\">#93979b</Color>\n    <Color x:Key=\"Color.Gray4\">#abaeb2</Color>\n    <Color x:Key=\"Color.Gray5\">#bbbec1</Color>\n    <Color x:Key=\"Color.Gray6\">#cbced0</Color>\n    <Color x:Key=\"Color.Gray7\">#dcdee0</Color>\n    <Color x:Key=\"Color.Gray8\">#e5e6e8</Color>\n    <Color x:Key=\"Color.Gray9\">#edeff0</Color>\n\n    <Color x:Key=\"Color.White\">#f6f7f8</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "Assets/GUI/Theme/Fonts.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:sys=\"clr-namespace:System;assembly=mscorlib\">\n\n    <FontFamily x:Key=\"Font.Family.Default\">#Consolas</FontFamily>\n    <sys:Double x:Key=\"Font.Size.Header\">17</sys:Double>\n    <sys:Double x:Key=\"Font.Size.Normal\">15</sys:Double>\n    <sys:Double x:Key=\"Font.Size.ToolTip\">12</sys:Double>\n\n</ResourceDictionary>"
  },
  {
    "path": "Assets/GUI/Theme/NEWorld.xaml",
    "content": "﻿<ResourceDictionary\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:sys=\"clr-namespace:System;assembly=mscorlib\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Brushes.xaml\" />\n        <ResourceDictionary Source=\"Fonts.xaml\" />\n        <ResourceDictionary Source=\"Styles.xaml\" />\n    </ResourceDictionary.MergedDictionaries>\n\n</ResourceDictionary>"
  },
  {
    "path": "Assets/GUI/Theme/Styles.xaml",
    "content": "﻿<ResourceDictionary\n  xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n  xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n  xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"\n  xmlns:local=\"clr-namespace:NoesisGUIExtensions\">\n\n  <sys:Double x:Key=\"Size.ScrollBar\">17</sys:Double>\n  <sys:Double x:Key=\"Size.GroupBox.Border\">1</sys:Double>\n\n  <Thickness x:Key=\"Border.Normal\">0</Thickness>\n  <Thickness x:Key=\"Border.Box\">1</Thickness>\n  <Thickness x:Key=\"Border.Over\">2</Thickness>\n  <Thickness x:Key=\"Border.Focus\">2</Thickness>\n\n  <Thickness x:Key=\"Margin.Focus.Uniform\">-3</Thickness>\n  <Thickness x:Key=\"Margin.Focus.Padded\">-8,-3</Thickness>\n\n  <CornerRadius x:Key=\"Corner.Border0\">1.75</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border0.Top\">1.75,1.75,0,0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border0.Bottom\">0,0,1.75,1.75</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border0.Left\">1.75,0,0,1.75</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border0.Right\">0,1.75,1.75,0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border1\">1.25</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border1.Top\">1.25,1.25,0,0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border1.Bottom\">0,0,1.25,1.25</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border1.Left\">1.25,0,0,1.25</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border1.Right\">0,1.25,1.25,0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border2\">0.75</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border2.Top\">0.75,0.75,0,0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border2.Bottom\">0,0,0.75,0.75</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border2.Left\">0.75,0,0,0.75</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Border2.Right\">0,0.75,0.75,0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Focus\">0</CornerRadius>\n  <CornerRadius x:Key=\"Corner.Popup\">3</CornerRadius>\n\n  <!-- Focus Styles -->\n  <Style x:Key=\"Style.Focus.Outer.Uniform\" TargetType=\"Control\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Control\">\n          <Border BorderThickness=\"{DynamicResource Border.Focus}\" BorderBrush=\"{DynamicResource Brush.Border.Focus}\" Margin=\"{DynamicResource Margin.Focus.Uniform}\" CornerRadius=\"{DynamicResource Corner.Focus}\"/>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <Style x:Key=\"Style.Focus.Outer.Padded\" TargetType=\"Control\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Control\">\n          <Border BorderThickness=\"{DynamicResource Border.Focus}\" BorderBrush=\"{DynamicResource Brush.Border.Focus}\" Margin=\"{DynamicResource Margin.Focus.Padded}\" CornerRadius=\"{DynamicResource Corner.Focus}\"/>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <Style x:Key=\"Style.Focus.Inner.Uniform\" TargetType=\"Control\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Control\">\n          <Border BorderThickness=\"{DynamicResource Border.Focus}\" BorderBrush=\"{DynamicResource Brush.Border.Focus}\" CornerRadius=\"{DynamicResource Corner.Focus}\"/>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n\n  <!-- Base Control Styles -->\n  <Style x:Key=\"Style.Base\">\n    <Setter Property=\"Control.Foreground\" Value=\"{DynamicResource Brush.Foreground.Normal}\"/>\n    <Setter Property=\"Control.HorizontalContentAlignment\" Value=\"Stretch\"/>\n    <Setter Property=\"Control.VerticalContentAlignment\" Value=\"Stretch\"/>\n    <Setter Property=\"FrameworkElement.FocusVisualStyle\" Value=\"{x:Null}\"/>\n  </Style>\n  <Style TargetType=\"Control\" BasedOn=\"{StaticResource Style.Base}\"/>\n  <Style TargetType=\"ContentControl\" BasedOn=\"{StaticResource Style.Base}\"/>\n  <Style TargetType=\"HeaderedContentControl\" BasedOn=\"{StaticResource Style.Base}\"/>\n  <Style TargetType=\"ItemsControl\" BasedOn=\"{StaticResource Style.Base}\">\n    <Setter Property=\"ScrollViewer.PanningMode\" Value=\"VerticalFirst\"/>\n  </Style>\n  <Style TargetType=\"HeaderedItemsControl\" BasedOn=\"{StaticResource Style.Base}\">\n    <Setter Property=\"ScrollViewer.PanningMode\" Value=\"VerticalFirst\"/>\n  </Style>\n  <Style TargetType=\"UserControl\" BasedOn=\"{StaticResource Style.Base}\"/>\n  <Style TargetType=\"Page\" BasedOn=\"{StaticResource Style.Base}\"/>\n  <Style TargetType=\"Separator\">\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Normal}\"/>\n  </Style>\n\n  <!-- ToolTip -->\n  <ControlTemplate x:Key=\"Template.ToolTip\" TargetType=\"ToolTip\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"1\" CornerRadius=\"{DynamicResource Corner.Popup}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n    </Border>\n  </ControlTemplate>\n  <Style TargetType=\"ToolTip\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.ToolTip}\"/>\n    <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Normal}\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Popup}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Popup}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"1\"/>\n    <Setter Property=\"Padding\" Value=\"8,6\"/>\n    <Setter Property=\"Placement\" Value=\"Mouse\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolTip}\"/>\n  </Style>\n\n  <!-- Label -->\n  <ControlTemplate x:Key=\"Template.Label\" TargetType=\"Label\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"Label\" BasedOn=\"{StaticResource {x:Type Control}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"BorderBrush\" Value=\"Transparent\"/>\n    <Setter Property=\"UseLayoutRounding\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.Label}\"/>\n  </Style>\n\n  <!-- Button/RepeatButton -->\n  <ControlTemplate x:Key=\"Template.ButtonBase\" TargetType=\"ButtonBase\">\n    <Grid x:Name=\"TemplateRoot\" RenderTransformOrigin=\"0.5,0.5\">\n      <Grid.RenderTransform>\n        <ScaleTransform />\n      </Grid.RenderTransform>\n      <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border0}\"/>\n      <ContentPresenter x:Name=\"PART_ContentPresenter\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Grid>\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Press\">\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"0.99\" Duration=\"0:0:0.1\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"0.99\" Duration=\"0:0:0.1\" DecelerationRatio=\"0.25\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Release\">\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n\t    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Press}\"/>\n        </Trigger.EnterActions>\n        <Trigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Release}\"/>\n        </Trigger.ExitActions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ButtonBase\" TargetType=\"ButtonBase\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Padding\" Value=\"8,7\"/>\n    <Setter Property=\"UseLayoutRounding\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ButtonBase}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n  </Style>\n  <Style TargetType=\"Button\" BasedOn=\"{StaticResource Style.ButtonBase}\"/>\n  <Style TargetType=\"RepeatButton\" BasedOn=\"{StaticResource Style.ButtonBase}\"/>\n\n  <!-- ToggleButton -->\n  <ControlTemplate x:Key=\"Template.ToggleButton\" TargetType=\"ToggleButton\">\n    <Grid x:Name=\"TemplateRoot\" RenderTransformOrigin=\"0.5,0.5\">\n      <Grid.RenderTransform>\n        <ScaleTransform />\n      </Grid.RenderTransform>\n      <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border0}\"/>\n      <ContentPresenter x:Name=\"PART_ContentPresenter\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Grid>\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Press\">\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"0.99\" Duration=\"0:0:0.1\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"0.99\" Duration=\"0:0:0.1\" DecelerationRatio=\"0.25\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Release\">\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n        <DoubleAnimation Storyboard.TargetName=\"TemplateRoot\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Press}\"/>\n        </Trigger.EnterActions>\n        <Trigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Release}\"/>\n        </Trigger.ExitActions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsChecked\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEnabled\" Value=\"False\"/>\n          <Condition Property=\"IsChecked\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.DisabledChecked}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"Border\"/>\n      </MultiTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ToggleButton\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Padding\" Value=\"8,7\"/>\n    <Setter Property=\"UseLayoutRounding\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToggleButton}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n  </Style>\n\n  <!-- ToggleSwitch -->\n  <ControlTemplate x:Key=\"Template.ToggleSwitch\" TargetType=\"ToggleButton\">\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Loaded\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\"Tag\" Storyboard.TargetName=\"Root\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"1\"/>\n        </ObjectAnimationUsingKeyFrames>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Check.Loaded\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.X\" Storyboard.TargetName=\"Knob\" To=\"24\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Check\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.X\" Storyboard.TargetName=\"Knob\" To=\"24\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Uncheck\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.X\" Storyboard.TargetName=\"Knob\" To=\"0\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"Root\" Background=\"Transparent\" Tag=\"0\">\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n      </Grid.ColumnDefinitions>\n      <Border Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding Background}\"/>\n      <Border x:Name=\"Border\" Grid.Column=\"0\" Background=\"{DynamicResource Brush.Background.Normal}\" BorderBrush=\"{DynamicResource Brush.Border.Normal}\" BorderThickness=\"{DynamicResource Border.Normal}\" Width=\"44\" Height=\"20\" CornerRadius=\"10\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n      <Decorator Margin=\"{TemplateBinding BorderThickness}\">\n        <Ellipse x:Name=\"Knob\" Grid.Column=\"0\" Width=\"10\" Height=\"10\" Margin=\"5\" HorizontalAlignment=\"Left\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Fill=\"{DynamicResource Brush.Glyph.Normal}\">\n          <Ellipse.RenderTransform>\n            <TranslateTransform/>\n          </Ellipse.RenderTransform>\n        </Ellipse>\n      </Decorator>\n      <ContentPresenter x:Name=\"PART_ContentPresenter\" Grid.Column=\"1\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <EventTrigger RoutedEvent=\"FrameworkElement.Loaded\" SourceName=\"Root\">\n        <BeginStoryboard Storyboard=\"{StaticResource Anim.Loaded}\"/>\n      </EventTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"9\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Knob\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"10\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Pressed}\" TargetName=\"Knob\"/>\n      </Trigger>\n      <Trigger Property=\"IsChecked\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Checked}\" TargetName=\"Knob\"/>\n      </Trigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Check.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Check}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Uncheck}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Knob\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEnabled\" Value=\"False\"/>\n          <Condition Property=\"IsChecked\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.DisabledChecked}\" TargetName=\"Knob\"/>\n      </MultiTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ToggleSwitch\" TargetType=\"ToggleButton\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"Left\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Padding\" Value=\"6,0,0,0\"/>\n    <Setter Property=\"UseLayoutRounding\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToggleSwitch}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Padded}\"/>\n  </Style>\n\n  <!-- CheckBox -->\n  <Geometry x:Key=\"Geometry.CheckBox.Check\">M0,7.5L5.5,12.5 14,2.75 11.75,1 5.5,9 1.5,5.5z</Geometry>\n  <ControlTemplate x:Key=\"Template.CheckBox\" TargetType=\"CheckBox\">\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Loaded\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\"Tag\" Storyboard.TargetName=\"Root\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"1\"/>\n        </ObjectAnimationUsingKeyFrames>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Check.Loaded\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleX\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0\"/>\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleY\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Check\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleX\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0:0:0.1\" DecelerationRatio=\"1\"/>\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleY\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0:0:0.1\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Uncheck\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleX\" Storyboard.TargetName=\"Check\" To=\"0\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleY\" Storyboard.TargetName=\"Check\" To=\"0\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"Root\" Background=\"Transparent\" Tag=\"0\">\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n      </Grid.ColumnDefinitions>\n      <Border Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding Background}\"/>\n      <Border x:Name=\"Border\" Grid.Column=\"0\" Background=\"{DynamicResource Brush.Background.Normal}\" BorderBrush=\"{DynamicResource Brush.Border.Normal}\" BorderThickness=\"{DynamicResource Border.Normal}\" CornerRadius=\"{DynamicResource Corner.Border0}\" Width=\"20\" Height=\"20\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n      <Decorator Grid.Column=\"0\" Margin=\"{TemplateBinding BorderThickness}\" Width=\"20\" Height=\"20\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\">\n        <Grid>\n          <Path x:Name=\"Check\" Data=\"{StaticResource Geometry.CheckBox.Check}\" Width=\"14\" Height=\"14\" Fill=\"{DynamicResource Brush.Glyph.Checked}\" RenderTransformOrigin=\"0.5,0.5\">\n            <Path.RenderTransform>\n              <ScaleTransform ScaleX=\"0\" ScaleY=\"0\"/>\n            </Path.RenderTransform>\n          </Path>\n          <Rectangle x:Name=\"CheckIndeterminate\" Width=\"10\" Height=\"10\" Fill=\"{DynamicResource Brush.Glyph.Normal}\" Visibility=\"Collapsed\"/>\n        </Grid>\n      </Decorator>\n      <ContentPresenter x:Name=\"PART_ContentPresenter\" Grid.Column=\"1\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <EventTrigger RoutedEvent=\"FrameworkElement.Loaded\" SourceName=\"Root\">\n        <BeginStoryboard Storyboard=\"{StaticResource Anim.Loaded}\"/>\n      </EventTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsChecked\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Check.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Check}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Uncheck}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsThreeState\" Value=\"True\"/>\n          <Condition Property=\"IsChecked\" Value=\"{x:Null}\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Visibility\" Value=\"Collapsed\" TargetName=\"Check\"/>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"CheckIndeterminate\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Check\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"CheckIndeterminate\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEnabled\" Value=\"False\"/>\n          <Condition Property=\"IsChecked\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.DisabledChecked}\" TargetName=\"Check\"/>\n      </MultiTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"CheckBox\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"Left\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Padding\" Value=\"6,0,0,0\"/>\n    <Setter Property=\"UseLayoutRounding\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.CheckBox}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Padded}\"/>\n  </Style>\n\n  <!-- RadioButton -->\n  <ControlTemplate x:Key=\"Template.RadioButton\" TargetType=\"RadioButton\">\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Loaded\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\"Tag\" Storyboard.TargetName=\"Root\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"1\"/>\n        </ObjectAnimationUsingKeyFrames>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Check.Loaded\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleX\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0\"/>\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleY\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Check\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleX\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0:0:0.1\" DecelerationRatio=\"1\"/>\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleY\" Storyboard.TargetName=\"Check\" To=\"1\" Duration=\"0:0:0.1\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Uncheck\">\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleX\" Storyboard.TargetName=\"Check\" To=\"0\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n        <DoubleAnimation Storyboard.TargetProperty=\"RenderTransform.ScaleY\" Storyboard.TargetName=\"Check\" To=\"0\" Duration=\"0:0:0.2\" DecelerationRatio=\"1\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"Root\" Background=\"Transparent\" Tag=\"0\">\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n      </Grid.ColumnDefinitions>\n      <Border Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding Background}\"/>\n      <Border x:Name=\"Border\" Grid.Column=\"0\" Background=\"{DynamicResource Brush.Background.Normal}\" BorderBrush=\"{DynamicResource Brush.Border.Normal}\" BorderThickness=\"{DynamicResource Border.Normal}\" Width=\"20\" Height=\"20\" CornerRadius=\"10\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n      <Decorator Grid.Column=\"0\" Margin=\"{TemplateBinding BorderThickness}\" Width=\"20\" Height=\"20\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\">\n        <Ellipse x:Name=\"Check\" Width=\"10\" Height=\"10\" Fill=\"{DynamicResource Brush.Glyph.Checked}\" RenderTransformOrigin=\"0.5,0.5\">\n          <Ellipse.RenderTransform>\n            <ScaleTransform ScaleX=\"0\" ScaleY=\"0\"/>\n          </Ellipse.RenderTransform>\n        </Ellipse>\n      </Decorator>\n      <ContentPresenter x:Name=\"PART_ContentPresenter\" Grid.Column=\"1\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <EventTrigger RoutedEvent=\"FrameworkElement.Loaded\" SourceName=\"Root\">\n        <BeginStoryboard Storyboard=\"{StaticResource Anim.Loaded}\"/>\n      </EventTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsChecked\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Check.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Check}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Uncheck}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Check\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEnabled\" Value=\"False\"/>\n          <Condition Property=\"IsChecked\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"Border\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.DisabledChecked}\" TargetName=\"Check\"/>\n      </MultiTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"RadioButton\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Normal}\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"Left\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Padding\" Value=\"6,0,0,0\"/>\n    <Setter Property=\"UseLayoutRounding\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.RadioButton}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Padded}\"/>\n  </Style>\n\n  <!-- ProgressBar -->\n  <ControlTemplate x:Key=\"Template.ProgressBar\" TargetType=\"ProgressBar\">\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Indeterminate\">\n        <DoubleAnimation Storyboard.TargetProperty=\"Background.RelativeTransform.X\" Storyboard.TargetName=\"IndicatorIndeterminate\" From=\"-1\" To=\"1\" Duration=\"0:0:2\" RepeatBehavior=\"Forever\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"TemplateRoot\" MinHeight=\"4\">\n      <Border x:Name=\"PART_Track\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\">\n        <Grid>\n          <Border x:Name=\"PART_Indicator\" Background=\"{TemplateBinding Foreground}\" HorizontalAlignment=\"Left\"/>\n          <Border x:Name=\"IndicatorIndeterminate\" Visibility=\"Collapsed\">\n            <Border.Background>\n              <LinearGradientBrush StartPoint=\"0,0\" EndPoint=\"1,0\">\n                <LinearGradientBrush.RelativeTransform>\n                  <TranslateTransform />\n                </LinearGradientBrush.RelativeTransform>\n                <GradientStop Color=\"{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}\" Offset=\"0\"/>\n                <GradientStop Color=\"{Binding Foreground.Color, RelativeSource={RelativeSource TemplatedParent}}\" Offset=\"0.5\"/>\n                <GradientStop Color=\"{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}\" Offset=\"1\"/>\n              </LinearGradientBrush>\n            </Border.Background>\n          </Border>\n        </Grid>\n      </Border>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"Orientation\" Value=\"Vertical\">\n        <Setter Property=\"LayoutTransform\" TargetName=\"TemplateRoot\">\n          <Setter.Value>\n            <RotateTransform Angle=\"-90\"/>\n          </Setter.Value>\n        </Setter>\n      </Trigger>\n      <Trigger Property=\"IsIndeterminate\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Indeterminate}\"/>\n        </Trigger.EnterActions>\n        <Setter Property=\"Visibility\" Value=\"Collapsed\" TargetName=\"PART_Indicator\"/>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"IndicatorIndeterminate\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"false\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ProgressBar\" BasedOn=\"{StaticResource {x:Type Control}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Background.Checked}\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Normal}\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ProgressBar}\"/>\n  </Style>\n\n  <!-- Slider -->\n  <Style x:Key=\"Style.Slider.Thumb\" TargetType=\"Thumb\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Thumb\">\n          <Grid Background=\"Transparent\">\n            <Ellipse x:Name=\"Thumb\" Fill=\"{TemplateBinding Background}\" Width=\"20\" Height=\"20\" Margin=\"-8\"/>\n          </Grid>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsDragging\" Value=\"True\">\n              <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Thumb.Pressed}\" TargetName=\"Thumb\"/>\n            </Trigger>\n            <Trigger Property=\"IsEnabled\" Value=\"False\">\n              <Setter Property=\"Draft\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"Thumb\"/>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <Style x:Key=\"Style.Slider.RepeatButton\" TargetType=\"RepeatButton\" BasedOn=\"{StaticResource {x:Type RepeatButton}}\">\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"IsTabStop\" Value=\"False\"/>\n    <Setter Property=\"Focusable\" Value=\"False\"/>\n    <Setter Property=\"ClickMode\" Value=\"Press\"/>\n    <Setter Property=\"Delay\" Value=\"250\"/>\n    <Setter Property=\"Interval\" Value=\"100\"/>\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"RepeatButton\">\n          <Border Background=\"Transparent\" Padding=\"{TemplateBinding Padding}\">\n            <Border Background=\"{TemplateBinding Background}\" CornerRadius=\"{DynamicResource Corner.Border2}\"/>\n          </Border>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <ControlTemplate x:Key=\"Template.Slider.Horizontal\" TargetType=\"Slider\">\n    <Grid x:Name=\"TemplateRoot\" Height=\"20\" Margin=\"8,0\">\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"*\"/>\n        <RowDefinition Height=\"Auto\"/>\n      </Grid.RowDefinitions>\n      <Border x:Name=\"Background\" Grid.Row=\"1\" Height=\"2\" Background=\"{TemplateBinding Background}\" CornerRadius=\"{DynamicResource Corner.Border2}\"/>\n      <Border Grid.Row=\"0\" Height=\"8\">\n        <TickBar x:Name=\"TopTicks\" Fill=\"{TemplateBinding Background}\" Placement=\"Top\" VerticalAlignment=\"Top\" Height=\"4\" Visibility=\"Hidden\"/>\n      </Border>\n      <Border Grid.Row=\"2\" Height=\"8\">\n        <TickBar x:Name=\"BottomTicks\" Fill=\"{TemplateBinding Background}\" Placement=\"Bottom\" VerticalAlignment=\"Bottom\" Height=\"4\" Visibility=\"Hidden\"/>\n      </Border>\n      <Track x:Name=\"PART_Track\" Grid.RowSpan=\"3\" Orientation=\"Horizontal\">\n        <Track.DecreaseRepeatButton>\n          <RepeatButton x:Name=\"DecreaseButton\" Command=\"Slider.DecreaseLarge\" Padding=\"0,9\" Margin=\"0,0,-1,0\" Background=\"{TemplateBinding Foreground}\" Style=\"{StaticResource Style.Slider.RepeatButton}\"/>\n        </Track.DecreaseRepeatButton>\n        <Track.IncreaseRepeatButton>\n          <RepeatButton x:Name=\"IncreaseButton\" Command=\"Slider.IncreaseLarge\" Padding=\"0,9\" Background=\"Transparent\" Style=\"{StaticResource Style.Slider.RepeatButton}\"/>\n        </Track.IncreaseRepeatButton>\n        <Track.Thumb>\n          <Thumb x:Name=\"Thumb\" Background=\"{TemplateBinding Foreground}\" Style=\"{StaticResource Style.Slider.Thumb}\"/>\n        </Track.Thumb>\n      </Track>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsDirectionReversed\" Value=\"True\">\n        <Setter Property=\"Margin\" Value=\"-2,0,0,0\" TargetName=\"DecreaseButton\"/>\n      </Trigger>\n      <Trigger Property=\"local:Element.IsFocusEngaged\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Thumb\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Thumb\"/>\n      </Trigger>\n      <Trigger Property=\"TickPlacement\" Value=\"TopLeft\">\n        <Setter TargetName=\"TopTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n      </Trigger>\n      <Trigger Property=\"TickPlacement\" Value=\"BottomRight\">\n        <Setter TargetName=\"BottomTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n      </Trigger>\n      <Trigger Property=\"TickPlacement\" Value=\"Both\">\n        <Setter TargetName=\"TopTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n        <Setter TargetName=\"BottomTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"DecreaseButton\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.Slider.Vertical\" TargetType=\"Slider\">\n    <Grid x:Name=\"TemplateRoot\" Width=\"20\" Margin=\"0,8\">\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n        <ColumnDefinition Width=\"Auto\"/>\n      </Grid.ColumnDefinitions>\n      <Border x:Name=\"Background\" Grid.Column=\"1\" Width=\"2\" Background=\"{TemplateBinding Background}\" CornerRadius=\"{DynamicResource Corner.Border2}\"/>\n      <Border Grid.Column=\"0\" Width=\"8\">\n        <TickBar x:Name=\"LeftTicks\" Fill=\"{TemplateBinding Background}\" Placement=\"Left\" HorizontalAlignment=\"Left\" Width=\"4\" Visibility=\"Hidden\"/>\n      </Border>\n      <Border Grid.Column=\"2\" Width=\"8\">\n        <TickBar x:Name=\"RightTicks\" Fill=\"{TemplateBinding Background}\" Placement=\"Right\" HorizontalAlignment=\"Right\" Width=\"4\" Visibility=\"Hidden\"/>\n      </Border>\n      <Track x:Name=\"PART_Track\" Grid.ColumnSpan=\"3\" Orientation=\"Vertical\">\n        <Track.DecreaseRepeatButton>\n          <RepeatButton x:Name=\"DecreaseButton\" Command=\"Slider.DecreaseLarge\" Padding=\"9,0\" Margin=\"0,-1,0,0\" Background=\"{TemplateBinding Foreground}\" Style=\"{StaticResource Style.Slider.RepeatButton}\"/>\n        </Track.DecreaseRepeatButton>\n        <Track.IncreaseRepeatButton>\n          <RepeatButton x:Name=\"IncreaseButton\" Command=\"Slider.IncreaseLarge\" Padding=\"9,0\" Background=\"Transparent\" Style=\"{StaticResource Style.Slider.RepeatButton}\"/>\n        </Track.IncreaseRepeatButton>\n        <Track.Thumb>\n          <Thumb x:Name=\"Thumb\" Background=\"{TemplateBinding Foreground}\" Style=\"{StaticResource Style.Slider.Thumb}\"/>\n        </Track.Thumb>\n      </Track>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsDirectionReversed\" Value=\"True\">\n        <Setter Property=\"Margin\" Value=\"0,0,0,-2\" TargetName=\"DecreaseButton\"/>\n      </Trigger>\n      <Trigger Property=\"local:Element.IsFocusEngaged\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Border.Pressed}\" TargetName=\"Thumb\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Thumb\"/>\n      </Trigger>\n      <Trigger Property=\"TickPlacement\" Value=\"TopLeft\">\n        <Setter TargetName=\"LeftTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n      </Trigger>\n      <Trigger Property=\"TickPlacement\" Value=\"BottomRight\">\n        <Setter TargetName=\"RightTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n      </Trigger>\n      <Trigger Property=\"TickPlacement\" Value=\"Both\">\n        <Setter TargetName=\"LeftTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n        <Setter TargetName=\"RightTicks\" Property=\"Visibility\" Value=\"Visible\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Border.DisabledChecked}\" TargetName=\"DecreaseButton\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"Slider\" BasedOn=\"{StaticResource {x:Type Control}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Background.Checked}\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Normal}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"Orientation\" Value=\"Horizontal\">\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.Slider.Horizontal}\"/>\n      </Trigger>\n      <Trigger Property=\"Orientation\" Value=\"Vertical\">\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.Slider.Vertical}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ScrollBar -->\n  <Style x:Key=\"Style.ScrollBar.Thumb\" TargetType=\"Thumb\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Thumb\">\n          <Grid Background=\"Transparent\">\n            <Border x:Name=\"Thumb\" Background=\"{DynamicResource Brush.Thumb.Normal}\"/>\n          </Grid>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Thumb.Over}\" TargetName=\"Thumb\"/>\n            </Trigger>\n            <Trigger Property=\"IsDragging\" Value=\"True\">\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Thumb.Pressed}\" TargetName=\"Thumb\"/>\n            </Trigger>\n            <Trigger Property=\"IsEnabled\" Value=\"False\">\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Thumb.Disabled}\" TargetName=\"Thumb\"/>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <Style x:Key=\"Style.ScrollBar.LineButton\" TargetType=\"RepeatButton\" BasedOn=\"{StaticResource {x:Type RepeatButton}}\">\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"IsTabStop\" Value=\"False\"/>\n    <Setter Property=\"Focusable\" Value=\"False\"/>\n    <Setter Property=\"ClickMode\" Value=\"Press\"/>\n    <Setter Property=\"Delay\" Value=\"250\"/>\n    <Setter Property=\"Interval\" Value=\"100\"/>\n    <Setter Property=\"Width\" Value=\"{DynamicResource Size.ScrollBar}\"/>\n    <Setter Property=\"Height\" Value=\"{DynamicResource Size.ScrollBar}\"/>\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"RepeatButton\">\n          <Border x:Name=\"Border\" Background=\"Transparent\">\n            <Path x:Name=\"Arrow\" Data=\"{TemplateBinding Content}\" Stroke=\"{DynamicResource Brush.Glyph.Normal}\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/>\n          </Border>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Normal}\" TargetName=\"Border\"/>\n              <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Arrow\"/>\n            </Trigger>\n            <Trigger Property=\"IsPressed\" Value=\"True\">\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Thumb.Pressed}\" TargetName=\"Border\"/>\n              <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Pressed}\" TargetName=\"Arrow\"/>\n            </Trigger>\n            <Trigger Property=\"IsEnabled\" Value=\"False\">\n              <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Arrow\"/>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <Style x:Key=\"Style.ScrollBar.PageButton\" TargetType=\"RepeatButton\" BasedOn=\"{StaticResource {x:Type RepeatButton}}\">\n    <Setter Property=\"IsTabStop\" Value=\"False\"/>\n    <Setter Property=\"Focusable\" Value=\"False\"/>\n    <Setter Property=\"ClickMode\" Value=\"Press\"/>\n    <Setter Property=\"Delay\" Value=\"250\"/>\n    <Setter Property=\"Interval\" Value=\"100\"/>\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"RepeatButton\">\n          <Border Background=\"Transparent\"/>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ScrollBar.Horizontal\" TargetType=\"ScrollBar\">\n    <ControlTemplate.Resources>\n      <Geometry x:Key=\"Geometry.LeftArrow\">M4,0L0,4 4,8</Geometry>\n      <Geometry x:Key=\"Geometry.RightArrow\">M0,0 L4,4 0,8</Geometry>\n      <Storyboard x:Key=\"Anim.ExpandBar\" BeginTime=\"0:0:0.1\">\n        <DoubleAnimation Storyboard.TargetName=\"Background\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineLeftButton\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineRightButton\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"Thumb\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0:0:0.1\" DecelerationRatio=\"0.5\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.CollapseBar\" BeginTime=\"0:0:2\">\n        <DoubleAnimation Storyboard.TargetName=\"Background\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineLeftButton\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineRightButton\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"Thumb\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"0.2\" Duration=\"0:0:0.15\" DecelerationRatio=\"0.5\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"TemplateRoot\" Background=\"Transparent\">\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n        <ColumnDefinition Width=\"Auto\"/>\n      </Grid.ColumnDefinitions>\n      <Border x:Name=\"Background\" Grid.ColumnSpan=\"3\" Background=\"{DynamicResource Brush.Track.Normal}\" Opacity=\"0\"/>\n      <RepeatButton x:Name=\"LineLeftButton\" Grid.Column=\"0\" Command=\"ScrollBar.LineLeftCommand\" Style=\"{StaticResource Style.ScrollBar.LineButton}\" Content=\"{StaticResource Geometry.LeftArrow}\" Opacity=\"0\"/>\n      <RepeatButton x:Name=\"LineRightButton\" Grid.Column=\"2\" Command=\"ScrollBar.LineRightCommand\" Style=\"{StaticResource Style.ScrollBar.LineButton}\" Content=\"{StaticResource Geometry.RightArrow}\" Opacity=\"0\"/>\n      <Track x:Name=\"PART_Track\" Grid.Column=\"1\" Orientation=\"Horizontal\">\n        <Track.DecreaseRepeatButton>\n          <RepeatButton x:Name=\"PageLeftButton\" Command=\"ScrollBar.PageLeftCommand\" Style=\"{StaticResource Style.ScrollBar.PageButton}\"/>\n        </Track.DecreaseRepeatButton>\n        <Track.IncreaseRepeatButton>\n          <RepeatButton x:Name=\"IncreaseButton\" Command=\"ScrollBar.PageRightCommand\" Style=\"{StaticResource Style.ScrollBar.PageButton}\"/>\n        </Track.IncreaseRepeatButton>\n        <Track.Thumb>\n          <Thumb x:Name=\"Thumb\" Style=\"{StaticResource Style.ScrollBar.Thumb}\" RenderTransformOrigin=\"0,0.8\">\n            <Thumb.RenderTransform>\n              <ScaleTransform ScaleY=\"0.2\"/>\n            </Thumb.RenderTransform>\n          </Thumb>\n        </Track.Thumb>\n      </Track>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.ExpandBar}\"/>\n        </Trigger.EnterActions>\n        <Trigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.CollapseBar}\"/>\n        </Trigger.ExitActions>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.ScrollBar.Vertical\" TargetType=\"ScrollBar\">\n    <ControlTemplate.Resources>\n      <Geometry x:Key=\"Geometry.UpArrow\">M0,4L4,0 8,4</Geometry>\n      <Geometry x:Key=\"Geometry.DownArrow\">M0,0 L4,4 8,0</Geometry>\n      <Storyboard x:Key=\"Anim.ExpandBar\" BeginTime=\"0:0:0.1\">\n        <DoubleAnimation Storyboard.TargetName=\"Background\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineUpButton\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineDownButton\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"Thumb\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0:0:0.1\" DecelerationRatio=\"0.5\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.CollapseBar\" BeginTime=\"0:0:2\">\n        <DoubleAnimation Storyboard.TargetName=\"Background\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineUpButton\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"LineDownButton\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"Thumb\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"0.2\" Duration=\"0:0:0.15\" DecelerationRatio=\"0.5\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"TemplateRoot\" Background=\"Transparent\">\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"*\"/>\n        <RowDefinition Height=\"Auto\"/>\n      </Grid.RowDefinitions>\n      <Border x:Name=\"Background\" Grid.RowSpan=\"3\" Background=\"{DynamicResource Brush.Track.Normal}\" Opacity=\"0\"/>\n      <RepeatButton x:Name=\"LineUpButton\" Grid.Row=\"0\" Command=\"ScrollBar.LineUpCommand\" Style=\"{StaticResource Style.ScrollBar.LineButton}\" Content=\"{StaticResource Geometry.UpArrow}\" Opacity=\"0\"/>\n      <RepeatButton x:Name=\"LineDownButton\" Grid.Row=\"2\" Command=\"ScrollBar.LineDownCommand\" Style=\"{StaticResource Style.ScrollBar.LineButton}\" Content=\"{StaticResource Geometry.DownArrow}\" Opacity=\"0\"/>\n      <Track x:Name=\"PART_Track\" Grid.Row=\"1\" Orientation=\"Vertical\" IsDirectionReversed=\"True\">\n        <Track.DecreaseRepeatButton>\n          <RepeatButton x:Name=\"PageUpButton\" Command=\"ScrollBar.PageUpCommand\" Style=\"{StaticResource Style.ScrollBar.PageButton}\"/>\n        </Track.DecreaseRepeatButton>\n        <Track.IncreaseRepeatButton>\n          <RepeatButton x:Name=\"IncreaseButton\" Command=\"ScrollBar.PageDownCommand\" Style=\"{StaticResource Style.ScrollBar.PageButton}\"/>\n        </Track.IncreaseRepeatButton>\n        <Track.Thumb>\n          <Thumb x:Name=\"Thumb\" Style=\"{StaticResource Style.ScrollBar.Thumb}\" RenderTransformOrigin=\"0.8,0\">\n            <Thumb.RenderTransform>\n              <ScaleTransform ScaleX=\"0.2\"/>\n            </Thumb.RenderTransform>\n          </Thumb>\n        </Track.Thumb>\n      </Track>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.ExpandBar}\"/>\n        </Trigger.EnterActions>\n        <Trigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.CollapseBar}\"/>\n        </Trigger.ExitActions>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ScrollBar\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"ForceCursor\" Value=\"True\"/>\n    <Setter Property=\"Cursor\" Value=\"Arrow\"/>\n    <Style.Triggers>\n      <Trigger Property=\"Orientation\" Value=\"Horizontal\">\n        <Setter Property=\"Height\" Value=\"{DynamicResource Size.ScrollBar}\"/>\n        <Setter Property=\"MinHeight\" Value=\"{DynamicResource Size.ScrollBar}\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.ScrollBar.Horizontal}\"/>\n      </Trigger>\n      <Trigger Property=\"Orientation\" Value=\"Vertical\">\n        <Setter Property=\"Width\" Value=\"{DynamicResource Size.ScrollBar}\"/>\n        <Setter Property=\"MinWidth\" Value=\"{DynamicResource Size.ScrollBar}\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.ScrollBar.Vertical}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ScrollViewer -->\n  <ControlTemplate x:Key=\"Template.ScrollViewer\" TargetType=\"ScrollViewer\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border0}\">\n      <Grid>\n        <Grid.RowDefinitions>\n          <RowDefinition Height=\"*\"/>\n          <RowDefinition Height=\"Auto\"/>\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n          <ColumnDefinition Width=\"*\"/>\n          <ColumnDefinition Width=\"Auto\"/>\n        </Grid.ColumnDefinitions>\n        <Decorator x:Name=\"ShowBarsRef\" IsEnabled=\"False\"/>\n        <ScrollContentPresenter Grid.RowSpan=\"2\" Grid.ColumnSpan=\"2\" Content=\"{TemplateBinding Content}\" ContentTemplate=\"{TemplateBinding ContentTemplate}\" ContentTemplateSelector=\"{TemplateBinding ContentTemplateSelector}\" CanContentScroll=\"{TemplateBinding CanContentScroll}\" Margin=\"{TemplateBinding Padding}\"/>\n        <Rectangle x:Name=\"Corner\" Grid.Row=\"1\" Grid.Column=\"1\" Fill=\"{DynamicResource Brush.Track.Normal}\" IsEnabled=\"False\" Opacity=\"0\"/>\n        <ScrollBar x:Name=\"PART_HorizontalScrollBar\" Grid.Column=\"0\" Grid.Row=\"1\" Orientation=\"Horizontal\" MaxHeight=\"{TemplateBinding MinHeight}\" Visibility=\"{TemplateBinding ComputedHorizontalScrollBarVisibility}\" Value=\"{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}\" ViewportSize=\"{TemplateBinding ViewportWidth}\" Minimum=\"0\" Maximum=\"{TemplateBinding ScrollableWidth}\" Opacity=\"0\"/>\n        <ScrollBar x:Name=\"PART_VerticalScrollBar\" Grid.Column=\"1\" Grid.Row=\"0\" Orientation=\"Vertical\" MaxWidth=\"{TemplateBinding MinWidth}\" Visibility=\"{TemplateBinding ComputedVerticalScrollBarVisibility}\" Value=\"{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}\" ViewportSize=\"{TemplateBinding ViewportHeight}\" Minimum=\"0\" Maximum=\"{TemplateBinding ScrollableHeight}\" Opacity=\"0\"/>\n      </Grid>\n    </Border>\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.ShowBars\" BeginTime=\"0:0:0.1\">\n        <DoubleAnimation Storyboard.TargetName=\"PART_HorizontalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.5\" DecelerationRatio=\"0.5\"/>\n        <DoubleAnimation Storyboard.TargetName=\"PART_VerticalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.5\" DecelerationRatio=\"0.5\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.HideBars\" BeginTime=\"0:0:2\">\n        <DoubleAnimation Storyboard.TargetName=\"PART_HorizontalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n        <DoubleAnimation Storyboard.TargetName=\"PART_VerticalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.ShowCorner\" BeginTime=\"0:0:0.1\">\n        <DoubleAnimation Storyboard.TargetName=\"Corner\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.HideCorner\" BeginTime=\"0:0:2\">\n        <DoubleAnimation Storyboard.TargetName=\"Corner\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsKeyboardFocusWithin\" Value=\"True\">\n        <Setter Property=\"IsEnabled\" Value=\"True\" TargetName=\"ShowBarsRef\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"IsEnabled\" Value=\"True\" TargetName=\"ShowBarsRef\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"True\" SourceName=\"ShowBarsRef\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.ShowBars}\"/>\n        </Trigger.EnterActions>\n        <Trigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.HideBars}\"/>\n        </Trigger.ExitActions>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"PART_HorizontalScrollBar\">\n        <Setter Property=\"IsEnabled\" Value=\"True\" TargetName=\"Corner\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"PART_VerticalScrollBar\">\n        <Setter Property=\"IsEnabled\" Value=\"True\" TargetName=\"Corner\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"True\" SourceName=\"Corner\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.ShowCorner}\"/>\n        </Trigger.EnterActions>\n        <Trigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.HideCorner}\"/>\n        </Trigger.ExitActions>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ScrollViewer\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"PanningMode\" Value=\"VerticalFirst\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ScrollViewer}\"/>\n  </Style>\n\n  <!-- GroupBox -->\n  <ControlTemplate x:Key=\"Template.GroupBox.NoHeader\" TargetType=\"GroupBox\">\n    <Grid>\n      <Rectangle Stroke=\"{TemplateBinding BorderBrush}\" StrokeThickness=\"{DynamicResource Size.GroupBox.Border}\" RadiusX=\"4\" RadiusY=\"4\"/>\n      <Decorator Margin=\"5\">\n        <ContentPresenter Margin=\"{TemplateBinding Padding}\"/>\n      </Decorator>\n    </Grid>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.GroupBox\" TargetType=\"GroupBox\">\n    <Grid>\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"5\" MaxHeight=\"5\"/>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"*\"/>\n        <RowDefinition Height=\"5\"/>\n      </Grid.RowDefinitions>\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n        <ColumnDefinition Width=\"9\" MaxWidth=\"9\"/>\n      </Grid.ColumnDefinitions>\n      <Path Grid.Row=\"1\" Grid.Column=\"0\" Data=\"M8,0L4,0A4,4,0,0,0,0,4\" StrokeStartLineCap=\"Square\" Stretch=\"Fill\" StrokeEndLineCap=\"Square\" Stroke=\"{TemplateBinding BorderBrush}\" StrokeThickness=\"{DynamicResource Size.GroupBox.Border}\" VerticalAlignment=\"Bottom\" Margin=\"0,4,0,-4\"/>\n      <Border Grid.Row=\"2\" Grid.RowSpan=\"2\" Grid.Column=\"0\" HorizontalAlignment=\"Left\" Background=\"{TemplateBinding BorderBrush}\" Width=\"{DynamicResource Size.GroupBox.Border}\" Margin=\"0,4,0,0\"/>\n      <Path Grid.Row=\"4\" Grid.Column=\"0\" Data=\"M0,0A4,4,0,0,0,4,4L8,4\" StrokeStartLineCap=\"Square\" Stretch=\"Fill\" StrokeEndLineCap=\"Square\" Stroke=\"{TemplateBinding BorderBrush}\" StrokeThickness=\"{DynamicResource Size.GroupBox.Border}\"/>\n      <Border Grid.Row=\"5\" Grid.Column=\"1\" Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding BorderBrush}\" Height=\"{DynamicResource Size.GroupBox.Border}\" VerticalAlignment=\"Bottom\"/>\n      <Path Grid.Row=\"4\" Grid.Column=\"3\" Data=\"M0,4L4,4A4,4,0,0,0,8,0\" StrokeStartLineCap=\"Square\" Stretch=\"Fill\" StrokeEndLineCap=\"Square\" Stroke=\"{TemplateBinding BorderBrush}\" StrokeThickness=\"{DynamicResource Size.GroupBox.Border}\"/>\n      <Border Grid.Row=\"2\" Grid.RowSpan=\"2\" Grid.Column=\"3\" HorizontalAlignment=\"Right\" Background=\"{TemplateBinding BorderBrush}\" Width=\"{DynamicResource Size.GroupBox.Border}\" Margin=\"0,4,0,0\"/>\n      <Path Grid.Row=\"1\" Grid.Column=\"3\" Data=\"M8,4A4,4,0,0,0,4,0L0,0\" StrokeStartLineCap=\"Square\" Stretch=\"Fill\" StrokeEndLineCap=\"Square\" Stroke=\"{TemplateBinding BorderBrush}\" StrokeThickness=\"{DynamicResource Size.GroupBox.Border}\" VerticalAlignment=\"Bottom\" Margin=\"0,4,0,-4\"/>\n      <Border Grid.Row=\"1\" Grid.Column=\"2\" Background=\"{TemplateBinding BorderBrush}\" Height=\"{DynamicResource Size.GroupBox.Border}\" VerticalAlignment=\"Bottom\"/>\n      <Border x:Name=\"ContentHost\" Grid.Row=\"3\" Grid.Column=\"1\" Grid.ColumnSpan=\"2\" Margin=\"-4,0\">\n        <ContentPresenter Margin=\"{TemplateBinding Padding}\"/>\n      </Border>\n      <ContentPresenter x:Name=\"HeaderHost\" Grid.Row=\"0\" Grid.RowSpan=\"3\" Grid.Column=\"1\" ContentSource=\"Header\" Margin=\"4,0\" TextElement.FontSize=\"{DynamicResource Font.Size.Header}\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"Header\" Value=\"{x:Null}\">\n        <Setter Property=\"Margin\" Value=\"0\" TargetName=\"HeaderHost\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"GroupBox\" BasedOn=\"{StaticResource {x:Type HeaderedContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"Padding\" Value=\"4\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.GroupBox}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"Header\" Value=\"{x:Null}\">\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.GroupBox.NoHeader}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- Expander -->\n  <Style x:Key=\"Style.Expander.Toggle\" TargetType=\"ToggleButton\" BasedOn=\"{StaticResource {x:Type ToggleButton}}\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"ToggleButton\">\n          <Grid Background=\"Transparent\">\n            <ContentPresenter />\n          </Grid>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n  </Style>\n  <Geometry x:Key=\"Geometry.Expander.Arrow\">M0,0L6,6 0,12</Geometry>\n  <ControlTemplate x:Key=\"Template.Expander\" TargetType=\"Expander\">\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Loaded\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\"Tag\" Storyboard.TargetName=\"Root\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"1\"/>\n        </ObjectAnimationUsingKeyFrames>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Vertical.Loaded\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ContentBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{x:Static Visibility.Visible}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0\"/>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Vertical.Up.Loaded\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"-90\" Duration=\"0\"/>\n        <StaticResource ResourceKey=\"Anim.Expand.Vertical.Loaded\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Vertical.Down.Loaded\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"90\" Duration=\"0\"/>\n        <StaticResource ResourceKey=\"Anim.Expand.Vertical.Loaded\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Horizontal.Loaded\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"90\" Duration=\"0\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ContentBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{x:Static Visibility.Visible}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0\">\n          <DoubleAnimation.EasingFunction>\n            <BackEase EasingMode=\"EaseOut\" Amplitude=\"0.2\"/>\n          </DoubleAnimation.EasingFunction>\n        </DoubleAnimation>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Vertical\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ContentBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{x:Static Visibility.Visible}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0\"/>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0:0:0.2\">\n          <DoubleAnimation.EasingFunction>\n            <BackEase EasingMode=\"EaseOut\" Amplitude=\"0.2\"/>\n          </DoubleAnimation.EasingFunction>\n        </DoubleAnimation>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Vertical.Up\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"-90\" Duration=\"0:0:0.15\"/>\n        <StaticResource ResourceKey=\"Anim.Expand.Vertical\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Vertical.Down\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"90\" Duration=\"0:0:0.15\"/>\n        <StaticResource ResourceKey=\"Anim.Expand.Vertical\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Collapse.Vertical\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"0\" Duration=\"0:0:0.2\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ContentBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0:0:0.2\" Value=\"{x:Static Visibility.Collapsed}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"0\" Duration=\"0:0:0.2\" AccelerationRatio=\"0.5\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Horizontal\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"90\" Duration=\"0:0:0.15\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ContentBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{x:Static Visibility.Visible}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"1\" Duration=\"0:0:0.2\">\n          <DoubleAnimation.EasingFunction>\n            <BackEase EasingMode=\"EaseOut\" Amplitude=\"0.2\"/>\n          </DoubleAnimation.EasingFunction>\n        </DoubleAnimation>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Collapse.Horizontal\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"0\" Duration=\"0:0:0.2\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ContentBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0:0:0.2\" Value=\"{x:Static Visibility.Collapsed}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ContentBg\" Storyboard.TargetProperty=\"RenderTransform.ScaleX\" To=\"0\" Duration=\"0:0:0.2\" AccelerationRatio=\"0.5\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"Root\" Tag=\"0\">\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"*\"/>\n        <RowDefinition Height=\"Auto\"/>\n      </Grid.RowDefinitions>\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n        <ColumnDefinition Width=\"Auto\"/>\n      </Grid.ColumnDefinitions>\n      <ToggleButton x:Name=\"ExpanderButton\" Style=\"{StaticResource Style.Expander.Toggle}\" IsChecked=\"{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" Foreground=\"{TemplateBinding Foreground}\">\n        <Border x:Name=\"HeaderBg\" Background=\"{DynamicResource Brush.Header.Normal}\" CornerRadius=\"{DynamicResource Corner.Border0.Top}\">\n          <Grid>\n            <Grid.ColumnDefinitions>\n              <ColumnDefinition Width=\"Auto\"/>\n              <ColumnDefinition Width=\"*\"/>\n            </Grid.ColumnDefinitions>\n            <Path x:Name=\"Arrow\" Grid.Column=\"0\" Stroke=\"{DynamicResource Brush.Glyph.Normal}\" Data=\"{StaticResource Geometry.Expander.Arrow}\" Margin=\"14,11.5\" RenderTransformOrigin=\"0.5,0.5\">\n              <Path.RenderTransform>\n                <RotateTransform />\n              </Path.RenderTransform>\n            </Path>\n            <ContentPresenter x:Name=\"HeaderHost\" Grid.Column=\"1\" ContentSource=\"Header\" VerticalAlignment=\"Center\" Margin=\"0,0,14,0\" TextElement.FontSize=\"{DynamicResource Font.Size.Header}\"/>\n          </Grid>\n        </Border>\n      </ToggleButton>\n      <Decorator x:Name=\"ContentBorder\" Grid.Row=\"1\" Grid.Column=\"1\" Visibility=\"Collapsed\">\n        <Border x:Name=\"ContentBg\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border0.Bottom}\" Padding=\"{TemplateBinding Padding}\">\n          <Border.RenderTransform>\n            <ScaleTransform ScaleX=\"0\" ScaleY=\"0\"/>\n          </Border.RenderTransform>\n          <ContentPresenter x:Name=\"ContentHost\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n        </Border>\n      </Decorator>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <EventTrigger RoutedEvent=\"FrameworkElement.Loaded\" SourceName=\"Root\">\n        <BeginStoryboard Storyboard=\"{StaticResource Anim.Loaded}\"/>\n      </EventTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ExpanderButton\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Over}\" TargetName=\"HeaderBg\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\" SourceName=\"ExpanderButton\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Pressed}\" TargetName=\"HeaderBg\"/>\n      </Trigger>\n      <Trigger Property=\"IsExpanded\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Checked}\" TargetName=\"HeaderBg\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsExpanded\" Value=\"True\"/>\n          <Condition Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ExpanderButton\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.CheckedOver}\" TargetName=\"HeaderBg\"/>\n      </MultiTrigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsExpanded\" Value=\"True\"/>\n          <Condition Property=\"IsPressed\" Value=\"True\" SourceName=\"ExpanderButton\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.CheckedPressed}\" TargetName=\"HeaderBg\"/>\n      </MultiTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Up\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Vertical.Up.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Down\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Vertical.Down.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Left\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Horizontal.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Right\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Horizontal.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Up\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Vertical.Up}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Collapse.Vertical}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Down\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Vertical.Down}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Collapse.Vertical}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Left\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Horizontal}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Collapse.Horizontal}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding ExpandDirection, RelativeSource={RelativeSource Self}}\" Value=\"Right\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Horizontal}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Collapse.Horizontal}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEnabled\" Value=\"False\"/>\n          <Condition Property=\"IsExpanded\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.DisabledChecked}\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Foreground.DisabledChecked}\" TargetName=\"Arrow\"/>\n      </MultiTrigger>\n      <Trigger Property=\"ExpandDirection\" Value=\"Down\">\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"Grid.Column\" Value=\"1\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"RenderTransformOrigin\" Value=\"0.5,0\" TargetName=\"ContentBg\"/>\n      </Trigger>\n      <Trigger Property=\"ExpandDirection\" Value=\"Up\">\n        <Setter Property=\"Grid.Row\" Value=\"2\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"Grid.Column\" Value=\"1\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"RenderTransformOrigin\" Value=\"0.5,1\" TargetName=\"ContentBg\"/>\n      </Trigger>\n      <Trigger Property=\"ExpandDirection\" Value=\"Right\">\n        <Setter Property=\"Grid.Row\" Value=\"1\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"RenderTransformOrigin\" Value=\"0,0.5\" TargetName=\"ContentBg\"/>\n        <Setter Property=\"LayoutTransform\" TargetName=\"ExpanderButton\">\n          <Setter.Value>\n            <RotateTransform Angle=\"-90\"/>\n          </Setter.Value>\n        </Setter>\n      </Trigger>\n      <Trigger Property=\"ExpandDirection\" Value=\"Left\">\n        <Setter Property=\"Grid.Row\" Value=\"1\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"Grid.Column\" Value=\"2\" TargetName=\"ExpanderButton\"/>\n        <Setter Property=\"RenderTransformOrigin\" Value=\"1,0.5\" TargetName=\"ContentBg\"/>\n        <Setter Property=\"LayoutTransform\" TargetName=\"ExpanderButton\">\n          <Setter.Value>\n            <RotateTransform Angle=\"90\"/>\n          </Setter.Value>\n        </Setter>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"Expander\" BasedOn=\"{StaticResource {x:Type HeaderedContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"Transparent\"/>\n    <Setter Property=\"Padding\" Value=\"8\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.Expander}\"/>\n  </Style>\n\n  <!-- Hyperlink -->\n  <Style TargetType=\"Hyperlink\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Hyperlink.Normal}\"/>\n    <Setter Property=\"TextDecorations\" Value=\"Underline\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Hyperlink.Over}\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"True\">\n        <Setter Property=\"Cursor\" Value=\"Hand\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- Menu -->\n  <ControlTemplate x:Key=\"Template.Menu\" TargetType=\"Menu\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ItemsPresenter />\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ItemsPanelTemplate x:Key=\"Template.Menu.ItemsPanel\">\n    <StackPanel Orientation=\"Horizontal\"/>\n  </ItemsPanelTemplate>\n  <Style TargetType=\"Menu\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Normal}\"/>\n    <Setter Property=\"ItemsPanel\" Value=\"{StaticResource Template.Menu.ItemsPanel}\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.Menu}\"/>\n  </Style>\n\n  <!-- MenuItem Separator -->\n  <ControlTemplate x:Key=\"Template.MenuItem.Separator\" TargetType=\"Separator\">\n    <Rectangle Height=\"1\" Fill=\"{DynamicResource Brush.Border.Popup}\" Margin=\"32,5,4,5\"/>\n  </ControlTemplate>\n  <Style x:Key=\"Style.MenuItem.Separator\" TargetType=\"Separator\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.Separator}\"/>\n  </Style>\n\n  <!-- MenuItem -->\n  <Geometry x:Key=\"Geometry.MenuItem.Arrow\">M0,0L6,6 0,12</Geometry>\n  <ControlTemplate x:Key=\"Template.MenuItem.TopLevelHeader\" TargetType=\"MenuItem\">\n    <Grid>\n      <Border x:Name=\"Header\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n        <ContentPresenter ContentSource=\"Header\" VerticalAlignment=\"Center\"/>\n      </Border>\n      <Popup IsOpen=\"{TemplateBinding IsSubmenuOpen}\" Focusable=\"False\" Placement=\"Bottom\" PopupAnimation=\"Fade\" AllowsTransparency=\"True\">\n        <Border Background=\"{DynamicResource Brush.Background.Popup}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1\" CornerRadius=\"{DynamicResource Corner.Border1}\" Padding=\"0,6\">\n          <ItemsPresenter />\n        </Border>\n      </Popup>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsHighlighted\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Header\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.MenuItem.TopLevelItem\" TargetType=\"MenuItem\">\n    <Border x:Name=\"Header\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter ContentSource=\"Header\" VerticalAlignment=\"Center\"/>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsHighlighted\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Header\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.MenuItem.SubmenuHeader\" TargetType=\"MenuItem\">\n    <Grid>\n      <Border x:Name=\"Header\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n        <Grid>\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\" SharedSizeGroup=\"Icon\"/>\n            <ColumnDefinition Width=\"*\" SharedSizeGroup=\"Header\"/>\n            <ColumnDefinition Width=\"Auto\" SharedSizeGroup=\"Shortcut\"/>\n            <ColumnDefinition Width=\"Auto\"/>\n          </Grid.ColumnDefinitions>\n          <Decorator Grid.Column=\"0\" MinWidth=\"32\">\n            <ContentPresenter x:Name=\"Icon\" Margin=\"6,0\" VerticalAlignment=\"Center\" ContentSource=\"Icon\"/>\n          </Decorator>\n          <ContentPresenter x:Name=\"HeaderHost\" Grid.Column=\"1\" Margin=\"6,0\" ContentSource=\"Header\" VerticalAlignment=\"Center\"/>\n          <Decorator Grid.Column=\"3\" MinWidth=\"24\">\n            <Path x:Name=\"Arrow\" Data=\"{StaticResource Geometry.MenuItem.Arrow}\" Margin=\"6,0\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Stroke=\"{DynamicResource Brush.Glyph.Normal}\"/>\n          </Decorator>\n        </Grid>\n      </Border>\n      <Popup IsOpen=\"{TemplateBinding IsSubmenuOpen}\" Focusable=\"False\" HorizontalOffset=\"-2\" VerticalOffset=\"-7\" Placement=\"Right\" PopupAnimation=\"Fade\" AllowsTransparency=\"True\">\n        <Border Background=\"{DynamicResource Brush.Background.Popup}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1\" CornerRadius=\"{DynamicResource Corner.Border1}\" Padding=\"0,6\">\n          <ItemsPresenter />\n        </Border>\n      </Popup>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsHighlighted\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Header\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.MenuItem.SubmenuItem\" TargetType=\"MenuItem\">\n    <Border x:Name=\"Header\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <Grid>\n        <Grid.ColumnDefinitions>\n          <ColumnDefinition Width=\"Auto\" SharedSizeGroup=\"Icon\"/>\n          <ColumnDefinition Width=\"*\" SharedSizeGroup=\"Header\"/>\n          <ColumnDefinition Width=\"Auto\" SharedSizeGroup=\"Shortcut\"/>\n          <ColumnDefinition Width=\"Auto\"/>\n        </Grid.ColumnDefinitions>\n        <Decorator Grid.Column=\"0\" MinWidth=\"32\">\n          <Grid>\n            <ContentPresenter x:Name=\"Icon\" Margin=\"6,0\" VerticalAlignment=\"Center\" ContentSource=\"Icon\"/>\n            <Path x:Name=\"Check\" Margin=\"6,0\" Data=\"{StaticResource Geometry.CheckBox.Check}\" Fill=\"{DynamicResource Brush.Glyph.Normal}\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Visibility=\"Hidden\"/>\n          </Grid>\n        </Decorator>\n        <ContentPresenter x:Name=\"HeaderHost\" Grid.Column=\"1\" Margin=\"6,0\" ContentSource=\"Header\" VerticalAlignment=\"Center\"/>\n        <TextBlock x:Name=\"InputGestureText\" Grid.Column=\"2\" Text=\"{TemplateBinding InputGestureText}\" Foreground=\"{DynamicResource Brush.Foreground.Placeholder}\" VerticalAlignment=\"Center\" Margin=\"12,0,0,0\"/>\n        <Decorator Grid.Column=\"3\" MinWidth=\"24\"/>\n      </Grid>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsCheckable\" Value=\"True\">\n        <Setter TargetName=\"Icon\" Property=\"Visibility\" Value=\"Collapsed\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsCheckable\" Value=\"True\"/>\n          <Condition Property=\"IsChecked\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter TargetName=\"Check\" Property=\"Visibility\" Value=\"Visible\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsHighlighted\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Selected}\" TargetName=\"Header\"/>\n        <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Check\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ItemsPanelTemplate x:Key=\"Template.MenuItem.ItemsPanel\">\n    <StackPanel Grid.IsSharedSizeScope=\"True\" KeyboardNavigation.DirectionalNavigation=\"Cycle\"/>\n  </ItemsPanelTemplate>\n  <Style TargetType=\"MenuItem\" BasedOn=\"{StaticResource {x:Type HeaderedItemsControl}}\">\n    <Style.Resources>\n      <Style x:Key=\"{x:Static MenuItem.SeparatorStyleKey}\" TargetType=\"Separator\" BasedOn=\"{StaticResource Style.MenuItem.Separator}\"/>\n    </Style.Resources>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"TopLevelHeader\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Header}\"/>\n        <Setter Property=\"Padding\" Value=\"12,7.5\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.TopLevelHeader}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"TopLevelItem\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Header}\"/>\n        <Setter Property=\"Padding\" Value=\"12,7.5\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.TopLevelItem}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"SubmenuHeader\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Normal}\"/>\n        <Setter Property=\"Padding\" Value=\"0,7\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.SubmenuHeader}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"SubmenuItem\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Normal}\"/>\n        <Setter Property=\"Padding\" Value=\"0,7\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.SubmenuItem}\"/>\n      </Trigger>\n    </Style.Triggers>\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"ItemsPanel\" Value=\"{StaticResource Template.MenuItem.ItemsPanel}\"/>\n  </Style>\n\n  <!-- ContextMenu -->\n  <ControlTemplate x:Key=\"Template.ContextMenu\" TargetType=\"ContextMenu\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\" CornerRadius=\"{DynamicResource Corner.Border1}\">\n      <ItemsPresenter />\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ContextMenu\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Style.Resources>\n      <Style x:Key=\"{x:Static MenuItem.SeparatorStyleKey}\" TargetType=\"Separator\" BasedOn=\"{StaticResource Style.MenuItem.Separator}\"/>\n    </Style.Resources>\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Background.Popup}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Popup}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"1\"/>\n    <Setter Property=\"Padding\" Value=\"0,6\"/>\n    <Setter Property=\"Cursor\" Value=\"Arrow\"/>\n    <Setter Property=\"ForceCursor\" Value=\"True\"/>\n    <Setter Property=\"ItemsPanel\" Value=\"{StaticResource Template.MenuItem.ItemsPanel}\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ContextMenu}\"/>\n  </Style>\n\n  <!-- TextBox -->\n  <Style x:Key=\"Style.TextBox.Placeholder\" TargetType=\"TextBlock\">\n    <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Placeholder}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.DisabledPlaceholder}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n  <ControlTemplate x:Key=\"Template.TextBox\" TargetType=\"TextBox\">\n    <Grid>\n      <Border Background=\"{TemplateBinding Background}\" CornerRadius=\"{DynamicResource Corner.Border0}\"/>\n      <Grid>\n        <Border x:Name=\"Placeholder\" Padding=\"{TemplateBinding Padding}\" Margin=\"2,0\" Visibility=\"Collapsed\">\n          <TextBlock Text=\"{Binding (local:Text.Placeholder), RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{DynamicResource Style.TextBox.Placeholder}\"/>\n        </Border>\n        <ScrollViewer x:Name=\"PART_ContentHost\" Focusable=\"False\" Padding=\"{TemplateBinding Padding}\" Foreground=\"{TemplateBinding Foreground}\"/>\n      </Grid>\n      <Border x:Name=\"Border\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border1}\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"Text\" Value=\"\">\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsKeyboardFocused\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Focused}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Focus}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\"/>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"TextBox\" BasedOn=\"{StaticResource {x:Type Control}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Box}\"/>\n    <Setter Property=\"CaretBrush\" Value=\"{DynamicResource Brush.Foreground.Normal}\"/>\n    <Setter Property=\"SelectionBrush\" Value=\"{DynamicResource Brush.Background.Checked}\"/>\n    <Setter Property=\"SelectionOpacity\" Value=\"0.25\"/>\n    <Setter Property=\"Padding\" Value=\"6,7\"/>\n    <Setter Property=\"KeyboardNavigation.TabNavigation\" Value=\"None\"/>\n    <Setter Property=\"AllowDrop\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.TextBox}\"/>\n    <Setter Property=\"ContextMenu\">\n      <Setter.Value>\n        <ContextMenu>\n          <MenuItem Command=\"ApplicationCommands.Cut\"/>\n          <MenuItem Command=\"ApplicationCommands.Copy\"/>\n          <MenuItem Command=\"ApplicationCommands.Paste\"/>\n        </ContextMenu>\n      </Setter.Value>\n    </Setter>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"True\">\n        <Setter Property=\"Cursor\" Value=\"IBeam\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- PasswordBox -->\n  <ControlTemplate x:Key=\"Template.PasswordBox.Scroll\" TargetType=\"ScrollViewer\">\n    <ScrollContentPresenter Margin=\"{TemplateBinding Padding}\"/>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.PasswordBox\" TargetType=\"PasswordBox\">\n    <Grid>\n      <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border1}\"/>\n      <Grid>\n        <Border x:Name=\"Placeholder\" Padding=\"{TemplateBinding Padding}\" Margin=\"2,0\" Visibility=\"Collapsed\">\n          <TextBlock Text=\"{Binding (local:Text.Placeholder), RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{DynamicResource Style.TextBox.Placeholder}\"/>\n        </Border>\n        <ScrollViewer x:Name=\"PART_ContentHost\" Template=\"{StaticResource Template.PasswordBox.Scroll}\" Focusable=\"False\" Padding=\"{TemplateBinding Padding}\" Foreground=\"{TemplateBinding Foreground}\"/>\n      </Grid>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"local:Text.PasswordLength\" Value=\"0\">\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsKeyboardFocused\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Focused}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Focus}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\"/>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"PasswordBox\" BasedOn=\"{StaticResource {x:Type Control}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Box}\"/>\n    <Setter Property=\"CaretBrush\" Value=\"{DynamicResource Brush.Foreground.Normal}\"/>\n    <Setter Property=\"SelectionBrush\" Value=\"{DynamicResource Brush.Background.Checked}\"/>\n    <Setter Property=\"SelectionOpacity\" Value=\"0.5\"/>\n    <Setter Property=\"Padding\" Value=\"6,7\"/>\n    <Setter Property=\"KeyboardNavigation.TabNavigation\" Value=\"None\"/>\n    <Setter Property=\"PasswordChar\" Value=\"•\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.PasswordBox}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"True\">\n        <Setter Property=\"Cursor\" Value=\"IBeam\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ListBox -->\n  <ControlTemplate x:Key=\"Template.ListBox\" TargetType=\"ListBox\">\n    <ScrollViewer Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\" Focusable=\"False\">\n      <ItemsPresenter />\n    </ScrollViewer>\n  </ControlTemplate>\n  <Style TargetType=\"ListBox\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"True\"/>\n    <Setter Property=\"ScrollViewer.PanningMode\" Value=\"VerticalFirst\"/>\n    <Setter Property=\"VirtualizingPanel.VirtualizationMode\" Value=\"Recycling\"/>\n    <Setter Property=\"VirtualizingPanel.ScrollUnit\" Value=\"Pixel\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ListBox}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ListBoxItem -->\n  <ControlTemplate x:Key=\"Template.ListBoxItem\" TargetType=\"ListBoxItem\">\n    <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsSelected\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Selected}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsMouseOver\" Value=\"True\"/>\n          <Condition Property=\"IsSelected\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.SelectedOver}\" TargetName=\"Border\"/>\n      </MultiTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ListBoxItem\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}\"/>\n    <Setter Property=\"Padding\" Value=\"12,8.5\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ListBoxItem}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Inner.Uniform}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ListView -->\n  <Style x:Key=\"Style.GridView.Scroll\" TargetType=\"ScrollViewer\" BasedOn=\"{StaticResource {x:Type ScrollViewer}}\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"ScrollViewer\">\n          <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border0}\">\n            <Grid>\n              <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\"/>\n                <RowDefinition Height=\"*\"/>\n                <RowDefinition Height=\"Auto\"/>\n              </Grid.RowDefinitions>\n              <Grid.ColumnDefinitions>\n                <ColumnDefinition Width=\"*\"/>\n                <ColumnDefinition Width=\"Auto\"/>\n              </Grid.ColumnDefinitions>\n              <ScrollViewer Grid.Row=\"0\" Grid.ColumnSpan=\"2\" MinHeight=\"0\" HorizontalScrollBarVisibility=\"Hidden\" VerticalScrollBarVisibility=\"Hidden\" PanningMode=\"HorizontalFirst\" Focusable=\"False\">\n                <GridViewHeaderRowPresenter AllowsColumnReorder=\"{Binding Path=TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}\" ColumnHeaderContainerStyle=\"{Binding Path=TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}\" ColumnHeaderContextMenu=\"{Binding Path=TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}\" ColumnHeaderTemplate=\"{Binding Path=TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}\" ColumnHeaderTemplateSelector=\"{Binding Path=TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}\" ColumnHeaderToolTip=\"{Binding Path=TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}\" Columns=\"{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}\"/>\n              </ScrollViewer>\n              <ScrollContentPresenter x:Name=\"PART_ScrollContentPresenter\" Grid.Row=\"1\" Grid.RowSpan=\"2\" Grid.Column=\"0\" Grid.ColumnSpan=\"2\" CanContentScroll=\"{TemplateBinding CanContentScroll}\" Margin=\"{TemplateBinding Padding}\" KeyboardNavigation.DirectionalNavigation=\"Local\"/>\n              <Rectangle x:Name=\"Corner\" Grid.Row=\"2\" Grid.Column=\"1\" Fill=\"{DynamicResource Brush.Track.Normal}\" IsEnabled=\"False\" Opacity=\"0\"/>\n              <ScrollBar x:Name=\"PART_VerticalScrollBar\" Grid.Row=\"1\" Grid.Column=\"1\" Visibility=\"{TemplateBinding ComputedVerticalScrollBarVisibility}\" Orientation=\"Vertical\" ViewportSize=\"{TemplateBinding ViewportHeight}\" Maximum=\"{TemplateBinding ScrollableHeight}\" Minimum=\"0.0\" Value=\"{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}\" Opacity=\"0\"/>\n              <ScrollBar x:Name=\"PART_HorizontalScrollBar\" Grid.Row=\"2\" Grid.Column=\"0\" Visibility=\"{TemplateBinding ComputedHorizontalScrollBarVisibility}\" Orientation=\"Horizontal\" ViewportSize=\"{TemplateBinding ViewportWidth}\" Maximum=\"{TemplateBinding ScrollableWidth}\" Minimum=\"0.0\" Value=\"{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}\" Opacity=\"0\"/>\n            </Grid>\n          </Border>\n          <ControlTemplate.Resources>\n            <Storyboard x:Key=\"Anim.ShowBars\" BeginTime=\"0:0:0.1\">\n              <DoubleAnimation Storyboard.TargetName=\"PART_HorizontalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.5\" DecelerationRatio=\"0.5\"/>\n              <DoubleAnimation Storyboard.TargetName=\"PART_VerticalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.5\" DecelerationRatio=\"0.5\"/>\n            </Storyboard>\n            <Storyboard x:Key=\"Anim.HideBars\" BeginTime=\"0:0:2\">\n              <DoubleAnimation Storyboard.TargetName=\"PART_HorizontalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n              <DoubleAnimation Storyboard.TargetName=\"PART_VerticalScrollBar\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n            </Storyboard>\n            <Storyboard x:Key=\"Anim.ShowCorner\" BeginTime=\"0:0:0.1\">\n              <DoubleAnimation Storyboard.TargetName=\"Corner\" Storyboard.TargetProperty=\"Opacity\" To=\"1\" Duration=\"0:0:0.1\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n            </Storyboard>\n            <Storyboard x:Key=\"Anim.HideCorner\" BeginTime=\"0:0:2\">\n              <DoubleAnimation Storyboard.TargetName=\"Corner\" Storyboard.TargetProperty=\"Opacity\" To=\"0\" Duration=\"0:0:0.15\" AccelerationRatio=\"0.75\" DecelerationRatio=\"0.25\"/>\n            </Storyboard>\n          </ControlTemplate.Resources>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n              <Trigger.EnterActions>\n                <BeginStoryboard Storyboard=\"{StaticResource Anim.ShowBars}\"/>\n              </Trigger.EnterActions>\n              <Trigger.ExitActions>\n                <BeginStoryboard Storyboard=\"{StaticResource Anim.HideBars}\"/>\n              </Trigger.ExitActions>\n            </Trigger>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"PART_HorizontalScrollBar\">\n              <Setter Property=\"IsEnabled\" Value=\"True\" TargetName=\"Corner\"/>\n            </Trigger>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"PART_VerticalScrollBar\">\n              <Setter Property=\"IsEnabled\" Value=\"True\" TargetName=\"Corner\"/>\n            </Trigger>\n            <Trigger Property=\"IsEnabled\" Value=\"True\" SourceName=\"Corner\">\n              <Trigger.EnterActions>\n                <BeginStoryboard Storyboard=\"{StaticResource Anim.ShowCorner}\"/>\n              </Trigger.EnterActions>\n              <Trigger.ExitActions>\n                <BeginStoryboard Storyboard=\"{StaticResource Anim.HideCorner}\"/>\n              </Trigger.ExitActions>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ListView\" TargetType=\"ListView\">\n    <ScrollViewer Style=\"{StaticResource Style.GridView.Scroll}\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\" Focusable=\"False\">\n      <ItemsPresenter />\n    </ScrollViewer>\n  </ControlTemplate>\n  <Style TargetType=\"ListView\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"True\"/>\n    <Setter Property=\"ScrollViewer.PanningMode\" Value=\"VerticalFirst\"/>\n    <Setter Property=\"VirtualizingPanel.VirtualizationMode\" Value=\"Recycling\"/>\n    <Setter Property=\"VirtualizingPanel.ScrollUnit\" Value=\"Pixel\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ListView}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ListViewItem -->\n  <ControlTemplate x:Key=\"Template.ListViewItem\" TargetType=\"ListViewItem\">\n    <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <GridViewRowPresenter />\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsSelected\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Selected}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsMouseOver\" Value=\"True\"/>\n          <Condition Property=\"IsSelected\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.SelectedOver}\" TargetName=\"Border\"/>\n      </MultiTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ListViewItem\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}\"/>\n    <Setter Property=\"Padding\" Value=\"0,8.5\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ListViewItem}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Inner.Uniform}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- GridViewColumnHeader -->\n  <Style x:Key=\"Style.GridViewColumnHeader.Gripper\" TargetType=\"Thumb\">\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Thumb\">\n          <Border Background=\"Transparent\" Margin=\"-12,0\">\n            <Rectangle x:Name=\"Rect\" Fill=\"{TemplateBinding BorderBrush}\" Width=\"1\" Margin=\"12,0\"/>\n          </Border>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n              <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Rect\"/>\n              <Setter Property=\"Width\" Value=\"2\" TargetName=\"Rect\"/>\n              <Setter Property=\"Margin\" Value=\"11.5,0\" TargetName=\"Rect\"/>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n    <Style.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"True\">\n        <Setter Property=\"Cursor\" Value=\"ScrollWE\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n  <ControlTemplate x:Key=\"Template.GridViewColumnHeader.Normal\" TargetType=\"GridViewColumnHeader\">\n    <Grid>\n      <Border x:Name=\"HeaderBorder\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\"/>\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n      <Thumb x:Name=\"PART_HeaderGripper\" Style=\"{StaticResource Style.GridViewColumnHeader.Gripper}\" BorderBrush=\"{TemplateBinding BorderBrush}\" HorizontalAlignment=\"Right\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Over}\" TargetName=\"HeaderBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Pressed}\" TargetName=\"HeaderBorder\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.GridViewColumnHeader.Floating\" TargetType=\"GridViewColumnHeader\">\n    <Canvas x:Name=\"PART_FloatingHeaderCanvas\">\n      <Rectangle Width=\"{TemplateBinding ActualWidth}\" Height=\"{TemplateBinding ActualHeight}\" Fill=\"#7F7F7F7F\"/>\n    </Canvas>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.GridViewColumnHeader.Padding\" TargetType=\"GridViewColumnHeader\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\"/>\n  </ControlTemplate>\n  <Style TargetType=\"GridViewColumnHeader\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Header.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"0,0,0,1\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Padding\" Value=\"6,5\"/>\n    <Style.Triggers>\n      <Trigger Property=\"Role\" Value=\"Normal\">\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.GridViewColumnHeader.Normal}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"Floating\">\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.GridViewColumnHeader.Floating}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"Padding\">\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.GridViewColumnHeader.Padding}\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ComboBox -->\n  <Style x:Key=\"Style.ComboBox.Toggle\" TargetType=\"ToggleButton\" BasedOn=\"{StaticResource Style.Expander.Toggle}\"/>\n  <ControlTemplate x:Key=\"Template.ComboBox.TextScroll\" TargetType=\"ScrollViewer\">\n    <Border Background=\"Transparent\" Padding=\"{TemplateBinding Padding}\">\n      <ScrollContentPresenter />\n    </Border>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.ComboBox.Text\" TargetType=\"TextBox\">\n    <ScrollViewer x:Name=\"PART_ContentHost\" Template=\"{StaticResource Template.ComboBox.TextScroll}\" Padding=\"{TemplateBinding Padding}\" Foreground=\"{TemplateBinding Foreground}\" Focusable=\"False\" HorizontalScrollBarVisibility=\"Hidden\" VerticalScrollBarVisibility=\"Disabled\"/>\n  </ControlTemplate>\n  <Geometry x:Key=\"Geometry.ComboBox.Arrow\">M0,0L6,6 12,0</Geometry>\n  <ControlTemplate x:Key=\"Template.ComboBox\" TargetType=\"ComboBox\">\n    <Grid>\n      <ToggleButton x:Name=\"DropDownButton\" Style=\"{StaticResource Style.ComboBox.Toggle}\" Foreground=\"{TemplateBinding Foreground}\" IsChecked=\"{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" IsTabStop=\"False\">\n        <Grid>\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\"/>\n            <ColumnDefinition Width=\"Auto\"/>\n          </Grid.ColumnDefinitions>\n          <Border x:Name=\"Border\" Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border1}\"/>\n          <Grid Grid.Column=\"0\" Margin=\"2,0\">\n            <ContentPresenter x:Name=\"SelectionBoxItemHost\" ContentSource=\"SelectionBoxItem\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"{TemplateBinding Padding}\"/>\n            <TextBlock x:Name=\"Placeholder\" Text=\"{Binding (local:Text.Placeholder), RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{DynamicResource Style.TextBox.Placeholder}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"{TemplateBinding Padding}\" Visibility=\"Collapsed\"/>\n          </Grid>\n          <Path x:Name=\"Arrow\" Grid.Column=\"1\" Data=\"{StaticResource Geometry.ComboBox.Arrow}\" Stroke=\"{DynamicResource Brush.Glyph.Normal}\" VerticalAlignment=\"Center\" Margin=\"10,1,10,0\" IsHitTestVisible=\"False\">\n            <Path.RenderTransform>\n              <TranslateTransform />\n            </Path.RenderTransform>\n          </Path>\n        </Grid>\n      </ToggleButton>\n      <Popup x:Name=\"PART_Popup\" IsOpen=\"{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}\" Focusable=\"False\" Placement=\"Bottom\" PopupAnimation=\"Slide\" AllowsTransparency=\"True\">\n        <Border x:Name=\"DropDownBorder\" Background=\"{DynamicResource Brush.Background.Popup}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1,0,1,1\" CornerRadius=\"{DynamicResource Corner.Border1.Bottom}\" MinWidth=\"{TemplateBinding ActualWidth}\" MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\n          <ScrollViewer Focusable=\"False\" Padding=\"0,6\" KeyboardNavigation.TabNavigation=\"Cycle\" KeyboardNavigation.DirectionalNavigation=\"Contained\">\n            <ItemsPresenter />\n          </ScrollViewer>\n        </Border>\n      </Popup>\n    </Grid>\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Expand\">\n        <DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Y\">\n          <EasingDoubleKeyFrame KeyTime=\"0:0:0.1\" Value=\"2\"/>\n          <EasingDoubleKeyFrame KeyTime=\"0:0:0.3\" Value=\"0\">\n            <EasingDoubleKeyFrame.EasingFunction>\n              <CubicEase EasingMode=\"EaseOut\"/>\n            </EasingDoubleKeyFrame.EasingFunction>\n          </EasingDoubleKeyFrame>\n        </DoubleAnimationUsingKeyFrames>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"SelectedIndex\" Value=\"-1\">\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\" SourceName=\"DropDownButton\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Pressed}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsDropDownOpen\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand}\"/>\n        </Trigger.EnterActions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Pressed}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2.Top}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Arrow\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.ComboBox.Editable\" TargetType=\"ComboBox\">\n    <Grid>\n      <ToggleButton x:Name=\"DropDownButton\" Style=\"{StaticResource Style.ComboBox.Toggle}\" Foreground=\"{TemplateBinding Foreground}\" IsChecked=\"{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" IsTabStop=\"False\">\n        <Grid>\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\"/>\n            <ColumnDefinition Width=\"Auto\"/>\n          </Grid.ColumnDefinitions>\n          <Border x:Name=\"Base\" Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding Background}\" CornerRadius=\"{DynamicResource Corner.Border0}\"/>\n          <Border x:Name=\"ArrowBorder\" Grid.Column=\"1\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1,0,0,0\" CornerRadius=\"{DynamicResource Corner.Border0.Right}\"/>\n          <Border x:Name=\"Border\" Grid.ColumnSpan=\"2\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border1}\" IsHitTestVisible=\"False\"/>\n          <TextBox x:Name=\"PART_EditableTextBox\" Template=\"{StaticResource Template.ComboBox.Text}\" Foreground=\"{TemplateBinding Foreground}\" IsReadOnly=\"{TemplateBinding IsReadOnly}\" Padding=\"{TemplateBinding Padding}\" CaretBrush=\"{TemplateBinding Foreground}\" SelectionBrush=\"{DynamicResource Brush.Background.Checked}\" SelectionOpacity=\"0.5\" FocusVisualStyle=\"{x:Null}\"/>\n          <Decorator Grid.Column=\"0\" Margin=\"2,0\">\n            <TextBlock x:Name=\"Placeholder\" Text=\"{Binding (local:Text.Placeholder), RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{DynamicResource Style.TextBox.Placeholder}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"{TemplateBinding Padding}\" Visibility=\"Collapsed\"/>\n          </Decorator>\n          <Path x:Name=\"Arrow\" Grid.Column=\"1\" Data=\"{StaticResource Geometry.ComboBox.Arrow}\" Stroke=\"{DynamicResource Brush.Glyph.Normal}\" VerticalAlignment=\"Center\" Margin=\"10,1,10,0\" IsHitTestVisible=\"False\">\n            <Path.RenderTransform>\n              <TranslateTransform />\n            </Path.RenderTransform>\n          </Path>\n        </Grid>\n      </ToggleButton>\n      <Popup x:Name=\"PART_Popup\" IsOpen=\"{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}\" Focusable=\"False\" Placement=\"Bottom\" PopupAnimation=\"Slide\" AllowsTransparency=\"True\">\n        <Border x:Name=\"DropDownBorder\" Background=\"{DynamicResource Brush.Background.Popup}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1,0,1,1\" CornerRadius=\"{DynamicResource Corner.Border1.Bottom}\" MinWidth=\"{TemplateBinding ActualWidth}\" MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\n          <ScrollViewer Focusable=\"False\" Padding=\"0,6\" KeyboardNavigation.TabNavigation=\"Cycle\" KeyboardNavigation.DirectionalNavigation=\"Contained\">\n            <ItemsPresenter />\n          </ScrollViewer>\n        </Border>\n      </Popup>\n    </Grid>\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Expand\">\n        <DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Y\">\n          <EasingDoubleKeyFrame KeyTime=\"0:0:0.1\" Value=\"2\"/>\n          <EasingDoubleKeyFrame KeyTime=\"0:0:0.3\" Value=\"0\">\n            <EasingDoubleKeyFrame.EasingFunction>\n              <CubicEase EasingMode=\"EaseOut\"/>\n            </EasingDoubleKeyFrame.EasingFunction>\n          </EasingDoubleKeyFrame>\n        </DoubleAnimationUsingKeyFrames>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"Text\" Value=\"\">\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ArrowBorder\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Over}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\" SourceName=\"DropDownButton\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Pressed}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsDropDownOpen\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand}\"/>\n        </Trigger.EnterActions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Focused}\" TargetName=\"Base\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2.Top}\" TargetName=\"Border\"/>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Pressed}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsKeyboardFocused\" Value=\"True\" SourceName=\"PART_EditableTextBox\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Edit}\" TargetName=\"Base\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Arrow\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ComboBox\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"1\"/>\n    <Setter Property=\"Padding\" Value=\"6,7\"/>\n    <Setter Property=\"MaxDropDownHeight\" Value=\"200\"/>\n    <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"False\"/>\n    <Setter Property=\"ScrollViewer.PanningMode\" Value=\"VerticalFirst\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ComboBox}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n    <Style.Triggers>\n      <Trigger Property=\"IsEditable\" Value=\"True\">\n        <Setter Property=\"IsTabStop\" Value=\"False\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.ComboBox.Editable}\"/>\n        <Setter Property=\"FocusVisualStyle\" Value=\"{x:Null}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n\n  <!-- ComboBoxItem -->\n  <ControlTemplate x:Key=\"Template.ComboBoxItem\" TargetType=\"ComboBoxItem\">\n    <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsSelected\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Selected}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsMouseOver\" Value=\"True\"/>\n          <Condition Property=\"IsSelected\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.SelectedOver}\" TargetName=\"Border\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ComboBoxItem\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}\"/>\n    <Setter Property=\"Padding\" Value=\"12,8.5\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ComboBoxItem}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Inner.Uniform}\"/>\n  </Style>\n\n  <!-- TabControl -->\n  <ControlTemplate x:Key=\"Template.TabControl\" TargetType=\"TabControl\">\n    <Grid KeyboardNavigation.TabNavigation=\"Local\">\n      <Grid.RowDefinitions>\n        <RowDefinition x:Name=\"Row0\"/>\n        <RowDefinition x:Name=\"Row1\"/>\n      </Grid.RowDefinitions>\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition x:Name=\"Col0\"/>\n        <ColumnDefinition x:Name=\"Col1\"/>\n      </Grid.ColumnDefinitions>\n      <TabPanel x:Name=\"HeaderPanel\" Panel.ZIndex=\"1\" Background=\"Transparent\" IsItemsHost=\"True\" KeyboardNavigation.TabIndex=\"1\"/>\n      <Border x:Name=\"SelectedContentBorder\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\" KeyboardNavigation.DirectionalNavigation=\"Contained\" KeyboardNavigation.TabIndex=\"2\" KeyboardNavigation.TabNavigation=\"Local\">\n        <ContentPresenter x:Name=\"PART_SelectedContentHost\" ContentSource=\"SelectedContent\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n      </Border>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"TabStripPlacement\" Value=\"Top\">\n        <Setter Property=\"Height\" Value=\"Auto\" TargetName=\"Row0\"/>\n        <Setter Property=\"Height\" Value=\"*\" TargetName=\"Row1\"/>\n        <Setter Property=\"Width\" Value=\"*\" TargetName=\"Col0\"/>\n        <Setter Property=\"Width\" Value=\"0\" TargetName=\"Col1\"/>\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Row\" Value=\"1\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0.Bottom}\" TargetName=\"SelectedContentBorder\"/>\n      </Trigger>\n      <Trigger Property=\"TabStripPlacement\" Value=\"Bottom\">\n        <Setter Property=\"Height\" Value=\"*\" TargetName=\"Row0\"/>\n        <Setter Property=\"Height\" Value=\"Auto\" TargetName=\"Row1\"/>\n        <Setter Property=\"Width\" Value=\"*\" TargetName=\"Col0\"/>\n        <Setter Property=\"Width\" Value=\"0\" TargetName=\"Col1\"/>\n        <Setter Property=\"Grid.Row\" Value=\"1\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0.Top}\" TargetName=\"SelectedContentBorder\"/>\n      </Trigger>\n      <Trigger Property=\"TabStripPlacement\" Value=\"Left\">\n        <Setter Property=\"Height\" Value=\"*\" TargetName=\"Row0\"/>\n        <Setter Property=\"Height\" Value=\"0\" TargetName=\"Row1\"/>\n        <Setter Property=\"Width\" Value=\"Auto\" TargetName=\"Col0\"/>\n        <Setter Property=\"Width\" Value=\"*\" TargetName=\"Col1\"/>\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"Grid.Column\" Value=\"1\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0.Right}\" TargetName=\"SelectedContentBorder\"/>\n      </Trigger>\n      <Trigger Property=\"TabStripPlacement\" Value=\"Right\">\n        <Setter Property=\"Height\" Value=\"*\" TargetName=\"Row0\"/>\n        <Setter Property=\"Height\" Value=\"0\" TargetName=\"Row1\"/>\n        <Setter Property=\"Width\" Value=\"*\" TargetName=\"Col0\"/>\n        <Setter Property=\"Width\" Value=\"Auto\" TargetName=\"Col1\"/>\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Column\" Value=\"1\" TargetName=\"HeaderPanel\"/>\n        <Setter Property=\"Grid.Row\" Value=\"0\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"Grid.Column\" Value=\"0\" TargetName=\"SelectedContentBorder\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border0.Left}\" TargetName=\"SelectedContentBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"TabControl\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TabControl.Normal}\"/>\n    <Setter Property=\"Padding\" Value=\"12\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.TabControl}\"/>\n  </Style>\n\n  <!-- TabItem -->\n  <ControlTemplate x:Key=\"Template.TabItem\" TargetType=\"TabItem\">\n    <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding Background}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter x:Name=\"ContentHost\" ContentSource=\"Header\" TextElement.FontSize=\"{DynamicResource Font.Size.Header}\" TextElement.Foreground=\"{DynamicResource Brush.Glyph.Normal}\"/>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Panel.ZIndex\" Value=\"1\"/>\n        <Setter Property=\"TextElement.Foreground\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"ContentHost\"/>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TabControl.Over}\"/>\n      </Trigger>\n      <Trigger Property=\"IsSelected\" Value=\"True\">\n        <Setter Property=\"Panel.ZIndex\" Value=\"2\"/>\n        <Setter Property=\"TextElement.Foreground\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"ContentHost\"/>\n        <Setter Property=\"Background\" Value=\"{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.TabControl.Selected}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"TextElement.Foreground\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"ContentHost\"/>\n      </Trigger>\n      <DataTrigger Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Top\">\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2.Top}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0,2,0,0\" TargetName=\"Border\"/>\n        <Setter Property=\"Margin\" Value=\"0,-2,0,0\" TargetName=\"ContentHost\"/>\n      </DataTrigger>\n      <DataTrigger Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Bottom\">\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2.Bottom}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0,0,0,2\" TargetName=\"Border\"/>\n        <Setter Property=\"Margin\" Value=\"0,0,0,-2\" TargetName=\"ContentHost\"/>\n      </DataTrigger>\n      <DataTrigger Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Left\">\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2.Left}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"2,0,0,0\" TargetName=\"Border\"/>\n        <Setter Property=\"Margin\" Value=\"-2,0,0,0\" TargetName=\"ContentHost\"/>\n      </DataTrigger>\n      <DataTrigger Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Right\">\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2.Right}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0,0,2,0\" TargetName=\"Border\"/>\n        <Setter Property=\"Margin\" Value=\"0,0,-2,0\" TargetName=\"ContentHost\"/>\n      </DataTrigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"TabItem\" BasedOn=\"{StaticResource {x:Type HeaderedContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Foreground\" Value=\"{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"Padding\" Value=\"12,7.5\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.TabItem}\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Inner.Uniform}\"/>\n  </Style>\n\n  <!-- TreeView -->\n  <ControlTemplate x:Key=\"Template.TreeView\" TargetType=\"TreeView\">\n    <ScrollViewer Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\" Focusable=\"False\">\n      <ItemsPresenter Tag=\"12\"/>\n    </ScrollViewer>\n  </ControlTemplate>\n  <Style TargetType=\"TreeView\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\"/>\n    <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n    <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"False\"/>\n    <Setter Property=\"ScrollViewer.PanningMode\" Value=\"VerticalFirst\"/>\n    <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource Style.Focus.Outer.Uniform}\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.TreeView}\"/>\n  </Style>\n\n  <!-- TreeViewItem -->\n  <Style x:Key=\"Style.TreeViewItem.Toggle\" TargetType=\"ToggleButton\" BasedOn=\"{StaticResource Style.Expander.Toggle}\"/>\n  <Geometry x:Key=\"Geometry.TreeViewItem.Arrow\">M0,0L6,6 0,12</Geometry>\n  <ControlTemplate x:Key=\"Template.TreeViewItem.NoAnim\" TargetType=\"TreeViewItem\">\n    <Grid>\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"*\"/>\n      </Grid.RowDefinitions>\n      <Border x:Name=\"Border\" Grid.Row=\"0\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\">\n        <Grid>\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"/>\n            <ColumnDefinition Width=\"Auto\"/>\n            <ColumnDefinition Width=\"*\"/>\n          </Grid.ColumnDefinitions>\n          <Border x:Name=\"Focus\" Grid.ColumnSpan=\"3\" BorderThickness=\"{DynamicResource Border.Focus}\" BorderBrush=\"{DynamicResource Brush.Border.Focus}\" CornerRadius=\"{DynamicResource Corner.Focus}\" Visibility=\"Collapsed\"/>\n          <Decorator x:Name=\"CurrentIndent\" Grid.Column=\"0\" Width=\"{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type ItemsPresenter}, AncestorLevel=1}}\"/>\n          <Decorator x:Name=\"ChildrenIndent\" Grid.Column=\"0\" Grid.ColumnSpan=\"2\"/>\n          <ToggleButton x:Name=\"ExpandButton\" Grid.Column=\"1\" Style=\"{StaticResource Style.TreeViewItem.Toggle}\" IsChecked=\"{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" IsTabStop=\"False\" Width=\"32\" Margin=\"-8,0\">\n            <Path x:Name=\"Arrow\" Data=\"{StaticResource Geometry.TreeViewItem.Arrow}\" Stroke=\"{DynamicResource Brush.TreeView.Collapsed}\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" IsHitTestVisible=\"False\" RenderTransformOrigin=\"0.5,0.5\"/>\n          </ToggleButton>\n          <ContentPresenter x:Name=\"PART_Header\" Grid.Column=\"2\" ContentSource=\"Header\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n        </Grid>\n      </Border>\n      <Decorator x:Name=\"ItemsBorder\" Grid.Row=\"1\" Grid.Column=\"0\" Grid.ColumnSpan=\"3\" Visibility=\"Collapsed\">\n        <ItemsPresenter x:Name=\"ItemsHost\" Tag=\"{Binding ActualWidth, ElementName=ChildrenIndent}\"/>\n      </Decorator>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"HasItems\" Value=\"False\">\n        <Setter Property=\"Visibility\" Value=\"Hidden\" TargetName=\"ExpandButton\"/>\n      </Trigger>\n      <Trigger Property=\"IsExpanded\" Value=\"True\">\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.TreeView.Expanded}\" TargetName=\"Arrow\"/>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"ItemsBorder\"/>\n        <Setter Property=\"RenderTransform\" TargetName=\"Arrow\">\n          <Setter.Value>\n            <RotateTransform Angle=\"90\"/>\n          </Setter.Value>\n        </Setter>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"Border\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ExpandButton\">\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.TreeView.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ExpandButton\"/>\n          <Condition Property=\"IsSelected\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.TreeView.SelectedOver}\" TargetName=\"Arrow\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsSelected\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Selected}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Path=(local:Element.IsFocusEngaged), RelativeSource={RelativeSource AncestorType={x:Type TreeView}}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding Path=IsKeyboardFocused, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Focus\"/>\n      </MultiDataTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.TreeViewItem\" TargetType=\"TreeViewItem\">\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Loaded\">\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Root\" Storyboard.TargetProperty=\"Tag\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"1\"/>\n        </ObjectAnimationUsingKeyFrames>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand.Loaded\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"90\" Duration=\"0\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ItemsBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{x:Static Visibility.Visible}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ItemsHost\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0\"/>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Expand\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"90\" Duration=\"0:0:0.15\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ItemsBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{x:Static Visibility.Visible}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ItemsHost\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"1\" Duration=\"0:0:0.2\">\n          <DoubleAnimation.EasingFunction>\n            <BackEase EasingMode=\"EaseOut\" Amplitude=\"0.2\"/>\n          </DoubleAnimation.EasingFunction>\n        </DoubleAnimation>\n      </Storyboard>\n      <Storyboard x:Key=\"Anim.Collapse\">\n        <DoubleAnimation Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Angle\" To=\"0\" Duration=\"0:0:0.2\"/>\n        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"ItemsBorder\" Storyboard.TargetProperty=\"Visibility\">\n          <DiscreteObjectKeyFrame KeyTime=\"0:0:0.2\" Value=\"{x:Static Visibility.Collapsed}\"/>\n        </ObjectAnimationUsingKeyFrames>\n        <DoubleAnimation Storyboard.TargetName=\"ItemsHost\" Storyboard.TargetProperty=\"RenderTransform.ScaleY\" To=\"0\" Duration=\"0:0:0.2\" AccelerationRatio=\"0.5\"/>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <Grid x:Name=\"Root\" Tag=\"0\">\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"/>\n        <RowDefinition Height=\"*\"/>\n      </Grid.RowDefinitions>\n      <Border x:Name=\"Border\" Grid.Row=\"0\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\">\n        <Grid>\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"/>\n            <ColumnDefinition Width=\"Auto\"/>\n            <ColumnDefinition Width=\"*\"/>\n          </Grid.ColumnDefinitions>\n          <Border x:Name=\"Focus\" Grid.ColumnSpan=\"3\" BorderThickness=\"{DynamicResource Border.Focus}\" BorderBrush=\"{DynamicResource Brush.Border.Focus}\" CornerRadius=\"{DynamicResource Corner.Focus}\" Visibility=\"Collapsed\"/>\n          <Decorator x:Name=\"CurrentIndent\" Grid.Column=\"0\" Width=\"{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type ItemsPresenter}, AncestorLevel=1}}\"/>\n          <Decorator x:Name=\"ChildrenIndent\" Grid.Column=\"0\" Grid.ColumnSpan=\"2\"/>\n          <ToggleButton x:Name=\"ExpandButton\" Grid.Column=\"1\" Style=\"{StaticResource Style.TreeViewItem.Toggle}\" IsChecked=\"{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" IsTabStop=\"False\" Width=\"32\" Margin=\"-8,0\">\n            <Path x:Name=\"Arrow\" Data=\"{StaticResource Geometry.TreeViewItem.Arrow}\" Stroke=\"{DynamicResource Brush.TreeView.Collapsed}\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" IsHitTestVisible=\"False\" RenderTransformOrigin=\"0.5,0.5\">\n              <Path.RenderTransform>\n                <RotateTransform />\n              </Path.RenderTransform>\n            </Path>\n          </ToggleButton>\n          <ContentPresenter x:Name=\"PART_Header\" Grid.Column=\"2\" ContentSource=\"Header\" HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n        </Grid>\n      </Border>\n      <Decorator x:Name=\"ItemsBorder\" Grid.Row=\"1\" Grid.Column=\"0\" Grid.ColumnSpan=\"3\" Visibility=\"Collapsed\">\n        <ItemsPresenter x:Name=\"ItemsHost\" Tag=\"{Binding ActualWidth, ElementName=ChildrenIndent}\" RenderTransformOrigin=\"0.5,0\">\n          <ItemsPresenter.RenderTransform>\n            <ScaleTransform ScaleY=\"0\"/>\n          </ItemsPresenter.RenderTransform>\n        </ItemsPresenter>\n      </Decorator>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <EventTrigger RoutedEvent=\"FrameworkElement.Loaded\" SourceName=\"Root\">\n        <BeginStoryboard Storyboard=\"{StaticResource Anim.Loaded}\"/>\n      </EventTrigger>\n      <Trigger Property=\"HasItems\" Value=\"False\">\n        <Setter Property=\"Visibility\" Value=\"Hidden\" TargetName=\"ExpandButton\"/>\n      </Trigger>\n      <Trigger Property=\"IsExpanded\" Value=\"True\">\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.TreeView.Expanded}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"0\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand.Loaded}\"/>\n        </MultiDataTrigger.EnterActions>\n      </MultiDataTrigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Tag, ElementName=Root}\" Value=\"1\"/>\n          <Condition Binding=\"{Binding IsExpanded, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <MultiDataTrigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand}\"/>\n        </MultiDataTrigger.EnterActions>\n        <MultiDataTrigger.ExitActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Collapse}\"/>\n        </MultiDataTrigger.ExitActions>\n      </MultiDataTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"Border\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ExpandButton\">\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.TreeView.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ExpandButton\"/>\n          <Condition Property=\"IsSelected\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.TreeView.SelectedOver}\" TargetName=\"Arrow\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsSelected\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.Item.Selected}\" TargetName=\"Border\"/>\n      </Trigger>\n      <MultiDataTrigger>\n        <MultiDataTrigger.Conditions>\n          <Condition Binding=\"{Binding Path=(local:Element.IsFocusEngaged), RelativeSource={RelativeSource AncestorType={x:Type TreeView}}}\" Value=\"True\"/>\n          <Condition Binding=\"{Binding Path=IsKeyboardFocused, RelativeSource={RelativeSource Self}}\" Value=\"True\"/>\n        </MultiDataTrigger.Conditions>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Focus\"/>\n      </MultiDataTrigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"TreeViewItem\" BasedOn=\"{StaticResource {x:Type HeaderedItemsControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Foreground\" Value=\"{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type TreeView}}}\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"Padding\" Value=\"12,7\"/>\n    <Setter Property=\"HorizontalContentAlignment\" Value=\"{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type TreeView}}}\"/>\n    <Setter Property=\"VerticalContentAlignment\" Value=\"{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type TreeView}}}\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.TreeViewItem}\"/>\n  </Style>\n\n  <!-- ToolBar Controls -->\n  <ControlTemplate x:Key=\"Template.ToolBar.Separator\" TargetType=\"Separator\">\n    <Rectangle Width=\"1\" Fill=\"{DynamicResource Brush.Border.Normal}\" Margin=\"5,2\"/>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ToolBar.Separator\" TargetType=\"Separator\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolBar.Separator}\"/>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ToolBar.Button\" TargetType=\"Button\">\n    <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\" CornerRadius=\"{DynamicResource Corner.Border0}\">\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Pressed}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ToolBar.Button\" TargetType=\"Button\" BasedOn=\"{StaticResource {x:Type Button}}\">\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"BorderThickness\" Value=\"0\"/>\n    <Setter Property=\"Padding\" Value=\"5\"/>\n    <Setter Property=\"Margin\" Value=\"1\"/>\n    <Setter Property=\"MinWidth\" Value=\"28\"/>\n    <Setter Property=\"HorizontalAlignment\" Value=\"Center\"/>\n    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolBar.Button}\"/>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ToolBar.Toggle\" TargetType=\"ToggleButton\">\n    <Grid>\n      <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border0}\"/>\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\" Margin=\"{TemplateBinding Padding}\"/>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Over}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Pressed}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsChecked\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.ToolBar.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"BorderThickness\" Value=\"1\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border2}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ToolBar.Toggle\" TargetType=\"ToggleButton\" BasedOn=\"{StaticResource {x:Type ToggleButton}}\">\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"BorderThickness\" Value=\"0\"/>\n    <Setter Property=\"Padding\" Value=\"5\"/>\n    <Setter Property=\"Margin\" Value=\"1\"/>\n    <Setter Property=\"MinWidth\" Value=\"28\"/>\n    <Setter Property=\"HorizontalAlignment\" Value=\"Center\"/>\n    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolBar.Toggle}\"/>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ToolBar.ComboBox\" TargetType=\"ComboBox\">\n    <Grid>\n      <ToggleButton x:Name=\"DropDownButton\" Style=\"{StaticResource Style.ComboBox.Toggle}\" Foreground=\"{TemplateBinding Foreground}\" IsChecked=\"{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\" IsTabStop=\"False\">\n        <Grid>\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\"/>\n            <ColumnDefinition Width=\"Auto\"/>\n          </Grid.ColumnDefinitions>\n          <Border x:Name=\"Base\" Grid.ColumnSpan=\"2\" Background=\"{TemplateBinding Background}\" CornerRadius=\"{DynamicResource Corner.Border0}\"/>\n          <Border x:Name=\"ArrowBorder\" Grid.Column=\"1\" Background=\"{DynamicResource Brush.ComboBox.Normal}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1,0,0,0\" CornerRadius=\"{DynamicResource Corner.Border0.Right}\"/>\n          <Border x:Name=\"Border\" Grid.ColumnSpan=\"2\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border1}\" IsHitTestVisible=\"False\"/>\n          <Grid Grid.Column=\"0\" Margin=\"2,0\">\n            <ContentPresenter x:Name=\"SelectionBoxItemHost\" ContentSource=\"SelectionBoxItem\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"{TemplateBinding Padding}\"/>\n            <TextBlock x:Name=\"Placeholder\" Text=\"{Binding (local:Text.Placeholder), RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{DynamicResource Style.TextBox.Placeholder}\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"{TemplateBinding Padding}\" Visibility=\"Collapsed\"/>\n          </Grid>\n          <TextBox x:Name=\"PART_EditableTextBox\" Template=\"{StaticResource Template.ComboBox.Text}\" Foreground=\"{TemplateBinding Foreground}\" IsReadOnly=\"{TemplateBinding IsReadOnly}\" Padding=\"{TemplateBinding Padding}\" CaretBrush=\"{TemplateBinding Foreground}\" SelectionBrush=\"{DynamicResource Brush.Background.Checked}\" SelectionOpacity=\"0.5\" Visibility=\"Hidden\"/>\n          <Path x:Name=\"Arrow\" Grid.Column=\"1\" Data=\"{StaticResource Geometry.ComboBox.Arrow}\" Stroke=\"{DynamicResource Brush.Glyph.Normal}\" VerticalAlignment=\"Center\" Margin=\"7.5,1,7.5,0\" IsHitTestVisible=\"False\">\n            <Path.RenderTransform>\n              <TranslateTransform />\n            </Path.RenderTransform>\n          </Path>\n        </Grid>\n      </ToggleButton>\n      <Popup x:Name=\"PART_Popup\" IsOpen=\"{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}\" Focusable=\"False\" Placement=\"Bottom\" PopupAnimation=\"Slide\" AllowsTransparency=\"True\">\n        <Border x:Name=\"DropDownBorder\" Background=\"{DynamicResource Brush.Background.Popup}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1,0,1,1\" CornerRadius=\"{DynamicResource Corner.Border1.Bottom}\" MinWidth=\"{TemplateBinding ActualWidth}\" MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\n          <ScrollViewer Focusable=\"False\" Padding=\"0,6\" KeyboardNavigation.TabNavigation=\"Cycle\" KeyboardNavigation.DirectionalNavigation=\"Contained\">\n            <ItemsPresenter />\n          </ScrollViewer>\n        </Border>\n      </Popup>\n    </Grid>\n    <ControlTemplate.Resources>\n      <Storyboard x:Key=\"Anim.Expand\">\n        <DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"Arrow\" Storyboard.TargetProperty=\"RenderTransform.Y\">\n          <EasingDoubleKeyFrame KeyTime=\"0:0:0.1\" Value=\"2\"/>\n          <EasingDoubleKeyFrame KeyTime=\"0:0:0.3\" Value=\"0\">\n            <EasingDoubleKeyFrame.EasingFunction>\n              <CubicEase EasingMode=\"EaseOut\"/>\n            </EasingDoubleKeyFrame.EasingFunction>\n          </EasingDoubleKeyFrame>\n        </DoubleAnimationUsingKeyFrames>\n      </Storyboard>\n    </ControlTemplate.Resources>\n    <ControlTemplate.Triggers>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEditable\" Value=\"False\"/>\n          <Condition Property=\"SelectedIndex\" Value=\"-1\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </MultiTrigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEditable\" Value=\"True\"/>\n          <Condition Property=\"Text\" Value=\"\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"Border\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Arrow\"/>\n      </Trigger>\n      <MultiTrigger>\n        <MultiTrigger.Conditions>\n          <Condition Property=\"IsEditable\" Value=\"False\"/>\n          <Condition Property=\"IsMouseOver\" Value=\"True\"/>\n        </MultiTrigger.Conditions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Over}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </MultiTrigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\" SourceName=\"ArrowBorder\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Over}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsPressed\" Value=\"True\" SourceName=\"DropDownButton\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Pressed}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsDropDownOpen\" Value=\"True\">\n        <Trigger.EnterActions>\n          <BeginStoryboard Storyboard=\"{StaticResource Anim.Expand}\"/>\n        </Trigger.EnterActions>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Focused}\" TargetName=\"Base\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n        <Setter Property=\"CornerRadius\" Value=\"{DynamicResource Corner.Border1.Top}\" TargetName=\"Border\"/>\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Pressed}\" TargetName=\"ArrowBorder\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"ArrowBorder\"/>\n      </Trigger>\n      <Trigger Property=\"IsEditable\" Value=\"True\">\n        <Setter Property=\"Visibility\" Value=\"Hidden\" TargetName=\"SelectionBoxItemHost\"/>\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"PART_EditableTextBox\"/>\n      </Trigger>\n      <Trigger Property=\"IsKeyboardFocused\" Value=\"True\" SourceName=\"PART_EditableTextBox\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Edit}\" TargetName=\"Base\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\" TargetName=\"Border\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\" TargetName=\"Border\"/>\n        <Setter Property=\"Stroke\" Value=\"{DynamicResource Brush.Glyph.Disabled}\" TargetName=\"Arrow\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ToolBar.ComboBox\" TargetType=\"ComboBox\" BasedOn=\"{StaticResource {x:Type ComboBox}}\">\n    <Style.Resources>\n      <Style TargetType=\"ComboBoxItem\" BasedOn=\"{StaticResource {x:Type ComboBoxItem}}\">\n        <Setter Property=\"Padding\" Value=\"10,7\"/>\n      </Style>\n    </Style.Resources>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ComboBox.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"1\"/>\n    <Setter Property=\"Padding\" Value=\"5,4.5\"/>\n    <Setter Property=\"Margin\" Value=\"1\"/>\n    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolBar.ComboBox}\"/>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ToolBar.TextBox.Scroll\" TargetType=\"ScrollViewer\">\n    <ScrollContentPresenter Margin=\"{TemplateBinding Padding}\"/>\n  </ControlTemplate>\n  <ControlTemplate x:Key=\"Template.ToolBar.TextBox\" TargetType=\"TextBox\">\n    <Grid>\n      <Border x:Name=\"Border\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" CornerRadius=\"{DynamicResource Corner.Border1}\"/>\n      <Grid>\n        <Border x:Name=\"Placeholder\" Padding=\"{TemplateBinding Padding}\" Margin=\"2,0\" Visibility=\"Collapsed\">\n          <TextBlock Text=\"{Binding (local:Text.Placeholder), RelativeSource={RelativeSource TemplatedParent}}\" Style=\"{DynamicResource Style.TextBox.Placeholder}\"/>\n        </Border>\n        <ScrollViewer x:Name=\"PART_ContentHost\" Template=\"{StaticResource Template.ToolBar.TextBox.Scroll}\" Padding=\"{TemplateBinding Padding}\" Foreground=\"{TemplateBinding Foreground}\" Focusable=\"False\"/>\n      </Grid>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"Text\" Value=\"\">\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"Placeholder\"/>\n      </Trigger>\n      <Trigger Property=\"IsMouseOver\" Value=\"True\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Over}\"/>\n      </Trigger>\n      <Trigger Property=\"IsKeyboardFocused\" Value=\"True\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Focused}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Checked}\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"False\">\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Disabled}\"/>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style x:Key=\"Style.ToolBar.TextBox\" TargetType=\"TextBox\" BasedOn=\"{StaticResource {x:Type TextBox}}\">\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.TextBox.Normal}\"/>\n    <Setter Property=\"BorderBrush\" Value=\"{DynamicResource Brush.Border.Normal}\"/>\n    <Setter Property=\"BorderThickness\" Value=\"{DynamicResource Border.Box}\"/>\n    <Setter Property=\"Padding\" Value=\"6,4.5\"/>\n    <Setter Property=\"Margin\" Value=\"1\"/>\n    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    <Setter Property=\"TextAlignment\" Value=\"Left\"/>\n    <Setter Property=\"KeyboardNavigation.TabNavigation\" Value=\"None\"/>\n    <Setter Property=\"AllowDrop\" Value=\"True\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolBar.TextBox}\"/>\n  </Style>\n  <Style x:Key=\"Style.ToolBar.MenuItem\" TargetType=\"MenuItem\" BasedOn=\"{StaticResource {x:Type MenuItem}}\">\n    <Style.Triggers>\n      <Trigger Property=\"Role\" Value=\"TopLevelHeader\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Normal}\"/>\n        <Setter Property=\"Padding\" Value=\"6,5\"/>\n        <Setter Property=\"Margin\" Value=\"1,0\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.TopLevelHeader}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"TopLevelItem\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Normal}\"/>\n        <Setter Property=\"Padding\" Value=\"6,5\"/>\n        <Setter Property=\"Margin\" Value=\"1,0\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.TopLevelItem}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"SubmenuHeader\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Normal}\"/>\n        <Setter Property=\"Padding\" Value=\"0,7\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.SubmenuHeader}\"/>\n      </Trigger>\n      <Trigger Property=\"Role\" Value=\"SubmenuItem\">\n        <Setter Property=\"FontSize\" Value=\"{DynamicResource Font.Size.Normal}\"/>\n        <Setter Property=\"Padding\" Value=\"0,7\"/>\n        <Setter Property=\"Template\" Value=\"{StaticResource Template.MenuItem.SubmenuItem}\"/>\n      </Trigger>\n    </Style.Triggers>\n  </Style>\n  <Style x:Key=\"Style.ToolBar.Menu\" TargetType=\"Menu\" BasedOn=\"{StaticResource {x:Type Menu}}\">\n    <Style.Resources>\n      <Style TargetType=\"MenuItem\" BasedOn=\"{StaticResource Style.ToolBar.MenuItem}\"/>\n    </Style.Resources>\n  </Style>\n\n  <!-- ToolBar -->\n  <Geometry x:Key=\"Geometry.ToolBar.Thumb\">M2,1 C2,1.5522847 1.5522847,2 1,2 C0.44771525,2 0,1.5522847 0,1 C0,0.44771525 0.44771525,0 1,0 C1.5522847,0 2,0.44771525 2,1 z M6,1 C6,1.5522847 5.5522847,2 5,2 C4.4477153,2 4,1.5522847 4,1 C4,0.44771525 4.4477153,0 5,0 C5.5522847,0 6,0.44771525 6,1 z M2,5 C2,5.5522847 1.5522847,6 1,6 C0.44771525,6 0,5.5522847 0,5 C0,4.4477153 0.44771525,4 1,4 C1.5522847,4 2,4.4477153 2,5 z M6,5 C6,5.5522847 5.5522847,6 5,6 C4.4477153,6 4,5.5522847 4,5 C4,4.4477153 4.4477153,4 5,4 C5.5522847,4 6,4.4477153 6,5 z M2,9 C2,9.5522847 1.5522847,10 1,10 C0.44771525,10 0,9.5522847 0,9 C0,8.4477153 0.44771525,8 1,8 C1.5522847,8 2,8.4477153 2,9 z M6,9 C6,9.5522847 5.5522847,10 5,10 C4.4477153,10 4,9.5522847 4,9 C4,8.4477153 4.4477153,8 5,8 C5.5522847,8 6,8.4477153 6,9 z</Geometry>\n  <Geometry x:Key=\"Geometry.Toolbar.Overflow\">M2,1 C2,1.5522847 1.5522847,2 1,2 C0.44771525,2 0,1.5522847 0,1 C0,0.44771525 0.44771525,0 1,0 C1.5522847,0 2,0.44771525 2,1 z M8,1 C8,1.5522847 7.5522847,2 7,2 C6.4477153,2 6,1.5522847 6,1 C6,0.44771525 6.4477153,0 7,0 C7.5522847,0 8,0.44771525 8,1 z M14,1 C14,1.5522847 13.552285,2 13,2 C12.447715,2 12,1.5522847 12,1 C12,0.44771525 12.447715,0 13,0 C13.552285,0 14,0.44771525 14,1 z</Geometry>\n  <Style x:Key=\"Style.ToolBar.Thumb\" TargetType=\"Thumb\">\n    <Setter Property=\"Cursor\" Value=\"SizeAll\"/>\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"Thumb\">\n          <Grid Background=\"Transparent\">\n            <Path x:Name=\"DragThumb\" Data=\"{StaticResource Geometry.ToolBar.Thumb}\" Fill=\"{DynamicResource Brush.Border.Normal}\" Height=\"11\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Margin=\"6,1,8,1\" UseLayoutRounding=\"False\"/>\n          </Grid>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n              <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Border.Over}\" TargetName=\"DragThumb\"/>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <Style x:Key=\"Style.ToolBar.OverflowButton\" TargetType=\"ToggleButton\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Template\">\n      <Setter.Value>\n        <ControlTemplate TargetType=\"ToggleButton\">\n          <Grid x:Name=\"TemplateRoot\" Background=\"Transparent\">\n            <Path x:Name=\"Dots\" Data=\"{StaticResource Geometry.Toolbar.Overflow}\" Fill=\"{DynamicResource Brush.Glyph.Normal}\" VerticalAlignment=\"Center\" Margin=\"9,0\"/>\n          </Grid>\n          <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n              <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Dots\"/>\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Over}\" TargetName=\"TemplateRoot\"/>\n            </Trigger>\n            <Trigger Property=\"IsPressed\" Value=\"True\">\n              <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Dots\"/>\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Pressed}\" TargetName=\"TemplateRoot\"/>\n            </Trigger>\n            <Trigger Property=\"IsChecked\" Value=\"True\">\n              <Setter Property=\"Fill\" Value=\"{DynamicResource Brush.Glyph.Over}\" TargetName=\"Dots\"/>\n              <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Pressed}\" TargetName=\"TemplateRoot\"/>\n            </Trigger>\n          </ControlTemplate.Triggers>\n        </ControlTemplate>\n      </Setter.Value>\n    </Setter>\n  </Style>\n  <ControlTemplate x:Key=\"Template.ToolBar\" TargetType=\"ToolBar\">\n    <Grid>\n      <Grid.ColumnDefinitions>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"Auto\"/>\n        <ColumnDefinition Width=\"*\"/>\n        <ColumnDefinition Width=\"Auto\"/>\n      </Grid.ColumnDefinitions>\n      <Border Grid.ColumnSpan=\"4\" Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\"/>\n      <Thumb x:Name=\"ToolBarThumb\" Grid.Column=\"0\" Margin=\"0,2\" Style=\"{StaticResource Style.ToolBar.Thumb}\"/>\n      <ContentPresenter x:Name=\"ToolBarHeader\" Grid.Column=\"1\" ContentSource=\"Header\" VerticalAlignment=\"Center\" TextElement.Foreground=\"{DynamicResource Brush.Foreground.Placeholder}\" Margin=\"0,0,8,0\"/>\n      <ToolBarPanel x:Name=\"PART_ToolBarPanel\" Grid.Column=\"2\" IsItemsHost=\"true\" Margin=\"{TemplateBinding Padding}\"/>\n      <Grid Grid.Column=\"3\">\n        <ToggleButton x:Name=\"OverflowButton\" Style=\"{StaticResource Style.ToolBar.OverflowButton}\" Visibility=\"Collapsed\" ClickMode=\"Press\" IsChecked=\"{Binding IsOverflowOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}\"/>\n        <Popup x:Name=\"OverflowPopup\" IsOpen=\"{Binding IsOverflowOpen, RelativeSource={RelativeSource TemplatedParent}}\" Placement=\"Bottom\" StaysOpen=\"True\" Focusable=\"False\">\n          <Border Background=\"{DynamicResource Brush.Background.Popup}\" BorderBrush=\"{DynamicResource Brush.Border.Popup}\" BorderThickness=\"1\" CornerRadius=\"{DynamicResource Corner.Border1.Bottom}\" Padding=\"8\">\n            <ToolBarOverflowPanel x:Name=\"PART_ToolBarOverflowPanel\" Focusable=\"True\" KeyboardNavigation.DirectionalNavigation=\"Cycle\" KeyboardNavigation.TabNavigation=\"Cycle\"/>\n          </Border>\n        </Popup>\n      </Grid>\n    </Grid>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"Header\" Value=\"{x:Null}\">\n        <Setter Property=\"Visibility\" Value=\"Collapsed\" TargetName=\"ToolBarHeader\"/>\n      </Trigger>\n      <Trigger Property=\"ToolBarTray.IsLocked\" Value=\"True\">\n        <Setter Property=\"Visibility\" Value=\"Collapsed\" TargetName=\"ToolBarThumb\"/>\n      </Trigger>\n      <Trigger Property=\"IsOverflowOpen\" Value=\"True\">\n        <Setter Property=\"IsEnabled\" Value=\"False\" TargetName=\"ToolBarThumb\"/>\n      </Trigger>\n      <Trigger Property=\"HasOverflowItems\" Value=\"True\">\n        <Setter Property=\"Visibility\" Value=\"Visible\" TargetName=\"OverflowButton\"/>\n      </Trigger>\n      <Trigger Property=\"IsEnabled\" Value=\"false\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"ToolBar\" BasedOn=\"{StaticResource {x:Type HeaderedItemsControl}}\">\n    <Style.Resources>\n      <Style x:Key=\"{x:Static ToolBar.SeparatorStyleKey}\" TargetType=\"Separator\" BasedOn=\"{StaticResource Style.ToolBar.Separator}\"/>\n      <Style x:Key=\"{x:Static ToolBar.ButtonStyleKey}\" TargetType=\"Button\" BasedOn=\"{StaticResource Style.ToolBar.Button}\"/>\n      <Style x:Key=\"{x:Static ToolBar.ToggleButtonStyleKey}\" TargetType=\"ToggleButton\" BasedOn=\"{StaticResource Style.ToolBar.Toggle}\"/>\n      <Style x:Key=\"{x:Static ToolBar.CheckBoxStyleKey}\" TargetType=\"CheckBox\" BasedOn=\"{StaticResource Style.ToolBar.Toggle}\"/>\n      <Style x:Key=\"{x:Static ToolBar.RadioButtonStyleKey}\" TargetType=\"RadioButton\" BasedOn=\"{StaticResource Style.ToolBar.Toggle}\"/>\n      <Style x:Key=\"{x:Static ToolBar.ComboBoxStyleKey}\" TargetType=\"ComboBox\" BasedOn=\"{StaticResource Style.ToolBar.ComboBox}\"/>\n      <Style x:Key=\"{x:Static ToolBar.TextBoxStyleKey}\" TargetType=\"TextBox\" BasedOn=\"{StaticResource Style.ToolBar.TextBox}\"/>\n      <Style x:Key=\"{x:Static ToolBar.MenuStyleKey}\" TargetType=\"Menu\" BasedOn=\"{StaticResource Style.ToolBar.Menu}\"/>\n    </Style.Resources>\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Normal}\"/>\n    <Setter Property=\"Padding\" Value=\"0,1\"/>\n    <Setter Property=\"MinHeight\" Value=\"28\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.ToolBar}\"/>\n  </Style>\n\n  <!-- StatusBar Style -->\n  <ControlTemplate x:Key=\"Template.StatusBar\" TargetType=\"StatusBar\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ItemsPresenter />\n    </Border>\n    <ControlTemplate.Triggers>\n      <Trigger Property=\"IsEnabled\" Value=\"false\">\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource Brush.Foreground.Disabled}\"/>\n      </Trigger>\n    </ControlTemplate.Triggers>\n  </ControlTemplate>\n  <Style TargetType=\"StatusBar\" BasedOn=\"{StaticResource {x:Type ItemsControl}}\">\n    <Style.Resources>\n      <Style x:Key=\"{x:Static StatusBar.SeparatorStyleKey}\" TargetType=\"Separator\" BasedOn=\"{StaticResource Style.ToolBar.Separator}\"/>\n    </Style.Resources>\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"{DynamicResource Brush.ToolBar.Normal}\"/>\n    <Setter Property=\"MinHeight\" Value=\"28\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.StatusBar}\"/>\n  </Style>\n\n  <!-- StatusBarItem Style -->\n  <ControlTemplate x:Key=\"Template.StatusBarItem\" TargetType=\"StatusBarItem\">\n    <Border Background=\"{TemplateBinding Background}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Padding=\"{TemplateBinding Padding}\">\n      <ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"/>\n    </Border>\n  </ControlTemplate>\n  <Style TargetType=\"StatusBarItem\" BasedOn=\"{StaticResource {x:Type ContentControl}}\">\n    <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n    <Setter Property=\"Background\" Value=\"Transparent\"/>\n    <Setter Property=\"Padding\" Value=\"4\"/>\n    <Setter Property=\"Template\" Value=\"{StaticResource Template.StatusBarItem}\"/>\n  </Style>\n\n</ResourceDictionary>"
  },
  {
    "path": "Assets/Shaders/Depth.fsh",
    "content": "#version 450\nlayout(location = 0) out vec4 color;\nvoid main() {\n\tfloat shade = (gl_FragCoord.z / gl_FragCoord.w + 1.0) * 0.5;\n\tcolor = vec4(shade, shade, shade, 1.0);\n}\n"
  },
  {
    "path": "Assets/Shaders/Depth.vsh",
    "content": "#version 450 compatibility\nlayout(location = 4) in vec3 vCrood;\nvoid main() {\n\tgl_Position = gl_ModelViewProjectionMatrix * vec4(vCrood, 1.0);\n}"
  },
  {
    "path": "Assets/Shaders/Main.fsh",
    "content": "#version 450 compatibility\n#define SMOOTH_SHADOW\n\nconst mat4 normalize = mat4(\n\t0.5, 0.0, 0.0, 0.0,\n\t0.0, 0.5, 0.0, 0.0,\n\t0.0, 0.0, 0.5, 0.0,\n\t0.5, 0.5, 0.499, 1.0);\nconst float delta = 0.05;\n\nlayout(location=0) uniform sampler2D Tex;\nlayout(location=1) uniform sampler2D DepthTex;\nlayout(location=2) uniform mat4 Depth_proj;\nlayout(location=3) uniform mat4 Depth_modl;\nlayout(location=4) uniform mat4 TransMat;\nlayout(location=5) uniform vec4 SkyColor;\nlayout(location=6) uniform float renderdist;\n\nin vOut {\n\tvec2 fTexCrood;\n\tvec3 fShade;\n\tvec3 fCrood;\n\tflat int facing;\n};\nout vec4 fragment;\n\nvoid main() {\n\tvec4 vertex = vec4(fCrood, 1.0);\n\tmat4 transf = normalize * Depth_proj * Depth_modl * TransMat;\n\tvec4 ShadowCoords = transf * vertex;\n#ifdef SMOOTH_SHADOW\n\tvec4 ShadowCoords01;\n\tvec4 ShadowCoords21;\n\tvec4 ShadowCoords10;\n\tvec4 ShadowCoords12;\n\tfloat dist = length(gl_ModelViewMatrix * vertex);\n\t//Shadow smoothing (Super-sample)\n\tif (dist < 16.0) {\n\t\tif (facing == 0 || facing == 1) {\n\t\t\tShadowCoords01 = transf * vec4(vertex.x - delta, vertex.yzw);\n\t\t\tShadowCoords21 = transf * vec4(vertex.x + delta, vertex.yzw);\n\t\t\tShadowCoords10 = transf * vec4(vertex.x, vertex.y - delta, vertex.zw);\n\t\t\tShadowCoords12 = transf * vec4(vertex.x, vertex.y + delta, vertex.zw);\n\t\t}\n\t\telse if (facing == 2 || facing == 3) {\n\t\t\tShadowCoords01 = transf * vec4(vertex.x, vertex.y - delta, vertex.zw);\n\t\t\tShadowCoords21 = transf * vec4(vertex.x, vertex.y + delta, vertex.zw);\n\t\t\tShadowCoords10 = transf * vec4(vertex.xy, vertex.z - delta, vertex.w);\n\t\t\tShadowCoords12 = transf * vec4(vertex.xy, vertex.z + delta, vertex.w);\n\t\t}\n\t\telse if (facing == 4 || facing == 5) {\n\t\t\tShadowCoords01 = transf * vec4(vertex.x - delta, vertex.yzw);\n\t\t\tShadowCoords21 = transf * vec4(vertex.x + delta, vertex.yzw);\n\t\t\tShadowCoords10 = transf * vec4(vertex.xy, vertex.z - delta, vertex.w);\n\t\t\tShadowCoords12 = transf * vec4(vertex.xy, vertex.z + delta, vertex.w);\n\t\t}\n\t}\n#endif\n\n\t//Shadow calculation\n\tfloat shadow = 0.0;\n\tif (facing == 1 || facing == 2 || facing == 5) shadow = 0.5;\n\telse if (ShadowCoords.x >= 0.0 && ShadowCoords.x <= 1.0 &&\n\t\t\t ShadowCoords.y >= 0.0 && ShadowCoords.y <= 1.0 && ShadowCoords.z <= 1.0) {\n\t\tif (ShadowCoords.z < texture2D(DepthTex, ShadowCoords.xy).z) shadow += 1.2; else shadow += 0.5;\n#ifdef SMOOTH_SHADOW\n\t\tif (dist < 16.0) {\n\t\t\tif (ShadowCoords01.z < texture2D(DepthTex, ShadowCoords01.xy).z) shadow += 1.2; else shadow += 0.5;\n\t\t\tif (ShadowCoords21.z < texture2D(DepthTex, ShadowCoords21.xy).z) shadow += 1.2; else shadow += 0.5;\n\t\t\tif (ShadowCoords10.z < texture2D(DepthTex, ShadowCoords10.xy).z) shadow += 1.2; else shadow += 0.5;\n\t\t\tif (ShadowCoords12.z < texture2D(DepthTex, ShadowCoords12.xy).z) shadow += 1.2; else shadow += 0.5;\n\t\t\tshadow *= 0.2;\n\t\t}\n#endif\n\t}\n\telse shadow = 1.2;\n\t\n\t//Texture color\n\tvec4 texel = texture2D(Tex, fTexCrood);\n\tvec4 color = vec4(texel.rgb * shadow * fShade, texel.a);\n\t\n\t//Fog calculation & Final color\n\t//if (color.a < 0.99) color = vec4(color.rgb, mix(1.0, 0.3, clamp((renderdist * 0.5 - dist) / 64.0, 0.0, 1.0)));\n\tfragment = mix(SkyColor, color, clamp((renderdist - dist) / 32.0, 0.0, 1.0));\n}"
  },
  {
    "path": "Assets/Shaders/Main.vsh",
    "content": "#version 450 compatibility\n\nlayout(location = 1) in float VertexAttrib;\nlayout(location = 2) in vec2 vTexCrood;\nlayout(location = 3) in vec3 vShade;\nlayout(location = 4) in vec3 vCrood;\n\nout vOut {\n\tvec2 fTexCrood;\n\tvec3 fShade;\n\tvec3 fCrood;\n\tflat int facing;\n};\n\nvoid main() {\n\tfacing = int(VertexAttrib + 0.5);\n\tfTexCrood = vTexCrood;\n\tfShade = vShade;\n\tfCrood = vCrood;\n\tgl_Position = gl_ModelViewProjectionMatrix * vec4(vCrood, 1.0);\n}"
  },
  {
    "path": "Assets/Shaders/Particle.fsh",
    "content": "#version 450\n\nlayout(location=0) uniform sampler2D Tex;\n\nin vOut {\n    vec2 fTexCrood;\n    flat vec2 fShade;\n};\nout vec4 fragment;\n\nvoid main() {\n    fragment = texture(Tex, fTexCrood) * vec4(vec3(fShade.x), fShade.y);\n}"
  },
  {
    "path": "Assets/Shaders/Particle.vsh",
    "content": "#version 450 compatibility\n\nlayout(location = 1) in vec2 vTexCrood;\nlayout(location = 2) in vec2 vShade;\nlayout(location = 3) in vec3 vCrood;\n\nout vOut {\n\tvec2 fTexCrood;\n\tflat vec2 fShade;\n};\n\nvoid main() {\n\tfTexCrood = vTexCrood;\n\tfShade = vShade;\n\tgl_Position = gl_ModelViewProjectionMatrix * vec4(vCrood, 1.0);\n}"
  },
  {
    "path": "Assets/Shaders/Shadow.fsh",
    "content": "#version 450\nlayout(location = 0) out vec4 color;\nvoid main() {\n\tcolor = vec4(1.0, 1.0, 1.0, 1.0);\n}\n"
  },
  {
    "path": "Assets/Shaders/Shadow.vsh",
    "content": "#version 450 compatibility\nlayout(location = 4) in vec3 vCrood;\nvoid main() {\n\tgl_Position = gl_ModelViewProjectionMatrix * vec4(vCrood, 1.0);\n}"
  },
  {
    "path": "Assets/Shaders/Simple.fsh",
    "content": "#version 450\n\nlayout(location=0) uniform sampler2D Tex;\n\nin vOut {\n\tvec2 fTexCrood;\n\tvec3 fShade;\n};\nout vec4 fragment;\n\nvoid main() {\n\tfragment = texture(Tex, fTexCrood) * vec4(fShade, 1.0);\n}"
  },
  {
    "path": "Assets/Shaders/Simple.vsh",
    "content": "#version 450 compatibility\n\nlayout(location = 2) in vec2 vTexCrood;\nlayout(location = 3) in vec3 vShade;\nlayout(location = 4) in vec3 vCrood;\n\nout vOut {\n\tvec2 fTexCrood;\n\tvec3 fShade;\n};\n\nvoid main() {\n\tfTexCrood = vTexCrood;\n\tfShade = vShade;\n\tgl_Position = gl_ModelViewProjectionMatrix * vec4(vCrood, 1.0);\n}"
  },
  {
    "path": "Assets/Translations/Keys.json",
    "content": "[\n\"NEWorld.yes\",\n\"NEWorld.no\",\n\"NEWorld.enabled\",\n\"NEWorld.disabled\",\n\"NEWorld.Blocks.Air\",\n\"NEWorld.Blocks.Rock\",\n\"NEWorld.Blocks.Grass\",\n\"NEWorld.Blocks.Dirt\",\n\"NEWorld.Blocks.Stone\",\n\"NEWorld.Blocks.Plank\",\n\"NEWorld.Blocks.Wood\",\n\"NEWorld.Blocks.Bedrock\",\n\"NEWorld.Blocks.Leaf\",\n\"NEWorld.Blocks.Glass\",\n\"NEWorld.Blocks.Water\",\n\"NEWorld.Blocks.Lava\",\n\"NEWorld.Blocks.GlowStone\",\n\"NEWorld.Blocks.Sand\",\n\"NEWorld.Blocks.Cement\",\n\"NEWorld.Blocks.Ice\",\n\"NEWorld.Blocks.Coal Block\",\n\"NEWorld.Blocks.Iron Block\",\n\"NEWorld.Blocks.TNT\",\n\"NEWorld.Blocks.Null Block\",\n\"NEWorld.main.start\",\n\"NEWorld.main.options\",\n\"NEWorld.main.exit\",\n\"NEWorld.options.caption\",\n\"NEWorld.options.fov\",\n\"NEWorld.options.sensitivity\",\n\"NEWorld.options.distance\",\n\"NEWorld.options.rendermenu\",\n\"NEWorld.options.guimenu\",\n\"NEWorld.options.languagemenu\",\n\"NEWorld.options.soundmenu\",\n\"NEWorld.options.back\",\n\"NEWorld.options.save\",\n\"NEWorld.render.caption\",\n\"NEWorld.render.smooth\",\n\"NEWorld.render.grasstex\",\n\"NEWorld.render.merge\",\n\"NEWorld.render.multisample\",\n\"NEWorld.render.shaders\",\n\"NEWorld.render.vsync\",\n\"NEWorld.render.back\",\n\"NEWorld.shaders.caption\",\n\"NEWorld.shaders.enable\",\n\"NEWorld.shaders.shadowres\",\n\"NEWorld.shaders.distance\",\n\"NEWorld.shaders.back\",\n\"NEWorld.gui.caption\",\n\"NEWorld.gui.unicode\",\n\"NEWorld.gui.blur\",\n\"NEWorld.gui.stretch\",\n\"NEWorld.gui.back\",\n\"NEWorld.worlds.caption\",\n\"NEWorld.worlds.Enter\",\n\"NEWorld.worlds.delete\",\n\"NEWorld.worlds.new\",\n\"NEWorld.worlds.back\",\n\"NEWorld.pause.caption\",\n\"NEWorld.pause.continue\",\n\"NEWorld.pause.back\",\n\"NEWorld.create.caption\",\n\"NEWorld.create.inputname\",\n\"NEWorld.create.ok\",\n\"NEWorld.create.back\",\n\"NEWorld.language.caption\",\n\"NEWorld.language.back\",\n\"NEWorld.Sound.caption\",\n\"NEWorld.Sound.MusicGain\",\n\"NEWorld.Sound.SoundGain\"\n]\n"
  },
  {
    "path": "Assets/Translations/Langs.txt",
    "content": "3\nen_US\nzh_CN\nzh_TW"
  },
  {
    "path": "Assets/Translations/en_US.lang",
    "content": "English\nEnglish\nYes\nNo\nEnabled\nDisabled\nAir\nRock\nGrass\nDirt\nStone\nPlank\nWood\nBedrock\nLeaves\nGlass\nWater\nLava\nGlowstone\nSand\nCement\nIce\nCoal Block\nIron Block\nTNT\nNull Block\nStart game\n>> Options\nExit\n=================< Options >==================\nField of view: \nMouse sensitivity: \nRender distance: \n>> Render options\n>> GUI options\n>> Select language\n>> Sound options\n<< Back to main menu\nSave options\n=============< Render options >===============\nSmooth lighting: \nConnect grass texture: \nMerge face rendering: \nMSAA: \n>> Advanced rendering \nVertical sync: \n<< Back to options menu\n============< Advanced rendering >============\nEnabled: \nShadowMap resolution: \nMax shadow distance:\n<< Back to render options\n===============< GUI options >================\nForce Unicode font: \nBackground blur: \nPPI stretch (EXPERIMENTAL)\n<< Back to options menu\n==============< Select world >================\nEnter this world\nDelete this world\n>> Create a new world \n<< Back to main menu\n================< Game menu >=================\nResume game\n<< Back to main menu\n==============< Create a world >==============\nWorld name\nOK\n<< Back to world menu\n================< Languages >=================\n<< Back to options menu\n==================< Sound >===================\nMusicGain\nSoundGain\n"
  },
  {
    "path": "Assets/Translations/zh_CN.lang",
    "content": "Chinese_Simplified\n中文（简体）\n是\n否\n开启\n关闭\n空气\n岩石\n草\n泥土\n石头\n木板\n原木\n基岩\n树叶\n玻璃块\n水\n岩浆\n萤石\n沙子\n水泥\n冰\n煤块\n铁块\nTNT\nNull Block\n开始游戏\n>> 选项 ...\n退出\n==================< 选 项 >===================\n视野角度：\n鼠标灵敏度：\n渲染距离：\n>> 渲染选项 ...\n>> 图形界面选项 ...\n>> Select language...\n>> 音效选项 ...\n<< 返回主菜单\n保存设置\n===============< 渲 染 选 项 >================\n平滑光照：\n草方块材质连接：\n合并面渲染：\n多重采样抗锯齿：\n>> 光影选项 ...\n垂直同步：\n<< 返回选项菜单\n===============< 光 影 选 项 >================\n启用光影：\nShadowMap大小：\n最大距离：\n<< 返回渲染选项\n===============< 图形界面选项 >===============\n全部使用Unicode字体：\n背景模糊：\nPPI stretch (实验性)\n<< 返回选项菜单\n===============< 选 择 世 界 >================\n进入选定的世界\n删除选定的世界\n>> 创建新的世界 ...\n<< 返回主菜单\n===============< 游 戏 菜 单 >================\n继续游戏\n<< 返回主菜单\n===============< 新 建 世 界 >================\n输入世界名称\n确定\n<< 返回世界菜单\n===============< 语 言 设 置 >================\n<< 返回选项菜单\n===============< 音 效 设 置 >================\n背景音乐音量\n音效音量"
  },
  {
    "path": "Assets/Translations/zh_TW.lang",
    "content": "Traditional_Chinese\n中文（繁體）\n是\n否\n開啟\n關閉\n空氣\n石頭\n草方塊\n泥土\n碎石\n木材\n原木\n基岩\n樹葉\n玻璃\n水\n岩漿\n螢光石\n沙\n水泥\n冰\n煤炭磚\n鐵磚\nTNT\nNull方塊\n開始遊戲\n>>選項…\n退出\n===================< 選 項 >===================\n視野角度：\n滑鼠靈敏度：\n視野距離：\n>> 進階顯示設定 ...\n>> 用戶圖形界面設定 ...\n>> Select language...\n>> 音效選項 ...\n<< 返回主菜單\n保存設定\n===============< 顯 示 設 定 >=================\n柔和化照明效果：\n連接草方塊材質：\n合併面渲染：\n多重採樣抗鋸齒：\n>> 進階渲染選項 ...\n垂直同步：\n<< 返回選項菜單\n===============< 進階渲染選項 >================\n啟用進階渲染：\nShadowMap大小：\n最大距離：\n<< 返回顯示設定\n=============< 用戶圖形界面設定 >==============\n強制使用Unicode字符：\n背景模糊：\nPPI stretch (實驗性)\n<< 返回選項菜單\n================< 選 擇 世 界 >================\n進入選定的世界\n移除選定的世界\n>> 創建新的世界 ...\n<< 返回主菜單\n================< 遊戲已暫停 >=================\n返回遊戲\n<< 保存並回到主菜單\n===============< 創建新的世界 >================\n世界名稱\n確認\n<< 返回選擇世界菜單\n================< 選 擇 語 言 >================\n<< 返回選項菜單\n================< 音 效 設 置 >================\n背景音樂音量\n音效音量"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\ninclude(Kaleidoscope/Build/Config.cmake)\nproject(NEWorld)\nkls_configure()\n\nkls_define_modules(NoesisGUI)\nkls_define_modules(Kaleidoscope)\n\nkls_add_library_module(NEWorld.Base NW::Base)\nkls_public_source_directory(NEWorld.Base NEWorld.Base)\ntarget_link_libraries(NEWorld.Base PUBLIC klsxx::essential)\nkls_add_executable_module(NEWorld)\nkls_public_source_directory(NEWorld NEWorld.Game)\ntarget_compile_definitions(NEWorld PRIVATE NEWORLD_GAME)\ntarget_link_libraries(NEWorld PRIVATE klsxx::essential klsxx::thread klsxx::coroutine)\n\n# copy assets\nadd_custom_command(\n        TARGET NEWorld POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy_directory\n        ${CMAKE_SOURCE_DIR}/Assets $<TARGET_FILE_DIR:NEWorld>/Assets\n)\n\n# opengl\nkls_vcpkg_package(glew)\nkls_vcpkg_package(glfw3)\nfind_package(GLEW REQUIRED)\nfind_package(glfw3 CONFIG REQUIRED)\nfind_package(OpenGL REQUIRED)\ntarget_link_libraries(NEWorld PRIVATE OpenGL::GL GLEW::GLEW glfw)\n\n# nlohmann json\nkls_vcpkg_package(nlohmann-json)\nfind_package(nlohmann_json CONFIG REQUIRED)\ntarget_link_libraries(NEWorld PRIVATE nlohmann_json::nlohmann_json)\n\n# All others\n# we do not use the tsl on vcpkg as currently there are some problems on the public repo\nkls_vcpkg_package(leveldb)\nkls_vcpkg_package(openal-soft)\nfind_package(Threads REQUIRED)\nfind_package(OpenAL REQUIRED)\nfind_package(leveldb CONFIG REQUIRED)\ntarget_include_directories(NEWorld PRIVATE ${OPENAL_INCLUDE_DIR} External)\ntarget_link_libraries(NEWorld PRIVATE leveldb::leveldb NoesisGUI NEWorld.Base)\n"
  },
  {
    "path": "External/bvh/binned_sah_builder.hpp",
    "content": "#ifndef BVH_BINNED_SAH_BUILDER_HPP\n#define BVH_BINNED_SAH_BUILDER_HPP\n\n#include <optional>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/top_down_builder.hpp\"\n#include \"bvh/sah_based_algorithm.hpp\"\n\nnamespace bvh {\n\ntemplate <typename, size_t> class BinnedSahBuildTask;\n\n/// This is a top-down, classic binned SAH BVH builder. It works by approximating\n/// the SAH with bins of fixed size at every step of the recursion.\n/// See \"On fast Construction of SAH-based Bounding Volume Hierarchies\",\n/// by I. Wald.\ntemplate <typename Bvh, size_t BinCount>\nclass BinnedSahBuilder : public TopDownBuilder, public SahBasedAlgorithm<Bvh> {\n    using Scalar    = typename Bvh::ScalarType;\n    using BuildTask = BinnedSahBuildTask<Bvh, BinCount>;\n\n    using TopDownBuilder::run_task;\n\n    friend BuildTask;\n\n    Bvh& bvh;\n\npublic:\n    using TopDownBuilder::max_depth;\n    using TopDownBuilder::max_leaf_size;\n    using SahBasedAlgorithm<Bvh>::traversal_cost;\n\n    BinnedSahBuilder(Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    void build(\n        const BoundingBox<Scalar>& global_bbox,\n        const BoundingBox<Scalar>* bboxes,\n        const Vector3<Scalar>* centers,\n        size_t primitive_count)\n    {\n        assert(primitive_count > 0);\n\n        // Allocate buffers\n        bvh.nodes = std::make_unique<typename Bvh::Node[]>(2 * primitive_count + 1);\n        bvh.primitive_indices = std::make_unique<size_t[]>(primitive_count);\n\n        bvh.node_count = 1;\n        bvh.nodes[0].bounding_box_proxy() = global_bbox;\n\n        #pragma omp parallel\n        {\n            #pragma omp for\n            for (size_t i = 0; i < primitive_count; ++i)\n                bvh.primitive_indices[i] = i;\n\n            #pragma omp single\n            {\n                BuildTask first_task(*this, bboxes, centers);\n                run_task(first_task, 0, 0, primitive_count, 0);\n            }\n        }\n    }\n};\n\ntemplate <typename Bvh, size_t BinCount>\nclass BinnedSahBuildTask : public TopDownBuildTask {\n    using Scalar  = typename Bvh::ScalarType;\n    using Builder = BinnedSahBuilder<Bvh, BinCount>;\n\n    using TopDownBuildTask::WorkItem;\n\n    struct Bin {\n        BoundingBox<Scalar> bbox;\n        size_t primitive_count;\n        Scalar right_cost;\n    };\n\n    static constexpr size_t bin_count = BinCount;\n    std::array<Bin, bin_count> bins_per_axis[3];\n\n    Builder& builder;\n    const BoundingBox<Scalar>* bboxes;\n    const Vector3<Scalar>* centers;\n\n    std::pair<Scalar, size_t> find_split(int axis) {\n        auto& bins = bins_per_axis[axis];\n\n        // Right sweep to compute partial SAH\n        auto   current_bbox  = BoundingBox<Scalar>::empty();\n        size_t current_count = 0;\n        for (size_t i = bin_count - 1; i > 0; --i) {\n            current_bbox.extend(bins[i].bbox);\n            current_count += bins[i].primitive_count;\n            bins[i].right_cost = current_bbox.half_area() * current_count;\n        }\n\n        // Left sweep to compute full cost and find minimum\n        current_bbox  = BoundingBox<Scalar>::empty();\n        current_count = 0;\n\n        auto best_split = std::pair<Scalar, size_t>(std::numeric_limits<Scalar>::max(), bin_count);\n        for (size_t i = 0; i < bin_count - 1; ++i) {\n            current_bbox.extend(bins[i].bbox);\n            current_count += bins[i].primitive_count;\n            auto cost = current_bbox.half_area() * current_count + bins[i + 1].right_cost;\n            if (cost < best_split.first)\n                best_split = std::make_pair(cost, i + 1);\n        }\n        return best_split;\n    }\n\npublic:\n    using WorkItemType = WorkItem;\n\n    BinnedSahBuildTask(Builder& builder, const BoundingBox<Scalar>* bboxes, const Vector3<Scalar>* centers)\n        : builder(builder), bboxes(bboxes), centers(centers)\n    {}\n\n    std::optional<std::pair<WorkItem, WorkItem>> build(const WorkItem& item) {\n        auto& bvh  = builder.bvh;\n        auto& node = bvh.nodes[item.node_index];\n\n        auto make_leaf = [] (typename Bvh::Node& node, size_t begin, size_t end) {\n            node.first_child_or_primitive = begin;\n            node.primitive_count          = end - begin;\n        };\n\n        if (item.work_size() <= 1 || item.depth >= builder.max_depth) {\n            make_leaf(node, item.begin, item.end);\n            return std::nullopt;\n        }\n\n        auto primitive_indices = bvh.primitive_indices.get();\n\n        std::pair<Scalar, size_t> best_splits[3];\n\n        auto bbox = node.bounding_box_proxy().to_bounding_box();\n        auto center_to_bin = bbox.diagonal().inverse() * Scalar(bin_count);\n        auto bin_offset    = -bbox.min * center_to_bin;\n        auto compute_bin_index = [=] (const Vector3<Scalar>& center, int axis) {\n            auto bin_index = fast_multiply_add(center[axis], center_to_bin[axis], bin_offset[axis]);\n            return std::min(bin_count - 1, size_t(std::max(Scalar(0), bin_index)));\n        };\n\n        // Setup bins\n        for (int axis = 0; axis < 3; ++axis) {\n            for (auto& bin : bins_per_axis[axis]) {\n                bin.bbox = BoundingBox<Scalar>::empty();\n                bin.primitive_count = 0;\n            }\n        }\n\n        // Fill bins with primitives\n        for (size_t i = item.begin; i < item.end; ++i) {\n            auto primitive_index = bvh.primitive_indices[i];\n            for (int axis = 0; axis < 3; ++axis) {\n                Bin& bin = bins_per_axis[axis][compute_bin_index(centers[primitive_index], axis)];\n                bin.primitive_count++;\n                bin.bbox.extend(bboxes[primitive_index]);\n            }\n        }\n\n        for (int axis = 0; axis < 3; ++axis)\n            best_splits[axis] = find_split(axis);\n\n        int best_axis = 0;\n        if (best_splits[0].first > best_splits[1].first)\n            best_axis = 1;\n        if (best_splits[best_axis].first > best_splits[2].first)\n            best_axis = 2;\n\n        auto split_index = best_splits[best_axis].second;\n\n        // Make sure the cost of splitting does not exceed the cost of not splitting\n        auto max_split_cost = node.bounding_box_proxy().half_area() * (item.work_size() - builder.traversal_cost);\n        if (best_splits[best_axis].second == bin_count || best_splits[best_axis].first >= max_split_cost) {\n            if (item.work_size() > builder.max_leaf_size) {\n                // Fallback strategy: approximate median split on largest axis\n                best_axis = node.bounding_box_proxy().to_bounding_box().largest_axis();\n                for (size_t i = 0, count = 0; i < bin_count - 1; ++i) {\n                    count += bins_per_axis[best_axis][i].primitive_count;\n                    // Split when we reach 0.4 times the number of primitives in the node\n                    if (count >= (item.work_size() * 2 / 5 + 1)) {\n                        split_index = i + 1;\n                        break;\n                    }\n                }\n            } else {\n                make_leaf(node, item.begin, item.end);\n                return std::nullopt;\n            }\n        }\n\n        // Split primitives according to split position\n        size_t begin_right = std::partition(primitive_indices + item.begin, primitive_indices + item.end, [&] (size_t i) {\n            return compute_bin_index(centers[i], best_axis) < split_index;\n        }) - primitive_indices;\n\n        // Check that the split does not leave one side empty\n        if (begin_right > item.begin && begin_right < item.end) {\n            // Allocate two nodes\n            size_t first_child;\n            #pragma omp atomic capture\n            { first_child = bvh.node_count; bvh.node_count += 2; }\n\n            auto& left  = bvh.nodes[first_child + 0];\n            auto& right = bvh.nodes[first_child + 1];\n            node.first_child_or_primitive = first_child;\n            node.primitive_count          = 0;\n\n            // Compute the bounding boxes of each node\n            auto& bins = bins_per_axis[best_axis];\n            auto left_bbox  = BoundingBox<Scalar>::empty();\n            auto right_bbox = BoundingBox<Scalar>::empty();\n            for (size_t i = 0; i < best_splits[best_axis].second; ++i)\n                left_bbox.extend(bins[i].bbox);\n            for (size_t i = split_index; i < bin_count; ++i)\n                right_bbox.extend(bins[i].bbox);\n            left.bounding_box_proxy()  = left_bbox;\n            right.bounding_box_proxy() = right_bbox;\n\n            // Return new work items\n            WorkItem first_item (first_child + 0, item.begin, begin_right, item.depth + 1);\n            WorkItem second_item(first_child + 1, begin_right, item.end,   item.depth + 1);\n            return std::make_optional(std::make_pair(first_item, second_item));\n        }\n\n        make_leaf(node, item.begin, item.end);\n        return std::nullopt;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/bottom_up_algorithm.hpp",
    "content": "#ifndef BVH_BOTTOM_UP_ALGORITHM_HPP\n#define BVH_BOTTOM_UP_ALGORITHM_HPP\n\n#include <memory>\n#include <cstddef>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/platform.hpp\"\n\nnamespace bvh {\n\n/// Base class for bottom-up BVH traversal algorithms. The implementation is inspired\n/// from T. Karras' bottom-up refitting algorithm, explained in the article\n/// \"Maximizing Parallelism in the Construction of BVHs, Octrees, and k-d Trees\".\ntemplate <typename Bvh>\nclass BottomUpAlgorithm {\nprotected:\n    std::unique_ptr<size_t[]> parents;\n    std::unique_ptr<int[]> flags;\n\n    Bvh& bvh;\n\n    BottomUpAlgorithm(Bvh& bvh)\n        : bvh(bvh)\n    {\n        bvh::assert_not_in_parallel();\n        parents = std::make_unique<size_t[]>(bvh.node_count);\n        flags   = std::make_unique<int[]>(bvh.node_count);\n\n        parents[0] = 0;\n\n        // Compute parent indices\n        #pragma omp parallel for\n        for (size_t i = 0; i < bvh.node_count; i++) {\n            auto& node = bvh.nodes[i];\n            if (node.is_leaf())\n                continue;\n            auto first_child = node.first_child_or_primitive;\n            assert(first_child < bvh.node_count);\n            parents[first_child + 0] = i;\n            parents[first_child + 1] = i;\n        }\n    }\n\n    ~BottomUpAlgorithm() {}\n\n    template <typename ProcessLeaf, typename ProcessInnerNode>\n    void traverse_in_parallel(\n        const ProcessLeaf& process_leaf,\n        const ProcessInnerNode& process_inner_node)\n    {\n        bvh::assert_in_parallel();\n\n        #pragma omp single nowait\n        {\n            // Special case if the BVH is just a leaf\n            if (bvh.node_count == 1)\n                process_leaf(0);\n        }\n\n        #pragma omp for\n        for (size_t i = 1; i < bvh.node_count; ++i) {\n            // Only process leaves\n            if (!bvh.nodes[i].is_leaf())\n                continue;\n\n            process_leaf(i);\n\n            // Process inner nodes on the path from that leaf up to the root\n            size_t j = i;\n            do {\n                j = parents[j];\n\n                // Make sure that the children of this inner node have been processed\n                int previous_flag;\n                #pragma omp atomic capture\n                { previous_flag = flags[j]; flags[j]++; }\n                if (previous_flag != 1)\n                    break;\n                flags[j] = 0;\n\n                process_inner_node(j);\n            } while (j != 0);\n        }\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/bounding_box.hpp",
    "content": "#ifndef BVH_BOUNDING_BOX_HPP\n#define BVH_BOUNDING_BOX_HPP\n\n#include \"bvh/vector.hpp\"\n\nnamespace bvh {\n\n/// A bounding box, represented with two extreme points.\ntemplate <typename Scalar>\nstruct BoundingBox {\n    Vector3<Scalar> min, max;\n\n    BoundingBox() = default;\n    bvh_always_inline BoundingBox(const Vector3<Scalar>& v) : min(v), max(v) {}\n    bvh_always_inline BoundingBox(const Vector3<Scalar>& min, const Vector3<Scalar>& max) : min(min), max(max) {}\n\n    bvh_always_inline BoundingBox& shrink(const BoundingBox& bbox) {\n        min = bvh::max(min, bbox.min);\n        max = bvh::min(max, bbox.max);\n        return *this;\n    }\n\n    bvh_always_inline BoundingBox& extend(const BoundingBox& bbox) {\n        min = bvh::min(min, bbox.min);\n        max = bvh::max(max, bbox.max);\n        return *this;\n    }\n\n    bvh_always_inline BoundingBox& extend(const Vector3<Scalar>& v) {\n        min = bvh::min(min, v);\n        max = bvh::max(max, v);\n        return *this;\n    }\n\n    bvh_always_inline Vector3<Scalar> diagonal() const {\n        return max - min;\n    }\n\n    bvh_always_inline Vector3<Scalar> center() const {\n        return (max + min) * Scalar(0.5);\n    }\n\n    bvh_always_inline Scalar half_area() const {\n        auto d = diagonal();\n        return (d[0] + d[1]) * d[2] + d[0] * d[1];\n    }\n\n    bvh_always_inline Scalar volume() const {\n        auto d = diagonal();\n        return d[0] * d[1] * d[2];\n    }\n\n    bvh_always_inline size_t largest_axis() const {\n        auto d = diagonal();\n        size_t axis = 0;\n        if (d[0] < d[1]) axis = 1;\n        if (d[axis] < d[2]) axis = 2;\n        return axis;\n    }\n\n    bvh_always_inline Scalar largest_extent() const {\n        return diagonal()[largest_axis()];\n    }\n\n    bvh_always_inline bool is_contained_in(const BoundingBox& other) const {\n        return\n            max[0] <= other.max[0] && min[0] >= other.min[0] &&\n            max[1] <= other.max[1] && min[1] >= other.min[1] &&\n            max[2] <= other.max[2] && min[2] >= other.min[2];\n    }\n\n    bvh_always_inline static BoundingBox full() {\n        return BoundingBox(\n            Vector3<Scalar>(-std::numeric_limits<Scalar>::max()),\n            Vector3<Scalar>(std::numeric_limits<Scalar>::max()));\n    }\n\n    bvh_always_inline static BoundingBox empty() {\n        return BoundingBox(\n            Vector3<Scalar>(std::numeric_limits<Scalar>::max()),\n            Vector3<Scalar>(-std::numeric_limits<Scalar>::max()));\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/bvh.hpp",
    "content": "#ifndef BVH_BVH_HPP\n#define BVH_BVH_HPP\n\n#include <climits>\n#include <memory>\n#include <cassert>\n\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/utilities.hpp\"\n\nnamespace bvh {\n\n/// This structure represents a BVH with a list of nodes and primitives indices.\n/// The memory layout is such that the children of a node are always grouped together.\n/// This means that each node only needs one index to point to its children, as the other\n/// child can be obtained by adding one to the index of the first child. The root of the\n/// hierarchy is located at index 0 in the array of nodes.\ntemplate <typename Scalar>\nstruct Bvh {\n    using IndexType  = typename SizedIntegerType<sizeof(Scalar) * CHAR_BIT>::Unsigned;\n    using ScalarType = Scalar;\n\n    // The size of this structure should be 32 bytes in\n    // single precision and 64 bytes in double precision.\n    struct Node {\n        Scalar bounds[6];\n        IndexType primitive_count;\n        IndexType first_child_or_primitive;\n\n        bool is_leaf() const { return primitive_count != 0; }\n\n        /// Accessor to simplify the manipulation of the bounding box of a node.\n        /// This type is convertible to a `BoundingBox`.\n        struct BoundingBoxProxy {\n            Node& node;\n\n            BoundingBoxProxy(Node& node)\n                : node(node)\n            {}\n\n            BoundingBoxProxy& operator = (const BoundingBox<Scalar>& bbox) {\n                node.bounds[0] = bbox.min[0];\n                node.bounds[1] = bbox.max[0];\n                node.bounds[2] = bbox.min[1];\n                node.bounds[3] = bbox.max[1];\n                node.bounds[4] = bbox.min[2];\n                node.bounds[5] = bbox.max[2];\n                return *this;\n            }\n\n            operator BoundingBox<Scalar> () const {\n                return BoundingBox<Scalar>(\n                    Vector3<Scalar>(node.bounds[0], node.bounds[2], node.bounds[4]),\n                    Vector3<Scalar>(node.bounds[1], node.bounds[3], node.bounds[5]));\n            }\n\n            BoundingBox<Scalar> to_bounding_box() const {\n                return static_cast<BoundingBox<Scalar>>(*this);\n            }\n\n            Scalar half_area() const { return to_bounding_box().half_area(); }\n\n            BoundingBoxProxy& extend(const BoundingBox<Scalar>& bbox) {\n                return *this = to_bounding_box().extend(bbox);\n            }\n\n            BoundingBoxProxy& extend(const Vector3<Scalar>& vector) {\n                return *this = to_bounding_box().extend(vector);\n            }\n        };\n\n        BoundingBoxProxy bounding_box_proxy() {\n            return BoundingBoxProxy(*this);\n        }\n\n        const BoundingBoxProxy bounding_box_proxy() const {\n            return BoundingBoxProxy(*const_cast<Node*>(this));\n        }\n    };\n\n    /// Given a node index, returns the index of its sibling.\n    static size_t sibling(size_t index) {\n        assert(index != 0);\n        return index % 2 == 1 ? index + 1 : index - 1;\n    }\n\n    /// Returns true if the given node is the left sibling of another.\n    static bool is_left_sibling(size_t index) {\n        assert(index != 0);\n        return index % 2 == 1;\n    }\n\n    std::unique_ptr<Node[]>   nodes;\n    std::unique_ptr<size_t[]> primitive_indices;\n\n    size_t node_count = 0;\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/heuristic_primitive_splitter.hpp",
    "content": "#ifndef BVH_HEURISTIC_PRIMITIVE_SPLITTER_HPP\n#define BVH_HEURISTIC_PRIMITIVE_SPLITTER_HPP\n\n#include <algorithm>\n#include <optional>\n#include <stack>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/prefix_sum.hpp\"\n\nnamespace bvh {\n\n/// Heuristic-based primitive splitter, inspired by the algorithm described in:\n/// \"Fast Parallel Construction of High-Quality Bounding Volume Hierarchies\",\n/// by T. Karras and T. Aila.\ntemplate <typename Primitive>\nclass HeuristicPrimitiveSplitter {\n    using Scalar = typename Primitive::ScalarType;\n\n    std::unique_ptr<size_t[]> original_indices;\n    PrefixSum<size_t> prefix_sum;\n\n    /// Returns the splitting priority of a primitive.\n    static Scalar compute_priority(const Primitive& primitive, const BoundingBox<Scalar>& bbox) {\n        // This is inspired from the priority function in the original paper,\n        // except that the expression 2^i has been replaced by the largest\n        // extent of the bounding box, which is similar in nature.\n        return std::cbrt(bbox.largest_extent() * (Scalar(2) * bbox.half_area() - primitive.area()));\n    }\n\npublic:\n    /// Performs triangle splitting on the given array of triangles.\n    /// It returns the number of triangles after splitting.\n    std::tuple<size_t, std::unique_ptr<BoundingBox<Scalar>[]>, std::unique_ptr<Vector3<Scalar>[]>>\n    split(\n        const BoundingBox<Scalar>& global_bbox,\n        const Primitive* primitives,\n        size_t primitive_count,\n        Scalar split_factor = Scalar(0.5))\n    {\n        auto split_indices = std::make_unique<size_t[]>(primitive_count);\n\n        std::unique_ptr<BoundingBox<Scalar>[]> bboxes;\n        std::unique_ptr<Vector3<Scalar>[]> centers;\n\n        Scalar total_priority = 0;\n        size_t reference_count = 0;\n\n        #pragma omp parallel\n        {\n            #pragma omp for reduction(+: total_priority)\n            for (size_t i = 0; i < primitive_count; ++i)\n                total_priority += compute_priority(primitives[i], primitives[i].bounding_box());\n\n            #pragma omp for\n            for (size_t i = 0; i < primitive_count; ++i) {\n                auto priority = compute_priority(primitives[i], primitives[i].bounding_box());\n                split_indices[i] = 1 + priority * (Scalar(primitive_count) * split_factor / total_priority);\n            }\n\n            prefix_sum.sum_in_parallel(split_indices.get(), split_indices.get(), primitive_count);\n\n            #pragma omp single\n            {\n                reference_count = split_indices[primitive_count - 1];\n                bboxes = std::make_unique<BoundingBox<Scalar>[]>(reference_count);\n                centers = std::make_unique<Vector3<Scalar>[]>(reference_count);\n                original_indices = std::make_unique<size_t[]>(reference_count);\n            }\n\n            std::stack<std::pair<BoundingBox<Scalar>, size_t>> stack;\n\n            #pragma omp for\n            for (size_t i = 0; i < primitive_count; ++i) {\n                size_t split_begin = i > 0 ? split_indices[i - 1] : 0;\n                size_t split_count = split_indices[i] - split_begin;\n\n                // Use the primitive's center instead of the bounding box\n                // center if the primitive is not split.\n                if (split_count == 1) {\n                    bboxes[split_begin]  = primitives[i].bounding_box();\n                    centers[split_begin] = primitives[i].center();\n                    original_indices[split_begin] = i;\n                    continue;\n                }\n\n                // Split this primitive\n                size_t j = split_begin;\n                stack.emplace(primitives[i].bounding_box(), split_count);\n                while (!stack.empty()) {\n                    auto [bbox, count] = stack.top();\n                    stack.pop();\n\n                    if (count == 1) {\n                        bboxes[j]  = bbox;\n                        centers[j] = bbox.center();\n                        original_indices[j] = i;\n                        j++;\n                        continue;\n                    }\n\n                    auto axis = bbox.largest_axis();\n\n                    // Find the split depth (i.e. a power of 2 grid size)\n                    auto depth = std::min(Scalar(-1), std::floor(std::log2(bbox.largest_extent() / global_bbox.diagonal()[axis])));\n                    auto cell_size = std::exp2(depth) * global_bbox.diagonal()[axis];\n                    if (cell_size >= bbox.largest_extent())\n                        cell_size *= Scalar(0.5);\n\n                    // Compute the split position\n                    auto mid_pos   = (bbox.min[axis] + bbox.max[axis]) * Scalar(0.5);\n                    auto split_pos = global_bbox.min[axis] + std::round((mid_pos - global_bbox.min[axis]) / cell_size) * cell_size;\n                    if (split_pos < bbox.min[axis] || split_pos > bbox.max[axis]) {\n                        // Should only happen very rarely because of floating-point errors\n                        split_pos = mid_pos;\n                    }\n\n                    // Split the primitive and process fragments\n                    auto [left_bbox, right_bbox] = primitives[i].split(axis, split_pos);\n                    left_bbox.shrink(bbox);\n                    right_bbox.shrink(bbox);\n\n                    auto left_extent  = left_bbox.largest_extent();\n                    auto right_extent = right_bbox.largest_extent();\n                    size_t left_count = count * left_extent / (right_extent + left_extent);\n                    left_count = std::max(size_t(1), std::min(count - 1, left_count));\n\n                    stack.emplace(left_bbox, left_count);\n                    stack.emplace(right_bbox, count - left_count);\n                }\n            }\n        }\n\n        return std::make_tuple(reference_count, std::move(bboxes), std::move(centers));\n    }\n\n    /// Remaps BVH primitive indices and removes duplicate triangle references in the BVH leaves.\n    void repair_bvh_leaves(Bvh<Scalar>& bvh) {\n        #pragma omp parallel for\n        for (size_t i = 0; i < bvh.node_count; ++i) {\n            auto& node = bvh.nodes[i];\n            if (node.is_leaf()) {\n                auto begin = bvh.primitive_indices.get() + node.first_child_or_primitive;\n                auto end   = begin + node.primitive_count;\n                std::transform(begin, end, begin, [&] (size_t i) { return original_indices[i]; });\n                std::sort(begin, end);\n                node.primitive_count = std::unique(begin, end) - begin;\n            }\n        }\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/hierarchy_refitter.hpp",
    "content": "#ifndef BVH_HIERARCHY_REFITTER_HPP\n#define BVH_HIERARCHY_REFITTER_HPP\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/bottom_up_algorithm.hpp\"\n#include \"bvh/platform.hpp\"\n\nnamespace bvh {\n\ntemplate <typename Bvh>\nclass HierarchyRefitter : public BottomUpAlgorithm<Bvh> {\nprotected:\n    using BottomUpAlgorithm<Bvh>::bvh;\n    using BottomUpAlgorithm<Bvh>::traverse_in_parallel;\n    using BottomUpAlgorithm<Bvh>::parents;\n\n    template <typename UpdateLeaf>\n    void refit_in_parallel(const UpdateLeaf& update_leaf) {\n        bvh::assert_in_parallel();\n\n        // Refit every node of the tree in parallel\n        traverse_in_parallel(\n            [&] (size_t i) { update_leaf(bvh.nodes[i]); },\n            [&] (size_t i) {\n                auto& node = bvh.nodes[i];\n                auto first_child = node.first_child_or_primitive;\n                node.bounding_box_proxy() = bvh.nodes[first_child + 0]\n                    .bounding_box_proxy()\n                    .to_bounding_box()\n                    .extend(bvh.nodes[first_child + 1].bounding_box_proxy());\n            });\n    }\n\npublic:\n    HierarchyRefitter(Bvh& bvh)\n        : BottomUpAlgorithm<Bvh>(bvh)\n    {}\n\n    template <typename UpdateLeaf>\n    void refit(const UpdateLeaf& update_leaf) {\n        #pragma omp parallel\n        {\n            refit_in_parallel(update_leaf);\n        }\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/leaf_collapser.hpp",
    "content": "#ifndef BVH_LEAF_COLLAPSER_HPP\n#define BVH_LEAF_COLLAPSER_HPP\n\n#include <memory>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/sah_based_algorithm.hpp\"\n#include \"bvh/bottom_up_algorithm.hpp\"\n#include \"bvh/prefix_sum.hpp\"\n#include \"bvh/platform.hpp\"\n\nnamespace bvh {\n\n/// Collapses leaves of the BVH according to the SAH. This optimization\n/// is only helpful for bottom-up builders, as top-down builders already\n/// have a termination criterion that prevents leaf creation when the SAH\n/// cost does not improve.\ntemplate <typename Bvh>\nclass LeafCollapser : public SahBasedAlgorithm<Bvh>, public BottomUpAlgorithm<Bvh> {\n    using Scalar = typename Bvh::ScalarType;\n\n    PrefixSum<size_t> prefix_sum;\n\n    using BottomUpAlgorithm<Bvh>::traverse_in_parallel;\n    using BottomUpAlgorithm<Bvh>::parents;\n    using BottomUpAlgorithm<Bvh>::bvh;\n\npublic:\n    using SahBasedAlgorithm<Bvh>::traversal_cost;\n\n    LeafCollapser(Bvh& bvh)\n        : BottomUpAlgorithm<Bvh>(bvh)\n    {}\n\n    void collapse() {\n        if (bvh_unlikely(bvh.nodes[0].is_leaf()))\n            return;\n\n        std::unique_ptr<size_t[]> primitive_indices_copy;\n        std::unique_ptr<typename Bvh::Node[]> nodes_copy;\n\n        auto node_counts      = std::make_unique<size_t[]>(bvh.node_count);\n        auto primitive_counts = std::make_unique<size_t[]>(bvh.node_count);\n        size_t node_count = 0;\n\n        #pragma omp parallel\n        {\n            #pragma omp for\n            for (size_t i = 0; i < bvh.node_count; ++i)\n                node_counts[i] = 1;\n\n            // Bottom-up traversal to collapse leaves\n            traverse_in_parallel(\n                [&] (size_t i) { primitive_counts[i] = bvh.nodes[i].primitive_count; },\n                [&] (size_t i) {\n                    const auto& node = bvh.nodes[i];\n                    assert(!node.is_leaf());\n                    auto first_child  = node.first_child_or_primitive;\n\n                    auto left_primitive_count  = primitive_counts[first_child + 0];\n                    auto right_primitive_count = primitive_counts[first_child + 1];\n                    auto total_primitive_count = left_primitive_count + right_primitive_count;\n\n                    // Compute the cost of collapsing this node when both children are leaves\n                    if (left_primitive_count > 0 && right_primitive_count > 0) {\n                        const auto& left_child  = bvh.nodes[first_child + 0];\n                        const auto& right_child = bvh.nodes[first_child + 1];\n                        auto collapse_cost =\n                            node.bounding_box_proxy().to_bounding_box().half_area() * (Scalar(total_primitive_count) - traversal_cost);\n                        auto base_cost =\n                            left_child .bounding_box_proxy().to_bounding_box().half_area() * left_primitive_count +\n                            right_child.bounding_box_proxy().to_bounding_box().half_area() * right_primitive_count;\n                        if (collapse_cost <= base_cost) {\n                            primitive_counts[i] = total_primitive_count;\n                            primitive_counts[first_child + 0] = 0;\n                            primitive_counts[first_child + 1] = 0;\n                            node_counts[first_child + 0] = 0;\n                            node_counts[first_child + 1] = 0;\n                        }\n                    }\n                });\n\n            prefix_sum.sum_in_parallel(node_counts.get(), node_counts.get(), bvh.node_count);\n            prefix_sum.sum_in_parallel(primitive_counts.get(), primitive_counts.get(), bvh.node_count);\n\n            #pragma omp single\n            {\n                node_count = node_counts[bvh.node_count - 1];\n                if (primitive_counts[0] > 0) {\n                    // This means the root node has become a leaf.\n                    // We avoid copying the data and just swap the old primitive array with the new one.\n                    bvh.nodes[0].first_child_or_primitive = 0;\n                    bvh.nodes[0].primitive_count = primitive_counts[0];\n                    std::swap(bvh.primitive_indices, primitive_indices_copy);\n                    std::swap(bvh.nodes, nodes_copy);\n                    bvh.node_count = 0;\n                } else {\n                    nodes_copy = std::make_unique<typename Bvh::Node[]>(node_count);\n                    primitive_indices_copy = std::make_unique<size_t[]>(primitive_counts[bvh.node_count - 1]);\n                    nodes_copy[0] = bvh.nodes[0];\n                    nodes_copy[0].first_child_or_primitive =\n                        node_counts[nodes_copy[0].first_child_or_primitive - 1];\n                }\n            }\n\n            #pragma omp for\n            for (size_t i = 1; i < bvh.node_count; i++) {\n                size_t node_index = node_counts[i - 1];\n                if (node_index == node_counts[i])\n                    continue;\n\n                nodes_copy[node_index] = bvh.nodes[i];\n                size_t first_primitive = primitive_counts[i - 1];\n                if (first_primitive != primitive_counts[i]) {\n                    nodes_copy[node_index].primitive_count = primitive_counts[i] - first_primitive;\n                    nodes_copy[node_index].first_child_or_primitive = first_primitive;\n\n                    // Top-down traversal to store the primitives contained in this subtree.\n                    size_t j = i;\n                    while (true) {\n                        const auto& node = bvh.nodes[j];\n                        if (node.primitive_count != 0) {\n                            std::copy(\n                                bvh.primitive_indices.get() + node.first_child_or_primitive,\n                                bvh.primitive_indices.get() + node.first_child_or_primitive + node.primitive_count,\n                                primitive_indices_copy.get() + first_primitive);\n                            first_primitive += node.primitive_count;\n                            while (!bvh.is_left_sibling(j) && j != i)\n                                j = parents[j];\n                            if (j == i)\n                                break;\n                            j = bvh.sibling(j);\n                        } else\n                            j = node.first_child_or_primitive;\n                    }\n                    assert(first_primitive == primitive_counts[i]);\n                } else {\n                    auto& first_child = nodes_copy[node_index].first_child_or_primitive;\n                    first_child = node_counts[first_child - 1];\n                }\n            }\n        }\n\n        std::swap(bvh.nodes, nodes_copy);\n        std::swap(bvh.primitive_indices, primitive_indices_copy);\n        bvh.node_count = node_count;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/linear_bvh_builder.hpp",
    "content": "#ifndef BVH_LINEAR_BVH_BUILDER_HPP\n#define BVH_LINEAR_BVH_BUILDER_HPP\n\n#include <cstdint>\n#include <cassert>\n#include <numeric>\n\n#include \"bvh/morton_code_based_builder.hpp\"\n#include \"bvh/prefix_sum.hpp\"\n\nnamespace bvh {\n\n/// Bottom-up BVH builder that uses Morton codes to create the hierarchy.\n/// This implementation is vaguely inspired from the original LBVH publication:\n/// \"Fast BVH Construction on GPUs\", by C. Lauterbach et al.\ntemplate <typename Bvh, typename Morton>\nclass LinearBvhBuilder : public MortonCodeBasedBuilder<Bvh, Morton> {\n    using Scalar = typename Bvh::ScalarType;\n\n    using ParentBuilder = MortonCodeBasedBuilder<Bvh, Morton>;\n    using ParentBuilder::sort_primitives_by_morton_code;\n\n    using Level = typename SizedIntegerType<round_up_log2(sizeof(Morton) * CHAR_BIT + 1)>::Unsigned;\n    using Node  = typename Bvh::Node;\n\n    Bvh& bvh;\n\n    PrefixSum<size_t> prefix_sum;\n\n    std::pair<size_t, size_t> merge(\n        const Node* bvh_restrict input_nodes,\n        Node* bvh_restrict output_nodes,\n        const Level* bvh_restrict input_levels,\n        Level* bvh_restrict output_levels,\n        size_t* bvh_restrict merged_index,\n        size_t* bvh_restrict needs_merge,\n        size_t begin, size_t end,\n        size_t previous_end)\n    {\n        size_t next_begin = 0;\n        size_t next_end   = 0;\n\n        merged_index[end - 1] = 0;\n        needs_merge [end - 1] = 0;\n\n        #pragma omp parallel if (end - begin > loop_parallel_threshold)\n        {\n            // Determine, for each node, if it should be merged with the one on the right.\n            #pragma omp for\n            for (size_t i = begin; i < end - 1; ++i)\n                needs_merge[i] = input_levels[i] >= input_levels[i + 1] && (i == begin || input_levels[i] >= input_levels[i - 1]);\n\n            // Resolve conflicts between nodes that want to be merged with different neighbors.\n            #pragma omp for\n            for (size_t i = begin; i < end - 1; i += 2) {\n                if (needs_merge[i] && needs_merge[i + 1])\n                    needs_merge[i] = 0;\n            }\n            #pragma omp for\n            for (size_t i = begin + 1; i < end - 1; i += 2) {\n                if (needs_merge[i] && needs_merge[i + 1])\n                    needs_merge[i] = 0;\n            }\n\n            // Perform a prefix sum to compute the insertion indices\n            prefix_sum.sum_in_parallel(needs_merge + begin, merged_index + begin, end - begin);\n            size_t merged_count   = merged_index[end - 1];\n            size_t unmerged_count = end - begin - merged_count;\n            size_t children_count = merged_count * 2;\n            size_t children_begin = end - children_count;\n            size_t unmerged_begin = end - (children_count + unmerged_count);\n\n            #pragma omp single nowait\n            {\n                next_begin = unmerged_begin;\n                next_end   = children_begin;\n            }\n\n            // Perform one step of node merging\n            #pragma omp for nowait\n            for (size_t i = begin; i < end; ++i) {\n                if (needs_merge[i]) {\n                    size_t unmerged_index = unmerged_begin + i + 1 - begin - merged_index[i];\n                    auto& unmerged_node = output_nodes[unmerged_index];\n                    auto first_child = children_begin + (merged_index[i] - 1) * 2;\n                    unmerged_node.bounding_box_proxy() = input_nodes[i]\n                        .bounding_box_proxy()\n                        .to_bounding_box()\n                        .extend(input_nodes[i + 1].bounding_box_proxy());\n                    unmerged_node.primitive_count = 0;\n                    unmerged_node.first_child_or_primitive = first_child;\n                    output_nodes[first_child + 0] = input_nodes[i + 0];\n                    output_nodes[first_child + 1] = input_nodes[i + 1];\n                    output_levels[unmerged_index] = input_levels[i + 1];\n                } else if (i == begin || !needs_merge[i - 1]) {\n                    size_t unmerged_index = unmerged_begin + i - begin - merged_index[i];\n                    output_nodes [unmerged_index] = input_nodes[i];\n                    output_levels[unmerged_index] = input_levels[i];\n                }\n            }\n\n            // Copy the nodes of the previous level into the current array of nodes.\n            #pragma omp for nowait\n            for (size_t i = end; i < previous_end; ++i)\n                output_nodes[i] = input_nodes[i];\n        }\n\n        return std::make_pair(next_begin, next_end);\n    }\n\npublic:\n    using ParentBuilder::loop_parallel_threshold;\n\n    LinearBvhBuilder(Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    void build(\n        const BoundingBox<Scalar>& global_bbox,\n        const BoundingBox<Scalar>* bboxes,\n        const Vector3<Scalar>* centers,\n        size_t primitive_count)\n    {\n        assert(primitive_count > 0);\n\n        std::unique_ptr<size_t[]> primitive_indices;\n        std::unique_ptr<Morton[]> morton_codes;\n\n        std::tie(primitive_indices, morton_codes) =\n            sort_primitives_by_morton_code(global_bbox, centers, primitive_count);\n\n        auto node_count = 2 * primitive_count - 1;\n\n        auto nodes          = std::make_unique<Node[]>(node_count);\n        auto nodes_copy     = std::make_unique<Node[]>(node_count);\n        auto auxiliary_data = std::make_unique<size_t[]>(node_count * 2);\n        auto level_data     = std::make_unique<Level[]>(node_count * 2);\n\n        size_t begin        = node_count - primitive_count;\n        size_t end          = node_count;\n        size_t previous_end = end;\n\n        auto input_levels  = level_data.get();\n        auto output_levels = level_data.get() + node_count;\n\n        #pragma omp parallel if (primitive_count > loop_parallel_threshold)\n        {\n            // Create the leaves\n            #pragma omp for nowait\n            for (size_t i = 0; i < primitive_count; ++i) {\n                auto& node = nodes[begin + i];\n                node.bounding_box_proxy()     = bboxes[primitive_indices[i]];\n                node.primitive_count          = 1;\n                node.first_child_or_primitive = i;\n            }\n\n            // Compute the level of the tree where the current node is joined with the next.\n            #pragma omp for nowait\n            for (size_t i = 0; i < primitive_count - 1; ++i)\n                input_levels[begin + i] = count_leading_zeros(morton_codes[i] ^ morton_codes[i + 1]);\n        }\n\n        while (end - begin > 1) {\n            auto [next_begin, next_end] = merge(\n                nodes.get(),\n                nodes_copy.get(),\n                input_levels,\n                output_levels,\n                auxiliary_data.get(),\n                auxiliary_data.get() + node_count,\n                begin, end,\n                previous_end);\n\n            std::swap(nodes, nodes_copy);\n            std::swap(input_levels, output_levels);\n\n            previous_end = end;\n            begin        = next_begin;\n            end          = next_end;\n        }\n\n        std::swap(bvh.nodes, nodes);\n        std::swap(bvh.primitive_indices, primitive_indices);\n        bvh.node_count = node_count;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/locally_ordered_clustering_builder.hpp",
    "content": "#ifndef BVH_LOCALLY_ORDERED_CLUSTERING_BUILDER_HPP\n#define BVH_LOCALLY_ORDERED_CLUSTERING_BUILDER_HPP\n\n#include <numeric>\n\n#include \"bvh/morton_code_based_builder.hpp\"\n#include \"bvh/prefix_sum.hpp\"\n#include \"bvh/platform.hpp\"\n\nnamespace bvh {\n\n/// Bottom-up BVH builder based on agglomerative clustering. The algorithm starts\n/// by sorting primitives by their Morton code, and then clusters them iteratively\n/// to form the BVH nodes. Clusters are built starting from each primitive, by\n/// agglomerating nearby clusters that minimize a distance metric. The distance\n/// metric is in this case the area of the union of the bounding boxes of the two\n/// clusters of interest.\n/// See \"Parallel Locally-Ordered Clustering for Bounding Volume Hierarchy Construction\",\n/// by D. Meister and J. Bittner.\ntemplate <typename Bvh, typename Morton>\nclass LocallyOrderedClusteringBuilder : public MortonCodeBasedBuilder<Bvh, Morton> {\n    using Scalar = typename Bvh::ScalarType;\n    using Node   = typename Bvh::Node;\n\n    using ParentBuilder = MortonCodeBasedBuilder<Bvh, Morton>;\n    using ParentBuilder::sort_primitives_by_morton_code;\n\n    Bvh& bvh;\n\n    PrefixSum<size_t> prefix_sum;\n\n    std::pair<size_t, size_t> search_range(size_t i, size_t begin, size_t end) const {\n        return std::make_pair(\n            i > begin + search_radius ? i - search_radius : begin,\n            std::min(i + search_radius + 1, end));\n    }\n\n    std::pair<size_t, size_t> cluster(\n        const Node* bvh_restrict input,\n        Node* bvh_restrict output,\n        size_t* bvh_restrict neighbors,\n        size_t* bvh_restrict merged_index,\n        size_t begin, size_t end,\n        size_t previous_end)\n    {\n        size_t next_begin = 0;\n        size_t next_end   = 0;\n\n        #pragma omp parallel if (end - begin > loop_parallel_threshold)\n        {\n            auto thread_count = bvh::get_thread_count();\n            auto thread_id    = bvh::get_thread_id();\n            auto chunk_size   = (end - begin) / thread_count;\n            auto chunk_begin  = begin + thread_id * chunk_size;\n            auto chunk_end    = thread_id != thread_count - 1 ? chunk_begin + chunk_size : end;\n\n            auto distances = std::make_unique<Scalar[]>((search_radius + 1) * search_radius);\n            auto distance_matrix = std::make_unique<Scalar*[]>(search_radius + 1);\n            for (size_t i = 0; i <= search_radius; ++i)\n                distance_matrix[i] = &distances[i * search_radius];\n\n            // Initialize the distance matrix, which caches the distances between\n            // neighboring nodes in the array. A brute force approach that recomputes the\n            // distances for every neighbor can be implemented without a distance matrix,\n            // but would be slower for larger search radii.\n            for (size_t i = search_range(chunk_begin, begin, end).first; i < chunk_begin; ++i) {\n                auto search_end = search_range(i, begin, end).second;\n                for (size_t j = i + 1; j < search_end; ++j) {\n                    distance_matrix[chunk_begin - i][j - i - 1] = input[i]\n                        .bounding_box_proxy()\n                        .to_bounding_box()\n                        .extend(input[j].bounding_box_proxy())\n                        .half_area();\n                }\n            }\n\n            // Nearest neighbor search\n            for (size_t i = chunk_begin; i < chunk_end; i++) {\n                auto [search_begin, search_end] = search_range(i, begin, end);\n                Scalar best_distance = std::numeric_limits<Scalar>::max();\n                size_t best_neighbor = -1;\n\n                // Backward search (using the previously-computed distances stored in the distance matrix)\n                for (size_t j = search_begin; j < i; ++j) {\n                    auto distance = distance_matrix[i - j][i - j - 1];\n                    if (distance < best_distance) {\n                        best_distance = distance;\n                        best_neighbor = j;\n                    }\n                }\n\n                // Forward search (caching computed distances in the distance matrix)\n                for (size_t j = i + 1; j < search_end; ++j) {\n                    auto distance = input[i]\n                        .bounding_box_proxy()\n                        .to_bounding_box()\n                        .extend(input[j].bounding_box_proxy())\n                        .half_area();\n                    distance_matrix[0][j - i - 1] = distance;\n                    if (distance < best_distance) {\n                        best_distance = distance;\n                        best_neighbor = j;\n                    }\n                }\n\n                assert(best_neighbor != size_t(-1));\n                neighbors[i] = best_neighbor;\n\n                // Rotate the distance matrix columns\n                auto last = distance_matrix[search_radius];\n                std::move_backward(\n                    distance_matrix.get(),\n                    distance_matrix.get() + search_radius,\n                    distance_matrix.get() + search_radius + 1);\n                distance_matrix[0] = last;\n            }\n\n            #pragma omp barrier\n\n            // Mark nodes that are the closest as merged, but keep\n            // the one with lowest index to act as the parent\n            #pragma omp for\n            for (size_t i = begin; i < end; ++i) {\n                auto j = neighbors[i];\n                bool is_mergeable = neighbors[j] == i;\n                merged_index[i] = i < j && is_mergeable ? 1 : 0;\n            }\n\n            // Perform a prefix sum to compute the insertion indices\n            prefix_sum.sum_in_parallel(merged_index + begin, merged_index + begin, end - begin);\n            size_t merged_count   = merged_index[end - 1];\n            size_t unmerged_count = end - begin - merged_count;\n            size_t children_count = merged_count * 2;\n            size_t children_begin = end - children_count;\n            size_t unmerged_begin = end - (children_count + unmerged_count);\n\n            #pragma omp single nowait\n            {\n                next_begin = unmerged_begin;\n                next_end   = children_begin;\n            }\n\n            // Finally, merge nodes that are marked for merging and create\n            // their parents using the indices computed previously.\n            #pragma omp for nowait\n            for (size_t i = begin; i < end; ++i) {\n                auto j = neighbors[i];\n                if (neighbors[j] == i) {\n                    if (i < j) {\n                        auto& unmerged_node = output[unmerged_begin + j - begin - merged_index[j]];\n                        auto first_child = children_begin + (merged_index[i] - 1) * 2;\n                        unmerged_node.bounding_box_proxy() = input[j]\n                            .bounding_box_proxy()\n                            .to_bounding_box()\n                            .extend(input[i].bounding_box_proxy());\n                        unmerged_node.primitive_count = 0;\n                        unmerged_node.first_child_or_primitive = first_child;\n                        output[first_child + 0] = input[i];\n                        output[first_child + 1] = input[j];\n                    }\n                } else {\n                    output[unmerged_begin + i - begin - merged_index[i]] = input[i];\n                }\n            }\n\n            // Copy the nodes of the previous level into the current array of nodes.\n            #pragma omp for nowait\n            for (size_t i = end; i < previous_end; ++i)\n                output[i] = input[i];\n        }\n\n        return std::make_pair(next_begin, next_end);\n    }\n\npublic:\n    using ParentBuilder::loop_parallel_threshold;\n\n    /// Parameter of the algorithm. The larger the search radius,\n    /// the longer the search for neighboring nodes lasts.\n    size_t search_radius = 14;\n\n    LocallyOrderedClusteringBuilder(Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    void build(\n        const BoundingBox<Scalar>& global_bbox,\n        const BoundingBox<Scalar>* bboxes,\n        const Vector3<Scalar>* centers,\n        size_t primitive_count)\n    {\n        assert(primitive_count > 0);\n\n        auto primitive_indices =\n            sort_primitives_by_morton_code(global_bbox, centers, primitive_count).first;\n\n        auto node_count     = 2 * primitive_count - 1;\n        auto nodes          = std::make_unique<Node[]>(node_count);\n        auto nodes_copy     = std::make_unique<Node[]>(node_count);\n        auto auxiliary_data = std::make_unique<size_t[]>(node_count * 3);\n\n        size_t begin        = node_count - primitive_count;\n        size_t end          = node_count;\n        size_t previous_end = end;\n\n        // Create the leaves\n        #pragma omp parallel for\n        for (size_t i = 0; i < primitive_count; ++i) {\n            auto& node = nodes[begin + i];\n            node.bounding_box_proxy()     = bboxes[primitive_indices[i]];\n            node.primitive_count          = 1;\n            node.first_child_or_primitive = i;\n        }\n\n        while (end - begin > 1) {\n            auto [next_begin, next_end] = cluster(\n                nodes.get(),\n                nodes_copy.get(),\n                auxiliary_data.get(),\n                auxiliary_data.get() + node_count,\n                begin, end,\n                previous_end);\n\n            std::swap(nodes_copy, nodes);\n\n            previous_end = end;\n            begin        = next_begin;\n            end          = next_end;\n        }\n\n        std::swap(bvh.nodes, nodes);\n        std::swap(bvh.primitive_indices, primitive_indices);\n        bvh.node_count = node_count;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/morton.hpp",
    "content": "#ifndef BVH_MORTON_HPP\n#define BVH_MORTON_HPP\n\n#include <cstddef>\n#include <climits>\n#include <cassert>\n\n#include \"bvh/utilities.hpp\"\n\nnamespace bvh {\n\n/// Split an unsigned integer such that its bits are spaced by 2 zeros.\n/// For instance, morton_split(0b00110010) = 0b000000001001000000001000.\ntemplate <typename Morton>\nMorton morton_split(Morton x) {\n    constexpr size_t bit_count = sizeof(Morton) * CHAR_BIT;\n    constexpr size_t log_bits = round_up_log2(bit_count);\n    auto mask = Morton(-1) >> (bit_count / 2);\n    x &= mask;\n    for (size_t i = log_bits - 1, n = 1 << i; i > 0; --i, n >>= 1) {\n        mask = (mask | (mask << n)) & ~(mask << (n / 2));\n        x = (x | (x << n)) & mask;\n    }\n    return x;\n}\n\n/// Morton-encode three unsigned integers into one.\ntemplate <typename Morton>\nMorton morton_encode(Morton x, Morton y, Morton z) {\n    return morton_split(x)       |\n          (morton_split(y) << 1) |\n          (morton_split(z) << 2);\n}\n\ntemplate <typename Morton, typename Scalar>\nclass MortonEncoder {\n    Vector3<Scalar> world_to_grid;\n    Vector3<Scalar> grid_offset;\n    size_t grid_dim;\n\npublic:\n    static constexpr size_t max_grid_dim = 1 << (sizeof(Morton) * CHAR_BIT / 3);\n\n    MortonEncoder(const BoundingBox<Scalar>& bbox, size_t grid_dim = max_grid_dim)\n        : grid_dim(grid_dim)\n    {\n        assert(grid_dim <= max_grid_dim);\n        world_to_grid = Scalar(grid_dim) * bbox.diagonal().inverse();\n        grid_offset   = -bbox.min * world_to_grid;\n    }\n\n    /// Morton-encode a 3D point into one unsigned integer.\n    Morton encode(const Vector3<Scalar>& point) const {\n        auto grid_position = point * world_to_grid + grid_offset;\n        Morton x = std::min(Morton(grid_dim - 1), Morton(std::max(grid_position[0], Scalar(0))));\n        Morton y = std::min(Morton(grid_dim - 1), Morton(std::max(grid_position[1], Scalar(0))));\n        Morton z = std::min(Morton(grid_dim - 1), Morton(std::max(grid_position[2], Scalar(0))));\n        return morton_encode(x, y, z);\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/morton_code_based_builder.hpp",
    "content": "#ifndef BVH_MORTON_CODE_BASED_BUILDER_HPP\n#define BVH_MORTON_CODE_BASED_BUILDER_HPP\n\n#include <algorithm>\n#include <memory>\n#include <cassert>\n#include <type_traits>\n\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/vector.hpp\"\n#include \"bvh/morton.hpp\"\n#include \"bvh/radix_sort.hpp\"\n\nnamespace bvh {\n\ntemplate <typename Bvh, typename Morton>\nclass MortonCodeBasedBuilder {\n    using Scalar = typename Bvh::ScalarType;\n\n    /// Number of bits processed by every iteration of the radix sort.\n    static constexpr size_t bits_per_iteration = 10;\n\npublic:\n    static_assert(std::is_unsigned_v<Morton>);\n    using MortonType = Morton;\n\n    /// Maximum number of bits available per dimension.\n    static constexpr size_t max_bit_count = (sizeof(Morton) * CHAR_BIT) / 3;\n\n    /// Number of bits to use per dimension.\n    size_t bit_count = max_bit_count;\n\n    /// Threshold (number of nodes) under which the loops execute serially.\n    size_t loop_parallel_threshold = 256;\n\nprotected:\n    using SortedPairs = std::pair<std::unique_ptr<size_t[]>, std::unique_ptr<Morton[]>>;\n\n    RadixSort<bits_per_iteration> radix_sort;\n\n    ~MortonCodeBasedBuilder() {}\n\n    SortedPairs sort_primitives_by_morton_code(\n        const BoundingBox<Scalar>& global_bbox,\n        const Vector3<Scalar>* centers,\n        size_t primitive_count)\n    {\n        assert(bit_count <= max_bit_count);\n        auto morton_codes           = std::make_unique<Morton[]>(primitive_count);\n        auto morton_codes_copy      = std::make_unique<Morton[]>(primitive_count);\n        auto primitive_indices      = std::make_unique<size_t[]>(primitive_count);\n        auto primitive_indices_copy = std::make_unique<size_t[]>(primitive_count);\n\n        Morton* sorted_morton_codes        = morton_codes.get();\n        size_t* sorted_primitive_indices   = primitive_indices.get();\n        Morton* unsorted_morton_codes      = morton_codes_copy.get();\n        size_t* unsorted_primitive_indices = primitive_indices_copy.get();\n\n        MortonEncoder<Morton, Scalar> encoder(global_bbox, size_t(1) << bit_count);\n\n        #pragma omp parallel if (primitive_count > loop_parallel_threshold)\n        {\n            #pragma omp for\n            for (size_t i = 0; i < primitive_count; ++i) {\n                morton_codes[i] = encoder.encode(centers[i]);\n                primitive_indices[i] = i;\n            }\n\n            // Sort primitives by morton code\n            radix_sort.sort_in_parallel(\n                sorted_morton_codes,\n                unsorted_morton_codes,\n                sorted_primitive_indices,\n                unsorted_primitive_indices,\n                primitive_count, bit_count * 3);\n        }\n\n        if (sorted_morton_codes != morton_codes.get()) {\n            std::swap(morton_codes, morton_codes_copy);\n            std::swap(primitive_indices, primitive_indices_copy);\n        }\n\n        assert(std::is_sorted(morton_codes.get(), morton_codes.get() + primitive_count));\n        return std::make_pair(std::move(primitive_indices), std::move(morton_codes));\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/node_intersectors.hpp",
    "content": "#ifndef BVH_NODE_INTERSECTORS_HPP\n#define BVH_NODE_INTERSECTORS_HPP\n\n#include <cmath>\n\n#include \"bvh/vector.hpp\"\n#include \"bvh/ray.hpp\"\n#include \"bvh/platform.hpp\"\n#include \"bvh/utilities.hpp\"\n\nnamespace bvh {\n\n/// Base class for ray-node intersection algorithms. Does ray octant classification.\ntemplate <typename Bvh, typename Derived>\nstruct NodeIntersector {\n    using Scalar = typename Bvh::ScalarType;\n\n    std::array<int, 3> octant;\n\n    NodeIntersector(const Ray<Scalar>& ray)\n        : octant {\n            std::signbit(ray.direction[0]),\n            std::signbit(ray.direction[1]),\n            std::signbit(ray.direction[2])\n        }\n    {}\n\n    template <bool IsMin>\n    bvh_always_inline\n    Scalar intersect_axis(int axis, Scalar p, const Ray<Scalar>& ray) const {\n        return static_cast<const Derived*>(this)->template intersect_axis<IsMin>(axis, p, ray);\n    }\n\n    bvh_always_inline\n    std::pair<Scalar, Scalar> intersect(const typename Bvh::Node& node, const Ray<Scalar>& ray) const {\n        Vector3<Scalar> entry, exit;\n        entry[0] = intersect_axis<true >(0, node.bounds[0 * 2 +     octant[0]], ray);\n        entry[1] = intersect_axis<true >(1, node.bounds[1 * 2 +     octant[1]], ray);\n        entry[2] = intersect_axis<true >(2, node.bounds[2 * 2 +     octant[2]], ray);\n        exit [0] = intersect_axis<false>(0, node.bounds[0 * 2 + 1 - octant[0]], ray);\n        exit [1] = intersect_axis<false>(1, node.bounds[1 * 2 + 1 - octant[1]], ray);\n        exit [2] = intersect_axis<false>(2, node.bounds[2 * 2 + 1 - octant[2]], ray);\n        // Note: This order for the min/max operations is guaranteed not to produce NaNs\n        return std::make_pair(\n            robust_max(entry[0], robust_max(entry[1], robust_max(entry[2], ray.tmin))),\n            robust_min(exit [0], robust_min(exit [1], robust_min(exit [2], ray.tmax))));\n    }\n\nprotected:\n    ~NodeIntersector() {}\n};\n\n/// Fully robust ray-node intersection algorithm (see \"Robust BVH Ray Traversal\", by T. Ize).\ntemplate <typename Bvh>\nstruct RobustNodeIntersector : public NodeIntersector<Bvh, RobustNodeIntersector<Bvh>> {\n    using Scalar = typename Bvh::ScalarType;\n\n    // Padded inverse direction to avoid false-negatives in the ray-node test.\n    Vector3<Scalar> padded_inverse_direction;\n    Vector3<Scalar> inverse_direction;\n\n    RobustNodeIntersector(const Ray<Scalar>& ray)\n        : NodeIntersector<Bvh, RobustNodeIntersector<Bvh>>(ray)\n    {\n        inverse_direction = ray.direction.inverse();\n        padded_inverse_direction = Vector3<Scalar>(\n            add_ulp_magnitude(inverse_direction[0], 2),\n            add_ulp_magnitude(inverse_direction[1], 2),\n            add_ulp_magnitude(inverse_direction[2], 2));\n    }\n\n    template <bool IsMin>\n    bvh_always_inline\n    Scalar intersect_axis(int axis, Scalar p, const Ray<Scalar>& ray) const {\n        return (p - ray.origin[axis]) * (IsMin ? inverse_direction[axis] : padded_inverse_direction[axis]);\n    }\n\n    using NodeIntersector<Bvh, RobustNodeIntersector<Bvh>>::intersect;\n};\n\n/// Semi-robust, fast ray-node intersection algorithm.\ntemplate <typename Bvh>\nstruct FastNodeIntersector : public NodeIntersector<Bvh, FastNodeIntersector<Bvh>> {\n    using Scalar = typename Bvh::ScalarType;\n\n    Vector3<Scalar> scaled_origin;\n    Vector3<Scalar> inverse_direction;\n\n    FastNodeIntersector(const Ray<Scalar>& ray)\n        : NodeIntersector<Bvh, FastNodeIntersector<Bvh>>(ray) \n    {\n        inverse_direction = ray.direction.safe_inverse();\n        scaled_origin     = -ray.origin * inverse_direction;\n    }\n\n    template <bool>\n    bvh_always_inline\n    Scalar intersect_axis(int axis, Scalar p, const Ray<Scalar>&) const {\n        return fast_multiply_add(p, inverse_direction[axis], scaled_origin[axis]);\n    }\n\n    using NodeIntersector<Bvh, FastNodeIntersector<Bvh>>::intersect;\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/node_layout_optimizer.hpp",
    "content": "#ifndef BVH_NODE_LAYOUT_OPTIMIZER_HPP\n#define BVH_NODE_LAYOUT_OPTIMIZER_HPP\n\n#include <memory>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/utilities.hpp\"\n#include \"bvh/radix_sort.hpp\"\n\nnamespace bvh {\n\n/// Optimizes the layout of BVH nodes so that the nodes with\n/// the highest area are closer to the beginning of the array\n/// of nodes. This does not change the topology of the BVH;\n/// only the memory layout of the nodes is affected.\ntemplate <typename Bvh>\nclass NodeLayoutOptimizer {\n    using Scalar = typename Bvh::ScalarType;\n    using Key    = typename SizedIntegerType<sizeof(Scalar) * CHAR_BIT>::Unsigned;\n\n    RadixSort<8> radix_sort;\n\n    Bvh& bvh;\n\npublic:\n    NodeLayoutOptimizer(Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    void optimize() {\n        size_t pair_count = (bvh.node_count - 1) / 2;\n        auto keys         = std::make_unique<Key[]>(pair_count * 2);\n        auto indices      = std::make_unique<size_t[]>(pair_count * 2);\n        auto nodes_copy   = std::make_unique<typename Bvh::Node[]>(bvh.node_count);\n        nodes_copy[0] = bvh.nodes[0];\n\n        auto sorted_indices   = indices.get();\n        auto unsorted_indices = indices.get() + pair_count;\n        auto sorted_keys      = keys.get();\n        auto unsorted_keys    = keys.get() + pair_count;\n\n        #pragma omp parallel\n        {\n            // Compute the surface area of each pair of nodes\n            #pragma omp for\n            for (size_t i = 1; i < bvh.node_count; i += 2) {\n                auto area = bvh.nodes[i + 0]\n                    .bounding_box_proxy()\n                    .to_bounding_box()\n                    .extend(bvh.nodes[i + 1].bounding_box_proxy())\n                    .half_area();\n                size_t j = (i - 1) / 2;\n                keys[j]    = as<Key>(area);\n                indices[j] = j;\n            }\n\n            // Sort pairs of nodes by area. This can be done with a\n            // standard radix sort that interprets the floating point\n            // data as integers, because the area is positive, and\n            // positive floating point numbers can be compared like\n            // integers (mandated by IEEE-754).\n            radix_sort.sort_in_parallel(\n                sorted_keys,\n                unsorted_keys,\n                sorted_indices,\n                unsorted_indices,\n                pair_count,\n                sizeof(Scalar) * CHAR_BIT);\n\n            // Copy the nodes of the old layout into the new one\n            #pragma omp for\n            for (size_t i = 0; i < pair_count; ++i) {\n                auto j = sorted_indices[pair_count - i - 1];\n                auto k = 1 + j * 2;\n                auto l = 1 + i * 2;\n                nodes_copy[l + 0] = bvh.nodes[k + 0];\n                nodes_copy[l + 1] = bvh.nodes[k + 1];\n                unsorted_indices[j] = l;\n            }\n\n            // Remap children indices to the new layout\n            #pragma omp for\n            for (size_t i = 0; i < bvh.node_count; ++i) {\n                if (nodes_copy[i].is_leaf())\n                    continue;\n                nodes_copy[i].first_child_or_primitive =\n                    unsorted_indices[(nodes_copy[i].first_child_or_primitive - 1) / 2];\n            }\n        }\n\n        std::swap(nodes_copy, bvh.nodes);\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/parallel_reinsertion_optimizer.hpp",
    "content": "#ifndef BVH_PARALLEL_REINSERTION_OPTIMIZER_HPP\n#define BVH_PARALLEL_REINSERTION_OPTIMIZER_HPP\n\n#include <cassert>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/sah_based_algorithm.hpp\"\n#include \"bvh/hierarchy_refitter.hpp\"\n\nnamespace bvh {\n\n/// Optimization that tries to re-insert BVH nodes in such a way that the\n/// SAH cost of the tree decreases after the re-insertion. Inspired from the\n/// article \"Parallel Reinsertion for Bounding Volume Hierarchy Optimization\",\n/// by D. Meister and J. Bittner.\ntemplate <typename Bvh>\nclass ParallelReinsertionOptimizer :\n    public SahBasedAlgorithm<Bvh>,\n    protected HierarchyRefitter<Bvh>\n{\n    using Scalar    = typename Bvh::ScalarType;\n    using Insertion = std::pair<size_t, Scalar>;\n\n    using SahBasedAlgorithm<Bvh>::compute_cost;\n    using HierarchyRefitter<Bvh>::bvh;\n    using HierarchyRefitter<Bvh>::parents;\n    using HierarchyRefitter<Bvh>::refit_in_parallel;\n\npublic:\n    ParallelReinsertionOptimizer(Bvh& bvh)\n        : HierarchyRefitter<Bvh>(bvh)\n    {}\n\nprivate:\n    std::array<size_t, 6> get_conflicts(size_t in, size_t out) {\n        // Return an array of re-insertion conflicts for the given nodes\n        auto parent_in = parents[in];\n        return std::array<size_t, 6> {\n            in,\n            bvh.sibling(in),\n            parent_in,\n            parent_in == 0 ? in : parents[parent_in],\n            out,\n            out == 0 ? out : parents[out],\n        };\n    }\n\n    void reinsert(size_t in, size_t out) {\n        auto sibling_in   = bvh.sibling(in);\n        auto parent_in    = parents[in];\n        auto sibling_node = bvh.nodes[sibling_in];\n        auto out_node     = bvh.nodes[out];\n\n        // Re-insert it into the destination\n        bvh.nodes[out].bounding_box_proxy().extend(bvh.nodes[in].bounding_box_proxy());\n        bvh.nodes[out].first_child_or_primitive = std::min(in, sibling_in);\n        bvh.nodes[out].primitive_count = 0;\n        bvh.nodes[sibling_in] = out_node;\n        bvh.nodes[parent_in] = sibling_node;\n\n        // Update parent-child indices\n        if (!out_node.is_leaf()) {\n            parents[out_node.first_child_or_primitive + 0] = sibling_in;\n            parents[out_node.first_child_or_primitive + 1] = sibling_in;\n        }\n        if (!sibling_node.is_leaf()) {\n            parents[sibling_node.first_child_or_primitive + 0] = parent_in;\n            parents[sibling_node.first_child_or_primitive + 1] = parent_in;\n        }\n        parents[sibling_in] = out;\n        parents[in] = out;\n    }\n\n    Insertion search(size_t in) {\n        bool   down  = true;\n        size_t pivot = parents[in];\n        size_t out   = bvh.sibling(in);\n        size_t out_best = out;\n\n        auto bbox_in = bvh.nodes[in].bounding_box_proxy();\n        auto bbox_parent = bvh.nodes[pivot].bounding_box_proxy();\n        auto bbox_pivot = BoundingBox<Scalar>::empty();\n\n        Scalar d = 0;\n        Scalar d_best = 0;\n        const Scalar d_bound = bbox_parent.half_area() - bbox_in.half_area();\n\n        // Perform a search to find a re-insertion position for the given node\n        while (true) {\n            auto bbox_out = bvh.nodes[out].bounding_box_proxy().to_bounding_box();\n            auto bbox_merged = BoundingBox<Scalar>(bbox_in).extend(bbox_out);\n            if (down) {\n                auto d_direct = bbox_parent.half_area() - bbox_merged.half_area();\n                if (d_best < d_direct + d) {\n                    d_best = d_direct + d;\n                    out_best = out;\n                }\n                d = d + bbox_out.half_area() - bbox_merged.half_area();\n                if (bvh.nodes[out].is_leaf() || d_bound + d <= d_best)\n                    down = false;\n                else\n                    out = bvh.nodes[out].first_child_or_primitive;\n            } else {\n                d = d - bbox_out.half_area() + bbox_merged.half_area();\n                if (pivot == parents[out]) {\n                    bbox_pivot.extend(bbox_out);\n                    out = pivot;\n                    bbox_out = bvh.nodes[out].bounding_box_proxy();\n                    if (out != parents[in]) {\n                        bbox_merged = BoundingBox<Scalar>(bbox_in).extend(bbox_pivot);\n                        auto d_direct = bbox_parent.half_area() - bbox_merged.half_area();\n                        if (d_best < d_direct + d) {\n                            d_best = d_direct + d;\n                            out_best = out;\n                        }\n                        d = d + bbox_out.half_area() - bbox_pivot.half_area();\n                    }\n                    if (out == 0)\n                        break;\n                    out = bvh.sibling(pivot);\n                    pivot = parents[out];\n                    down = true;\n                } else {\n                    if (bvh.is_left_sibling(out)) {\n                        down = true;\n                        out = bvh.sibling(out);\n                    } else {\n                        out = parents[out];\n                    }\n                }\n            }\n        }\n\n        if (in == out_best || bvh.sibling(in) == out_best || parents[in] == out_best)\n            return Insertion { 0, 0 };\n        return Insertion { out_best, d_best };\n    }\n\npublic:\n    void optimize(size_t u = 9, Scalar threshold = 0.1) {\n        auto locks = std::make_unique<std::atomic<uint64_t>[]>(bvh.node_count);\n        auto outs  = std::make_unique<Insertion[]>(bvh.node_count);\n\n        auto old_cost = compute_cost(bvh);\n        for (size_t iteration = 0; ; ++iteration) {\n            size_t first_node = iteration % u + 1;\n\n            #pragma omp parallel\n            {\n                // Clear the locks\n                #pragma omp for nowait\n                for (size_t i = 0; i < bvh.node_count; i++)\n                    locks[i] = 0;\n\n                // Search for insertion candidates\n                #pragma omp for\n                for (size_t i = first_node; i < bvh.node_count; i += u)\n                    outs[i] = search(i);\n\n                // Resolve topological conflicts with locking\n                #pragma omp for\n                for (size_t i = first_node; i < bvh.node_count; i += u) {\n                    if (outs[i].second <= 0)\n                        continue;\n                    // Encode locks into 64 bits using the highest 32 bits for the cost and the\n                    // lowest 32 bits for the index of the node requesting the re-insertion.\n                    // This takes advantage of the fact that IEEE-754 floats can be compared\n                    // with regular integer comparisons.\n                    auto lock = (uint64_t(as<uint32_t>(float(outs[i].second))) << 32) | (uint64_t(i) & UINT64_C(0xFFFFFFFF));\n                    for (auto c : get_conflicts(i, outs[i].first))\n                        atomic_max(locks[c], lock);\n                }\n\n                // Check the locks to disable conflicting re-insertions\n                #pragma omp for\n                for (size_t i = first_node; i < bvh.node_count; i += u) {\n                    if (outs[i].second <= 0)\n                        continue;\n                    auto conflicts = get_conflicts(i, outs[i].first);\n                    // Make sure that this node owns all the locks for each and every conflicting node\n                    bool is_conflict_free = std::all_of(conflicts.begin(), conflicts.end(), [&] (size_t j) {\n                        return (locks[j] & UINT64_C(0xFFFFFFFF)) == i;\n                    });\n                    if (!is_conflict_free)\n                        outs[i] = Insertion { 0, 0 };\n                }\n\n                // Perform the reinsertions\n                #pragma omp for\n                for (size_t i = first_node; i < bvh.node_count; i += u) {\n                    if (outs[i].second > 0)\n                        reinsert(i, outs[i].first);\n                }\n\n                // Update the bounding boxes of each node in the tree\n                refit_in_parallel([] (typename Bvh::Node&) {});\n            }\n\n            // Compare the old SAH cost to the new one and decrease the number\n            // of nodes that are ignored during the optimization if the change\n            // in cost is below the threshold.\n            auto new_cost = compute_cost(bvh);\n            if (std::abs(new_cost - old_cost) <= threshold || iteration >= u) {\n                if (u <= 1)\n                    break;\n                u = u - 1;\n                iteration = 0;\n            }\n            old_cost = new_cost;\n        }\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/platform.hpp",
    "content": "#ifndef BVH_PLATFORM_HPP\n#define BVH_PLATFORM_HPP\n\n#include <cstddef>\n#include <cassert>\n\n#ifdef _OPENMP\n#include <omp.h>\n#endif\n\n#if defined(__GNUC__) || defined(__clang__)\n#define bvh_restrict      __restrict\n#define bvh_always_inline [[gnu::always_inline]]\n#elif defined(_MSC_VER)\n#define bvh_restrict      __restrict\n#define bvh_always_inline [[msvc::forceinline]]\n#else\n#define bvh_restrict\n#define bvh_always_inline\n#endif\n\nnamespace bvh {\n\n#ifdef _OPENMP\ninline size_t get_thread_count() { return omp_get_num_threads(); }\ninline size_t get_thread_id()    { return omp_get_thread_num(); }\ninline void assert_not_in_parallel() { assert(omp_get_level() == 0); }\ninline void assert_in_parallel() { assert(omp_get_level() > 0); }\n#else\ninline constexpr size_t get_thread_count() { return 1; }\ninline constexpr size_t get_thread_id()    { return 0; }\ninline void assert_not_in_parallel() {}\ninline void assert_in_parallel() {}\n#endif\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/prefix_sum.hpp",
    "content": "#ifndef BVH_PREFIX_SUM_HPP\n#define BVH_PREFIX_SUM_HPP\n\n#include <functional>\n#include <memory>\n#include <numeric>\n\n#include \"bvh/utilities.hpp\"\n\nnamespace bvh {\n\n/// Parallel prefix sum. The parallel algorithm used in this implementation\n/// needs twice the work as the naive serial version, and is thus enabled\n/// only if the number of cores if greater or equal than 3.\ntemplate <typename T>\nclass PrefixSum {\npublic:\n    /// Performs the prefix sum. Must be called from a parallel region.\n    template <typename F = std::plus<T>>\n    void sum_in_parallel(const T* input, T* output, size_t count, F f = F()) {\n        bvh::assert_in_parallel();\n\n        size_t thread_count = bvh::get_thread_count();\n        size_t thread_id    = bvh::get_thread_id();\n\n        // This algorithm is not effective when there are fewer than 2 threads.\n        if (thread_count <= 2) {\n            #pragma omp single\n            { std::partial_sum(input, input + count, output, f); }\n            return;\n        }\n\n        // Allocate temporary storage\n        #pragma omp single\n        {\n            if (per_thread_data_size < thread_count + 1) {\n                per_thread_sums = std::make_unique<T[]>(thread_count + 1);\n                per_thread_data_size = thread_count + 1;\n                per_thread_sums[0] = 0;\n            }\n        }\n\n        T sum = T(0);\n\n        // Compute partial sums\n        #pragma omp for nowait schedule(static)\n        for (size_t i = 0; i < count; ++i) {\n            sum = f(sum, input[i]);\n            output[i] = sum;\n        }\n        per_thread_sums[thread_id + 1] = sum;\n\n        #pragma omp barrier\n\n        // Fix the sums\n        auto offset = std::accumulate(per_thread_sums.get(), per_thread_sums.get() + thread_id + 1, 0, f);\n\n        #pragma omp for schedule(static)\n        for (size_t i = 0; i < count; ++i)\n            output[i] = f(output[i], offset);\n    }\n\nprivate:\n    std::unique_ptr<T[]> per_thread_sums;\n    size_t per_thread_data_size = 0;\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/primitive_intersectors.hpp",
    "content": "#ifndef BVH_PRIMITIVE_INTERSECTORS_HPP\n#define BVH_PRIMITIVE_INTERSECTORS_HPP\n\n#include <optional>\n\n#include \"bvh/ray.hpp\"\n\nnamespace bvh {\n\n/// Base class for primitive intersectors.\ntemplate <typename Bvh, typename Primitive, bool Permuted, bool AnyHit>\nstruct PrimitiveIntersector {\n    PrimitiveIntersector(const Bvh& bvh, const Primitive* primitives)\n        : bvh(bvh), primitives(primitives)\n    {}\n\n    std::pair<const Primitive&, size_t> primitive_at(size_t index) const {\n        index = Permuted ? index : bvh.primitive_indices[index];\n        return std::pair<const Primitive&, size_t> { primitives[index], index };\n    }\n\n    const Bvh& bvh;\n    const Primitive* primitives = nullptr;\n\n    static constexpr bool any_hit = AnyHit;\n\nprotected:\n    ~PrimitiveIntersector() {}\n};\n\n/// An intersector that looks for the closest intersection.\ntemplate <typename Bvh, typename Primitive, bool Permuted = false>\nstruct ClosestPrimitiveIntersector : public PrimitiveIntersector<Bvh, Primitive, Permuted, false> {\n    using Scalar       = typename Primitive::ScalarType;\n    using Intersection = typename Primitive::IntersectionType;\n\n    struct Result {\n        size_t       primitive_index;\n        Intersection intersection;\n\n        Scalar distance() const { return intersection.distance(); }\n    };\n\n    ClosestPrimitiveIntersector(const Bvh& bvh, const Primitive* primitives)\n        : PrimitiveIntersector<Bvh, Primitive, Permuted, false>(bvh, primitives)\n    {}\n\n    std::optional<Result> intersect(size_t index, const Ray<Scalar>& ray) const {\n        auto [p, i] = this->primitive_at(index);\n        if (auto hit = p.intersect(ray))\n            return std::make_optional(Result { i, *hit });\n        return std::nullopt;\n    }\n};\n\n/// An intersector that exits after the first hit and only stores the distance to the primitive.\ntemplate <typename Bvh, typename Primitive, bool Permuted = false>\nstruct AnyPrimitiveIntersector : public PrimitiveIntersector<Bvh, Primitive, Permuted, true> {\n    using Scalar = typename Primitive::ScalarType;\n\n    struct Result {\n        Scalar t;\n        Scalar distance() const { return t; }\n    };\n\n    AnyPrimitiveIntersector(const Bvh& bvh, const Primitive* primitives)\n        : PrimitiveIntersector<Bvh, Primitive, Permuted, true>(bvh, primitives)\n    {}\n\n    std::optional<Result> intersect(size_t index, const Ray<Scalar>& ray) const {\n        auto [p, i] = this->primitive_at(index);\n        if (auto hit = p.intersect(ray))\n            return std::make_optional(Result { hit->distance() });\n        return std::nullopt;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/radix_sort.hpp",
    "content": "#ifndef BVH_RADIX_SORT_HPP\n#define BVH_RADIX_SORT_HPP\n\n#include <memory>\n#include <algorithm>\n#include <cstddef>\n\n#include \"bvh/platform.hpp\"\n#include \"bvh/utilities.hpp\"\n\nnamespace bvh {\n\n/// Parallel implementation of the radix sort algorithm.\ntemplate <size_t BitsPerIteration>\nclass RadixSort {\npublic:\n    static constexpr size_t bits_per_iteration = BitsPerIteration;\n\n    /// Performs the sort. Must be called from a parallel region.\n    template <typename Key, typename Value>\n    void sort_in_parallel(\n        Key* bvh_restrict& keys,\n        Key* bvh_restrict& keys_copy,\n        Value* bvh_restrict& values,\n        Value* bvh_restrict& values_copy,\n        size_t count, size_t bit_count)\n    {\n        bvh::assert_in_parallel();\n\n        static constexpr size_t bucket_count = 1 << bits_per_iteration;\n        static constexpr Key mask = (Key(1) << bits_per_iteration) - 1;\n\n        size_t thread_count = bvh::get_thread_count();\n        size_t thread_id    = bvh::get_thread_id();\n\n        // Allocate temporary storage\n        #pragma omp single\n        {\n            size_t data_size = (thread_count + 1) * bucket_count;\n            if (per_thread_data_size < data_size) {\n                per_thread_buckets   = std::make_unique<size_t[]>(data_size);\n                per_thread_data_size = data_size;\n            }\n        }\n\n        for (size_t bit = 0; bit < bit_count; bit += BitsPerIteration) {\n            auto buckets = &per_thread_buckets[thread_id * bucket_count];\n            std::fill(buckets, buckets + bucket_count, 0);\n\n            #pragma omp for schedule(static)\n            for (size_t i = 0; i < count; ++i)\n                buckets[(keys[i] >> bit) & mask]++;\n\n            #pragma omp for\n            for (size_t i = 0; i < bucket_count; i++) {\n                // Do a prefix sum of the elements in one bucket over all threads\n                size_t sum = 0;\n                for (size_t j = 0; j < thread_count; ++j) {\n                    size_t old_sum = sum;\n                    sum += per_thread_buckets[j * bucket_count + i];\n                    per_thread_buckets[j * bucket_count + i] = old_sum;\n                }\n                per_thread_buckets[thread_count * bucket_count + i] = sum;\n            }\n\n            for (size_t i = 0, sum = 0; i < bucket_count; ++i) {\n                size_t old_sum = sum;\n                sum += per_thread_buckets[thread_count * bucket_count + i];\n                buckets[i] += old_sum;\n            }\n\n            #pragma omp for schedule(static)\n            for (size_t i = 0; i < count; ++i) {\n                size_t j = buckets[(keys[i] >> bit) & mask]++;\n                keys_copy[j]   = keys[i];\n                values_copy[j] = values[i];\n            }\n\n            #pragma omp single\n            {\n                std::swap(keys_copy, keys);\n                std::swap(values_copy, values);\n            }\n        }\n    }\n\n    /// Creates a radix sort key from a floating point value.\n    template <typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0>\n    static typename SizedIntegerType<sizeof(T) * CHAR_BIT>::Unsigned make_key(T x) {\n        using U = typename SizedIntegerType<sizeof(T) * CHAR_BIT>::Unsigned;\n        auto mask = U(1) << (sizeof(T) * CHAR_BIT - 1);\n        auto y = as<U>(x);\n        return (y & mask ? (-y) ^ mask : y) ^ mask;\n    }\n\nprivate:\n    std::unique_ptr<size_t[]> per_thread_buckets;\n    size_t per_thread_data_size = 0;\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/ray.hpp",
    "content": "#ifndef BVH_RAY_HPP\n#define BVH_RAY_HPP\n\n#include \"bvh/vector.hpp\"\n\nnamespace bvh {\n\n/// A ray, defined by an origin and a direction, with minimum and maximum distances along the direction from the origin.\ntemplate <typename Scalar>\nstruct Ray {\n    Vector3<Scalar> origin;\n    Vector3<Scalar> direction;\n    Scalar tmin;\n    Scalar tmax;\n\n    Ray() = default;\n    Ray(const Vector3<Scalar>& origin,\n        const Vector3<Scalar>& direction,\n        Scalar tmin = Scalar(0),\n        Scalar tmax = std::numeric_limits<Scalar>::max())\n        : origin(origin), direction(direction), tmin(tmin), tmax(tmax)\n    {}\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/sah_based_algorithm.hpp",
    "content": "#ifndef BVH_SAH_BASED_ALGORITHM_HPP\n#define BVH_SAH_BASED_ALGORITHM_HPP\n\n#include \"bvh/bvh.hpp\"\n\nnamespace bvh {\n\ntemplate <typename Bvh>\nclass SahBasedAlgorithm {\n    using Scalar = typename Bvh::ScalarType;\n\npublic:\n    /// Cost of intersecting a ray with a node of the data structure.\n    /// This cost is relative to the cost of intersecting a primitive,\n    /// which is assumed to be equal to 1.\n    Scalar traversal_cost = 1;\n\nprotected:\n    ~SahBasedAlgorithm() {}\n\n    Scalar compute_cost(const Bvh& bvh) const {\n        // Compute the SAH cost for the entire BVH\n        Scalar cost(0);\n        #pragma omp parallel for reduction(+: cost)\n        for (size_t i = 0; i < bvh.node_count; ++i) {\n            if (bvh.nodes[i].is_leaf())\n                cost += bvh.nodes[i].bounding_box_proxy().half_area() * bvh.nodes[i].primitive_count;\n            else\n                cost += traversal_cost * bvh.nodes[i].bounding_box_proxy().half_area();\n        }\n        return cost / bvh.nodes[0].bounding_box_proxy().half_area();\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/single_ray_traverser.hpp",
    "content": "#ifndef BVH_SINGLE_RAY_TRAVERSAL_HPP\n#define BVH_SINGLE_RAY_TRAVERSAL_HPP\n\n#include <cassert>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/ray.hpp\"\n#include \"bvh/node_intersectors.hpp\"\n#include \"bvh/utilities.hpp\"\n\nnamespace bvh {\n\n/// Single ray traversal algorithm, using the provided ray-node intersector.\ntemplate <typename Bvh, size_t StackSize = 64, typename NodeIntersector = FastNodeIntersector<Bvh>>\nclass SingleRayTraverser {\npublic:\n    static constexpr size_t stack_size = StackSize;\n\nprivate:\n    using Scalar = typename Bvh::ScalarType;\n\n    struct Stack {\n        using Element = typename Bvh::IndexType;\n\n        Element elements[stack_size];\n        size_t size = 0;\n\n        void push(const Element& t) {\n            assert(size < stack_size);\n            elements[size++] = t;\n        }\n\n        Element pop() {\n            assert(!empty());\n            return elements[--size];\n        }\n\n        bool empty() const { return size == 0; }\n    };\n\n    template <typename PrimitiveIntersector, typename Statistics>\n    bvh_always_inline\n    std::optional<typename PrimitiveIntersector::Result>& intersect_leaf(\n        const typename Bvh::Node& node,\n        Ray<Scalar>& ray,\n        std::optional<typename PrimitiveIntersector::Result>& best_hit,\n        PrimitiveIntersector& primitive_intersector,\n        Statistics& statistics) const\n    {\n        assert(node.is_leaf());\n        size_t begin = node.first_child_or_primitive;\n        size_t end   = begin + node.primitive_count;\n        statistics.intersections += end - begin;\n        for (size_t i = begin; i < end; ++i) {\n            if (auto hit = primitive_intersector.intersect(i, ray)) {\n                best_hit = hit;\n                if (primitive_intersector.any_hit)\n                    return best_hit;\n                ray.tmax = hit->distance();\n            }\n        }\n        return best_hit;\n    }\n\n    template <typename PrimitiveIntersector, typename Statistics>\n    bvh_always_inline\n    std::optional<typename PrimitiveIntersector::Result>\n    intersect(Ray<Scalar> ray, PrimitiveIntersector& primitive_intersector, Statistics& statistics) const {\n        auto best_hit = std::optional<typename PrimitiveIntersector::Result>(std::nullopt);\n\n        // If the root is a leaf, intersect it and return\n        if (bvh_unlikely(bvh.nodes[0].is_leaf()))\n            return intersect_leaf(bvh.nodes[0], ray, best_hit, primitive_intersector, statistics);\n\n        NodeIntersector node_intersector(ray);\n\n        // This traversal loop is eager, because it immediately processes leaves instead of pushing them on the stack.\n        // This is generally beneficial for performance because intersections will likely be found which will\n        // allow to cull more subtrees with the ray-box test of the traversal loop.\n        Stack stack;\n        auto* left_child = &bvh.nodes[bvh.nodes[0].first_child_or_primitive];\n        while (true) {\n            statistics.traversal_steps++;\n\n            auto* right_child = left_child + 1;\n            auto distance_left  = node_intersector.intersect(*left_child,  ray);\n            auto distance_right = node_intersector.intersect(*right_child, ray);\n\n            if (distance_left.first <= distance_left.second) {\n                if (bvh_unlikely(left_child->is_leaf())) {\n                    if (intersect_leaf(*left_child, ray, best_hit, primitive_intersector, statistics) &&\n                        primitive_intersector.any_hit)\n                        break;\n                    left_child = nullptr;\n                }\n            } else\n                left_child = nullptr;\n\n            if (distance_right.first <= distance_right.second) {\n                if (bvh_unlikely(right_child->is_leaf())) {\n                    if (intersect_leaf(*right_child, ray, best_hit, primitive_intersector, statistics) &&\n                        primitive_intersector.any_hit)\n                        break;\n                    right_child = nullptr;\n                }\n            } else\n                right_child = nullptr;\n\n            if (left_child) {\n                if (right_child) {\n                    if (distance_left.first > distance_right.first)\n                        std::swap(left_child, right_child);\n                    stack.push(right_child->first_child_or_primitive);\n                }\n                left_child = &bvh.nodes[left_child->first_child_or_primitive];\n            } else if (right_child) {\n                left_child = &bvh.nodes[right_child->first_child_or_primitive];\n            } else {\n                if (stack.empty())\n                    break;\n                left_child = &bvh.nodes[stack.pop()];\n            }\n        }\n\n        return best_hit;\n    }\n\n    const Bvh& bvh;\n\npublic:\n    /// Statistics collected during traversal.\n    struct Statistics {\n        size_t traversal_steps = 0;\n        size_t intersections   = 0;\n    };\n\n    SingleRayTraverser(const Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    /// Intersects the BVH with the given ray and intersector.\n    template <typename PrimitiveIntersector>\n    bvh_always_inline\n    std::optional<typename PrimitiveIntersector::Result>\n    traverse(const Ray<Scalar>& ray, PrimitiveIntersector& intersector) const {\n        struct {\n            struct Empty {\n                Empty& operator ++ (int)    { return *this; }\n                Empty& operator ++ ()       { return *this; }\n                Empty& operator += (size_t) { return *this; }\n            } traversal_steps, intersections;\n        } statistics;\n        return intersect(ray, intersector, statistics);\n    }\n\n    /// Intersects the BVH with the given ray and intersector.\n    /// Record statistics on the number of traversal and intersection steps.\n    template <typename PrimitiveIntersector>\n    bvh_always_inline\n    std::optional<typename PrimitiveIntersector::Result>\n    traverse(const Ray<Scalar>& ray, PrimitiveIntersector& primitive_intersector, Statistics& statistics) const {\n        return intersect(ray, primitive_intersector, statistics);\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/spatial_split_bvh_builder.hpp",
    "content": "#ifndef BVH_SPATIAL_SPLIT_BVH_BUILDER_HPP\n#define BVH_SPATIAL_SPLIT_BVH_BUILDER_HPP\n\n#include <optional>\n#include <algorithm>\n#include <vector>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/top_down_builder.hpp\"\n#include \"bvh/sah_based_algorithm.hpp\"\n\nnamespace bvh {\n\ntemplate <typename, typename, size_t> class SpatialSplitBvhBuildTask;\n\n/// This is a top-down, spatial split BVH builder based on:\n/// \"Spatial Splits in Bounding Volume Hierarchies\", by M. Stich et al.\n/// Even though the object splitting strategy is a full-sweep SAH evaluation,\n/// this builder is not as efficient as bvh::SweepSahBuilder when spatial splits\n/// are disabled, because it needs to sort primitive references at every step.\ntemplate <typename Bvh, typename Primitive, size_t BinCount>\nclass SpatialSplitBvhBuilder : public TopDownBuilder, public SahBasedAlgorithm<Bvh> {\n    using Scalar    = typename Bvh::ScalarType;\n    using BuildTask = SpatialSplitBvhBuildTask<Bvh, Primitive, BinCount>;\n    using Reference = typename BuildTask::ReferenceType;\n\n    using TopDownBuilder::run_task;\n\n    friend BuildTask;\n\n    Bvh& bvh;\n\npublic:\n    using TopDownBuilder::max_depth;\n    using TopDownBuilder::max_leaf_size;\n    using SahBasedAlgorithm<Bvh>::traversal_cost;\n\n    /// Number of spatial binning passes that are run in order to\n    /// find a spatial split. This brings additional accuracy without\n    /// increasing the number of bins.\n    size_t binning_pass_count = 2;\n\n    SpatialSplitBvhBuilder(Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    size_t build(\n        const BoundingBox<Scalar>& global_bbox,\n        const Primitive* primitives,\n        const BoundingBox<Scalar>* bboxes,\n        const Vector3<Scalar>* centers,\n        size_t primitive_count,\n        Scalar alpha = Scalar(1e-5),\n        Scalar split_factor = Scalar(0.3))\n    {\n        assert(primitive_count > 0);\n\n        size_t max_reference_count = primitive_count + primitive_count * split_factor;\n        size_t reference_count = 0;\n\n        bvh.nodes = std::make_unique<typename Bvh::Node[]>(2 * max_reference_count + 1);\n        bvh.primitive_indices = std::make_unique<size_t[]>(max_reference_count); \n\n        auto accumulated_bboxes = std::make_unique<BoundingBox<Scalar>[]>(max_reference_count);\n        auto reference_data     = std::make_unique<Reference[]>(max_reference_count * 3);\n\n        std::array<Reference*, 3> references = {\n            reference_data.get(),\n            reference_data.get() + max_reference_count,\n            reference_data.get() + 2 * max_reference_count\n        };\n\n        // Compute the spatial split threshold, as specified in the original publication\n        auto spatial_threshold = alpha * Scalar(2) * global_bbox.half_area();\n\n        bvh.node_count = 1;\n        bvh.nodes[0].bounding_box_proxy() = global_bbox;\n\n        #pragma omp parallel\n        {\n            #pragma omp for\n            for (size_t i = 0; i < primitive_count; ++i) {\n                for (int j = 0; j < 3; ++j) {\n                    references[j][i].bbox   = bboxes[i];\n                    references[j][i].center = centers[i];\n                    references[j][i].primitive_index = i;\n                }\n            }\n\n            #pragma omp single\n            {\n                BuildTask first_task(\n                    *this,\n                    primitives,\n                    accumulated_bboxes.get(),\n                    references,\n                    reference_count,\n                    primitive_count,\n                    spatial_threshold);\n                run_task(first_task, 0, 0, primitive_count, max_reference_count, 0, false);\n            }\n        }\n\n        return reference_count;\n    }\n};\n\ntemplate <typename Bvh, typename Primitive, size_t BinCount>\nclass SpatialSplitBvhBuildTask : public TopDownBuildTask {\n    using Scalar  = typename Bvh::ScalarType;\n    using Builder = SpatialSplitBvhBuilder<Bvh, Primitive, BinCount>;\n\n    struct WorkItem : public TopDownBuildTask::WorkItem {\n        size_t split_end;\n        bool   is_sorted;\n\n        WorkItem() = default;\n        WorkItem(\n            size_t node_index,\n            size_t begin,\n            size_t end,\n            size_t split_end,\n            size_t depth,\n            bool is_sorted = false)\n            : TopDownBuildTask::WorkItem(node_index, begin, end, depth)\n            , split_end(split_end)\n            , is_sorted(is_sorted)\n        {}\n    };\n\n    struct Reference {\n        BoundingBox<Scalar> bbox;\n        Vector3<Scalar>     center;\n        size_t primitive_index;\n    };\n\n    struct Bin {\n        BoundingBox<Scalar> bbox;\n        BoundingBox<Scalar> accumulated_bbox;\n        size_t entry;\n        size_t exit;\n    };\n\n    struct ObjectSplit {\n        Scalar cost;\n        size_t index;\n        int    axis;\n\n        BoundingBox<Scalar> left_bbox;\n        BoundingBox<Scalar> right_bbox;\n\n        ObjectSplit(\n            Scalar cost = std::numeric_limits<Scalar>::max(),\n            size_t index = 1,\n            int axis = 0,\n            const BoundingBox<Scalar>& left_bbox = BoundingBox<Scalar>::empty(),\n            const BoundingBox<Scalar>& right_bbox = BoundingBox<Scalar>::empty())\n            : cost(cost), index(index), axis(axis), left_bbox(left_bbox), right_bbox(right_bbox)\n        {}\n    };\n\n    struct SpatialSplit {\n        Scalar cost;\n        Scalar position;\n        int    axis;\n\n        SpatialSplit(\n            Scalar cost = std::numeric_limits<Scalar>::max(),\n            Scalar position = 0,\n            int axis = 0)\n            : cost(cost), position(position), axis(axis)\n        {}\n    };\n\n    Builder& builder;\n\n    const Primitive*     primitives;\n    BoundingBox<Scalar>* accumulated_bboxes;\n    std::vector<bool>    reference_marks;\n\n    std::array<Reference* bvh_restrict, 3> references;\n\n    size_t& reference_count;\n    size_t  primitive_count;\n    Scalar  spatial_threshold;\n\n    static constexpr size_t bin_count = BinCount;\n    std::array<Bin, bin_count> bins;\n\n    ObjectSplit find_object_split(size_t begin, size_t end, bool is_sorted) const {\n        if (!is_sorted) {\n            // Sort references by the projection of their centers on this axis\n            #pragma omp taskloop if (end - begin > builder.task_spawn_threshold) grainsize(1) default(shared)\n            for (int axis = 0; axis < 3; ++axis) {\n                std::sort(references[axis] + begin, references[axis] + end, [&] (const Reference& a, const Reference& b) {\n                    return a.center[axis] < b.center[axis];\n                });\n            }\n        }\n\n        ObjectSplit best_split;\n        for (int axis = 0; axis < 3; ++axis) {\n            // Sweep from the right to the left to accumulate bounding boxes\n            auto bbox = BoundingBox<Scalar>::empty();\n            for (size_t i = end - 1; i > begin; --i) {\n                bbox.extend(references[axis][i].bbox);\n                accumulated_bboxes[i] = bbox;\n            }\n\n            // Sweep from the left to the right to compute the SAH cost\n            bbox = BoundingBox<Scalar>::empty();\n            for (size_t i = begin; i < end - 1; ++i) {\n                bbox.extend(references[axis][i].bbox);\n                auto cost = bbox.half_area() * (i + 1 - begin) + accumulated_bboxes[i + 1].half_area() * (end - (i + 1));\n                if (cost < best_split.cost)\n                    best_split = ObjectSplit(cost, i + 1, axis, bbox, accumulated_bboxes[i + 1]);\n            }\n        }\n        return best_split;\n    }\n\n    std::pair<WorkItem, WorkItem> allocate_children(\n        Bvh& bvh,\n        const WorkItem& item,\n        size_t right_begin, size_t right_end,\n        const BoundingBox<Scalar>& left_bbox,\n        const BoundingBox<Scalar>& right_bbox,\n        bool is_sorted)\n    {\n        auto& parent = bvh.nodes[item.node_index];\n\n        // Allocate two nodes for the children\n        size_t first_child;\n        #pragma omp atomic capture\n        { first_child = bvh.node_count; bvh.node_count += 2; }\n\n        auto& left  = bvh.nodes[first_child + 0];\n        auto& right = bvh.nodes[first_child + 1];\n        parent.first_child_or_primitive = first_child;\n        parent.primitive_count          = 0;\n\n        left.bounding_box_proxy()  = left_bbox;\n        right.bounding_box_proxy() = right_bbox;\n\n        // Allocate split space for the two children based on their SAH cost.\n        // This assumes that reference ranges look like this:\n        // - [item.begin...right_begin[ is the range of references on the left,\n        // - [right_begin...right_end[ is the range of references on the right,\n        // - [right_end...item.split_end[ is the free split space\n        assert(item.begin < right_begin && right_begin < right_end && right_end <= item.split_end);\n        size_t remaining_split_count = item.split_end - right_end;\n        auto left_cost  = left_bbox.half_area() * (right_begin - item.begin);\n        auto right_cost = right_bbox.half_area() * (right_end - right_begin);\n        auto left_split_ratio = left_cost + right_cost > 0 ? left_cost / (left_cost + right_cost) : Scalar(0.5);\n        size_t left_split_count = remaining_split_count * left_split_ratio;\n        assert(left_split_count <= remaining_split_count);\n\n        // Move references of the right child to leave some split space for the left one \n        if (left_split_count > 0) {\n            std::move_backward(references[0] + right_begin, references[0] + right_end, references[0] + right_end + left_split_count);\n            std::move_backward(references[1] + right_begin, references[1] + right_end, references[1] + right_end + left_split_count);\n            std::move_backward(references[2] + right_begin, references[2] + right_end, references[2] + right_end + left_split_count);\n        }\n\n        size_t left_end = right_begin;\n        right_begin += left_split_count;\n        right_end   += left_split_count;\n        assert(right_begin < item.split_end);\n        assert(right_end <= item.split_end);\n        return std::make_pair(\n            WorkItem(first_child + 0, item.begin,  left_end,  right_begin,    item.depth + 1, is_sorted),\n            WorkItem(first_child + 1, right_begin, right_end, item.split_end, item.depth + 1, is_sorted));\n    }\n\n    std::pair<WorkItem, WorkItem> apply_object_split(Bvh& bvh, const ObjectSplit& split, const WorkItem& item) {\n        int other_axis[2] = { (split.axis + 1) % 3, (split.axis + 2) % 3 };\n        reference_marks.resize(primitive_count);\n        for (size_t i = item.begin;  i < split.index; ++i)\n            reference_marks[references[split.axis][i].primitive_index] = true;\n        for (size_t i = split.index; i < item.end;    ++i)\n            reference_marks[references[split.axis][i].primitive_index] = false;\n        auto partition_predicate = [&] (const Reference& reference) { return reference_marks[reference.primitive_index]; };\n\n        #pragma omp taskgroup\n        {\n            #pragma omp task if (item.work_size() > builder.task_spawn_threshold) default(shared)\n            { std::stable_partition(references[other_axis[0]] + item.begin, references[other_axis[0]] + item.end, partition_predicate); }\n            #pragma omp task if (item.work_size() > builder.task_spawn_threshold) default(shared)\n            { std::stable_partition(references[other_axis[1]] + item.begin, references[other_axis[1]] + item.end, partition_predicate); }\n        }\n\n        return allocate_children(bvh, item, split.index, item.end, split.left_bbox, split.right_bbox, true);\n    }\n\n    std::optional<std::pair<Scalar, Scalar>>\n    run_binning_pass(SpatialSplit& split, int axis, size_t begin, size_t end, Scalar min, Scalar max) {\n        for (size_t i = 0; i < bin_count; ++i) {\n            bins[i].bbox = BoundingBox<Scalar>::empty();\n            bins[i].entry = 0;\n            bins[i].exit  = 0;\n        }\n\n        // Split primitives and add the bounding box of the fragments to the bins\n        auto bin_size = (max - min) / bin_count;\n        auto inv_size = Scalar(1) / bin_size;\n        for (size_t i = begin; i < end; ++i) {\n            auto& reference = references[0][i];\n            auto first_bin = std::min(bin_count - 1, size_t(std::max(Scalar(0), inv_size * (reference.bbox.min[axis] - min))));\n            auto last_bin  = std::min(bin_count - 1, size_t(std::max(Scalar(0), inv_size * (reference.bbox.max[axis] - min))));\n            auto current_bbox = reference.bbox;\n            for (size_t j = first_bin; j < last_bin; ++j) {\n                auto [left_bbox, right_bbox] = primitives[reference.primitive_index].split(axis, min + (j + 1) * bin_size);\n                bins[j].bbox.extend(left_bbox.shrink(current_bbox));\n                current_bbox.shrink(right_bbox);\n            }\n            bins[last_bin].bbox.extend(current_bbox);\n            bins[first_bin].entry++;\n            bins[last_bin].exit++;\n        }\n\n        // Accumulate bounding boxes\n        auto current_bbox = BoundingBox<Scalar>::empty();\n        for (size_t i = bin_count; i > 0; --i)\n            bins[i - 1].accumulated_bbox = current_bbox.extend(bins[i - 1].bbox);\n\n        // Sweep and compute SAH cost\n        size_t left_count = 0, right_count = end - begin;\n        current_bbox = BoundingBox<Scalar>::empty();\n        bool found = false;\n        for (size_t i = 0; i < bin_count - 1; ++i) {\n            left_count  += bins[i].entry;\n            right_count -= bins[i].exit;\n            current_bbox.extend(bins[i].bbox);\n\n            auto cost = left_count * current_bbox.half_area() + right_count * bins[i + 1].accumulated_bbox.half_area();\n            if (cost < split.cost) {\n                split.cost = cost;\n                split.axis = axis;\n                split.position = min + (i + 1) * bin_size;\n                found = true;\n            }\n        }\n\n        return found ? std::make_optional(std::make_pair(split.position - bin_size, split.position + bin_size)) : std::nullopt;\n    }\n\n    SpatialSplit find_spatial_split(const BoundingBox<Scalar>& node_bbox, size_t begin, size_t end, size_t binning_pass_count) {\n        SpatialSplit split;\n        for (int axis = 0; axis < 3; ++axis) {\n            auto min = node_bbox.min[axis];\n            auto max = node_bbox.max[axis];\n            // Run several binning passes to get the best possible split\n            for (size_t pass = 0; pass < binning_pass_count; ++pass) {\n                auto next_bounds = run_binning_pass(split, axis, begin, end, min, max);\n                if (next_bounds)\n                    std::tie(min, max) = *next_bounds;\n                else\n                    break;\n            }\n        } \n        return split;\n    }\n\n    std::pair<WorkItem, WorkItem> apply_spatial_split(Bvh& bvh, const SpatialSplit& split, const WorkItem& item) {\n        size_t left_end    = item.begin;\n        size_t right_begin = item.end;\n        size_t right_end   = item.end;\n\n        auto left_bbox  = BoundingBox<Scalar>::empty();\n        auto right_bbox = BoundingBox<Scalar>::empty();\n\n        // Choosing the references that are sorted on the split axis\n        // is more efficient than the others, since fewers swaps are\n        // necessary for primitives that are completely contained on\n        // one side of the partition.\n        auto references_to_split = references[split.axis];\n\n        // Partition references such that:\n        // - [item.begin...left_end[ is on the left,\n        // - [left_end...right_begin[ is in between,\n        // - [right_begin...item.end[ is on the right\n        for (size_t i = item.begin; i < right_begin;) {\n            auto& bbox = references_to_split[i].bbox;\n            if (bbox.max[split.axis] <= split.position) {\n                left_bbox.extend(bbox);\n                std::swap(references_to_split[i++], references_to_split[left_end++]);\n            } else if (bbox.min[split.axis] >= split.position) {\n                right_bbox.extend(bbox);\n                std::swap(references_to_split[i], references_to_split[--right_begin]);\n            } else {\n                i++;\n            }\n        }\n\n        size_t left_count  = left_end  - item.begin;\n        size_t right_count = right_end - right_begin;\n        if ((left_count == 0 || right_count == 0) && left_end == right_begin) {\n            // Sometimes, because of numerical imprecision,\n            // the algorithm will report that a spatial split is\n            // possible, but all references end up on the same side\n            // when applying it. To counteract this, we simply put\n            // half of the primitives of the left side in the right side\n            // and continue as usual.\n            if (left_count > 0) {\n                left_end -= left_count / 2;\n                right_begin = left_end;\n            } else {\n                left_end += right_count / 2;\n                right_begin = left_end;\n            }\n            // Recompute the left and right bounding boxes\n            left_bbox  = BoundingBox<Scalar>::empty();\n            right_bbox = BoundingBox<Scalar>::empty();\n            for (size_t i = item.begin; i < left_end; ++i)\n                left_bbox.extend(references_to_split[i].bbox);\n            for (size_t i = left_end; i < item.end; ++i)\n                right_bbox.extend(references_to_split[i].bbox);\n        }\n\n        // Handle straddling references\n        while (left_end < right_begin) {\n            auto reference = references_to_split[left_end];\n            auto [left_primitive_bbox, right_primitive_bbox] =\n                primitives[reference.primitive_index].split(split.axis, split.position);\n            left_primitive_bbox .shrink(reference.bbox);\n            right_primitive_bbox.shrink(reference.bbox);\n\n            // Make sure there is enough space to split that reference\n            if (item.split_end - right_end > 0) {\n                left_bbox .extend(left_primitive_bbox);\n                right_bbox.extend(right_primitive_bbox);\n                references_to_split[right_end++] = Reference {\n                    right_primitive_bbox,\n                    right_primitive_bbox.center(),\n                    reference.primitive_index\n                };\n                references_to_split[left_end++] = Reference {\n                    left_primitive_bbox,\n                    left_primitive_bbox.center(),\n                    reference.primitive_index\n                };\n                left_count++;\n                right_count++;\n            } else if (left_count < right_count) {\n                left_bbox.extend(reference.bbox);\n                left_end++;\n                left_count++;\n            } else {\n                right_bbox.extend(reference.bbox);\n                std::swap(references_to_split[--right_begin], references_to_split[left_end]);\n                right_count++;\n            }\n        } \n\n        std::copy(\n            references_to_split + item.begin,\n            references_to_split + right_end,\n            references[(split.axis + 1) % 3] + item.begin);\n        std::copy(\n            references_to_split + item.begin,\n            references_to_split + right_end,\n            references[(split.axis + 2) % 3] + item.begin);\n\n        assert(left_end == right_begin);\n        assert(right_end <= item.split_end);\n        return allocate_children(bvh, item, right_begin, right_end, left_bbox, right_bbox, false);\n    }\n\npublic:\n    using ReferenceType = Reference;\n    using WorkItemType  = WorkItem;\n\n    SpatialSplitBvhBuildTask(\n        Builder& builder,\n        const Primitive* primitives,\n        BoundingBox<Scalar>* accumulated_bboxes,\n        const std::array<Reference*, 3>& references,\n        size_t& reference_count,\n        size_t  primitive_count,\n        Scalar spatial_threshold)\n        : builder(builder)\n        , primitives(primitives)\n        , accumulated_bboxes(accumulated_bboxes)\n        , references { references[0], references[1], references[2] }\n        , reference_count(reference_count)\n        , primitive_count(primitive_count)\n        , spatial_threshold(spatial_threshold)\n    {}\n\n    SpatialSplitBvhBuildTask(const SpatialSplitBvhBuildTask& other)\n        : builder(other.builder)\n        , primitives(other.primitives)\n        , accumulated_bboxes(other.accumulated_bboxes)\n        , references(other.references)\n        , reference_count(other.reference_count)\n        , primitive_count(other.primitive_count)\n        , spatial_threshold(other.spatial_threshold)\n    {\n        // Note: no need to copy reference marks as it is\n        // the task's private data and should not be shared\n    }\n\n    std::optional<std::pair<WorkItem, WorkItem>> build(const WorkItem& item) {\n        auto& bvh  = builder.bvh;\n        auto& node = bvh.nodes[item.node_index];\n\n        auto make_leaf = [&] (typename Bvh::Node& node, size_t begin, size_t end) {\n            size_t primitive_count = end - begin;\n\n            // Reserve space for the primitives\n            size_t first_primitive;\n            #pragma omp atomic capture\n            { first_primitive = reference_count; reference_count += primitive_count; }\n\n            // Copy the primitives indices from the references to the BVH\n            for (size_t i = 0; i < primitive_count; ++i)\n                bvh.primitive_indices[first_primitive + i] = references[0][begin + i].primitive_index;\n            node.first_child_or_primitive = first_primitive;\n            node.primitive_count          = primitive_count;\n        };\n\n        if (item.work_size() <= 1 || item.depth >= builder.max_depth) {\n            make_leaf(node, item.begin, item.end);\n            return std::nullopt;\n        }\n\n        ObjectSplit best_object_split = find_object_split(item.begin, item.end, item.is_sorted);\n\n        // Find a spatial split when the size\n        SpatialSplit best_spatial_split;\n        auto overlap = BoundingBox<Scalar>(best_object_split.left_bbox).shrink(best_object_split.right_bbox).half_area();\n        if (overlap > spatial_threshold && item.split_end - item.end > 0) {\n            auto binning_pass_count = static_cast<SpatialSplitBvhBuilder<Bvh, Primitive, BinCount>&>(builder).binning_pass_count;\n            best_spatial_split = find_spatial_split(node.bounding_box_proxy(), item.begin, item.end, binning_pass_count);\n        }\n\n        auto best_cost = std::min(best_spatial_split.cost, best_object_split.cost);\n        bool use_spatial_split = best_cost < best_object_split.cost;\n\n        // Make sure the cost of splitting does not exceed the cost of not splitting\n        auto max_split_cost = node.bounding_box_proxy().half_area() * (item.work_size() - builder.traversal_cost);\n        if (best_cost >= max_split_cost) {\n            if (item.work_size() > builder.max_leaf_size) {\n                // Fallback strategy: median split on the largest axis\n                use_spatial_split = false;\n                best_object_split.index = (item.begin + item.end) / 2;\n                best_object_split.axis  = node.bounding_box_proxy().to_bounding_box().largest_axis();\n                best_object_split.left_bbox  = BoundingBox<Scalar>::empty();\n                best_object_split.right_bbox = BoundingBox<Scalar>::empty();\n                for (size_t i = item.begin; i < best_object_split.index; ++i)\n                    best_object_split.left_bbox.extend(references[best_object_split.axis][i].bbox);\n                for (size_t i = best_object_split.index; i < item.end; ++i)\n                    best_object_split.right_bbox.extend(references[best_object_split.axis][i].bbox);\n            } else {\n                make_leaf(node, item.begin, item.end);\n                return std::nullopt;\n            }\n        }\n\n        // Apply the (object/spatial) split\n        return use_spatial_split\n            ? std::make_optional(apply_spatial_split(bvh, best_spatial_split, item))\n            : std::make_optional(apply_object_split(bvh, best_object_split, item));\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/sphere.hpp",
    "content": "#ifndef BVH_SPHERE_HPP\n#define BVH_SPHERE_HPP\n\n#include <optional>\n\n#include \"bvh/vector.hpp\"\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/ray.hpp\"\n\nnamespace bvh {\n\n/// Sphere primitive defined by a center and a radius.\ntemplate <typename Scalar>\nstruct Sphere  {\n    struct Intersection {\n        Scalar  t;\n\n        Scalar distance() const { return t; }\n    };\n\n    using ScalarType       = Scalar;\n    using IntersectionType = Intersection;\n\n    Vector3<Scalar> origin;\n    Scalar radius;\n\n    Sphere() = default;\n    Sphere(const Vector3<Scalar>& origin, Scalar radius)\n        : origin(origin), radius(radius)\n    {}\n\n    Vector3<Scalar> center() const {\n        return origin;\n    }\n\n    BoundingBox<Scalar> bounding_box() const {\n        return BoundingBox<Scalar>(origin - Vector3<Scalar>(radius), origin + Vector3<Scalar>(radius));\n    }\n\n    template <bool AssumeNormalized = false>\n    std::optional<Intersection> intersect(const Ray<Scalar>& ray) const {\n        auto oc = ray.origin - origin;\n        auto a = AssumeNormalized ? Scalar(1) : dot(ray.direction, ray.direction);\n        auto b = 2 * dot(ray.direction, oc);\n        auto c = dot(oc, oc) - radius * radius;\n\n        auto delta = b * b - 4 * a * c;\n        if (delta >= 0) {\n            auto inv = -Scalar(0.5) / a;\n            auto sqrt_delta = std::sqrt(delta);\n            auto t0 = (b + sqrt_delta) * inv;\n            if (t0 >= ray.tmin && t0 <= ray.tmax)\n                return std::make_optional(Intersection { t0 });\n            auto t1 = (b - sqrt_delta) * inv;\n            if (t1 >= ray.tmin && t1 <= ray.tmax)\n                return std::make_optional(Intersection { t1 });\n        }\n\n        return std::nullopt;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/sweep_sah_builder.hpp",
    "content": "#ifndef BVH_SWEEP_SAH_BUILDER_HPP\n#define BVH_SWEEP_SAH_BUILDER_HPP\n\n#include <array>\n#include <optional>\n\n#include \"bvh/bvh.hpp\"\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/top_down_builder.hpp\"\n#include \"bvh/sah_based_algorithm.hpp\"\n#include \"bvh/radix_sort.hpp\"\n\nnamespace bvh {\n\ntemplate <typename> class SweepSahBuildTask;\n\n/// This is a top-down, full-sweep SAH-based BVH builder. Primitives are only\n/// sorted once, and a stable partitioning algorithm is used when splitting,\n/// so as to keep the relative order of primitives within each partition intact.\ntemplate <typename Bvh>\nclass SweepSahBuilder : public TopDownBuilder, public SahBasedAlgorithm<Bvh> {\n    using Scalar    = typename Bvh::ScalarType;\n    using BuildTask = SweepSahBuildTask<Bvh>;\n    using Key       = typename SizedIntegerType<sizeof(Scalar) * CHAR_BIT>::Unsigned;\n    using Mark      = typename BuildTask::MarkType;\n\n    using TopDownBuilder::run_task;\n\n    friend BuildTask;\n\n    RadixSort<10> radix_sort;\n    Bvh& bvh;\n\npublic:\n    using TopDownBuilder::max_depth;\n    using TopDownBuilder::max_leaf_size;\n    using SahBasedAlgorithm<Bvh>::traversal_cost;\n\n    SweepSahBuilder(Bvh& bvh)\n        : bvh(bvh)\n    {}\n\n    void build(\n        const BoundingBox<Scalar>& global_bbox,\n        const BoundingBox<Scalar>* bboxes,\n        const Vector3<Scalar>* centers,\n        size_t primitive_count)\n    {\n        assert(primitive_count > 0);\n\n        // Allocate buffers\n        bvh.nodes = std::make_unique<typename Bvh::Node[]>(2 * primitive_count + 1);\n        bvh.primitive_indices = std::make_unique<size_t[]>(primitive_count);\n\n        auto reference_data = std::make_unique<size_t[]>(primitive_count * 3);\n        auto cost_data      = std::make_unique<Scalar[]>(primitive_count * 3);\n        auto key_data       = std::make_unique<Key[]>(primitive_count * 2);\n        auto mark_data      = std::make_unique<Mark[]>(primitive_count);\n\n        std::array<Scalar*, 3> costs = {\n            cost_data.get(),\n            cost_data.get() + primitive_count,\n            cost_data.get() + 2 * primitive_count\n        };\n\n        std::array<size_t*, 3> sorted_references;\n        size_t* unsorted_references = bvh.primitive_indices.get();\n        Key* sorted_keys = key_data.get();\n        Key* unsorted_keys = key_data.get() + primitive_count;\n\n        bvh.node_count = 1;\n        bvh.nodes[0].bounding_box_proxy() = global_bbox;\n\n        #pragma omp parallel\n        {\n            // Sort the primitives on each axis once\n            for (int axis = 0; axis < 3; ++axis) {\n                #pragma omp single\n                {\n                    sorted_references[axis] = unsorted_references;\n                    unsorted_references = reference_data.get() + axis * primitive_count;\n                    // Make sure that one array is the final array of references used by the BVH\n                    if (axis != 0 && sorted_references[axis] == bvh.primitive_indices.get())\n                        std::swap(sorted_references[axis], unsorted_references);\n                    assert(axis < 2 ||\n                           sorted_references[0] == bvh.primitive_indices.get() ||\n                           sorted_references[1] == bvh.primitive_indices.get());\n                }\n\n                #pragma omp for\n                for (size_t i = 0; i < primitive_count; ++i) {\n                    sorted_keys[i] = radix_sort.make_key(centers[i][axis]);\n                    sorted_references[axis][i] = i;\n                }\n\n                radix_sort.sort_in_parallel(\n                    sorted_keys,\n                    unsorted_keys,\n                    sorted_references[axis],\n                    unsorted_references,\n                    primitive_count,\n                    sizeof(Scalar) * CHAR_BIT);\n            }\n\n            #pragma omp single\n            {\n                BuildTask first_task(*this, bboxes, centers, sorted_references, costs, mark_data.get());\n                run_task(first_task, 0, 0, primitive_count, 0);\n            }\n        }\n    }\n};\n\ntemplate <typename Bvh>\nclass SweepSahBuildTask : public TopDownBuildTask {\n    using Scalar  = typename Bvh::ScalarType;\n    using Builder = SweepSahBuilder<Bvh>;\n    using Mark    = uint_fast8_t;\n\n    using TopDownBuildTask::WorkItem;\n\n    Builder& builder;\n    const BoundingBox<Scalar>* bboxes;\n    const Vector3<Scalar>* centers;\n\n    std::array<size_t* bvh_restrict, 3> references;\n    std::array<Scalar* bvh_restrict, 3> costs;\n    Mark* marks;\n\n    std::pair<Scalar, size_t> find_split(int axis, size_t begin, size_t end) {\n        auto bbox = BoundingBox<Scalar>::empty();\n        for (size_t i = end - 1; i > begin; --i) {\n            bbox.extend(bboxes[references[axis][i]]);\n            costs[axis][i] = bbox.half_area() * (end - i);\n        }\n        bbox = BoundingBox<Scalar>::empty();\n        auto best_split = std::pair<Scalar, size_t>(std::numeric_limits<Scalar>::max(), end);\n        for (size_t i = begin; i < end - 1; ++i) {\n            bbox.extend(bboxes[references[axis][i]]);\n            auto cost = bbox.half_area() * (i + 1 - begin) + costs[axis][i + 1];\n            if (cost < best_split.first)\n                best_split = std::make_pair(cost, i + 1);\n        }\n        return best_split;\n    }\n\npublic:\n    using MarkType     = Mark;\n    using WorkItemType = WorkItem;\n\n    SweepSahBuildTask(\n        Builder& builder,\n        const BoundingBox<Scalar>* bboxes,\n        const Vector3<Scalar>* centers,\n        const std::array<size_t*, 3>& references,\n        const std::array<Scalar*, 3>& costs,\n        Mark* marks)\n        : builder(builder)\n        , bboxes(bboxes)\n        , centers(centers)\n        , references { references[0], references[1], references[2] }\n        , costs { costs[0], costs[1], costs[2] }\n        , marks(marks)\n    {}\n\n    std::optional<std::pair<WorkItem, WorkItem>> build(const WorkItem& item) {\n        auto& bvh  = builder.bvh;\n        auto& node = bvh.nodes[item.node_index];\n\n        auto make_leaf = [] (typename Bvh::Node& node, size_t begin, size_t end) {\n            node.first_child_or_primitive = begin;\n            node.primitive_count          = end - begin;\n        };\n\n        if (item.work_size() <= 1 || item.depth >= builder.max_depth) {\n            make_leaf(node, item.begin, item.end);\n            return std::nullopt;\n        }\n\n        std::pair<Scalar, size_t> best_splits[3];\n        [[maybe_unused]] bool should_spawn_tasks = item.work_size() > builder.task_spawn_threshold;\n\n        // Sweep primitives to find the best cost\n        #pragma omp taskloop if (should_spawn_tasks) grainsize(1) default(shared)\n        for (int axis = 0; axis < 3; ++axis)\n            best_splits[axis] = find_split(axis, item.begin, item.end);\n\n        int best_axis = 0;\n        if (best_splits[0].first > best_splits[1].first)\n            best_axis = 1;\n        if (best_splits[best_axis].first > best_splits[2].first)\n            best_axis = 2;\n\n        auto split_index = best_splits[best_axis].second;\n\n        // Make sure the cost of splitting does not exceed the cost of not splitting\n        auto max_split_cost = node.bounding_box_proxy().half_area() * (item.work_size() - builder.traversal_cost);\n        if (best_splits[best_axis].first >= max_split_cost) {\n            if (item.work_size() > builder.max_leaf_size) {\n                // Fallback strategy: median split on largest axis\n                best_axis = node.bounding_box_proxy().to_bounding_box().largest_axis();\n                split_index = (item.begin + item.end) / 2;\n            } else {\n                make_leaf(node, item.begin, item.end);\n                return std::nullopt;\n            }\n        }\n\n        int other_axis[2] = { (best_axis + 1) % 3, (best_axis + 2) % 3 };\n\n        for (size_t i = item.begin;  i < split_index; ++i) marks[references[best_axis][i]] = 1;\n        for (size_t i = split_index; i < item.end;    ++i) marks[references[best_axis][i]] = 0;\n        auto partition_predicate = [&] (size_t i) { return marks[i] != 0; };\n\n        auto left_bbox  = BoundingBox<Scalar>::empty();\n        auto right_bbox = BoundingBox<Scalar>::empty();\n\n        // Partition reference arrays and compute bounding boxes\n        #pragma omp taskgroup\n        {\n            #pragma omp task if (should_spawn_tasks) default(shared)\n            { std::stable_partition(references[other_axis[0]] + item.begin, references[other_axis[0]] + item.end, partition_predicate); }\n            #pragma omp task if (should_spawn_tasks) default(shared)\n            { std::stable_partition(references[other_axis[1]] + item.begin, references[other_axis[1]] + item.end, partition_predicate); }\n            #pragma omp task if (should_spawn_tasks) default(shared)\n            {\n                for (size_t i = item.begin; i < split_index; ++i)\n                    left_bbox.extend(bboxes[references[best_axis][i]]);\n            }\n            #pragma omp task if (should_spawn_tasks) default(shared)\n            {\n                for (size_t i = split_index; i < item.end; ++i)\n                    right_bbox.extend(bboxes[references[best_axis][i]]);\n            }\n        }\n\n        // Allocate space for children\n        size_t first_child;\n        #pragma omp atomic capture\n        { first_child = bvh.node_count; bvh.node_count += 2; }\n\n        auto& left  = bvh.nodes[first_child + 0];\n        auto& right = bvh.nodes[first_child + 1];\n        node.first_child_or_primitive = first_child;\n        node.primitive_count          = 0;\n\n        left.bounding_box_proxy()  = left_bbox;\n        right.bounding_box_proxy() = right_bbox;\n        WorkItem first_item (first_child + 0, item.begin, split_index, item.depth + 1);\n        WorkItem second_item(first_child + 1, split_index, item.end,   item.depth + 1);\n        return std::make_optional(std::make_pair(first_item, second_item));\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/top_down_builder.hpp",
    "content": "#ifndef BVH_TOP_DOWN_BUILDER_HPP\n#define BVH_TOP_DOWN_BUILDER_HPP\n\n#include <stack>\n#include <cassert>\n\nnamespace bvh {\n\n/// Base class for top-down build tasks.\nclass TopDownBuildTask {\nprotected:\n    struct WorkItem {\n        size_t node_index;\n        size_t begin;\n        size_t end;\n        size_t depth;\n\n        WorkItem() = default;\n        WorkItem(size_t node_index, size_t begin, size_t end, size_t depth)\n            : node_index(node_index), begin(begin), end(end), depth(depth)\n        {}\n\n        size_t work_size() const { return end - begin; }\n    };\n};\n\n/// Base class for top-down BVH builders.\nclass TopDownBuilder {\npublic:\n    /// Threshold (number of primitives) under which the builder\n    /// doesn't spawn any more OpenMP tasks.\n    size_t task_spawn_threshold = 1024;\n\n    /// Maximum depth of the generated tree. This can be used to make\n    /// sure the required traversal stack size is under a given constant.\n    size_t max_depth = 64;\n\n    /// Largest permissible leaf size. The builder will attempt to split\n    /// using a median split on the largest axis as a default strategy\n    /// to avoid creating leaves that are larger than this threshold.\n    size_t max_leaf_size = 16;\n\nprotected:\n    ~TopDownBuilder() {}\n\n    template <typename BuildTask, typename... Args>\n    void run_task(BuildTask& task, Args&&... args) {\n        using WorkItem = typename BuildTask::WorkItemType;\n        std::stack<WorkItem> stack;\n        stack.emplace(std::forward<Args&&>(args)...);\n        while (!stack.empty()) {\n            auto work_item = stack.top();\n            assert(work_item.depth <= max_depth);\n            stack.pop();\n\n            auto more_work = task.build(work_item);\n            if (more_work) {\n                if (more_work->first.work_size() > more_work->second.work_size())\n                    std::swap(more_work->first, more_work->second);\n\n                stack.push(more_work->second);\n                auto first_item = more_work->first;\n                if (first_item.work_size() > task_spawn_threshold) {\n                    BuildTask new_task(task);\n                    #pragma omp task firstprivate(new_task, first_item)\n                    { run_task(new_task, first_item); }\n                } else {\n                    stack.push(first_item);\n                }\n            }\n        }\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/triangle.hpp",
    "content": "#ifndef BVH_TRIANGLE_HPP\n#define BVH_TRIANGLE_HPP\n\n#include <optional>\n#include <cassert>\n\n#include \"bvh/utilities.hpp\"\n#include \"bvh/vector.hpp\"\n#include \"bvh/bounding_box.hpp\"\n#include \"bvh/ray.hpp\"\n\nnamespace bvh {\n\n/// Triangle primitive, defined by three points, and using the Moeller-Trumbore test.\n/// By default, the normal is left-handed, which minimizes the number of operations in\n/// the intersection routine.\ntemplate <typename Scalar, bool LeftHandedNormal = true>\nstruct Triangle {\n    struct Intersection {\n        Scalar t, u, v;\n        Scalar distance() const { return t; }\n    };\n\n    using ScalarType       = Scalar;\n    using IntersectionType = Intersection;\n\n    Vector3<Scalar> p0, e1, e2, n;\n\n    Triangle() = default;\n    Triangle(const Vector3<Scalar>& p0, const Vector3<Scalar>& p1, const Vector3<Scalar>& p2)\n        : p0(p0), e1(p0 - p1), e2(p2 - p0)\n    {\n        n = LeftHandedNormal ? cross(e1, e2) : cross(e2, e1);\n    }\n\n    Vector3<Scalar> p1() const { return p0 - e1; }\n    Vector3<Scalar> p2() const { return p0 + e2; }\n\n    BoundingBox<Scalar> bounding_box() const {\n        BoundingBox<Scalar> bbox(p0);\n        bbox.extend(p1());\n        bbox.extend(p2());\n        return bbox;\n    }\n\n    Vector3<Scalar> center() const {\n        return (p0 + p1() + p2()) * (Scalar(1.0) / Scalar(3.0));\n    }\n\n    std::pair<Vector3<Scalar>, Vector3<Scalar>> edge(size_t i) const {\n        assert(i < 3);\n        Vector3<Scalar> p[] = { p0, p1(), p2() };\n        return std::make_pair(p[i], p[(i + 1) % 3]);\n    }\n\n    Scalar area() const {\n        return length(n) * Scalar(0.5);\n    }\n\n    std::pair<BoundingBox<Scalar>, BoundingBox<Scalar>> split(size_t axis, Scalar position) const {\n        Vector3<Scalar> p[] = { p0, p1(), p2() };\n        auto left  = BoundingBox<Scalar>::empty();\n        auto right = BoundingBox<Scalar>::empty();\n        auto split_edge = [=] (const Vector3<Scalar>& a, const Vector3<Scalar>& b) {\n            auto t = (position - a[axis]) / (b[axis] - a[axis]);\n            return a + t * (b - a);\n        };\n        auto q0 = p[0][axis] <= position;\n        auto q1 = p[1][axis] <= position;\n        auto q2 = p[2][axis] <= position;\n        if (q0) left.extend(p[0]);\n        else    right.extend(p[0]);\n        if (q1) left.extend(p[1]);\n        else    right.extend(p[1]);\n        if (q2) left.extend(p[2]);\n        else    right.extend(p[2]);\n        if (q0 ^ q1) {\n            auto m = split_edge(p[0], p[1]);\n            left.extend(m);\n            right.extend(m);\n        }\n        if (q1 ^ q2) {\n            auto m = split_edge(p[1], p[2]);\n            left.extend(m);\n            right.extend(m);\n        }\n        if (q2 ^ q0) {\n            auto m = split_edge(p[2], p[0]);\n            left.extend(m);\n            right.extend(m);\n        }\n        return std::make_pair(left, right);\n    }\n\n    std::optional<Intersection> intersect(const Ray<Scalar>& ray) const {\n        auto negate_when_right_handed = [] (Scalar x) { return LeftHandedNormal ? x : -x; };\n\n        auto c = p0 - ray.origin;\n        auto r = cross(ray.direction, c);\n        auto inv_det = negate_when_right_handed(1.0) / dot(n, ray.direction);\n\n        auto u = dot(r, e2) * inv_det;\n        auto v = dot(r, e1) * inv_det;\n        auto w = Scalar(1.0) - u - v;\n\n        // These comparisons are designed to return false\n        // when one of t, u, or v is a NaN\n        if (u >= 0 && v >= 0 && w >= 0) {\n            auto t = negate_when_right_handed(dot(n, c)) * inv_det;\n            if (t >= ray.tmin && t <= ray.tmax)\n                return std::make_optional(Intersection{ t, u, v });\n        }\n\n        return std::nullopt;\n    }\n};\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/utilities.hpp",
    "content": "#ifndef BVH_UTILITIES_HPP\n#define BVH_UTILITIES_HPP\n\n#include <cstring>\n#include <cstdint>\n#include <atomic>\n#include <memory>\n#include <queue>\n#include <algorithm>\n#include <cmath>\n#include <climits>\n#include <type_traits>\n\n#include \"bvh/bounding_box.hpp\"\n\nnamespace bvh {\n\n/// Safe function to reinterpret the bits of the given value as another type.\ntemplate <typename To, typename From>\nTo as(From from) {\n    static_assert(sizeof(To) == sizeof(From));\n    To to;\n    std::memcpy(&to, &from, sizeof(from));\n    return to;\n}\n\n/// Equivalent to copysign(x, x * y).\ninline float product_sign(float x, float y) {\n    return as<float>(as<uint32_t>(x) ^ (as<uint32_t>(y) & UINT32_C(0x80000000)));\n}\n\n/// Equivalent to copysign(x, x * y).\ninline double product_sign(double x, double y) {\n    return as<double>(as<uint64_t>(x) ^ (as<uint64_t>(y) & UINT64_C(0x8000000000000000)));\n}\n\n#ifdef _MSC_VER\n#pragma fp_contract(on)\n#else\n#pragma STDC FP_CONTRACT ON\n#endif\n\ninline float fast_multiply_add(float x, float y, float z) {\n#ifdef FP_FAST_FMAF\n    return std::fmaf(x, y, z);\n#else\n    return x * y + z;\n#endif\n}\n\ninline double fast_multiply_add(double x, double y, double z) {\n#ifdef FP_FAST_FMA\n    return std::fma(x, y, z);\n#else\n    return x * y + z;\n#endif\n}\n\n/// Returns the mininum of two values.\n/// Guaranteed to return a non-NaN value if the right hand side is not a NaN.\ntemplate <typename T>\nconst T& robust_min(const T& x, const T& y) {\n    return x < y ? x : y;\n}\n\n/// Returns the maximum of two values.\n/// Guaranteed to return a non-NaN value if the right hand side is not a NaN.\ntemplate <typename T>\nconst T& robust_max(const T& x, const T& y) {\n    return x > y ? x : y;\n}\n\ntemplate <typename T>\nvoid atomic_max(std::atomic<T>& x, T y) {\n    auto z = x.load();\n    while (z < y && !x.compare_exchange_weak(z, y)) ;\n}\n\n/// Templates that contains signed and unsigned integer types of the given number of bits.\ntemplate <size_t Bits>\nstruct SizedIntegerType {\n    static_assert(Bits <= 8);\n    using Signed   = int8_t;\n    using Unsigned = uint8_t;\n};\n\ntemplate <>\nstruct SizedIntegerType<64> {\n    using Signed   = int64_t;\n    using Unsigned = uint64_t;\n};\n\ntemplate <>\nstruct SizedIntegerType<32> {\n    using Signed   = int32_t;\n    using Unsigned = uint32_t;\n};\n\ntemplate <>\nstruct SizedIntegerType<16> {\n    using Signed   = int16_t;\n    using Unsigned = uint16_t;\n};\n\n/// Adds the given number of ULPs (Unit in the Last Place) to the floating-point argument.\ntemplate <typename T, std::enable_if_t<std::is_floating_point<T>::value, int> = 0>\nT add_ulp_magnitude(T x, unsigned ulps) {\n    using U = typename SizedIntegerType<sizeof(T) * CHAR_BIT>::Unsigned;\n    return std::isfinite(x) ? as<T>(as<U>(x) + ulps) : x;\n}\n\n/// Computes the (rounded-up) compile-time log in base-2 of an unsigned integer.\ninline constexpr size_t round_up_log2(size_t i, size_t p = 0) {\n    return (size_t(1) << p) >= i ? p : round_up_log2(i, p + 1);\n}\n\n/// Returns the number of bits that are equal to zero,\n/// starting from the most significant one.\ntemplate <typename T, std::enable_if_t<std::is_unsigned<T>::value, int> = 0>\nsize_t count_leading_zeros(T value) {\n    static constexpr size_t bit_count = sizeof(T) * CHAR_BIT;\n    size_t a = 0;\n    size_t b = bit_count;\n    auto all = T(-1);\n    for (size_t i = 0; i < round_up_log2(bit_count); i++) {\n        auto m = (a + b) / 2;\n        auto mask = all << m;\n        if (value & mask) a = m + 1;\n        else              b = m;\n    }\n    return bit_count - b;\n}\n\n/// Permutes primitives such that the primitive at index i is `primitives[indices[i]]`.\n/// Allows to remove indirections in the primitive intersectors.\ntemplate <typename Primitive>\nstd::unique_ptr<Primitive[]> permute_primitives(const Primitive* primitives, const size_t* indices, size_t primitive_count) {\n    auto primitives_copy = std::make_unique<Primitive[]>(primitive_count);\n    #pragma omp parallel for\n    for (size_t i = 0; i < primitive_count; ++i)\n        primitives_copy[i] = primitives[indices[i]];\n    return primitives_copy;\n}\n\n/// Computes the bounding box and the center of each primitive in given array.\ntemplate <typename Primitive, typename Scalar = typename Primitive::ScalarType>\nstd::pair<std::unique_ptr<BoundingBox<Scalar>[]>, std::unique_ptr<Vector3<Scalar>[]>>\ncompute_bounding_boxes_and_centers(const Primitive* primitives, size_t primitive_count) {\n    auto bounding_boxes  = std::make_unique<BoundingBox<Scalar>[]>(primitive_count);\n    auto centers         = std::make_unique<Vector3<Scalar>[]>(primitive_count);\n\n    #pragma omp parallel for\n    for (size_t i = 0; i < primitive_count; ++i) {\n        bounding_boxes[i] = primitives[i].bounding_box();\n        centers[i]        = primitives[i].center();\n    }\n\n    return std::make_pair(std::move(bounding_boxes), std::move(centers));\n}\n\n/// Computes the union of all the bounding boxes in the given array.\ntemplate <typename Scalar>\nBoundingBox<Scalar> compute_bounding_boxes_union(const BoundingBox<Scalar>* bboxes, size_t count) {\n    auto bbox = BoundingBox<Scalar>::empty();\n\n    #pragma omp declare reduction \\\n        (bbox_extend:BoundingBox<Scalar>:omp_out.extend(omp_in)) \\\n        initializer(omp_priv = BoundingBox<Scalar>::empty())\n\n    #pragma omp parallel for reduction(bbox_extend: bbox)\n    for (size_t i = 0; i < count; ++i)\n        bbox.extend(bboxes[i]);\n\n    return bbox;\n}\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/bvh/vector.hpp",
    "content": "#ifndef BVH_VECTOR_HPP\n#define BVH_VECTOR_HPP\n\n#include <cstddef>\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <limits>\n#include <type_traits>\n\n#include \"bvh/platform.hpp\"\n\nnamespace bvh {\n\ntemplate <typename, size_t> struct Vector;\n\n/// Helper class to set the elements of a vector.\ntemplate <size_t I, typename Scalar, size_t N>\nstruct VectorSetter {\n    template <typename... Args>\n    bvh_always_inline static void set(Vector<Scalar, N>& v, Scalar s, Args... args) {\n        v[I] = s;\n        VectorSetter<I + 1, Scalar, N>::set(v, args...);\n    }\n};\n\ntemplate <typename Scalar, size_t N>\nstruct VectorSetter<N, Scalar, N> {\n    bvh_always_inline static void set(Vector<Scalar, N>&) {}\n};\n\n/// An N-dimensional vector class.\ntemplate <typename Scalar, size_t N>\nstruct Vector {\n    Scalar values[N];\n\n    Vector() = default;\n    bvh_always_inline explicit Vector(Scalar s) { std::fill(values, values + N, s); }\n\n    template <size_t M, std::enable_if_t<(M > N), int> = 0>\n    bvh_always_inline explicit Vector(const Vector<Scalar, M>& other) {\n        std::copy(other.values, other.values + N, values);\n    }\n\n    template <typename... Args>\n    bvh_always_inline Vector(Scalar first, Scalar second, Args... args) {\n        set(first, second, args...);\n    }\n\n    template <typename F, std::enable_if_t<std::is_invocable<F, size_t>::value, int> = 0>\n    bvh_always_inline Vector(F f) {\n        for (size_t i = 0; i < N; ++i)\n            values[i] = f(i);\n    }\n\n    template <typename... Args>\n    bvh_always_inline void set(Args... args) {\n        VectorSetter<0, Scalar, N>::set(*this, Scalar(args)...);\n    }\n\n    bvh_always_inline Vector operator - () const {\n        return Vector([this] (size_t i) { return -values[i]; });\n    }\n\n    bvh_always_inline Vector inverse() const {\n        return Vector([this] (size_t i) { return Scalar(1) / values[i]; });\n    }\n\n    bvh_always_inline Vector safe_inverse() const {\n        static constexpr auto threshold = std::numeric_limits<Scalar>::epsilon();\n        return Vector([&] (size_t i) {\n            return Scalar(1) / (std::fabs(values[i]) < threshold ? std::copysign(threshold, values[i]) : values[i]);\n        });\n    }\n\n    bvh_always_inline Vector& operator += (const Vector& other) {\n        return *this = *this + other;\n    }\n\n    bvh_always_inline Vector& operator -= (const Vector& other) {\n        return *this = *this - other;\n    }\n\n    bvh_always_inline Vector& operator *= (const Vector& other) {\n        return *this = *this * other;\n    }\n\n    bvh_always_inline Scalar& operator [] (size_t i) { return values[i]; }\n    bvh_always_inline Scalar  operator [] (size_t i) const { return values[i]; }\n};\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> operator + (const Vector<Scalar, N>& a, const Vector<Scalar, N>& b) {\n    return Vector<Scalar, N>([=] (size_t i) { return a[i] + b[i]; });\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> operator - (const Vector<Scalar, N>& a, const Vector<Scalar, N>& b) {\n    return Vector<Scalar, N>([=] (size_t i) { return a[i] - b[i]; });\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> operator * (const Vector<Scalar, N>& a, const Vector<Scalar, N>& b) {\n    return Vector<Scalar, N>([=] (size_t i) { return a[i] * b[i]; });\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> min(const Vector<Scalar, N>& a, const Vector<Scalar, N>& b) {\n    return Vector<Scalar, N>([=] (size_t i) { return std::min(a[i], b[i]); });\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> max(const Vector<Scalar, N>& a, const Vector<Scalar, N>& b) {\n    return Vector<Scalar, N>([=] (size_t i) { return std::max(a[i], b[i]); });\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> operator * (const Vector<Scalar, N>& a, Scalar s) {\n    return Vector<Scalar, N>([=] (size_t i) { return a[i] * s; });\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> operator * (Scalar s, const Vector<Scalar, N>& b) {\n    return b * s;\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Scalar dot(const Vector<Scalar, N>& a, const Vector<Scalar, N>& b) {\n    Scalar sum = a[0] * b[0];\n    for (size_t i = 1; i < N; ++i)\n        sum += a[i] * b[i];\n    return sum;\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Scalar length(const Vector<Scalar, N>& v) {\n    return std::sqrt(dot(v, v));\n}\n\ntemplate <typename Scalar, size_t N>\nbvh_always_inline\ninline Vector<Scalar, N> normalize(const Vector<Scalar, N>& v) {\n    auto inv = Scalar(1) / length(v);\n    return v * inv;\n}\n\ntemplate <typename Scalar>\nusing Vector3 = Vector<Scalar, 3>;\n\ntemplate <typename Scalar>\nbvh_always_inline\ninline Vector3<Scalar> cross(const Vector3<Scalar>& a, const Vector3<Scalar>& b) {\n    return Vector3<Scalar>([=] (size_t i) {\n        size_t j = (i + 1) % 3;\n        size_t k = (i + 2) % 3;\n        return a[j] * b[k] - a[k] * b[j];\n    });\n}\n\n} // namespace bvh\n\n#endif\n"
  },
  {
    "path": "External/stb/stb_rect_pack.h",
    "content": "// stb_rect_pack.h - v1.01 - public domain - rectangle packing\n// Sean Barrett 2014\n//\n// Useful for e.g. packing rectangular textures into an atlas.\n// Does not do rotation.\n//\n// Before #including,\n//\n//    #define STB_RECT_PACK_IMPLEMENTATION\n//\n// in the file that you want to have the implementation.\n//\n// Not necessarily the awesomest packing method, but better than\n// the totally naive one in stb_truetype (which is primarily what\n// this is meant to replace).\n//\n// Has only had a few tests run, may have issues.\n//\n// More docs to come.\n//\n// No memory allocations; uses qsort() and assert() from stdlib.\n// Can override those by defining STBRP_SORT and STBRP_ASSERT.\n//\n// This library currently uses the Skyline Bottom-Left algorithm.\n//\n// Please note: better rectangle packers are welcome! Please\n// implement them to the same API, but with a different init\n// function.\n//\n// Credits\n//\n//  Library\n//    Sean Barrett\n//  Minor features\n//    Martins Mozeiko\n//    github:IntellectualKitty\n//\n//  Bugfixes / warning fixes\n//    Jeremy Jaussaud\n//    Fabian Giesen\n//\n// Version history:\n//\n//     1.01  (2021-07-11)  always use large rect mode, expose STBRP__MAXVAL in public section\n//     1.00  (2019-02-25)  avoid small space waste; gracefully fail too-wide rectangles\n//     0.99  (2019-02-07)  warning fixes\n//     0.11  (2017-03-03)  return packing success/fail result\n//     0.10  (2016-10-25)  remove cast-away-const to avoid warnings\n//     0.09  (2016-08-27)  fix compiler warnings\n//     0.08  (2015-09-13)  really fix bug with empty rects (w=0 or h=0)\n//     0.07  (2015-09-13)  fix bug with empty rects (w=0 or h=0)\n//     0.06  (2015-04-15)  added STBRP_SORT to allow replacing qsort\n//     0.05:  added STBRP_ASSERT to allow replacing assert\n//     0.04:  fixed minor bug in STBRP_LARGE_RECTS support\n//     0.01:  initial release\n//\n// LICENSE\n//\n//   See end of file for license information.\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//       INCLUDE SECTION\n//\n\n#ifndef STB_INCLUDE_STB_RECT_PACK_H\n#define STB_INCLUDE_STB_RECT_PACK_H\n\n#define STB_RECT_PACK_VERSION  1\n\n#ifdef STBRP_STATIC\n#define STBRP_DEF static\n#else\n#define STBRP_DEF extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct stbrp_context stbrp_context;\ntypedef struct stbrp_node    stbrp_node;\ntypedef struct stbrp_rect    stbrp_rect;\n\ntypedef int            stbrp_coord;\n\n#define STBRP__MAXVAL  0x7fffffff\n// Mostly for internal use, but this is the maximum supported coordinate value.\n\nSTBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);\n// Assign packed locations to rectangles. The rectangles are of type\n// 'stbrp_rect' defined below, stored in the array 'rects', and there\n// are 'num_rects' many of them.\n//\n// Rectangles which are successfully packed have the 'was_packed' flag\n// set to a non-zero value and 'x' and 'y' store the minimum location\n// on each axis (i.e. bottom-left in cartesian coordinates, top-left\n// if you imagine y increasing downwards). Rectangles which do not fit\n// have the 'was_packed' flag set to 0.\n//\n// You should not try to access the 'rects' array from another thread\n// while this function is running, as the function temporarily reorders\n// the array while it executes.\n//\n// To pack into another rectangle, you need to call stbrp_init_target\n// again. To continue packing into the same rectangle, you can call\n// this function again. Calling this multiple times with multiple rect\n// arrays will probably produce worse packing results than calling it\n// a single time with the full rectangle array, but the option is\n// available.\n//\n// The function returns 1 if all of the rectangles were successfully\n// packed and 0 otherwise.\n\nstruct stbrp_rect\n{\n   // reserved for your use:\n   int            id;\n\n   // input:\n   stbrp_coord    w, h;\n\n   // output:\n   stbrp_coord    x, y;\n   int            was_packed;  // non-zero if valid packing\n\n}; // 16 bytes, nominally\n\n\nSTBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);\n// Initialize a rectangle packer to:\n//    pack a rectangle that is 'width' by 'height' in dimensions\n//    using temporary storage provided by the array 'nodes', which is 'num_nodes' long\n//\n// You must call this function every time you start packing into a new target.\n//\n// There is no \"shutdown\" function. The 'nodes' memory must stay valid for\n// the following stbrp_pack_rects() call (or calls), but can be freed after\n// the call (or calls) finish.\n//\n// Note: to guarantee best results, either:\n//       1. make sure 'num_nodes' >= 'width'\n//   or  2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'\n//\n// If you don't do either of the above things, widths will be quantized to multiples\n// of small integers to guarantee the algorithm doesn't run out of temporary storage.\n//\n// If you do #2, then the non-quantized algorithm will be used, but the algorithm\n// may run out of temporary storage and be unable to pack some rectangles.\n\nSTBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);\n// Optionally call this function after init but before doing any packing to\n// change the handling of the out-of-temp-memory scenario, described above.\n// If you call init again, this will be reset to the default (false).\n\n\nSTBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);\n// Optionally select which packing heuristic the library should use. Different\n// heuristics will produce better/worse results for different data sets.\n// If you call init again, this will be reset to the default.\n\nenum\n{\n   STBRP_HEURISTIC_Skyline_default=0,\n   STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,\n   STBRP_HEURISTIC_Skyline_BF_sortHeight\n};\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// the details of the following structures don't matter to you, but they must\n// be visible so you can handle the memory allocations for them\n\nstruct stbrp_node\n{\n   stbrp_coord  x,y;\n   stbrp_node  *next;\n};\n\nstruct stbrp_context\n{\n   int width;\n   int height;\n   int align;\n   int init_mode;\n   int heuristic;\n   int num_nodes;\n   stbrp_node *active_head;\n   stbrp_node *free_head;\n   stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//     IMPLEMENTATION SECTION\n//\n\n#ifdef STB_RECT_PACK_IMPLEMENTATION\n#ifndef STBRP_SORT\n#include <stdlib.h>\n#define STBRP_SORT qsort\n#endif\n\n#ifndef STBRP_ASSERT\n#include <assert.h>\n#define STBRP_ASSERT assert\n#endif\n\n#ifdef _MSC_VER\n#define STBRP__NOTUSED(v)  (void)(v)\n#define STBRP__CDECL       __cdecl\n#else\n#define STBRP__NOTUSED(v)  (void)sizeof(v)\n#define STBRP__CDECL\n#endif\n\nenum\n{\n   STBRP__INIT_skyline = 1\n};\n\nSTBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)\n{\n   switch (context->init_mode) {\n      case STBRP__INIT_skyline:\n         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);\n         context->heuristic = heuristic;\n         break;\n      default:\n         STBRP_ASSERT(0);\n   }\n}\n\nSTBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)\n{\n   if (allow_out_of_mem)\n      // if it's ok to run out of memory, then don't bother aligning them;\n      // this gives better packing, but may fail due to OOM (even though\n      // the rectangles easily fit). @TODO a smarter approach would be to only\n      // quantize once we've hit OOM, then we could get rid of this parameter.\n      context->align = 1;\n   else {\n      // if it's not ok to run out of memory, then quantize the widths\n      // so that num_nodes is always enough nodes.\n      //\n      // I.e. num_nodes * align >= width\n      //                  align >= width / num_nodes\n      //                  align = ceil(width/num_nodes)\n\n      context->align = (context->width + context->num_nodes-1) / context->num_nodes;\n   }\n}\n\nSTBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)\n{\n   int i;\n\n   for (i=0; i < num_nodes-1; ++i)\n      nodes[i].next = &nodes[i+1];\n   nodes[i].next = NULL;\n   context->init_mode = STBRP__INIT_skyline;\n   context->heuristic = STBRP_HEURISTIC_Skyline_default;\n   context->free_head = &nodes[0];\n   context->active_head = &context->extra[0];\n   context->width = width;\n   context->height = height;\n   context->num_nodes = num_nodes;\n   stbrp_setup_allow_out_of_mem(context, 0);\n\n   // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)\n   context->extra[0].x = 0;\n   context->extra[0].y = 0;\n   context->extra[0].next = &context->extra[1];\n   context->extra[1].x = (stbrp_coord) width;\n   context->extra[1].y = (1<<30);\n   context->extra[1].next = NULL;\n}\n\n// find minimum y position if it starts at x1\nstatic int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)\n{\n   stbrp_node *node = first;\n   int x1 = x0 + width;\n   int min_y, visited_width, waste_area;\n\n   STBRP__NOTUSED(c);\n\n   STBRP_ASSERT(first->x <= x0);\n\n   #if 0\n   // skip in case we're past the node\n   while (node->next->x <= x0)\n      ++node;\n   #else\n   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency\n   #endif\n\n   STBRP_ASSERT(node->x <= x0);\n\n   min_y = 0;\n   waste_area = 0;\n   visited_width = 0;\n   while (node->x < x1) {\n      if (node->y > min_y) {\n         // raise min_y higher.\n         // we've accounted for all waste up to min_y,\n         // but we'll now add more waste for everything we've visted\n         waste_area += visited_width * (node->y - min_y);\n         min_y = node->y;\n         // the first time through, visited_width might be reduced\n         if (node->x < x0)\n            visited_width += node->next->x - x0;\n         else\n            visited_width += node->next->x - node->x;\n      } else {\n         // add waste area\n         int under_width = node->next->x - node->x;\n         if (under_width + visited_width > width)\n            under_width = width - visited_width;\n         waste_area += under_width * (min_y - node->y);\n         visited_width += under_width;\n      }\n      node = node->next;\n   }\n\n   *pwaste = waste_area;\n   return min_y;\n}\n\ntypedef struct\n{\n   int x,y;\n   stbrp_node **prev_link;\n} stbrp__findresult;\n\nstatic stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)\n{\n   int best_waste = (1<<30), best_x, best_y = (1 << 30);\n   stbrp__findresult fr;\n   stbrp_node **prev, *node, *tail, **best = NULL;\n\n   // align to multiple of c->align\n   width = (width + c->align - 1);\n   width -= width % c->align;\n   STBRP_ASSERT(width % c->align == 0);\n\n   // if it can't possibly fit, bail immediately\n   if (width > c->width || height > c->height) {\n      fr.prev_link = NULL;\n      fr.x = fr.y = 0;\n      return fr;\n   }\n\n   node = c->active_head;\n   prev = &c->active_head;\n   while (node->x + width <= c->width) {\n      int y,waste;\n      y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);\n      if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL\n         // bottom left\n         if (y < best_y) {\n            best_y = y;\n            best = prev;\n         }\n      } else {\n         // best-fit\n         if (y + height <= c->height) {\n            // can only use it if it first vertically\n            if (y < best_y || (y == best_y && waste < best_waste)) {\n               best_y = y;\n               best_waste = waste;\n               best = prev;\n            }\n         }\n      }\n      prev = &node->next;\n      node = node->next;\n   }\n\n   best_x = (best == NULL) ? 0 : (*best)->x;\n\n   // if doing best-fit (BF), we also have to try aligning right edge to each node position\n   //\n   // e.g, if fitting\n   //\n   //     ____________________\n   //    |____________________|\n   //\n   //            into\n   //\n   //   |                         |\n   //   |             ____________|\n   //   |____________|\n   //\n   // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned\n   //\n   // This makes BF take about 2x the time\n\n   if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {\n      tail = c->active_head;\n      node = c->active_head;\n      prev = &c->active_head;\n      // find first node that's admissible\n      while (tail->x < width)\n         tail = tail->next;\n      while (tail) {\n         int xpos = tail->x - width;\n         int y,waste;\n         STBRP_ASSERT(xpos >= 0);\n         // find the left position that matches this\n         while (node->next->x <= xpos) {\n            prev = &node->next;\n            node = node->next;\n         }\n         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);\n         y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);\n         if (y + height <= c->height) {\n            if (y <= best_y) {\n               if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {\n                  best_x = xpos;\n                  STBRP_ASSERT(y <= best_y);\n                  best_y = y;\n                  best_waste = waste;\n                  best = prev;\n               }\n            }\n         }\n         tail = tail->next;\n      }\n   }\n\n   fr.prev_link = best;\n   fr.x = best_x;\n   fr.y = best_y;\n   return fr;\n}\n\nstatic stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)\n{\n   // find best position according to heuristic\n   stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);\n   stbrp_node *node, *cur;\n\n   // bail if:\n   //    1. it failed\n   //    2. the best node doesn't fit (we don't always check this)\n   //    3. we're out of memory\n   if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {\n      res.prev_link = NULL;\n      return res;\n   }\n\n   // on success, create new node\n   node = context->free_head;\n   node->x = (stbrp_coord) res.x;\n   node->y = (stbrp_coord) (res.y + height);\n\n   context->free_head = node->next;\n\n   // insert the new node into the right starting point, and\n   // let 'cur' point to the remaining nodes needing to be\n   // stiched back in\n\n   cur = *res.prev_link;\n   if (cur->x < res.x) {\n      // preserve the existing one, so start testing with the next one\n      stbrp_node *next = cur->next;\n      cur->next = node;\n      cur = next;\n   } else {\n      *res.prev_link = node;\n   }\n\n   // from here, traverse cur and free the nodes, until we get to one\n   // that shouldn't be freed\n   while (cur->next && cur->next->x <= res.x + width) {\n      stbrp_node *next = cur->next;\n      // move the current node to the free list\n      cur->next = context->free_head;\n      context->free_head = cur;\n      cur = next;\n   }\n\n   // stitch the list back in\n   node->next = cur;\n\n   if (cur->x < res.x + width)\n      cur->x = (stbrp_coord) (res.x + width);\n\n#ifdef _DEBUG\n   cur = context->active_head;\n   while (cur->x < context->width) {\n      STBRP_ASSERT(cur->x < cur->next->x);\n      cur = cur->next;\n   }\n   STBRP_ASSERT(cur->next == NULL);\n\n   {\n      int count=0;\n      cur = context->active_head;\n      while (cur) {\n         cur = cur->next;\n         ++count;\n      }\n      cur = context->free_head;\n      while (cur) {\n         cur = cur->next;\n         ++count;\n      }\n      STBRP_ASSERT(count == context->num_nodes+2);\n   }\n#endif\n\n   return res;\n}\n\nstatic int STBRP__CDECL rect_height_compare(const void *a, const void *b)\n{\n   const stbrp_rect *p = (const stbrp_rect *) a;\n   const stbrp_rect *q = (const stbrp_rect *) b;\n   if (p->h > q->h)\n      return -1;\n   if (p->h < q->h)\n      return  1;\n   return (p->w > q->w) ? -1 : (p->w < q->w);\n}\n\nstatic int STBRP__CDECL rect_original_order(const void *a, const void *b)\n{\n   const stbrp_rect *p = (const stbrp_rect *) a;\n   const stbrp_rect *q = (const stbrp_rect *) b;\n   return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);\n}\n\nSTBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)\n{\n   int i, all_rects_packed = 1;\n\n   // we use the 'was_packed' field internally to allow sorting/unsorting\n   for (i=0; i < num_rects; ++i) {\n      rects[i].was_packed = i;\n   }\n\n   // sort according to heuristic\n   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);\n\n   for (i=0; i < num_rects; ++i) {\n      if (rects[i].w == 0 || rects[i].h == 0) {\n         rects[i].x = rects[i].y = 0;  // empty rect needs no space\n      } else {\n         stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);\n         if (fr.prev_link) {\n            rects[i].x = (stbrp_coord) fr.x;\n            rects[i].y = (stbrp_coord) fr.y;\n         } else {\n            rects[i].x = rects[i].y = STBRP__MAXVAL;\n         }\n      }\n   }\n\n   // unsort\n   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);\n\n   // set was_packed flags and all_rects_packed status\n   for (i=0; i < num_rects; ++i) {\n      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);\n      if (!rects[i].was_packed)\n         all_rects_packed = 0;\n   }\n\n   // return the all_rects_packed status\n   return all_rects_packed;\n}\n#endif\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "External/tsl/bhopscotch_map.h",
    "content": "/**\n * MIT License\n * \n * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef TSL_BHOPSCOTCH_MAP_H\n#define TSL_BHOPSCOTCH_MAP_H \n\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <map>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include \"hopscotch_hash.h\"\n\n\nnamespace tsl {\n\n    \n/**\n * Similar to tsl::hopscotch_map but instead of using a list for overflowing elements it uses\n * a binary search tree. It thus needs an additional template parameter Compare. Compare should\n * be arithmetically coherent with KeyEqual.\n * \n * The binary search tree allows the map to have a worst-case scenario of O(log n) for search \n * and delete, even if the hash function maps all the elements to the same bucket. \n * For insert, the amortized worst case is O(log n), but the worst case is O(n) in case of rehash.\n * \n * This makes the map resistant to DoS attacks (but doesn't preclude you to have a good hash function,\n * as an element in the bucket array is faster to retrieve than in the tree).\n * \n * @copydoc hopscotch_map\n */\ntemplate<class Key, \n         class T, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Compare = std::less<Key>,\n         class Allocator = std::allocator<std::pair<const Key, T>>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false,\n         class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>\nclass bhopscotch_map {\nprivate:\n    template<typename U>\n    using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;\n    \n    class KeySelect {\n    public:\n        using key_type = Key;\n        \n        const key_type& operator()(const std::pair<const Key, T>& key_value) const {\n            return key_value.first;\n        }\n        \n        const key_type& operator()(std::pair<const Key, T>& key_value) {\n            return key_value.first;\n        }\n    };  \n    \n    class ValueSelect {\n    public:\n        using value_type = T;\n        \n        const value_type& operator()(const std::pair<const Key, T>& key_value) const {\n            return key_value.second;\n        }\n        \n        value_type& operator()(std::pair<const Key, T>& key_value) {\n            return key_value.second;\n        }\n    };\n    \n    \n    // TODO Not optimal as we have to use std::pair<const Key, T> as ValueType which forbid \n    // us to move the key in the bucket array, we have to use copy. Optimize.\n    using overflow_container_type = std::map<Key, T, Compare, Allocator>;\n    using ht = detail_hopscotch_hash::hopscotch_hash<std::pair<const Key, T>, KeySelect, ValueSelect,\n                                                     Hash, KeyEqual, \n                                                     Allocator, NeighborhoodSize, \n                                                     StoreHash, GrowthPolicy,\n                                                     overflow_container_type>;\n    \npublic:\n    using key_type = typename ht::key_type;\n    using mapped_type = T;\n    using value_type = typename ht::value_type;\n    using size_type = typename ht::size_type;\n    using difference_type = typename ht::difference_type;\n    using hasher = typename ht::hasher;\n    using key_equal = typename ht::key_equal;\n    using key_compare = Compare;\n    using allocator_type = typename ht::allocator_type;\n    using reference = typename ht::reference;\n    using const_reference = typename ht::const_reference;\n    using pointer = typename ht::pointer;\n    using const_pointer = typename ht::const_pointer;\n    using iterator = typename ht::iterator;\n    using const_iterator = typename ht::const_iterator;\n    \n    \n    /*\n     * Constructors\n     */\n    bhopscotch_map() : bhopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {\n    }\n    \n    explicit bhopscotch_map(size_type bucket_count, \n                        const Hash& hash = Hash(),\n                        const KeyEqual& equal = KeyEqual(),\n                        const Allocator& alloc = Allocator(),\n                        const Compare& comp = Compare()) : \n                        m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR, comp)\n    {\n    }\n    \n    bhopscotch_map(size_type bucket_count,\n                  const Allocator& alloc) : bhopscotch_map(bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    bhopscotch_map(size_type bucket_count,\n                  const Hash& hash,\n                  const Allocator& alloc) : bhopscotch_map(bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n    \n    explicit bhopscotch_map(const Allocator& alloc) : bhopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {\n    }\n    \n    template<class InputIt>\n    bhopscotch_map(InputIt first, InputIt last,\n                size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                const Hash& hash = Hash(),\n                const KeyEqual& equal = KeyEqual(),\n                const Allocator& alloc = Allocator()) : bhopscotch_map(bucket_count, hash, equal, alloc)\n    {\n        insert(first, last);\n    }\n    \n    template<class InputIt>\n    bhopscotch_map(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Allocator& alloc) : bhopscotch_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    template<class InputIt>\n    bhopscotch_map(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Hash& hash,\n                const Allocator& alloc) : bhopscotch_map(first, last, bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    bhopscotch_map(std::initializer_list<value_type> init,\n                    size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                    const Hash& hash = Hash(),\n                    const KeyEqual& equal = KeyEqual(),\n                    const Allocator& alloc = Allocator()) : \n                    bhopscotch_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)\n    {\n    }\n\n    bhopscotch_map(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Allocator& alloc) : \n                    bhopscotch_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n\n    bhopscotch_map(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Hash& hash,\n                    const Allocator& alloc) : \n                    bhopscotch_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    \n    bhopscotch_map& operator=(std::initializer_list<value_type> ilist) {\n        m_ht.clear();\n        \n        m_ht.reserve(ilist.size());\n        m_ht.insert(ilist.begin(), ilist.end());\n        \n        return *this;\n    }\n    \n    allocator_type get_allocator() const { return m_ht.get_allocator(); }\n    \n    \n    /*\n     * Iterators\n     */\n    iterator begin() noexcept { return m_ht.begin(); }\n    const_iterator begin() const noexcept { return m_ht.begin(); }\n    const_iterator cbegin() const noexcept { return m_ht.cbegin(); }\n    \n    iterator end() noexcept { return m_ht.end(); }\n    const_iterator end() const noexcept { return m_ht.end(); }\n    const_iterator cend() const noexcept { return m_ht.cend(); }\n    \n    \n    /*\n     * Capacity\n     */\n    bool empty() const noexcept { return m_ht.empty(); }\n    size_type size() const noexcept { return m_ht.size(); }\n    size_type max_size() const noexcept { return m_ht.max_size(); }\n    \n    /*\n     * Modifiers\n     */\n    void clear() noexcept { m_ht.clear(); }\n    \n    \n    \n    \n    std::pair<iterator, bool> insert(const value_type& value) { \n        return m_ht.insert(value); \n    }\n        \n    template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>\n    std::pair<iterator, bool> insert(P&& value) { \n        return m_ht.insert(std::forward<P>(value)); \n    }\n    \n    std::pair<iterator, bool> insert(value_type&& value) { \n        return m_ht.insert(std::move(value)); \n    }\n    \n    \n    iterator insert(const_iterator hint, const value_type& value) { \n        return m_ht.insert(hint, value); \n    }\n        \n    template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>\n    iterator insert(const_iterator hint, P&& value) { \n        return m_ht.insert(hint, std::forward<P>(value));\n    }\n    \n    iterator insert(const_iterator hint, value_type&& value) { \n        return m_ht.insert(hint, std::move(value)); \n    }\n    \n    \n    template<class InputIt>\n    void insert(InputIt first, InputIt last) { \n        m_ht.insert(first, last); \n    }\n    \n    void insert(std::initializer_list<value_type> ilist) { \n        m_ht.insert(ilist.begin(), ilist.end()); \n    }\n\n    \n    \n    \n    template<class M>\n    std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) { \n        return m_ht.insert_or_assign(k, std::forward<M>(obj)); \n    }\n\n    template<class M>\n    std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) { \n        return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj)); \n    }\n    \n    template<class M>\n    iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {\n        return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));\n    }\n    \n    template<class M>\n    iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {\n        return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));\n    }\n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace will need to move or copy the key-value once.\n     * The method is equivalent to insert(value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&&... args) { \n        return m_ht.emplace(std::forward<Args>(args)...); \n    }\n    \n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.\n     * The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    iterator emplace_hint(const_iterator hint, Args&&... args) {\n        return m_ht.emplace_hint(hint, std::forward<Args>(args)...);\n    }\n    \n    \n    \n    \n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) { \n        return m_ht.try_emplace(k, std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {\n        return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {\n        return m_ht.try_emplace(hint, k, std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {\n        return m_ht.try_emplace(hint, std::move(k), std::forward<Args>(args)...);\n    }\n    \n    \n\n    \n    iterator erase(iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }\n    size_type erase(const key_type& key) { return m_ht.erase(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */    \n    size_type erase(const key_type& key, std::size_t precalculated_hash) { \n        return m_ht.erase(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type erase(const K& key) { return m_ht.erase(key); }\n    \n    /**\n     * @copydoc erase(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type erase(const K& key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); }\n    \n    \n    \n    \n    void swap(bhopscotch_map& other) { other.m_ht.swap(m_ht); }\n    \n    /*\n     * Lookup\n     */\n    T& at(const Key& key) { return m_ht.at(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */    \n    T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }\n    \n    const T& at(const Key& key) const { return m_ht.at(key); }\n    \n    /**\n     * @copydoc at(const Key& key, std::size_t precalculated_hash)\n     */    \n    const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    T& at(const K& key) { return m_ht.at(key); }\n    \n    /**\n     * @copydoc at(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */    \n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }\n    \n    /**\n     * @copydoc at(const K& key)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    const T& at(const K& key) const { return m_ht.at(key); }\n    \n    /**\n     * @copydoc at(const K& key, std::size_t precalculated_hash)\n     */    \n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }\n    \n    \n    \n    \n    T& operator[](const Key& key) { return m_ht[key]; }    \n    T& operator[](Key&& key) { return m_ht[std::move(key)]; }\n    \n    \n    \n    \n    size_type count(const Key& key) const { return m_ht.count(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type count(const K& key) const { return m_ht.count(key); }\n    \n    /**\n     * @copydoc count(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */     \n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    \n    \n    \n    iterator find(const Key& key) { return m_ht.find(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    const_iterator find(const Key& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const Key& key, std::size_t precalculated_hash)\n     */\n    const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    iterator find(const K& key) { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    /**\n     * @copydoc find(const K& key)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    const_iterator find(const K& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key, std::size_t precalculated_hash)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }\n    \n    \n    \n    \n    bool contains(const Key& key) const { return m_ht.contains(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    bool contains(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key) const { return m_ht.contains(key); }\n    \n    /**\n     * @copydoc contains(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const Key& key, std::size_t precalculated_hash)\n     */\n    std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n\n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */    \n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    /**\n     * @copydoc equal_range(const K& key)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const K& key, std::size_t precalculated_hash)\n     */    \n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    /*\n     * Bucket interface \n     */\n    size_type bucket_count() const { return m_ht.bucket_count(); }\n    size_type max_bucket_count() const { return m_ht.max_bucket_count(); }\n    \n    \n    /*\n     *  Hash policy \n     */\n    float load_factor() const { return m_ht.load_factor(); }\n    float max_load_factor() const { return m_ht.max_load_factor(); }\n    void max_load_factor(float ml) { m_ht.max_load_factor(ml); }\n    \n    void rehash(size_type count_) { m_ht.rehash(count_); }\n    void reserve(size_type count_) { m_ht.reserve(count_); }\n    \n    \n    /*\n     * Observers\n     */\n    hasher hash_function() const { return m_ht.hash_function(); }\n    key_equal key_eq() const { return m_ht.key_eq(); }\n    key_compare key_comp() const { return m_ht.key_comp(); }\n    \n    /*\n     * Other\n     */\n    \n    /**\n     * Convert a const_iterator to an iterator.\n     */\n    iterator mutable_iterator(const_iterator pos) {\n        return m_ht.mutable_iterator(pos);\n    }\n    \n    size_type overflow_size() const noexcept { return m_ht.overflow_size(); }\n    \n    friend bool operator==(const bhopscotch_map& lhs, const bhopscotch_map& rhs) {\n        if(lhs.size() != rhs.size()) {\n            return false;\n        }\n        \n        for(const auto& element_lhs : lhs) {\n            const auto it_element_rhs = rhs.find(element_lhs.first);\n            if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {\n                return false;\n            }\n        }\n        \n        return true;\n    }\n\n    friend bool operator!=(const bhopscotch_map& lhs, const bhopscotch_map& rhs) {\n        return !operator==(lhs, rhs);\n    }\n\n    friend void swap(bhopscotch_map& lhs, bhopscotch_map& rhs) {\n        lhs.swap(rhs);\n    }\n\n\n    \nprivate:\n    ht m_ht;\n};\n\n\n/**\n * Same as `tsl::bhopscotch_map<Key, T, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.\n */\ntemplate<class Key, \n         class T, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Compare = std::less<Key>,\n         class Allocator = std::allocator<std::pair<const Key, T>>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false>\nusing bhopscotch_pg_map = bhopscotch_map<Key, T, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;\n\n} // end namespace tsl\n\n#endif\n"
  },
  {
    "path": "External/tsl/bhopscotch_set.h",
    "content": "/**\n * MIT License\n * \n * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef TSL_BHOPSCOTCH_SET_H\n#define TSL_BHOPSCOTCH_SET_H \n\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <memory>\n#include <set>\n#include <type_traits>\n#include <utility>\n#include \"hopscotch_hash.h\"\n\n\nnamespace tsl {\n    \n    \n/**\n * Similar to tsl::hopscotch_set but instead of using a list for overflowing elements it uses\n * a binary search tree. It thus needs an additional template parameter Compare. Compare should\n * be arithmetically coherent with KeyEqual.\n * \n * The binary search tree allows the set to have a worst-case scenario of O(log n) for search \n * and delete, even if the hash function maps all the elements to the same bucket. \n * For insert, the amortized worst case is O(log n), but the worst case is O(n) in case of rehash.\n * \n * This makes the set resistant to DoS attacks (but doesn't preclude you to have a good hash function,\n * as an element in the bucket array is faster to retrieve than in the tree).\n * \n * @copydoc hopscotch_set\n */\ntemplate<class Key, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Compare = std::less<Key>,\n         class Allocator = std::allocator<Key>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false,\n         class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>\nclass bhopscotch_set {\nprivate:    \n    template<typename U>\n    using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;\n    \n    class KeySelect {\n    public:\n        using key_type = Key;\n        \n        const key_type& operator()(const Key& key) const {\n            return key;\n        }\n        \n        key_type& operator()(Key& key) {\n            return key;\n        }\n    };\n    \n    \n    using overflow_container_type = std::set<Key, Compare, Allocator>;\n    using ht = tsl::detail_hopscotch_hash::hopscotch_hash<Key, KeySelect, void,\n                                                     Hash, KeyEqual, \n                                                     Allocator, NeighborhoodSize, \n                                                     StoreHash, GrowthPolicy,\n                                                     overflow_container_type>;\n            \npublic:\n    using key_type = typename ht::key_type;\n    using value_type = typename ht::value_type;\n    using size_type = typename ht::size_type;\n    using difference_type = typename ht::difference_type;\n    using hasher = typename ht::hasher;\n    using key_equal = typename ht::key_equal;\n    using key_compare = Compare;\n    using allocator_type = typename ht::allocator_type;\n    using reference = typename ht::reference;\n    using const_reference = typename ht::const_reference;\n    using pointer = typename ht::pointer;\n    using const_pointer = typename ht::const_pointer;\n    using iterator = typename ht::iterator;\n    using const_iterator = typename ht::const_iterator;\n\n    \n    /*\n     * Constructors\n     */\n    bhopscotch_set() : bhopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {\n    }\n    \n    explicit bhopscotch_set(size_type bucket_count, \n                        const Hash& hash = Hash(),\n                        const KeyEqual& equal = KeyEqual(),\n                        const Allocator& alloc = Allocator(),\n                        const Compare& comp = Compare()) : \n                        m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR, comp)\n    {\n    }\n    \n    bhopscotch_set(size_type bucket_count,\n                  const Allocator& alloc) : bhopscotch_set(bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    bhopscotch_set(size_type bucket_count,\n                  const Hash& hash,\n                  const Allocator& alloc) : bhopscotch_set(bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n    \n    explicit bhopscotch_set(const Allocator& alloc) : bhopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {\n    }\n    \n    template<class InputIt>\n    bhopscotch_set(InputIt first, InputIt last,\n                size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                const Hash& hash = Hash(),\n                const KeyEqual& equal = KeyEqual(),\n                const Allocator& alloc = Allocator()) : bhopscotch_set(bucket_count, hash, equal, alloc)\n    {\n        insert(first, last);\n    }\n    \n    template<class InputIt>\n    bhopscotch_set(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Allocator& alloc) : bhopscotch_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    template<class InputIt>\n    bhopscotch_set(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Hash& hash,\n                const Allocator& alloc) : bhopscotch_set(first, last, bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    bhopscotch_set(std::initializer_list<value_type> init,\n                    size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                    const Hash& hash = Hash(),\n                    const KeyEqual& equal = KeyEqual(),\n                    const Allocator& alloc = Allocator()) : \n                    bhopscotch_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)\n    {\n    }\n\n    bhopscotch_set(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Allocator& alloc) : \n                    bhopscotch_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n\n    bhopscotch_set(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Hash& hash,\n                    const Allocator& alloc) : \n                    bhopscotch_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    \n    bhopscotch_set& operator=(std::initializer_list<value_type> ilist) {\n        m_ht.clear();\n        \n        m_ht.reserve(ilist.size());\n        m_ht.insert(ilist.begin(), ilist.end());\n        \n        return *this;\n    }\n    \n    allocator_type get_allocator() const { return m_ht.get_allocator(); }\n    \n    \n    /*\n     * Iterators\n     */\n    iterator begin() noexcept { return m_ht.begin(); }\n    const_iterator begin() const noexcept { return m_ht.begin(); }\n    const_iterator cbegin() const noexcept { return m_ht.cbegin(); }\n    \n    iterator end() noexcept { return m_ht.end(); }\n    const_iterator end() const noexcept { return m_ht.end(); }\n    const_iterator cend() const noexcept { return m_ht.cend(); }\n    \n    \n    /*\n     * Capacity\n     */\n    bool empty() const noexcept { return m_ht.empty(); }\n    size_type size() const noexcept { return m_ht.size(); }\n    size_type max_size() const noexcept { return m_ht.max_size(); }\n    \n    /*\n     * Modifiers\n     */\n    void clear() noexcept { m_ht.clear(); }\n    \n    \n    \n    \n    std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }\n    std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }\n    \n    iterator insert(const_iterator hint, const value_type& value) { return m_ht.insert(hint, value); }\n    iterator insert(const_iterator hint, value_type&& value) { return m_ht.insert(hint, std::move(value)); }\n    \n    template<class InputIt>\n    void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }\n    void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }\n\n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace will need to move or copy the key-value once.\n     * The method is equivalent to insert(value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }\n    \n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.\n     * The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    iterator emplace_hint(const_iterator hint, Args&&... args) {\n        return m_ht.emplace_hint(hint, std::forward<Args>(args)...);\n    }\n\n    \n    \n    \n    iterator erase(iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }\n    size_type erase(const key_type& key) { return m_ht.erase(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */    \n    size_type erase(const key_type& key, std::size_t precalculated_hash) { \n        return m_ht.erase(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type erase(const K& key) { return m_ht.erase(key); }\n    \n    /**\n     * @copydoc erase(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type erase(const K& key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); }\n    \n    \n    \n    \n    void swap(bhopscotch_set& other) { other.m_ht.swap(m_ht); }\n    \n    \n    /*\n     * Lookup\n     */\n    size_type count(const Key& key) const { return m_ht.count(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type count(const K& key) const { return m_ht.count(key); }\n    \n    /**\n     * @copydoc count(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    \n    \n    \n    iterator find(const Key& key) { return m_ht.find(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    const_iterator find(const Key& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const Key& key, std::size_t precalculated_hash)\n     */\n    const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }\n        \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    iterator find(const K& key) { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    /**\n     * @copydoc find(const K& key)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    const_iterator find(const K& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }\n    \n    \n    \n    \n    bool contains(const Key& key) const { return m_ht.contains(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    bool contains(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key) const { return m_ht.contains(key); }\n    \n    /**\n     * @copydoc contains(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash);\n    }\n    \n    std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const Key& key, std::size_t precalculated_hash)\n     */\n    std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent \n     * and Compare::is_transparent exist. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }    \n    \n    /**\n     * @copydoc equal_range(const K& key)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }    \n    \n    /**\n     * @copydoc equal_range(const K& key, std::size_t precalculated_hash)\n     */\n    template<class K, class KE = KeyEqual, class CP = Compare, \n             typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    \n    \n\n    /*\n     * Bucket interface \n     */\n    size_type bucket_count() const { return m_ht.bucket_count(); }\n    size_type max_bucket_count() const { return m_ht.max_bucket_count(); }\n    \n    \n    /*\n     *  Hash policy \n     */\n    float load_factor() const { return m_ht.load_factor(); }\n    float max_load_factor() const { return m_ht.max_load_factor(); }\n    void max_load_factor(float ml) { m_ht.max_load_factor(ml); }\n    \n    void rehash(size_type count_) { m_ht.rehash(count_); }\n    void reserve(size_type count_) { m_ht.reserve(count_); }\n    \n    \n    /*\n     * Observers\n     */\n    hasher hash_function() const { return m_ht.hash_function(); }\n    key_equal key_eq() const { return m_ht.key_eq(); }\n    key_compare key_comp() const { return m_ht.key_comp(); }\n    \n    \n    /*\n     * Other\n     */\n    \n    /**\n     * Convert a const_iterator to an iterator.\n     */\n    iterator mutable_iterator(const_iterator pos) {\n        return m_ht.mutable_iterator(pos);\n    }\n    \n    size_type overflow_size() const noexcept { return m_ht.overflow_size(); }\n    \n    friend bool operator==(const bhopscotch_set& lhs, const bhopscotch_set& rhs) {\n        if(lhs.size() != rhs.size()) {\n            return false;\n        }\n        \n        for(const auto& element_lhs : lhs) {\n            const auto it_element_rhs = rhs.find(element_lhs);\n            if(it_element_rhs == rhs.cend()) {\n                return false;\n            }\n        }\n        \n        return true;\n    }\n\n    friend bool operator!=(const bhopscotch_set& lhs, const bhopscotch_set& rhs) {\n        return !operator==(lhs, rhs);\n    }\n\n    friend void swap(bhopscotch_set& lhs, bhopscotch_set& rhs) {\n        lhs.swap(rhs);\n    }\n    \nprivate:\n    ht m_ht;    \n};\n\n\n/**\n * Same as `tsl::bhopscotch_set<Key, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.\n */\ntemplate<class Key, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Compare = std::less<Key>,\n         class Allocator = std::allocator<Key>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false>\nusing bhopscotch_pg_set = bhopscotch_set<Key, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;\n\n} // end namespace tsl\n\n#endif \n"
  },
  {
    "path": "External/tsl/hopscotch_growth_policy.h",
    "content": "/**\n * MIT License\n * \n * Copyright (c) 2018 Thibaut Goetghebuer-Planchon <tessil@gmx.com>\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef TSL_HOPSCOTCH_GROWTH_POLICY_H\n#define TSL_HOPSCOTCH_GROWTH_POLICY_H \n\n\n#include <algorithm>\n#include <array>\n#include <climits>\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <iterator>\n#include <limits>\n#include <ratio>\n#include <stdexcept>\n\n\n/**\n * Only activate tsl_hh_assert if TSL_DEBUG is defined. \n * This way we avoid the performance hit when NDEBUG is not defined with assert as tsl_hh_assert is used a lot\n * (people usually compile with \"-O3\" and not \"-O3 -DNDEBUG\").\n */\n#ifdef TSL_DEBUG\n#    define tsl_hh_assert(expr) assert(expr)\n#else\n#    define tsl_hh_assert(expr) (static_cast<void>(0))\n#endif\n\n\n/**\n * If exceptions are enabled, throw the exception passed in parameter, otherwise call std::terminate.\n */\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined (_MSC_VER) && defined (_CPPUNWIND))) && !defined(TSL_NO_EXCEPTIONS)\n#    define TSL_HH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)\n#else\n#    define TSL_HH_NO_EXCEPTIONS\n#    ifdef NDEBUG\n#        define TSL_HH_THROW_OR_TERMINATE(ex, msg) std::terminate()\n#    else\n#        include <iostream>\n#        define TSL_HH_THROW_OR_TERMINATE(ex, msg) do { std::cerr << msg << std::endl; std::terminate(); } while(0)\n#    endif\n#endif\n\n\nnamespace tsl {\nnamespace hh {\n\n/**\n * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows\n * the table to use a mask operation instead of a modulo operation to map a hash to a bucket.\n * \n * GrowthFactor must be a power of two >= 2.\n */\ntemplate<std::size_t GrowthFactor>\nclass power_of_two_growth_policy {\npublic:\n    /**\n     * Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.\n     * This number is a minimum, the policy may update this value with a higher value if needed (but not lower).\n     *\n     * If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and\n     * bucket_for_hash must always return 0 in this case.\n     */\n    explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {\n        if(min_bucket_count_in_out > max_bucket_count()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        if(min_bucket_count_in_out > 0) {\n            min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);\n            m_mask = min_bucket_count_in_out - 1;\n        }\n        else {\n            m_mask = 0;\n        }\n    }\n    \n    /**\n     * Return the bucket [0, bucket_count()) to which the hash belongs. \n     * If bucket_count() is 0, it must always return 0.\n     */\n    std::size_t bucket_for_hash(std::size_t hash) const noexcept {\n        return hash & m_mask;\n    }\n    \n    /**\n     * Return the bucket count to use when the bucket array grows on rehash.\n     */\n    std::size_t next_bucket_count() const {\n        if((m_mask + 1) > max_bucket_count() / GrowthFactor) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        return (m_mask + 1) * GrowthFactor;\n    }\n    \n    /**\n     * Return the maximum number of buckets supported by the policy.\n     */\n    std::size_t max_bucket_count() const {\n        // Largest power of two.\n        return (std::numeric_limits<std::size_t>::max() / 2) + 1;\n    }\n    \n    /**\n     * Reset the growth policy as if it was created with a bucket count of 0.\n     * After a clear, the policy must always return 0 when bucket_for_hash is called.\n     */\n    void clear() noexcept {\n        m_mask = 0;\n    }\n    \nprivate:\n    static std::size_t round_up_to_power_of_two(std::size_t value) {\n        if(is_power_of_two(value)) {\n            return value;\n        }\n        \n        if(value == 0) {\n            return 1;\n        }\n            \n        --value;\n        for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {\n            value |= value >> i;\n        }\n        \n        return value + 1;\n    }\n    \n    static constexpr bool is_power_of_two(std::size_t value) {\n        return value != 0 && (value & (value - 1)) == 0;\n    }\n    \nprivate:\n    static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, \"GrowthFactor must be a power of two >= 2.\");\n    \n    std::size_t m_mask;\n};\n\n\n/**\n * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash\n * to a bucket. Slower but it can be useful if you want a slower growth.\n */\ntemplate<class GrowthFactor = std::ratio<3, 2>>\nclass mod_growth_policy {\npublic:\n    explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {\n        if(min_bucket_count_in_out > max_bucket_count()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        if(min_bucket_count_in_out > 0) {\n            m_mod = min_bucket_count_in_out;\n        }\n        else {\n            m_mod = 1;\n        }\n    }\n    \n    std::size_t bucket_for_hash(std::size_t hash) const noexcept {\n        return hash % m_mod;\n    }\n    \n    std::size_t next_bucket_count() const {\n        if(m_mod == max_bucket_count()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);\n        if(!std::isnormal(next_bucket_count)) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        if(next_bucket_count > double(max_bucket_count())) {\n            return max_bucket_count();\n        }\n        else {\n            return std::size_t(next_bucket_count);\n        }\n    }\n    \n    std::size_t max_bucket_count() const {\n        return MAX_BUCKET_COUNT;\n    }\n    \n    void clear() noexcept {\n        m_mod = 1;\n    }\n    \nprivate:\n    static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;\n    static const std::size_t MAX_BUCKET_COUNT = \n            std::size_t(double(\n                    std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR\n            ));\n            \n    static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, \"Growth factor should be >= 1.1.\");\n    \n    std::size_t m_mod;\n};\n\n\n\nnamespace detail {\n\n#if SIZE_MAX >= ULLONG_MAX\n#define TSL_HH_NB_PRIMES 51\n#elif SIZE_MAX >= ULONG_MAX\n#define TSL_HH_NB_PRIMES 40\n#else\n#define TSL_HH_NB_PRIMES 23\n#endif\n\nstatic constexpr const std::array<std::size_t, TSL_HH_NB_PRIMES> PRIMES = {{\n    1u, 5u, 17u, 29u, 37u, 53u, 67u, 79u, 97u, 131u, 193u, 257u, 389u, 521u, 769u, 1031u, \n    1543u, 2053u, 3079u, 6151u, 12289u, 24593u, 49157u,\n#if SIZE_MAX >= ULONG_MAX\n    98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, \n    25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, \n    3221225473ul, 4294967291ul,\n#endif\n#if SIZE_MAX >= ULLONG_MAX\n    6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull, 103079215111ull, 206158430209ull, \n    412316860441ull, 824633720831ull, 1649267441651ull, 3298534883309ull, 6597069766657ull,\n#endif\n}};\n\ntemplate<unsigned int IPrime>\nstatic constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }\n\n// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the\n// compiler can optimize the modulo code better with a constant known at the compilation.\nstatic constexpr const std::array<std::size_t(*)(std::size_t), TSL_HH_NB_PRIMES> MOD_PRIME = {{ \n    &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, \n    &mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>, \n    &mod<21>, &mod<22>,  \n#if SIZE_MAX >= ULONG_MAX\n    &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, &mod<31>, &mod<32>, \n    &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>,\n#endif\n#if SIZE_MAX >= ULLONG_MAX\n    &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>, &mod<46>, &mod<47>, &mod<48>, &mod<49>, \n    &mod<50>,\n#endif\n}};\n\n}\n\n/**\n * Grow the hash table by using prime numbers as bucket count. Slower than tsl::hh::power_of_two_growth_policy in  \n * general but will probably distribute the values around better in the buckets with a poor hash function.\n * \n * To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.\n * \n * With a switch the code would look like:\n * \\code\n * switch(iprime) { // iprime is the current prime of the hash table\n *     case 0: hash % 5ul;\n *             break;\n *     case 1: hash % 17ul;\n *             break;\n *     case 2: hash % 29ul;\n *             break;\n *     ...\n * }    \n * \\endcode\n * \n * Due to the constant variable in the modulo the compiler is able to optimize the operation\n * by a series of multiplications, substractions and shifts. \n * \n * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment.\n */\nclass prime_growth_policy {\npublic:\n    explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {\n        auto it_prime = std::lower_bound(detail::PRIMES.begin(), \n                                         detail::PRIMES.end(), min_bucket_count_in_out);\n        if(it_prime == detail::PRIMES.end()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));\n        if(min_bucket_count_in_out > 0) {\n            min_bucket_count_in_out = *it_prime;\n        }\n        else {\n            min_bucket_count_in_out = 0;\n        }\n    }\n    \n    std::size_t bucket_for_hash(std::size_t hash) const noexcept {\n        return detail::MOD_PRIME[m_iprime](hash);\n    }\n    \n    std::size_t next_bucket_count() const {\n        if(m_iprime + 1 >= detail::PRIMES.size()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The hash table exceeds its maximum size.\");\n        }\n        \n        return detail::PRIMES[m_iprime + 1];\n    }   \n    \n    std::size_t max_bucket_count() const {\n        return detail::PRIMES.back();\n    }\n    \n    void clear() noexcept {\n        m_iprime = 0;\n    }\n    \nprivate:\n    unsigned int m_iprime;\n    \n    static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(), \n                  \"The type of m_iprime is not big enough.\");\n}; \n\n}\n}\n\n#endif\n"
  },
  {
    "path": "External/tsl/hopscotch_hash.h",
    "content": "/**\n * MIT License\n * \n * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef TSL_HOPSCOTCH_HASH_H\n#define TSL_HOPSCOTCH_HASH_H\n\n\n#include <algorithm>\n#include <cassert>\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <exception>\n#include <functional>\n#include <initializer_list>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <stdexcept>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n#include \"hopscotch_growth_policy.h\"\n\n\n#if (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 9))\n#    define TSL_HH_NO_RANGE_ERASE_WITH_CONST_ITERATOR\n#endif\n\n\nnamespace tsl {\nnamespace detail_hopscotch_hash {\n    \n    \ntemplate<typename T>\nstruct make_void {\n    using type = void;\n};\n\n\ntemplate<typename T, typename = void>\nstruct has_is_transparent : std::false_type {\n};\n\ntemplate<typename T>\nstruct has_is_transparent<T, typename make_void<typename T::is_transparent>::type> : std::true_type {\n};\n\n\ntemplate<typename T, typename = void>\nstruct has_key_compare : std::false_type {\n};\n\ntemplate<typename T>\nstruct has_key_compare<T, typename make_void<typename T::key_compare>::type> : std::true_type {\n};\n\n\ntemplate<typename U>\nstruct is_power_of_two_policy: std::false_type {\n};\n\ntemplate<std::size_t GrowthFactor>\nstruct is_power_of_two_policy<tsl::hh::power_of_two_growth_policy<GrowthFactor>>: std::true_type {\n};\n\n\ntemplate<typename T, typename U>\nstatic T numeric_cast(U value, const char* error_message = \"numeric_cast() failed.\") {\n    T ret = static_cast<T>(value);\n    if(static_cast<U>(ret) != value) {\n        TSL_HH_THROW_OR_TERMINATE(std::runtime_error, error_message);\n    }\n    \n    const bool is_same_signedness = (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||\n                                    (std::is_signed<T>::value && std::is_signed<U>::value);\n    if(!is_same_signedness && (ret < T{}) != (value < U{})) {\n        TSL_HH_THROW_OR_TERMINATE(std::runtime_error, error_message);\n    }\n    \n    return ret;\n}\n\n\n/*\n * smallest_type_for_min_bits::type returns the smallest type that can fit MinBits.\n */\nstatic const std::size_t SMALLEST_TYPE_MAX_BITS_SUPPORTED = 64;\ntemplate<unsigned int MinBits, typename Enable = void>\nclass smallest_type_for_min_bits {\n};\n\ntemplate<unsigned int MinBits>\nclass smallest_type_for_min_bits<MinBits, typename std::enable_if<(MinBits > 0) && (MinBits <= 8)>::type> {\npublic:\n    using type = std::uint_least8_t;\n};\n\ntemplate<unsigned int MinBits>\nclass smallest_type_for_min_bits<MinBits, typename std::enable_if<(MinBits > 8) && (MinBits <= 16)>::type> {\npublic:\n    using type = std::uint_least16_t;\n};\n\ntemplate<unsigned int MinBits>\nclass smallest_type_for_min_bits<MinBits, typename std::enable_if<(MinBits > 16) && (MinBits <= 32)>::type> {\npublic:\n    using type = std::uint_least32_t;\n};\n\ntemplate<unsigned int MinBits>\nclass smallest_type_for_min_bits<MinBits, typename std::enable_if<(MinBits > 32) && (MinBits <= 64)>::type> {\npublic:\n    using type = std::uint_least64_t;\n};\n        \n\n\n/*\n * Each bucket may store up to three elements:\n * - An aligned storage to store a value_type object with placement-new.\n * - An (optional) hash of the value in the bucket.\n * - An unsigned integer of type neighborhood_bitmap used to tell us which buckets in the neighborhood of the \n *   current bucket contain a value with a hash belonging to the current bucket. \n * \n * For a bucket 'bct', a bit 'i' (counting from 0 and from the least significant bit to the most significant)\n * set to 1 means that the bucket 'bct + i' contains a value with a hash belonging to bucket 'bct'.\n * The bits used for that, start from the third least significant bit.\n * The two least significant bits are reserved:\n * - The least significant bit is set to 1 if there is a value in the bucket storage.\n * - The second least significant bit is set to 1 if there is an overflow. More than NeighborhoodSize values\n  * give the same hash, all overflow values are stored in the m_overflow_elements list of the map.\n *\n * Details regarding hopscotch hashing an its implementation can be found here:\n *  https://tessil.github.io/2016/08/29/hopscotch-hashing.html\n */\nstatic const std::size_t NB_RESERVED_BITS_IN_NEIGHBORHOOD = 2; \n\n\nusing truncated_hash_type = std::uint_least32_t;\n\n/**\n * Helper class that stores a truncated hash if StoreHash is true and nothing otherwise.\n */\ntemplate<bool StoreHash>\nclass hopscotch_bucket_hash {\npublic:\n    bool bucket_hash_equal(std::size_t /*hash*/) const noexcept {\n        return true;\n    }\n    \n    truncated_hash_type truncated_bucket_hash() const noexcept {\n        return 0;\n    }\n    \nprotected:    \n    void copy_hash(const hopscotch_bucket_hash& ) noexcept {\n    }\n    \n    void set_hash(truncated_hash_type /*hash*/) noexcept {\n    }\n};\n\ntemplate<>\nclass hopscotch_bucket_hash<true> {\npublic:\n    bool bucket_hash_equal(std::size_t hash) const noexcept {\n        return m_hash == truncated_hash_type(hash);\n    }\n    \n    truncated_hash_type truncated_bucket_hash() const noexcept {\n        return m_hash;\n    }\n    \nprotected:    \n    void copy_hash(const hopscotch_bucket_hash& bucket) noexcept {\n        m_hash = bucket.m_hash;\n    }\n    \n    void set_hash(truncated_hash_type hash) noexcept {\n        m_hash = hash;\n    }\n    \nprivate:    \n    truncated_hash_type m_hash;\n};\n\n\ntemplate<typename ValueType, unsigned int NeighborhoodSize, bool StoreHash>\nclass hopscotch_bucket: public hopscotch_bucket_hash<StoreHash> {\nprivate:\n    static const std::size_t MIN_NEIGHBORHOOD_SIZE = 4;\n    static const std::size_t MAX_NEIGHBORHOOD_SIZE = SMALLEST_TYPE_MAX_BITS_SUPPORTED - NB_RESERVED_BITS_IN_NEIGHBORHOOD; \n    \n    \n    static_assert(NeighborhoodSize >= 4, \"NeighborhoodSize should be >= 4.\");\n    // We can't put a variable in the message, ensure coherence\n    static_assert(MIN_NEIGHBORHOOD_SIZE == 4, \"\"); \n    \n    static_assert(NeighborhoodSize <= 62, \"NeighborhoodSize should be <= 62.\");\n    // We can't put a variable in the message, ensure coherence\n    static_assert(MAX_NEIGHBORHOOD_SIZE == 62, \"\"); \n    \n    \n    static_assert(!StoreHash || NeighborhoodSize <= 30, \n                  \"NeighborhoodSize should be <= 30 if StoreHash is true.\");\n    // We can't put a variable in the message, ensure coherence\n    static_assert(MAX_NEIGHBORHOOD_SIZE - 32 == 30, \"\");\n    \n    using bucket_hash = hopscotch_bucket_hash<StoreHash>;\n    \npublic:\n    using value_type = ValueType;\n    using neighborhood_bitmap = \n                typename smallest_type_for_min_bits<NeighborhoodSize + NB_RESERVED_BITS_IN_NEIGHBORHOOD>::type;\n\n\n    hopscotch_bucket() noexcept: bucket_hash(), m_neighborhood_infos(0) {\n        tsl_hh_assert(empty());\n    }\n    \n    \n    hopscotch_bucket(const hopscotch_bucket& bucket) \n        noexcept(std::is_nothrow_copy_constructible<value_type>::value): bucket_hash(bucket), \n                                                                         m_neighborhood_infos(0) \n    {\n        if(!bucket.empty()) {\n            ::new (static_cast<void*>(std::addressof(m_value))) value_type(bucket.value());\n        }\n        \n        m_neighborhood_infos = bucket.m_neighborhood_infos;\n    }\n    \n    hopscotch_bucket(hopscotch_bucket&& bucket)\n        noexcept(std::is_nothrow_move_constructible<value_type>::value) : bucket_hash(std::move(bucket)),\n                                                                          m_neighborhood_infos(0) \n    {\n        if(!bucket.empty()) {\n            ::new (static_cast<void*>(std::addressof(m_value))) value_type(std::move(bucket.value()));\n        }\n        \n        m_neighborhood_infos = bucket.m_neighborhood_infos;\n    }\n     \n    hopscotch_bucket& operator=(const hopscotch_bucket& bucket) \n        noexcept(std::is_nothrow_copy_constructible<value_type>::value) \n    {\n        if(this != &bucket) {\n            remove_value();\n            \n            bucket_hash::operator=(bucket);\n            if(!bucket.empty()) {\n                ::new (static_cast<void*>(std::addressof(m_value))) value_type(bucket.value());\n            }\n            \n            m_neighborhood_infos = bucket.m_neighborhood_infos;\n        }\n        \n        return *this;\n    }\n    \n    hopscotch_bucket& operator=(hopscotch_bucket&& ) = delete;\n     \n    ~hopscotch_bucket() noexcept {\n        if(!empty()) {\n            destroy_value();\n        }\n    }\n    \n    neighborhood_bitmap neighborhood_infos() const noexcept {\n        return neighborhood_bitmap(m_neighborhood_infos >> NB_RESERVED_BITS_IN_NEIGHBORHOOD);\n    }\n    \n    void set_overflow(bool has_overflow) noexcept {\n        if(has_overflow) {\n            m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos | 2);\n        }\n        else {\n            m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos & ~2);\n        }\n    }\n    \n    bool has_overflow() const noexcept {\n        return (m_neighborhood_infos & 2) != 0;\n    }\n    \n    bool empty() const noexcept {\n        return (m_neighborhood_infos & 1) == 0;\n    }\n    \n    void toggle_neighbor_presence(std::size_t ineighbor) noexcept {\n        tsl_hh_assert(ineighbor <= NeighborhoodSize);\n        m_neighborhood_infos = neighborhood_bitmap(\n                                    m_neighborhood_infos ^ (1ull << (ineighbor + NB_RESERVED_BITS_IN_NEIGHBORHOOD)));\n    }\n    \n    bool check_neighbor_presence(std::size_t ineighbor) const noexcept {\n        tsl_hh_assert(ineighbor <= NeighborhoodSize);\n        if(((m_neighborhood_infos >> (ineighbor + NB_RESERVED_BITS_IN_NEIGHBORHOOD)) & 1) == 1) {\n            return true;\n        }\n        \n        return false;\n    }\n    \n    value_type& value() noexcept {\n        tsl_hh_assert(!empty());\n        return *reinterpret_cast<value_type*>(std::addressof(m_value));\n    }\n    \n    const value_type& value() const noexcept {\n        tsl_hh_assert(!empty());\n        return *reinterpret_cast<const value_type*>(std::addressof(m_value));\n    }\n    \n    template<typename... Args>\n    void set_value_of_empty_bucket(truncated_hash_type hash, Args&&... value_type_args) {\n        tsl_hh_assert(empty());\n        \n        ::new (static_cast<void*>(std::addressof(m_value))) value_type(std::forward<Args>(value_type_args)...);\n        set_empty(false);\n        this->set_hash(hash);\n    }\n    \n    void swap_value_into_empty_bucket(hopscotch_bucket& empty_bucket) {\n        tsl_hh_assert(empty_bucket.empty());\n        if(!empty()) {\n            ::new (static_cast<void*>(std::addressof(empty_bucket.m_value))) value_type(std::move(value()));\n            empty_bucket.copy_hash(*this);\n            empty_bucket.set_empty(false);\n            \n            destroy_value();\n            set_empty(true);\n        }\n    }\n    \n    void remove_value() noexcept {\n        if(!empty()) {\n            destroy_value();\n            set_empty(true);\n        }\n    }\n    \n    void clear() noexcept {\n        if(!empty()) {\n            destroy_value();\n        }\n        \n        m_neighborhood_infos = 0;\n        tsl_hh_assert(empty());\n    }\n    \n    static truncated_hash_type truncate_hash(std::size_t hash) noexcept {\n        return truncated_hash_type(hash);\n    }\n    \nprivate:\n    void set_empty(bool is_empty) noexcept {\n        if(is_empty) {\n            m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos & ~1);\n        }\n        else {\n            m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos | 1);\n        }\n    }\n    \n    void destroy_value() noexcept {\n        tsl_hh_assert(!empty());\n        value().~value_type();\n    }\n    \nprivate:\n    using storage = typename std::aligned_storage<sizeof(value_type), alignof(value_type)>::type;\n    \n    neighborhood_bitmap m_neighborhood_infos;\n    storage m_value;\n};\n\n\n/**\n * Internal common class used by (b)hopscotch_map and (b)hopscotch_set.\n * \n * ValueType is what will be stored by hopscotch_hash (usually std::pair<Key, T> for a map and Key for a set).\n * \n * KeySelect should be a FunctionObject which takes a ValueType in parameter and returns a reference to the key.\n * \n * ValueSelect should be a FunctionObject which takes a ValueType in parameter and returns a reference to the value.\n * ValueSelect should be void if there is no value (in a set for example).\n * \n * OverflowContainer will be used as containers for overflown elements. Usually it should be a list<ValueType>\n * or a set<Key>/map<Key, T>.\n */\ntemplate<class ValueType,\n         class KeySelect,\n         class ValueSelect,\n         class Hash,\n         class KeyEqual,\n         class Allocator,\n         unsigned int NeighborhoodSize,\n         bool StoreHash,\n         class GrowthPolicy,\n         class OverflowContainer>\nclass hopscotch_hash: private Hash, private KeyEqual, private GrowthPolicy {\nprivate:\n    template<typename U>\n    using has_mapped_type = typename std::integral_constant<bool, !std::is_same<U, void>::value>;\n    \n    static_assert(noexcept(std::declval<GrowthPolicy>().bucket_for_hash(std::size_t(0))), \"GrowthPolicy::bucket_for_hash must be noexcept.\");\n    static_assert(noexcept(std::declval<GrowthPolicy>().clear()), \"GrowthPolicy::clear must be noexcept.\");\n    \npublic:\n    template<bool IsConst>\n    class hopscotch_iterator;\n    \n    using key_type = typename KeySelect::key_type;\n    using value_type = ValueType;\n    using size_type = std::size_t;\n    using difference_type = std::ptrdiff_t;\n    using hasher = Hash;\n    using key_equal = KeyEqual;\n    using allocator_type = Allocator;\n    using reference = value_type&;\n    using const_reference = const value_type&;\n    using pointer = value_type*;\n    using const_pointer = const value_type*;\n    using iterator = hopscotch_iterator<false>;\n    using const_iterator = hopscotch_iterator<true>;\n    \nprivate:\n    using hopscotch_bucket = tsl::detail_hopscotch_hash::hopscotch_bucket<ValueType, NeighborhoodSize, StoreHash>;\n    using neighborhood_bitmap = typename hopscotch_bucket::neighborhood_bitmap;\n    \n    using buckets_allocator = typename std::allocator_traits<allocator_type>::template rebind_alloc<hopscotch_bucket>;\n    using buckets_container_type = std::vector<hopscotch_bucket, buckets_allocator>;  \n    \n    using overflow_container_type = OverflowContainer;\n    \n    static_assert(std::is_same<typename overflow_container_type::value_type, ValueType>::value, \n                  \"OverflowContainer should have ValueType as type.\");\n    \n    static_assert(std::is_same<typename overflow_container_type::allocator_type, Allocator>::value, \n                  \"Invalid allocator, not the same type as the value_type.\");\n    \n    \n    using iterator_buckets = typename buckets_container_type::iterator; \n    using const_iterator_buckets = typename buckets_container_type::const_iterator;\n    \n    using iterator_overflow = typename overflow_container_type::iterator; \n    using const_iterator_overflow = typename overflow_container_type::const_iterator; \n    \npublic:    \n    /**\n     * The `operator*()` and `operator->()` methods return a const reference and const pointer respectively to the \n     * stored value type.\n     * \n     * In case of a map, to get a modifiable reference to the value associated to a key (the `.second` in the \n     * stored pair), you have to call `value()`.\n     */\n    template<bool IsConst>\n    class hopscotch_iterator {\n        friend class hopscotch_hash;\n    private:\n        using iterator_bucket = typename std::conditional<IsConst, \n                                                            typename hopscotch_hash::const_iterator_buckets, \n                                                            typename hopscotch_hash::iterator_buckets>::type;\n        using iterator_overflow = typename std::conditional<IsConst, \n                                                            typename hopscotch_hash::const_iterator_overflow, \n                                                            typename hopscotch_hash::iterator_overflow>::type;\n    \n        \n        hopscotch_iterator(iterator_bucket buckets_iterator, iterator_bucket buckets_end_iterator, \n                           iterator_overflow overflow_iterator) noexcept : \n            m_buckets_iterator(buckets_iterator), m_buckets_end_iterator(buckets_end_iterator),\n            m_overflow_iterator(overflow_iterator)\n        {\n        }\n        \n    public:\n        using iterator_category = std::forward_iterator_tag;\n        using value_type = const typename hopscotch_hash::value_type;\n        using difference_type = std::ptrdiff_t;\n        using reference = value_type&;\n        using pointer = value_type*;\n        \n        \n        hopscotch_iterator() noexcept {\n        }\n        \n        // Copy constructor from iterator to const_iterator.\n        template<bool TIsConst = IsConst, typename std::enable_if<TIsConst>::type* = nullptr>\n        hopscotch_iterator(const hopscotch_iterator<!TIsConst>& other) noexcept :\n            m_buckets_iterator(other.m_buckets_iterator), m_buckets_end_iterator(other.m_buckets_end_iterator),\n            m_overflow_iterator(other.m_overflow_iterator)\n        {\n        }\n\n        hopscotch_iterator(const hopscotch_iterator& other) = default;\n        hopscotch_iterator(hopscotch_iterator&& other) = default;\n        hopscotch_iterator& operator=(const hopscotch_iterator& other) = default;\n        hopscotch_iterator& operator=(hopscotch_iterator&& other) = default;\n        \n        const typename hopscotch_hash::key_type& key() const {\n            if(m_buckets_iterator != m_buckets_end_iterator) {\n                return KeySelect()(m_buckets_iterator->value());\n            }\n            \n            return KeySelect()(*m_overflow_iterator);\n        }\n\n        template<class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n        typename std::conditional<\n                        IsConst, \n                        const typename U::value_type&, \n                        typename U::value_type&>::type value() const\n        {\n            if(m_buckets_iterator != m_buckets_end_iterator) {\n                return U()(m_buckets_iterator->value());\n            }\n            \n            return U()(*m_overflow_iterator);\n        }\n        \n        reference operator*() const { \n            if(m_buckets_iterator != m_buckets_end_iterator) {\n                return m_buckets_iterator->value();\n            }\n            \n            return *m_overflow_iterator;\n        }\n        \n        pointer operator->() const { \n            if(m_buckets_iterator != m_buckets_end_iterator) {\n                return std::addressof(m_buckets_iterator->value()); \n            }\n            \n            return std::addressof(*m_overflow_iterator); \n        }\n        \n        hopscotch_iterator& operator++() {\n            if(m_buckets_iterator == m_buckets_end_iterator) {\n                ++m_overflow_iterator;\n                return *this;\n            }\n            \n            do {\n                ++m_buckets_iterator;\n            } while(m_buckets_iterator != m_buckets_end_iterator && m_buckets_iterator->empty());\n            \n            return *this; \n        }\n        \n        hopscotch_iterator operator++(int) {\n            hopscotch_iterator tmp(*this);\n            ++*this;\n            \n            return tmp;\n        }\n        \n        friend bool operator==(const hopscotch_iterator& lhs, const hopscotch_iterator& rhs) { \n            return lhs.m_buckets_iterator == rhs.m_buckets_iterator && \n                   lhs.m_overflow_iterator == rhs.m_overflow_iterator; \n        }\n        \n        friend bool operator!=(const hopscotch_iterator& lhs, const hopscotch_iterator& rhs) { \n            return !(lhs == rhs); \n        }\n        \n    private:\n        iterator_bucket m_buckets_iterator;\n        iterator_bucket m_buckets_end_iterator;\n        iterator_overflow m_overflow_iterator;\n    };\n    \npublic:\n    template<class OC = OverflowContainer, typename std::enable_if<!has_key_compare<OC>::value>::type* = nullptr>\n    hopscotch_hash(size_type bucket_count, \n                  const Hash& hash,\n                  const KeyEqual& equal,\n                  const Allocator& alloc,\n                  float max_load_factor) :  Hash(hash),\n                                            KeyEqual(equal),\n                                            GrowthPolicy(bucket_count),\n                                            m_buckets_data(alloc), \n                                            m_overflow_elements(alloc),\n                                            m_buckets(static_empty_bucket_ptr()),\n                                            m_nb_elements(0)\n    {\n        if(bucket_count > max_bucket_count()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The map exceeds its maximum size.\");\n        }\n        \n        if(bucket_count > 0) {\n            static_assert(NeighborhoodSize - 1 > 0, \"\");\n            \n            // Can't directly construct with the appropriate size in the initializer \n            // as m_buckets_data(bucket_count, alloc) is not supported by GCC 4.8\n            m_buckets_data.resize(bucket_count + NeighborhoodSize - 1);\n            m_buckets = m_buckets_data.data();\n        }\n        \n        \n        this->max_load_factor(max_load_factor);\n        \n        \n        // Check in the constructor instead of outside of a function to avoid compilation issues\n        // when value_type is not complete.\n        static_assert(std::is_nothrow_move_constructible<value_type>::value || \n                      std::is_copy_constructible<value_type>::value, \n                      \"value_type must be either copy constructible or nothrow move constructible.\");\n    }\n    \n    template<class OC = OverflowContainer, typename std::enable_if<has_key_compare<OC>::value>::type* = nullptr>\n    hopscotch_hash(size_type bucket_count, \n                  const Hash& hash,\n                  const KeyEqual& equal,\n                  const Allocator& alloc,\n                  float max_load_factor,\n                  const typename OC::key_compare& comp) : Hash(hash),\n                                                          KeyEqual(equal),\n                                                          GrowthPolicy(bucket_count),\n                                                          m_buckets_data(alloc), \n                                                          m_overflow_elements(comp, alloc),\n                                                          m_buckets(static_empty_bucket_ptr()),\n                                                          m_nb_elements(0)\n    {\n        \n        if(bucket_count > max_bucket_count()) {\n            TSL_HH_THROW_OR_TERMINATE(std::length_error, \"The map exceeds its maximum size.\");\n        }\n        \n        if(bucket_count > 0) {\n            static_assert(NeighborhoodSize - 1 > 0, \"\");\n            \n            // Can't directly construct with the appropriate size in the initializer \n            // as m_buckets_data(bucket_count, alloc) is not supported by GCC 4.8\n            m_buckets_data.resize(bucket_count + NeighborhoodSize - 1);\n            m_buckets = m_buckets_data.data();\n        }\n        \n        \n        this->max_load_factor(max_load_factor);\n        \n        \n        // Check in the constructor instead of outside of a function to avoid compilation issues\n        // when value_type is not complete.\n        static_assert(std::is_nothrow_move_constructible<value_type>::value || \n                      std::is_copy_constructible<value_type>::value, \n                      \"value_type must be either copy constructible or nothrow move constructible.\");\n    }\n    \n    hopscotch_hash(const hopscotch_hash& other): \n                          Hash(other),\n                          KeyEqual(other),\n                          GrowthPolicy(other),\n                          m_buckets_data(other.m_buckets_data),\n                          m_overflow_elements(other.m_overflow_elements),\n                          m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():\n                                                           m_buckets_data.data()),\n                          m_nb_elements(other.m_nb_elements),\n                          m_min_load_threshold_rehash(other.m_min_load_threshold_rehash),\n                          m_max_load_threshold_rehash(other.m_max_load_threshold_rehash),\n                          m_max_load_factor(other.m_max_load_factor)\n    {\n    }\n    \n    hopscotch_hash(hopscotch_hash&& other) \n                        noexcept(\n                            std::is_nothrow_move_constructible<Hash>::value &&\n                            std::is_nothrow_move_constructible<KeyEqual>::value &&\n                            std::is_nothrow_move_constructible<GrowthPolicy>::value &&\n                            std::is_nothrow_move_constructible<buckets_container_type>::value &&\n                            std::is_nothrow_move_constructible<overflow_container_type>::value\n                        ):\n                          Hash(std::move(static_cast<Hash&>(other))),\n                          KeyEqual(std::move(static_cast<KeyEqual&>(other))),\n                          GrowthPolicy(std::move(static_cast<GrowthPolicy&>(other))),\n                          m_buckets_data(std::move(other.m_buckets_data)),\n                          m_overflow_elements(std::move(other.m_overflow_elements)),\n                          m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():\n                                                           m_buckets_data.data()),\n                          m_nb_elements(other.m_nb_elements),\n                          m_min_load_threshold_rehash(other.m_min_load_threshold_rehash),\n                          m_max_load_threshold_rehash(other.m_max_load_threshold_rehash),\n                          m_max_load_factor(other.m_max_load_factor)\n    {\n        other.GrowthPolicy::clear();\n        other.m_buckets_data.clear();\n        other.m_overflow_elements.clear();\n        other.m_buckets = static_empty_bucket_ptr();\n        other.m_nb_elements = 0;\n        other.m_min_load_threshold_rehash = 0;\n        other.m_max_load_threshold_rehash = 0;\n    }\n    \n    hopscotch_hash& operator=(const hopscotch_hash& other) {\n        if(&other != this) {\n            Hash::operator=(other);\n            KeyEqual::operator=(other);\n            GrowthPolicy::operator=(other);\n            \n            m_buckets_data = other.m_buckets_data;\n            m_overflow_elements = other.m_overflow_elements;\n            m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr():\n                                               m_buckets_data.data();\n            m_nb_elements = other.m_nb_elements;\n            \n            m_min_load_threshold_rehash = other.m_min_load_threshold_rehash;\n            m_max_load_threshold_rehash = other.m_max_load_threshold_rehash;\n            m_max_load_factor = other.m_max_load_factor;\n        }\n        \n        return *this;\n    }\n    \n    hopscotch_hash& operator=(hopscotch_hash&& other) {\n        other.swap(*this);\n        other.clear();\n        \n        return *this;\n    }\n    \n    allocator_type get_allocator() const {\n        return m_buckets_data.get_allocator();\n    }\n    \n    \n    /*\n     * Iterators\n     */\n    iterator begin() noexcept {\n        auto begin = m_buckets_data.begin();\n        while(begin != m_buckets_data.end() && begin->empty()) {\n            ++begin;\n        }\n        \n        return iterator(begin, m_buckets_data.end(), m_overflow_elements.begin());\n    }\n    \n    const_iterator begin() const noexcept {\n        return cbegin();\n    }\n    \n    const_iterator cbegin() const noexcept {\n        auto begin = m_buckets_data.cbegin();\n        while(begin != m_buckets_data.cend() && begin->empty()) {\n            ++begin;\n        }\n        \n        return const_iterator(begin, m_buckets_data.cend(), m_overflow_elements.cbegin());\n    }\n    \n    iterator end() noexcept {\n        return iterator(m_buckets_data.end(), m_buckets_data.end(), m_overflow_elements.end());\n    }\n    \n    const_iterator end() const noexcept {\n        return cend();\n    }\n    \n    const_iterator cend() const noexcept {\n        return const_iterator(m_buckets_data.cend(), m_buckets_data.cend(), m_overflow_elements.cend());\n    }\n    \n    \n    /*\n     * Capacity\n     */\n    bool empty() const noexcept {\n        return m_nb_elements == 0;\n    }\n    \n    size_type size() const noexcept {\n        return m_nb_elements;\n    }\n    \n    size_type max_size() const noexcept {\n        return m_buckets_data.max_size();\n    }\n    \n    /*\n     * Modifiers\n     */\n    void clear() noexcept {\n        for(auto& bucket: m_buckets_data) {\n            bucket.clear();\n        }\n        \n        m_overflow_elements.clear();\n        m_nb_elements = 0;\n    }\n    \n    \n    std::pair<iterator, bool> insert(const value_type& value) { \n        return insert_impl(value); \n    }\n        \n    template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>\n    std::pair<iterator, bool> insert(P&& value) { \n        return insert_impl(value_type(std::forward<P>(value)));\n    }\n    \n    std::pair<iterator, bool> insert(value_type&& value) { \n        return insert_impl(std::move(value)); \n    }\n    \n    \n    iterator insert(const_iterator hint, const value_type& value) { \n        if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) { \n            return mutable_iterator(hint); \n        }\n        \n        return insert(value).first; \n    }\n        \n    template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>\n    iterator insert(const_iterator hint, P&& value) {\n        return emplace_hint(hint, std::forward<P>(value)); \n    }\n    \n    iterator insert(const_iterator hint, value_type&& value) { \n        if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) { \n            return mutable_iterator(hint); \n        }\n        \n        return insert(std::move(value)).first; \n    }\n    \n    \n    template<class InputIt>\n    void insert(InputIt first, InputIt last) {\n        if(std::is_base_of<std::forward_iterator_tag, \n                           typename std::iterator_traits<InputIt>::iterator_category>::value) \n        {\n            const auto nb_elements_insert = std::distance(first, last);\n            const std::size_t nb_elements_in_buckets = m_nb_elements - m_overflow_elements.size();\n            const std::size_t nb_free_buckets = m_max_load_threshold_rehash - nb_elements_in_buckets;\n            tsl_hh_assert(m_nb_elements >= m_overflow_elements.size());\n            tsl_hh_assert(m_max_load_threshold_rehash >= nb_elements_in_buckets);\n            \n            if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {\n                reserve(nb_elements_in_buckets + std::size_t(nb_elements_insert));\n            }\n        }\n        \n        for(; first != last; ++first) {\n            insert(*first);\n        }\n    }\n    \n    \n    template<class M>\n    std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) { \n        return insert_or_assign_impl(k, std::forward<M>(obj)); \n    }\n\n    template<class M>\n    std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) { \n        return insert_or_assign_impl(std::move(k), std::forward<M>(obj)); \n    }\n    \n    \n    template<class M>\n    iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {\n        if(hint != cend() && compare_keys(KeySelect()(*hint), k)) { \n            auto it = mutable_iterator(hint); \n            it.value() = std::forward<M>(obj);\n            \n            return it;\n        }\n        \n        return insert_or_assign(k, std::forward<M>(obj)).first;\n    }\n    \n    template<class M>\n    iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {\n        if(hint != cend() && compare_keys(KeySelect()(*hint), k)) {\n            auto it = mutable_iterator(hint); \n            it.value() = std::forward<M>(obj);\n            \n            return it;\n        }\n        \n        return insert_or_assign(std::move(k), std::forward<M>(obj)).first;\n    }\n    \n    \n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&&... args) {\n        return insert(value_type(std::forward<Args>(args)...));\n    }\n    \n    template<class... Args>\n    iterator emplace_hint(const_iterator hint, Args&&... args) {\n        return insert(hint, value_type(std::forward<Args>(args)...));        \n    }\n    \n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) { \n        return try_emplace_impl(k, std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {\n        return try_emplace_impl(std::move(k), std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) { \n        if(hint != cend() && compare_keys(KeySelect()(*hint), k)) { \n            return mutable_iterator(hint); \n        }\n        \n        return try_emplace(k, std::forward<Args>(args)...).first;\n    }\n    \n    template<class... Args>\n    iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {\n        if(hint != cend() && compare_keys(KeySelect()(*hint), k)) { \n            return mutable_iterator(hint); \n        }\n        \n        return try_emplace(std::move(k), std::forward<Args>(args)...).first;\n    }\n    \n    \n    /**\n     * Here to avoid `template<class K> size_type erase(const K& key)` being used when\n     * we use an iterator instead of a const_iterator.\n     */\n    iterator erase(iterator pos) {\n        return erase(const_iterator(pos));\n    }\n    \n    iterator erase(const_iterator pos) {\n        const std::size_t ibucket_for_hash = bucket_for_hash(hash_key(pos.key()));\n        \n        if(pos.m_buckets_iterator != pos.m_buckets_end_iterator) {\n            auto it_bucket = m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), pos.m_buckets_iterator);\n            erase_from_bucket(*it_bucket, ibucket_for_hash);\n            \n            return ++iterator(it_bucket, m_buckets_data.end(), m_overflow_elements.begin()); \n        }\n        else {\n            auto it_next_overflow = erase_from_overflow(pos.m_overflow_iterator, ibucket_for_hash);\n            return iterator(m_buckets_data.end(), m_buckets_data.end(), it_next_overflow);\n        }\n    }\n    \n    iterator erase(const_iterator first, const_iterator last) {\n        if(first == last) {\n            return mutable_iterator(first);\n        }\n        \n        auto to_delete = erase(first);\n        while(to_delete != last) {\n            to_delete = erase(to_delete);\n        }\n        \n        return to_delete;\n    }\n    \n    template<class K>\n    size_type erase(const K& key) {\n        return erase(key, hash_key(key));\n    }\n    \n    template<class K>\n    size_type erase(const K& key, std::size_t hash) {\n        const std::size_t ibucket_for_hash = bucket_for_hash(hash);\n\n        hopscotch_bucket* bucket_found = find_in_buckets(key, hash, m_buckets + ibucket_for_hash);\n        if(bucket_found != nullptr) {\n            erase_from_bucket(*bucket_found, ibucket_for_hash);\n\n            return 1;\n        }\n        \n        if(m_buckets[ibucket_for_hash].has_overflow()) {\n            auto it_overflow = find_in_overflow(key);\n            if(it_overflow != m_overflow_elements.end()) {\n                erase_from_overflow(it_overflow, ibucket_for_hash);\n                \n                return 1;\n            }\n        }\n        \n        return 0;\n    }\n    \n    void swap(hopscotch_hash& other) {\n        using std::swap;\n        \n        swap(static_cast<Hash&>(*this), static_cast<Hash&>(other));\n        swap(static_cast<KeyEqual&>(*this), static_cast<KeyEqual&>(other));\n        swap(static_cast<GrowthPolicy&>(*this), static_cast<GrowthPolicy&>(other));\n        swap(m_buckets_data, other.m_buckets_data);\n        swap(m_overflow_elements, other.m_overflow_elements);\n        swap(m_buckets, other.m_buckets);\n        swap(m_nb_elements, other.m_nb_elements);\n        swap(m_min_load_threshold_rehash, other.m_min_load_threshold_rehash);\n        swap(m_max_load_threshold_rehash, other.m_max_load_threshold_rehash);\n        swap(m_max_load_factor, other.m_max_load_factor);\n    }\n    \n    \n    /*\n     * Lookup\n     */\n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    typename U::value_type& at(const K& key) {\n        return at(key, hash_key(key));\n    }\n    \n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    typename U::value_type& at(const K& key, std::size_t hash) {\n        return const_cast<typename U::value_type&>(static_cast<const hopscotch_hash*>(this)->at(key, hash));\n    }\n    \n    \n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    const typename U::value_type& at(const K& key) const {\n        return at(key, hash_key(key));\n    }\n    \n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    const typename U::value_type& at(const K& key, std::size_t hash) const {\n        using T = typename U::value_type;\n        \n        const T* value = find_value_impl(key, hash, m_buckets + bucket_for_hash(hash));\n        if(value == nullptr) {\n            TSL_HH_THROW_OR_TERMINATE(std::out_of_range, \"Couldn't find key.\");\n        }\n        else {\n            return *value;\n        }\n    }\n    \n    \n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    typename U::value_type& operator[](K&& key) {\n        using T = typename U::value_type;\n        \n        const std::size_t hash = hash_key(key);\n        const std::size_t ibucket_for_hash = bucket_for_hash(hash);\n        \n        T* value = find_value_impl(key, hash, m_buckets + ibucket_for_hash);\n        if(value != nullptr) {\n            return *value;\n        }\n        else {\n            return insert_value(ibucket_for_hash, hash, std::piecewise_construct, \n                                                        std::forward_as_tuple(std::forward<K>(key)), \n                                                        std::forward_as_tuple()).first.value();\n        }\n    }\n    \n    \n    template<class K>\n    size_type count(const K& key) const {\n        return count(key, hash_key(key));\n    }\n    \n    template<class K>\n    size_type count(const K& key, std::size_t hash) const {\n        return count_impl(key, hash, m_buckets + bucket_for_hash(hash));\n    }\n    \n    \n    template<class K>\n    iterator find(const K& key) {\n        return find(key, hash_key(key));\n    }\n    \n    template<class K>\n    iterator find(const K& key, std::size_t hash) {\n        return find_impl(key, hash, m_buckets + bucket_for_hash(hash));\n    }\n    \n    \n    template<class K>\n    const_iterator find(const K& key) const {\n        return find(key, hash_key(key));\n    }\n    \n    template<class K>\n    const_iterator find(const K& key, std::size_t hash) const {\n        return find_impl(key, hash, m_buckets + bucket_for_hash(hash));\n    }\n    \n    \n    template<class K>\n    bool contains(const K& key) const {\n        return contains(key, hash_key(key));\n    }\n    \n    template<class K>\n    bool contains(const K& key, std::size_t hash) const {\n        return count(key, hash) != 0;\n    }\n    \n    \n    template<class K>\n    std::pair<iterator, iterator> equal_range(const K& key) {\n        return equal_range(key, hash_key(key));\n    }\n    \n    template<class K>\n    std::pair<iterator, iterator> equal_range(const K& key, std::size_t hash) {\n        iterator it = find(key, hash);\n        return std::make_pair(it, (it == end())?it:std::next(it));\n    }\n    \n    \n    template<class K>\n    std::pair<const_iterator, const_iterator> equal_range(const K& key) const {\n        return equal_range(key, hash_key(key));\n    }\n    \n    template<class K>\n    std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t hash) const {\n        const_iterator it = find(key, hash);\n        return std::make_pair(it, (it == cend())?it:std::next(it));\n    }\n    \n    /*\n     * Bucket interface \n     */\n    size_type bucket_count() const {\n        /*\n         * So that the last bucket can have NeighborhoodSize neighbors, the size of the bucket array is a little\n         * bigger than the real number of buckets when not empty. \n         * We could use some of the buckets at the beginning, but it is faster this way as we avoid extra checks.\n         */\n        if(m_buckets_data.empty()) {\n            return 0;\n        }\n        \n        return m_buckets_data.size() - NeighborhoodSize + 1; \n    }\n    \n    size_type max_bucket_count() const {\n        const std::size_t max_bucket_count = std::min(GrowthPolicy::max_bucket_count(), m_buckets_data.max_size());\n        return max_bucket_count - NeighborhoodSize + 1;\n    }\n    \n    \n    /*\n     *  Hash policy \n     */\n    float load_factor() const {\n        if(bucket_count() == 0) {\n            return 0;\n        }\n        \n        return float(m_nb_elements)/float(bucket_count());\n    }\n    \n    float max_load_factor() const {\n        return m_max_load_factor;\n    }\n    \n    void max_load_factor(float ml) {\n        m_max_load_factor = std::max(0.1f, std::min(ml, 0.95f));\n        m_min_load_threshold_rehash = size_type(float(bucket_count())*MIN_LOAD_FACTOR_FOR_REHASH);\n        m_max_load_threshold_rehash = size_type(float(bucket_count())*m_max_load_factor);\n    }\n    \n    void rehash(size_type count_) {\n        count_ = std::max(count_, size_type(std::ceil(float(size())/max_load_factor())));\n        rehash_impl(count_);\n    }\n    \n    void reserve(size_type count_) {\n        rehash(size_type(std::ceil(float(count_)/max_load_factor())));\n    }\n    \n    \n    /*\n     * Observers\n     */\n    hasher hash_function() const {\n        return static_cast<const Hash&>(*this);\n    }\n    \n    key_equal key_eq() const {\n        return static_cast<const KeyEqual&>(*this);\n    }\n    \n    /*\n     * Other\n     */\n    iterator mutable_iterator(const_iterator pos) {\n        if(pos.m_buckets_iterator != pos.m_buckets_end_iterator) {\n            // Get a non-const iterator\n            auto it = m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), pos.m_buckets_iterator);\n            return iterator(it, m_buckets_data.end(), m_overflow_elements.begin());\n        }\n        else {\n            // Get a non-const iterator\n            auto it = mutable_overflow_iterator(pos.m_overflow_iterator);\n            return iterator(m_buckets_data.end(), m_buckets_data.end(), it);\n        }\n    }\n    \n    size_type overflow_size() const noexcept {\n        return m_overflow_elements.size();\n    }\n    \n    template<class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* = nullptr>\n    typename U::key_compare key_comp() const {\n        return m_overflow_elements.key_comp();\n    }\n    \n    \nprivate:\n    template<class K>\n    std::size_t hash_key(const K& key) const {\n        return Hash::operator()(key);\n    }\n    \n    template<class K1, class K2>\n    bool compare_keys(const K1& key1, const K2& key2) const {\n        return KeyEqual::operator()(key1, key2);\n    }\n    \n    std::size_t bucket_for_hash(std::size_t hash) const {\n        const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash);\n        tsl_hh_assert(bucket < m_buckets_data.size() || (bucket == 0 && m_buckets_data.empty()));\n        \n        return bucket;\n    }\n    \n    template<typename U = value_type, \n             typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type* = nullptr>\n    void rehash_impl(size_type count_) {\n        hopscotch_hash new_map = new_hopscotch_hash(count_);\n        \n        if(!m_overflow_elements.empty()) {\n            new_map.m_overflow_elements.swap(m_overflow_elements);\n            new_map.m_nb_elements += new_map.m_overflow_elements.size();\n            \n            for(const value_type& value : new_map.m_overflow_elements) {\n                const std::size_t ibucket_for_hash = new_map.bucket_for_hash(new_map.hash_key(KeySelect()(value)));\n                new_map.m_buckets[ibucket_for_hash].set_overflow(true);\n            }\n        }\n        \n#ifndef TSL_HH_NO_EXCEPTIONS\n        try {\n#endif\n            const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_map.bucket_count());\n            for(auto it_bucket = m_buckets_data.begin(); it_bucket != m_buckets_data.end(); ++it_bucket) {\n                if(it_bucket->empty()) {\n                    continue;\n                }\n                \n                const std::size_t hash = use_stored_hash?\n                                            it_bucket->truncated_bucket_hash():\n                                            new_map.hash_key(KeySelect()(it_bucket->value()));\n                const std::size_t ibucket_for_hash = new_map.bucket_for_hash(hash);\n                \n                new_map.insert_value(ibucket_for_hash, hash, std::move(it_bucket->value()));\n                \n                \n                erase_from_bucket(*it_bucket, bucket_for_hash(hash));\n            }\n#ifndef TSL_HH_NO_EXCEPTIONS\n        }\n        /*\n         * The call to insert_value may throw an exception if an element is added to the overflow\n         * list and the memory allocation fails. Rollback the elements in this case.\n         */\n        catch(...) {\n            m_overflow_elements.swap(new_map.m_overflow_elements);\n            \n            const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_map.bucket_count());\n            for(auto it_bucket = new_map.m_buckets_data.begin(); it_bucket != new_map.m_buckets_data.end(); ++it_bucket) {\n                if(it_bucket->empty()) {\n                    continue;\n                }\n                \n                const std::size_t hash = use_stored_hash?\n                                            it_bucket->truncated_bucket_hash():\n                                            hash_key(KeySelect()(it_bucket->value()));\n                const std::size_t ibucket_for_hash = bucket_for_hash(hash);\n                \n                // The elements we insert were not in the overflow list before the switch.\n                // They will not be go in the overflow list if we rollback the switch.\n                insert_value(ibucket_for_hash, hash, std::move(it_bucket->value()));\n            }\n            \n            throw;\n        }\n#endif\n        \n        new_map.swap(*this);\n    }\n    \n    template<typename U = value_type, \n             typename std::enable_if<std::is_copy_constructible<U>::value && \n                                     !std::is_nothrow_move_constructible<U>::value>::type* = nullptr>\n    void rehash_impl(size_type count_) {\n        hopscotch_hash new_map = new_hopscotch_hash(count_);\n                \n        const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_map.bucket_count());\n        for(const hopscotch_bucket& bucket: m_buckets_data) {\n            if(bucket.empty()) {\n                continue;\n            }\n            \n            const std::size_t hash = use_stored_hash?\n                                         bucket.truncated_bucket_hash():\n                                         new_map.hash_key(KeySelect()(bucket.value()));\n            const std::size_t ibucket_for_hash = new_map.bucket_for_hash(hash);\n            \n            new_map.insert_value(ibucket_for_hash, hash, bucket.value());\n        }\n        \n        for(const value_type& value: m_overflow_elements) {\n            const std::size_t hash = new_map.hash_key(KeySelect()(value));\n            const std::size_t ibucket_for_hash = new_map.bucket_for_hash(hash);\n            \n            new_map.insert_value(ibucket_for_hash, hash, value);\n        }\n            \n        new_map.swap(*this);\n    }\n    \n#ifdef TSL_HH_NO_RANGE_ERASE_WITH_CONST_ITERATOR\n    iterator_overflow mutable_overflow_iterator(const_iterator_overflow it) {\n        return std::next(m_overflow_elements.begin(), std::distance(m_overflow_elements.cbegin(), it));        \n    }\n#else            \n    iterator_overflow mutable_overflow_iterator(const_iterator_overflow it) {\n        return m_overflow_elements.erase(it, it);       \n    }\n#endif    \n\n    // iterator is in overflow list\n    iterator_overflow erase_from_overflow(const_iterator_overflow pos, std::size_t ibucket_for_hash) {\n#ifdef TSL_HH_NO_RANGE_ERASE_WITH_CONST_ITERATOR        \n        auto it_next = m_overflow_elements.erase(mutable_overflow_iterator(pos));\n#else\n        auto it_next = m_overflow_elements.erase(pos);\n#endif\n        m_nb_elements--;\n        \n        \n        // Check if we can remove the overflow flag\n        tsl_hh_assert(m_buckets[ibucket_for_hash].has_overflow());\n        for(const value_type& value: m_overflow_elements) {\n            const std::size_t bucket_for_value = bucket_for_hash(hash_key(KeySelect()(value)));\n            if(bucket_for_value == ibucket_for_hash) {\n                return it_next;\n            }\n        }\n        \n        m_buckets[ibucket_for_hash].set_overflow(false);\n        return it_next;\n    }\n    \n\n    /**\n     * bucket_for_value is the bucket in which the value is.\n     * ibucket_for_hash is the bucket where the value belongs.\n     */\n    void erase_from_bucket(hopscotch_bucket& bucket_for_value, std::size_t ibucket_for_hash) noexcept {\n        const std::size_t ibucket_for_value = std::distance(m_buckets_data.data(), &bucket_for_value);\n        tsl_hh_assert(ibucket_for_value >= ibucket_for_hash);\n        \n        bucket_for_value.remove_value();\n        m_buckets[ibucket_for_hash].toggle_neighbor_presence(ibucket_for_value - ibucket_for_hash);\n        m_nb_elements--;\n    }\n    \n\n    \n    template<class K, class M>\n    std::pair<iterator, bool> insert_or_assign_impl(K&& key, M&& obj) {\n        auto it = try_emplace_impl(std::forward<K>(key), std::forward<M>(obj));\n        if(!it.second) {\n            it.first.value() = std::forward<M>(obj);\n        }\n        \n        return it;\n    }\n    \n    template<typename P, class... Args>\n    std::pair<iterator, bool> try_emplace_impl(P&& key, Args&&... args_value) {\n        const std::size_t hash = hash_key(key);\n        const std::size_t ibucket_for_hash = bucket_for_hash(hash);\n        \n        // Check if already presents\n        auto it_find = find_impl(key, hash, m_buckets + ibucket_for_hash);\n        if(it_find != end()) {\n            return std::make_pair(it_find, false);\n        }\n        \n        return insert_value(ibucket_for_hash, hash, std::piecewise_construct, \n                                                    std::forward_as_tuple(std::forward<P>(key)), \n                                                    std::forward_as_tuple(std::forward<Args>(args_value)...));\n    }\n    \n    template<typename P>\n    std::pair<iterator, bool> insert_impl(P&& value) {\n        const std::size_t hash = hash_key(KeySelect()(value));\n        const std::size_t ibucket_for_hash = bucket_for_hash(hash);\n        \n        // Check if already presents\n        auto it_find = find_impl(KeySelect()(value), hash, m_buckets + ibucket_for_hash);\n        if(it_find != end()) {\n            return std::make_pair(it_find, false);\n        }\n        \n        \n        return insert_value(ibucket_for_hash, hash, std::forward<P>(value));\n    }\n    \n    template<typename... Args>\n    std::pair<iterator, bool> insert_value(std::size_t ibucket_for_hash, std::size_t hash, Args&&... value_type_args) {\n        if((m_nb_elements - m_overflow_elements.size()) >= m_max_load_threshold_rehash) {\n            rehash(GrowthPolicy::next_bucket_count());\n            ibucket_for_hash = bucket_for_hash(hash);\n        }\n        \n        std::size_t ibucket_empty = find_empty_bucket(ibucket_for_hash);\n        if(ibucket_empty < m_buckets_data.size()) {\n            do {\n                tsl_hh_assert(ibucket_empty >= ibucket_for_hash);\n                \n                // Empty bucket is in range of NeighborhoodSize, use it\n                if(ibucket_empty - ibucket_for_hash < NeighborhoodSize) {\n                    auto it = insert_in_bucket(ibucket_empty, ibucket_for_hash, \n                                               hash, std::forward<Args>(value_type_args)...);\n                    return std::make_pair(iterator(it, m_buckets_data.end(), m_overflow_elements.begin()), true);\n                }\n            }\n            // else, try to swap values to get a closer empty bucket\n            while(swap_empty_bucket_closer(ibucket_empty));\n        }\n            \n        // Load factor is too low or a rehash will not change the neighborhood, put the value in overflow list\n        if(size() < m_min_load_threshold_rehash || !will_neighborhood_change_on_rehash(ibucket_for_hash)) {\n            auto it = insert_in_overflow(ibucket_for_hash, std::forward<Args>(value_type_args)...);\n            return std::make_pair(iterator(m_buckets_data.end(), m_buckets_data.end(), it), true);\n        }\n    \n        rehash(GrowthPolicy::next_bucket_count());\n        ibucket_for_hash = bucket_for_hash(hash);\n        \n        return insert_value(ibucket_for_hash, hash, std::forward<Args>(value_type_args)...);\n    }    \n    \n    /*\n     * Return true if a rehash will change the position of a key-value in the neighborhood of \n     * ibucket_neighborhood_check. In this case a rehash is needed instead of puting the value in overflow list.\n     */\n    bool will_neighborhood_change_on_rehash(size_t ibucket_neighborhood_check) const {\n        std::size_t expand_bucket_count = GrowthPolicy::next_bucket_count();\n        GrowthPolicy expand_growth_policy(expand_bucket_count);\n        \n        const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(expand_bucket_count);\n        for(size_t ibucket = ibucket_neighborhood_check; \n            ibucket < m_buckets_data.size() && (ibucket - ibucket_neighborhood_check) < NeighborhoodSize; \n            ++ibucket)\n        {\n            tsl_hh_assert(!m_buckets[ibucket].empty());\n            \n            const size_t hash = use_stored_hash?\n                                    m_buckets[ibucket].truncated_bucket_hash():\n                                    hash_key(KeySelect()(m_buckets[ibucket].value()));\n            if(bucket_for_hash(hash) != expand_growth_policy.bucket_for_hash(hash)) {\n                return true;\n            }\n        }\n        \n        return false;\n    }\n    \n    /*\n     * Return the index of an empty bucket in m_buckets_data.\n     * If none, the returned index equals m_buckets_data.size()\n     */\n    std::size_t find_empty_bucket(std::size_t ibucket_start) const {\n        const std::size_t limit = std::min(ibucket_start + MAX_PROBES_FOR_EMPTY_BUCKET, m_buckets_data.size());\n        for(; ibucket_start < limit; ibucket_start++) {\n            if(m_buckets[ibucket_start].empty()) {\n                return ibucket_start;\n            }\n        }\n        \n        return m_buckets_data.size();\n    }\n    \n    /*\n     * Insert value in ibucket_empty where value originally belongs to ibucket_for_hash\n     * \n     * Return bucket iterator to ibucket_empty\n     */\n    template<typename... Args>\n    iterator_buckets insert_in_bucket(std::size_t ibucket_empty, std::size_t ibucket_for_hash,\n                                      std::size_t hash, Args&&... value_type_args) \n    {\n        tsl_hh_assert(ibucket_empty >= ibucket_for_hash );\n        tsl_hh_assert(m_buckets[ibucket_empty].empty());\n        m_buckets[ibucket_empty].set_value_of_empty_bucket(hopscotch_bucket::truncate_hash(hash), std::forward<Args>(value_type_args)...);\n        \n        tsl_hh_assert(!m_buckets[ibucket_for_hash].empty());\n        m_buckets[ibucket_for_hash].toggle_neighbor_presence(ibucket_empty - ibucket_for_hash);\n        m_nb_elements++;\n        \n        return m_buckets_data.begin() + ibucket_empty;\n    }\n    \n    template<class... Args, class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* = nullptr>\n    iterator_overflow insert_in_overflow(std::size_t ibucket_for_hash, Args&&... value_type_args) {\n        auto it = m_overflow_elements.emplace(m_overflow_elements.end(), std::forward<Args>(value_type_args)...);\n        \n        m_buckets[ibucket_for_hash].set_overflow(true);\n        m_nb_elements++;\n            \n        return it;\n    }\n    \n    template<class... Args, class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* = nullptr>\n    iterator_overflow insert_in_overflow(std::size_t ibucket_for_hash, Args&&... value_type_args) {\n        auto it = m_overflow_elements.emplace(std::forward<Args>(value_type_args)...).first;\n        \n        m_buckets[ibucket_for_hash].set_overflow(true);\n        m_nb_elements++;\n        \n        return it;\n    }\n    \n    /*\n     * Try to swap the bucket ibucket_empty_in_out with a bucket preceding it while keeping the neighborhood \n     * conditions correct.\n     * \n     * If a swap was possible, the position of ibucket_empty_in_out will be closer to 0 and true will re returned.\n     */\n    bool swap_empty_bucket_closer(std::size_t& ibucket_empty_in_out) {\n        tsl_hh_assert(ibucket_empty_in_out >= NeighborhoodSize);\n        const std::size_t neighborhood_start = ibucket_empty_in_out - NeighborhoodSize + 1;\n        \n        for(std::size_t to_check = neighborhood_start; to_check < ibucket_empty_in_out; to_check++) {\n            neighborhood_bitmap neighborhood_infos = m_buckets[to_check].neighborhood_infos();\n            std::size_t to_swap = to_check;\n            \n            while(neighborhood_infos != 0 && to_swap < ibucket_empty_in_out) {\n                if((neighborhood_infos & 1) == 1) {\n                    tsl_hh_assert(m_buckets[ibucket_empty_in_out].empty());\n                    tsl_hh_assert(!m_buckets[to_swap].empty());\n                    \n                    m_buckets[to_swap].swap_value_into_empty_bucket(m_buckets[ibucket_empty_in_out]);\n                    \n                    tsl_hh_assert(!m_buckets[to_check].check_neighbor_presence(ibucket_empty_in_out - to_check));\n                    tsl_hh_assert(m_buckets[to_check].check_neighbor_presence(to_swap - to_check));\n                    \n                    m_buckets[to_check].toggle_neighbor_presence(ibucket_empty_in_out - to_check);\n                    m_buckets[to_check].toggle_neighbor_presence(to_swap - to_check);\n                    \n                    \n                    ibucket_empty_in_out = to_swap;\n                    \n                    return true;\n                }\n                \n                to_swap++;\n                neighborhood_infos = neighborhood_bitmap(neighborhood_infos >> 1);\n            }\n        }\n        \n        return false;\n    }\n    \n    \n    \n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    typename U::value_type* find_value_impl(const K& key, std::size_t hash, hopscotch_bucket* bucket_for_hash) {\n        return const_cast<typename U::value_type*>(\n                    static_cast<const hopscotch_hash*>(this)->find_value_impl(key, hash, bucket_for_hash));\n    }\n    \n    /*\n     * Avoid the creation of an iterator to just get the value for operator[] and at() in maps. Faster this way.\n     *\n     * Return null if no value for the key (TODO use std::optional when available).\n     */\n    template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>\n    const typename U::value_type* find_value_impl(const K& key, std::size_t hash, \n                                                  const hopscotch_bucket* bucket_for_hash) const \n    {\n        const hopscotch_bucket* bucket_found = find_in_buckets(key, hash, bucket_for_hash);\n        if(bucket_found != nullptr) {\n            return std::addressof(ValueSelect()(bucket_found->value()));\n        }\n        \n        if(bucket_for_hash->has_overflow()) {\n            auto it_overflow = find_in_overflow(key);\n            if(it_overflow != m_overflow_elements.end()) {\n                return std::addressof(ValueSelect()(*it_overflow));\n            }\n        }\n        \n        return nullptr;\n    }\n    \n    template<class K>\n    size_type count_impl(const K& key, std::size_t hash, const hopscotch_bucket* bucket_for_hash) const {\n        if(find_in_buckets(key, hash, bucket_for_hash) != nullptr) {\n            return 1;\n        }\n        else if(bucket_for_hash->has_overflow() && find_in_overflow(key) != m_overflow_elements.cend()) {\n            return 1;\n        }\n        else {\n            return 0;\n        }\n    }\n    \n    template<class K>\n    iterator find_impl(const K& key, std::size_t hash, hopscotch_bucket* bucket_for_hash) {\n        hopscotch_bucket* bucket_found = find_in_buckets(key, hash, bucket_for_hash);\n        if(bucket_found != nullptr) {\n            return iterator(m_buckets_data.begin() + std::distance(m_buckets_data.data(), bucket_found), \n                            m_buckets_data.end(), m_overflow_elements.begin());\n        }\n        \n        if(!bucket_for_hash->has_overflow()) {\n            return end();\n        }\n        \n        return iterator(m_buckets_data.end(), m_buckets_data.end(), find_in_overflow(key));\n    }\n    \n    template<class K>\n    const_iterator find_impl(const K& key, std::size_t hash, const hopscotch_bucket* bucket_for_hash) const {\n        const hopscotch_bucket* bucket_found = find_in_buckets(key, hash, bucket_for_hash);\n        if(bucket_found != nullptr) {\n            return const_iterator(m_buckets_data.cbegin() + std::distance(m_buckets_data.data(), bucket_found), \n                                  m_buckets_data.cend(), m_overflow_elements.cbegin());\n        }\n        \n        if(!bucket_for_hash->has_overflow()) {\n            return cend();\n        }\n\n        \n        return const_iterator(m_buckets_data.cend(), m_buckets_data.cend(), find_in_overflow(key));\n    }\n    \n    template<class K>\n    hopscotch_bucket* find_in_buckets(const K& key, std::size_t hash, hopscotch_bucket* bucket_for_hash) {   \n        const hopscotch_bucket* bucket_found = \n                                    static_cast<const hopscotch_hash*>(this)->find_in_buckets(key, hash, bucket_for_hash); \n        return const_cast<hopscotch_bucket*>(bucket_found);\n    }\n\n    \n    /**\n     * Return a pointer to the bucket which has the value, nullptr otherwise.\n     */\n    template<class K>\n    const hopscotch_bucket* find_in_buckets(const K& key, std::size_t hash, const hopscotch_bucket* bucket_for_hash) const {      \n        (void) hash; // Avoid warning of unused variable when StoreHash is false;\n\n        // TODO Try to optimize the function. \n        // I tried to use ffs and  __builtin_ffs functions but I could not reduce the time the function\n        // takes with -march=native\n        \n        neighborhood_bitmap neighborhood_infos = bucket_for_hash->neighborhood_infos();\n        while(neighborhood_infos != 0) {\n            if((neighborhood_infos & 1) == 1) {\n                // Check StoreHash before calling bucket_hash_equal. Functionally it doesn't change anythin. \n                // If StoreHash is false, bucket_hash_equal is a no-op. Avoiding the call is there to help \n                // GCC optimizes `hash` parameter away, it seems to not be able to do without this hint.\n                if((!StoreHash || bucket_for_hash->bucket_hash_equal(hash)) && \n                    compare_keys(KeySelect()(bucket_for_hash->value()), key)) \n                {\n                    return bucket_for_hash;\n                }\n            }\n            \n            ++bucket_for_hash;\n            neighborhood_infos = neighborhood_bitmap(neighborhood_infos >> 1);\n        }\n        \n        return nullptr;\n    }\n    \n\n    \n    template<class K, class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* = nullptr>\n    iterator_overflow find_in_overflow(const K& key) {\n        return std::find_if(m_overflow_elements.begin(), m_overflow_elements.end(), \n                            [&](const value_type& value) { \n                                return compare_keys(key, KeySelect()(value)); \n                            });\n    }\n    \n    template<class K, class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* = nullptr>\n    const_iterator_overflow find_in_overflow(const K& key) const {\n        return std::find_if(m_overflow_elements.cbegin(), m_overflow_elements.cend(), \n                            [&](const value_type& value) { \n                                return compare_keys(key, KeySelect()(value)); \n                            });\n    }\n    \n    template<class K, class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* = nullptr>\n    iterator_overflow find_in_overflow(const K& key) {\n        return m_overflow_elements.find(key);\n    }\n    \n    template<class K, class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* = nullptr>\n    const_iterator_overflow find_in_overflow(const K& key) const {\n        return m_overflow_elements.find(key);\n    }\n    \n    \n    \n    template<class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* = nullptr>\n    hopscotch_hash new_hopscotch_hash(size_type bucket_count) {\n        return hopscotch_hash(bucket_count, static_cast<Hash&>(*this), static_cast<KeyEqual&>(*this), \n                              get_allocator(), m_max_load_factor);\n    }\n    \n    template<class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* = nullptr>\n    hopscotch_hash new_hopscotch_hash(size_type bucket_count) {\n        return hopscotch_hash(bucket_count, static_cast<Hash&>(*this), static_cast<KeyEqual&>(*this), \n                              get_allocator(), m_max_load_factor, m_overflow_elements.key_comp());\n    }\n    \npublic:    \n    static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0;\n    static constexpr float DEFAULT_MAX_LOAD_FACTOR = (NeighborhoodSize <= 30)?0.8f:0.9f;\n    \nprivate:    \n    static const std::size_t MAX_PROBES_FOR_EMPTY_BUCKET = 12*NeighborhoodSize;\n    static constexpr float MIN_LOAD_FACTOR_FOR_REHASH = 0.1f;\n    \n    /**\n     * We can only use the hash on rehash if the size of the hash type is the same as the stored one or\n     * if we use a power of two modulo. In the case of the power of two modulo, we just mask\n     * the least significant bytes, we just have to check that the truncated_hash_type didn't truncated\n     * too much bytes.\n     */\n    template<class T = size_type, typename std::enable_if<std::is_same<T, truncated_hash_type>::value>::type* = nullptr>\n    static bool USE_STORED_HASH_ON_REHASH(size_type /*bucket_count*/) {\n        return StoreHash;\n    }\n    \n    template<class T = size_type, typename std::enable_if<!std::is_same<T, truncated_hash_type>::value>::type* = nullptr>\n    static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count) {\n        (void) bucket_count;\n        if(StoreHash && is_power_of_two_policy<GrowthPolicy>::value) {\n            tsl_hh_assert(bucket_count > 0);\n            return (bucket_count - 1) <= std::numeric_limits<truncated_hash_type>::max();\n        }\n        else {\n            return false;   \n        }\n    }\n    \n    /**\n     * Return an always valid pointer to an static empty hopscotch_bucket.\n     */            \n    hopscotch_bucket* static_empty_bucket_ptr() {\n        static hopscotch_bucket empty_bucket;\n        return &empty_bucket;\n    }\n    \nprivate:    \n    buckets_container_type m_buckets_data;\n    overflow_container_type m_overflow_elements;\n    \n    /**\n     * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points to static_empty_bucket_ptr.\n     * This variable is useful to avoid the cost of checking if m_buckets_data is empty when trying \n     * to find an element.\n     * \n     * TODO Remove m_buckets_data and only use a pointer+size instead of a pointer+vector to save some space in the hopscotch_hash object.\n     */\n    hopscotch_bucket* m_buckets;\n    \n    size_type m_nb_elements;\n    \n    /**\n     * Min size of the hash table before a rehash can occurs automatically (except if m_max_load_threshold_rehash os reached).\n     * If the neighborhood of a bucket is full before the min is reacher, the elements are put into m_overflow_elements.\n     */\n    size_type m_min_load_threshold_rehash;\n    \n    /**\n     * Max size of the hash table before a rehash occurs automatically to grow the table.\n     */\n    size_type m_max_load_threshold_rehash;\n    \n    float m_max_load_factor;\n};\n\n} // end namespace detail_hopscotch_hash\n\n\n} // end namespace tsl\n\n#endif\n"
  },
  {
    "path": "External/tsl/hopscotch_map.h",
    "content": "/**\n * MIT License\n * \n * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef TSL_HOPSCOTCH_MAP_H\n#define TSL_HOPSCOTCH_MAP_H\n\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <list>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include \"hopscotch_hash.h\"\n\n\nnamespace tsl {\n\n/**\n * Implementation of a hash map using the hopscotch hashing algorithm.\n * \n * The Key and the value T must be either nothrow move-constructible, copy-constructible or both.\n * \n * The size of the neighborhood (NeighborhoodSize) must be > 0 and <= 62 if StoreHash is false.\n * When StoreHash is true, 32-bits of the hash will be stored alongside the neighborhood limiting\n * the NeighborhoodSize to <= 30. There is no memory usage difference between \n * 'NeighborhoodSize 62; StoreHash false' and 'NeighborhoodSize 30; StoreHash true'.\n * \n * Storing the hash may improve performance on insert during the rehash process if the hash takes time\n * to compute. It may also improve read performance if the KeyEqual function takes time (or incurs a cache-miss).\n * If used with simple Hash and KeyEqual it may slow things down.\n * \n * StoreHash can only be set if the GrowthPolicy is set to tsl::power_of_two_growth_policy.\n * \n * GrowthPolicy defines how the map grows and consequently how a hash value is mapped to a bucket. \n * By default the map uses tsl::power_of_two_growth_policy. This policy keeps the number of buckets \n * to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.\n * You may define your own growth policy, check tsl::power_of_two_growth_policy for the interface.\n * \n * If the destructors of Key or T throw an exception, behaviour of the class is undefined.\n * \n * Iterators invalidation:\n *  - clear, operator=, reserve, rehash: always invalidate the iterators.\n *  - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators \n *    if a displacement is needed to resolve a collision (which mean that most of the time, \n *    insert will invalidate the iterators). Or if there is a rehash.\n *  - erase: iterator on the erased element is the only one which become invalid.\n */\ntemplate<class Key, \n         class T, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Allocator = std::allocator<std::pair<Key, T>>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false,\n         class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>\nclass hopscotch_map {\nprivate:    \n    template<typename U>\n    using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;\n    \n    class KeySelect {\n    public:\n        using key_type = Key;\n        \n        const key_type& operator()(const std::pair<Key, T>& key_value) const {\n            return key_value.first;\n        }\n        \n        key_type& operator()(std::pair<Key, T>& key_value) {\n            return key_value.first;\n        }\n    };  \n    \n    class ValueSelect {\n    public:\n        using value_type = T;\n        \n        const value_type& operator()(const std::pair<Key, T>& key_value) const {\n            return key_value.second;\n        }\n        \n        value_type& operator()(std::pair<Key, T>& key_value) {\n            return key_value.second;\n        }\n    };\n    \n    \n    using overflow_container_type = std::list<std::pair<Key, T>, Allocator>;\n    using ht = detail_hopscotch_hash::hopscotch_hash<std::pair<Key, T>, KeySelect, ValueSelect,\n                                                     Hash, KeyEqual, \n                                                     Allocator, NeighborhoodSize, \n                                                     StoreHash, GrowthPolicy,\n                                                     overflow_container_type>;\n    \npublic:\n    using key_type = typename ht::key_type;\n    using mapped_type = T;\n    using value_type = typename ht::value_type;\n    using size_type = typename ht::size_type;\n    using difference_type = typename ht::difference_type;\n    using hasher = typename ht::hasher;\n    using key_equal = typename ht::key_equal;\n    using allocator_type = typename ht::allocator_type;\n    using reference = typename ht::reference;\n    using const_reference = typename ht::const_reference;\n    using pointer = typename ht::pointer;\n    using const_pointer = typename ht::const_pointer;\n    using iterator = typename ht::iterator;\n    using const_iterator = typename ht::const_iterator;\n    \n    \n    \n    /*\n     * Constructors\n     */\n    hopscotch_map() : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {\n    }\n    \n    explicit hopscotch_map(size_type bucket_count, \n                        const Hash& hash = Hash(),\n                        const KeyEqual& equal = KeyEqual(),\n                        const Allocator& alloc = Allocator()) : \n                        m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)\n    {\n    }\n    \n    hopscotch_map(size_type bucket_count,\n                  const Allocator& alloc) : hopscotch_map(bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    hopscotch_map(size_type bucket_count,\n                  const Hash& hash,\n                  const Allocator& alloc) : hopscotch_map(bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n    \n    explicit hopscotch_map(const Allocator& alloc) : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {\n    }\n    \n    template<class InputIt>\n    hopscotch_map(InputIt first, InputIt last,\n                size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                const Hash& hash = Hash(),\n                const KeyEqual& equal = KeyEqual(),\n                const Allocator& alloc = Allocator()) : hopscotch_map(bucket_count, hash, equal, alloc)\n    {\n        insert(first, last);\n    }\n    \n    template<class InputIt>\n    hopscotch_map(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Allocator& alloc) : hopscotch_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    template<class InputIt>\n    hopscotch_map(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Hash& hash,\n                const Allocator& alloc) : hopscotch_map(first, last, bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    hopscotch_map(std::initializer_list<value_type> init,\n                    size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                    const Hash& hash = Hash(),\n                    const KeyEqual& equal = KeyEqual(),\n                    const Allocator& alloc = Allocator()) : \n                    hopscotch_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)\n    {\n    }\n\n    hopscotch_map(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Allocator& alloc) : \n                    hopscotch_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n\n    hopscotch_map(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Hash& hash,\n                    const Allocator& alloc) : \n                    hopscotch_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    \n    hopscotch_map& operator=(std::initializer_list<value_type> ilist) {\n        m_ht.clear();\n        \n        m_ht.reserve(ilist.size());\n        m_ht.insert(ilist.begin(), ilist.end());\n        \n        return *this;\n    }\n    \n    allocator_type get_allocator() const { return m_ht.get_allocator(); }\n    \n    \n    /*\n     * Iterators\n     */\n    iterator begin() noexcept { return m_ht.begin(); }\n    const_iterator begin() const noexcept { return m_ht.begin(); }\n    const_iterator cbegin() const noexcept { return m_ht.cbegin(); }\n    \n    iterator end() noexcept { return m_ht.end(); }\n    const_iterator end() const noexcept { return m_ht.end(); }\n    const_iterator cend() const noexcept { return m_ht.cend(); }\n    \n    \n    /*\n     * Capacity\n     */\n    bool empty() const noexcept { return m_ht.empty(); }\n    size_type size() const noexcept { return m_ht.size(); }\n    size_type max_size() const noexcept { return m_ht.max_size(); }\n    \n    /*\n     * Modifiers\n     */\n    void clear() noexcept { m_ht.clear(); }\n    \n    \n    \n    \n    std::pair<iterator, bool> insert(const value_type& value) { \n        return m_ht.insert(value); \n    }\n        \n    template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>\n    std::pair<iterator, bool> insert(P&& value) { \n        return m_ht.insert(std::forward<P>(value)); \n    }\n    \n    std::pair<iterator, bool> insert(value_type&& value) { \n        return m_ht.insert(std::move(value)); \n    }\n    \n    \n    iterator insert(const_iterator hint, const value_type& value) { \n        return m_ht.insert(hint, value); \n    }\n        \n    template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>\n    iterator insert(const_iterator hint, P&& value) { \n        return m_ht.insert(hint, std::forward<P>(value));\n    }\n    \n    iterator insert(const_iterator hint, value_type&& value) { \n        return m_ht.insert(hint, std::move(value)); \n    }\n    \n    \n    template<class InputIt>\n    void insert(InputIt first, InputIt last) { \n        m_ht.insert(first, last); \n    }\n    \n    void insert(std::initializer_list<value_type> ilist) { \n        m_ht.insert(ilist.begin(), ilist.end()); \n    }\n\n    \n    \n    \n    template<class M>\n    std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) { \n        return m_ht.insert_or_assign(k, std::forward<M>(obj)); \n    }\n\n    template<class M>\n    std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) { \n        return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj)); \n    }\n    \n    template<class M>\n    iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {\n        return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));\n    }\n    \n    template<class M>\n    iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {\n        return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));\n    }\n    \n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace will need to move or copy the key-value once.\n     * The method is equivalent to insert(value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&&... args) { \n        return m_ht.emplace(std::forward<Args>(args)...); \n    }\n    \n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.\n     * The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    iterator emplace_hint(const_iterator hint, Args&&... args) {\n        return m_ht.emplace_hint(hint, std::forward<Args>(args)...);\n    }\n    \n    \n    \n    \n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) { \n        return m_ht.try_emplace(k, std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {\n        return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {\n        return m_ht.try_emplace(hint, k, std::forward<Args>(args)...);\n    }\n    \n    template<class... Args>\n    iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {\n        return m_ht.try_emplace(hint, std::move(k), std::forward<Args>(args)...);\n    }\n    \n    \n\n    \n    iterator erase(iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }\n    size_type erase(const key_type& key) { return m_ht.erase(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */    \n    size_type erase(const key_type& key, std::size_t precalculated_hash) { \n        return m_ht.erase(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type erase(const K& key) { return m_ht.erase(key); }\n    \n    /**\n     * @copydoc erase(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type erase(const K& key, std::size_t precalculated_hash) { \n        return m_ht.erase(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    void swap(hopscotch_map& other) { other.m_ht.swap(m_ht); }\n    \n    /*\n     * Lookup\n     */\n    T& at(const Key& key) { return m_ht.at(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }\n    \n    \n    const T& at(const Key& key) const { return m_ht.at(key); }\n    \n    /**\n     * @copydoc at(const Key& key, std::size_t precalculated_hash)\n     */\n    const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }\n    \n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    T& at(const K& key) { return m_ht.at(key); }\n\n    /**\n     * @copydoc at(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }\n    \n    \n    /**\n     * @copydoc at(const K& key)\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    const T& at(const K& key) const { return m_ht.at(key); }\n    \n    /**\n     * @copydoc at(const K& key, std::size_t precalculated_hash)\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }\n    \n    \n    \n    \n    T& operator[](const Key& key) { return m_ht[key]; }    \n    T& operator[](Key&& key) { return m_ht[std::move(key)]; }\n    \n    \n    \n    \n    size_type count(const Key& key) const { return m_ht.count(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    size_type count(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.count(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type count(const K& key) const { return m_ht.count(key); }\n    \n    /**\n     * @copydoc count(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */     \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    \n    \n    \n    iterator find(const Key& key) { return m_ht.find(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    const_iterator find(const Key& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const Key& key, std::size_t precalculated_hash)\n     */\n    const_iterator find(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.find(key, precalculated_hash);\n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    iterator find(const K& key) { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    /**\n     * @copydoc find(const K& key)\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    const_iterator find(const K& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    const_iterator find(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.find(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    bool contains(const Key& key) const { return m_ht.contains(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    bool contains(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key) const { return m_ht.contains(key); }\n    \n    /**\n     * @copydoc contains(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const Key& key, std::size_t precalculated_hash)\n     */\n    std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n\n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }\n    \n    \n    /**\n     * @copydoc equal_range(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    /**\n     * @copydoc equal_range(const K& key)\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const K& key, std::size_t precalculated_hash)\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    /*\n     * Bucket interface \n     */\n    size_type bucket_count() const { return m_ht.bucket_count(); }\n    size_type max_bucket_count() const { return m_ht.max_bucket_count(); }\n    \n    \n    /*\n     *  Hash policy \n     */\n    float load_factor() const { return m_ht.load_factor(); }\n    float max_load_factor() const { return m_ht.max_load_factor(); }\n    void max_load_factor(float ml) { m_ht.max_load_factor(ml); }\n    \n    void rehash(size_type count_) { m_ht.rehash(count_); }\n    void reserve(size_type count_) { m_ht.reserve(count_); }\n    \n    \n    /*\n     * Observers\n     */\n    hasher hash_function() const { return m_ht.hash_function(); }\n    key_equal key_eq() const { return m_ht.key_eq(); }\n    \n    /*\n     * Other\n     */\n    \n    /**\n     * Convert a const_iterator to an iterator.\n     */\n    iterator mutable_iterator(const_iterator pos) {\n        return m_ht.mutable_iterator(pos);\n    }\n    \n    size_type overflow_size() const noexcept { return m_ht.overflow_size(); }\n    \n    friend bool operator==(const hopscotch_map& lhs, const hopscotch_map& rhs) {\n        if(lhs.size() != rhs.size()) {\n            return false;\n        }\n        \n        for(const auto& element_lhs : lhs) {\n            const auto it_element_rhs = rhs.find(element_lhs.first);\n            if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {\n                return false;\n            }\n        }\n        \n        return true;\n    }\n\n    friend bool operator!=(const hopscotch_map& lhs, const hopscotch_map& rhs) {\n        return !operator==(lhs, rhs);\n    }\n\n    friend void swap(hopscotch_map& lhs, hopscotch_map& rhs) {\n        lhs.swap(rhs);\n    }\n\n\n    \nprivate:\n    ht m_ht;\n};\n\n\n/**\n * Same as `tsl::hopscotch_map<Key, T, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.\n */\ntemplate<class Key, \n         class T, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Allocator = std::allocator<std::pair<Key, T>>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false>\nusing hopscotch_pg_map = hopscotch_map<Key, T, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;\n\n} // end namespace tsl\n\n#endif\n"
  },
  {
    "path": "External/tsl/hopscotch_set.h",
    "content": "/**\n * MIT License\n * \n * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#ifndef TSL_HOPSCOTCH_SET_H\n#define TSL_HOPSCOTCH_SET_H\n\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <list>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include \"hopscotch_hash.h\"\n\n\nnamespace tsl {\n\n/**\n * Implementation of a hash set using the hopscotch hashing algorithm.\n * \n * The Key must be either nothrow move-constructible, copy-constructible or both.\n * \n * The size of the neighborhood (NeighborhoodSize) must be > 0 and <= 62 if StoreHash is false.\n * When StoreHash is true, 32-bits of the hash will be stored alongside the neighborhood limiting\n * the NeighborhoodSize to <= 30. There is no memory usage difference between \n * 'NeighborhoodSize 62; StoreHash false' and 'NeighborhoodSize 30; StoreHash true'.\n * \n * Storing the hash may improve performance on insert during the rehash process if the hash takes time\n * to compute. It may also improve read performance if the KeyEqual function takes time (or incurs a cache-miss).\n * If used with simple Hash and KeyEqual it may slow things down.\n * \n * StoreHash can only be set if the GrowthPolicy is set to tsl::power_of_two_growth_policy.\n * \n * GrowthPolicy defines how the set grows and consequently how a hash value is mapped to a bucket. \n * By default the set uses tsl::power_of_two_growth_policy. This policy keeps the number of buckets \n * to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo.\n * You may define your own growth policy, check tsl::power_of_two_growth_policy for the interface.\n * \n * If the destructor of Key throws an exception, behaviour of the class is undefined.\n * \n * Iterators invalidation:\n *  - clear, operator=, reserve, rehash: always invalidate the iterators.\n *  - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators \n *    if a displacement is needed to resolve a collision (which mean that most of the time, \n *    insert will invalidate the iterators). Or if there is a rehash.\n *  - erase: iterator on the erased element is the only one which become invalid.\n */\ntemplate<class Key, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Allocator = std::allocator<Key>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false,\n         class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>\nclass hopscotch_set {\nprivate:    \n    template<typename U>\n    using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;\n    \n    class KeySelect {\n    public:\n        using key_type = Key;\n        \n        const key_type& operator()(const Key& key) const {\n            return key;\n        }\n        \n        key_type& operator()(Key& key) {\n            return key;\n        }\n    };\n    \n    \n    using overflow_container_type = std::list<Key, Allocator>;\n    using ht = detail_hopscotch_hash::hopscotch_hash<Key, KeySelect, void,\n                                                     Hash, KeyEqual, \n                                                     Allocator, NeighborhoodSize, \n                                                     StoreHash, GrowthPolicy,\n                                                     overflow_container_type>;\n            \npublic:\n    using key_type = typename ht::key_type;\n    using value_type = typename ht::value_type;\n    using size_type = typename ht::size_type;\n    using difference_type = typename ht::difference_type;\n    using hasher = typename ht::hasher;\n    using key_equal = typename ht::key_equal;\n    using allocator_type = typename ht::allocator_type;\n    using reference = typename ht::reference;\n    using const_reference = typename ht::const_reference;\n    using pointer = typename ht::pointer;\n    using const_pointer = typename ht::const_pointer;\n    using iterator = typename ht::iterator;\n    using const_iterator = typename ht::const_iterator;\n\n    \n    /*\n     * Constructors\n     */\n    hopscotch_set() : hopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {\n    }\n    \n    explicit hopscotch_set(size_type bucket_count, \n                        const Hash& hash = Hash(),\n                        const KeyEqual& equal = KeyEqual(),\n                        const Allocator& alloc = Allocator()) : \n                        m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)\n    {\n    }\n    \n    hopscotch_set(size_type bucket_count,\n                  const Allocator& alloc) : hopscotch_set(bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    hopscotch_set(size_type bucket_count,\n                  const Hash& hash,\n                  const Allocator& alloc) : hopscotch_set(bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n    \n    explicit hopscotch_set(const Allocator& alloc) : hopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {\n    }\n    \n    template<class InputIt>\n    hopscotch_set(InputIt first, InputIt last,\n                size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                const Hash& hash = Hash(),\n                const KeyEqual& equal = KeyEqual(),\n                const Allocator& alloc = Allocator()) : hopscotch_set(bucket_count, hash, equal, alloc)\n    {\n        insert(first, last);\n    }\n    \n    template<class InputIt>\n    hopscotch_set(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Allocator& alloc) : hopscotch_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n    \n    template<class InputIt>\n    hopscotch_set(InputIt first, InputIt last,\n                size_type bucket_count,\n                const Hash& hash,\n                const Allocator& alloc) : hopscotch_set(first, last, bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    hopscotch_set(std::initializer_list<value_type> init,\n                    size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,\n                    const Hash& hash = Hash(),\n                    const KeyEqual& equal = KeyEqual(),\n                    const Allocator& alloc = Allocator()) : \n                    hopscotch_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)\n    {\n    }\n\n    hopscotch_set(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Allocator& alloc) : \n                    hopscotch_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)\n    {\n    }\n\n    hopscotch_set(std::initializer_list<value_type> init,\n                    size_type bucket_count,\n                    const Hash& hash,\n                    const Allocator& alloc) : \n                    hopscotch_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)\n    {\n    }\n\n    \n    hopscotch_set& operator=(std::initializer_list<value_type> ilist) {\n        m_ht.clear();\n        \n        m_ht.reserve(ilist.size());\n        m_ht.insert(ilist.begin(), ilist.end());\n        \n        return *this;\n    }\n    \n    allocator_type get_allocator() const { return m_ht.get_allocator(); }\n    \n    \n    /*\n     * Iterators\n     */\n    iterator begin() noexcept { return m_ht.begin(); }\n    const_iterator begin() const noexcept { return m_ht.begin(); }\n    const_iterator cbegin() const noexcept { return m_ht.cbegin(); }\n    \n    iterator end() noexcept { return m_ht.end(); }\n    const_iterator end() const noexcept { return m_ht.end(); }\n    const_iterator cend() const noexcept { return m_ht.cend(); }\n    \n    \n    /*\n     * Capacity\n     */\n    bool empty() const noexcept { return m_ht.empty(); }\n    size_type size() const noexcept { return m_ht.size(); }\n    size_type max_size() const noexcept { return m_ht.max_size(); }\n    \n    /*\n     * Modifiers\n     */\n    void clear() noexcept { m_ht.clear(); }\n    \n    \n    \n    \n    std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }\n    std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }\n    \n    iterator insert(const_iterator hint, const value_type& value) { return m_ht.insert(hint, value); }\n    iterator insert(const_iterator hint, value_type&& value) { return m_ht.insert(hint, std::move(value)); }\n    \n    template<class InputIt>\n    void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }\n    void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }\n\n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace will need to move or copy the key-value once.\n     * The method is equivalent to insert(value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }\n    \n    \n    \n    \n    /**\n     * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.\n     * The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));\n     * \n     * Mainly here for compatibility with the std::unordered_map interface.\n     */\n    template<class... Args>\n    iterator emplace_hint(const_iterator hint, Args&&... args) {\n        return m_ht.emplace_hint(hint, std::forward<Args>(args)...);\n    }\n\n    \n    \n    \n    iterator erase(iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator pos) { return m_ht.erase(pos); }\n    iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }\n    size_type erase(const key_type& key) { return m_ht.erase(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */    \n    size_type erase(const key_type& key, std::size_t precalculated_hash) { \n        return m_ht.erase(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type erase(const K& key) { return m_ht.erase(key); }\n    \n    /**\n     * @copydoc erase(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type erase(const K& key, std::size_t precalculated_hash) { \n        return m_ht.erase(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    void swap(hopscotch_set& other) { other.m_ht.swap(m_ht); }\n    \n    \n    /*\n     * Lookup\n     */\n    size_type count(const Key& key) const { return m_ht.count(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type count(const K& key) const { return m_ht.count(key); }\n    \n    /**\n     * @copydoc count(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }\n    \n    \n    \n    \n    iterator find(const Key& key) { return m_ht.find(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    const_iterator find(const Key& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const Key& key, std::size_t precalculated_hash)\n     */\n    const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    iterator find(const K& key) { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }\n    \n    /**\n     * @copydoc find(const K& key)\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    const_iterator find(const K& key) const { return m_ht.find(key); }\n    \n    /**\n     * @copydoc find(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }\n    \n    \n    \n    \n    bool contains(const Key& key) const { return m_ht.contains(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    bool contains(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key) const { return m_ht.contains(key); }\n    \n    /**\n     * @copydoc contains(const K& key) const\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    bool contains(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.contains(key, precalculated_hash); \n    }\n    \n    \n    \n    \n    std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */\n    std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const Key& key, std::size_t precalculated_hash)\n     */\n    std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    /**\n     * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. \n     * If so, K must be hashable and comparable to Key.\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }\n    \n    /**\n     * @copydoc equal_range(const K& key)\n     * \n     * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same\n     * as hash_function()(key). Useful to speed-up the lookup if you already have the hash.\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    /**\n     * @copydoc equal_range(const K& key)\n     */\n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }\n\n    /**\n     * @copydoc equal_range(const K& key, std::size_t precalculated_hash)\n     */    \n    template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> \n    std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const { \n        return m_ht.equal_range(key, precalculated_hash); \n    }\n    \n    \n    \n\n    /*\n     * Bucket interface \n     */\n    size_type bucket_count() const { return m_ht.bucket_count(); }\n    size_type max_bucket_count() const { return m_ht.max_bucket_count(); }\n    \n    \n    /*\n     *  Hash policy \n     */\n    float load_factor() const { return m_ht.load_factor(); }\n    float max_load_factor() const { return m_ht.max_load_factor(); }\n    void max_load_factor(float ml) { m_ht.max_load_factor(ml); }\n    \n    void rehash(size_type count_) { m_ht.rehash(count_); }\n    void reserve(size_type count_) { m_ht.reserve(count_); }\n    \n    \n    /*\n     * Observers\n     */\n    hasher hash_function() const { return m_ht.hash_function(); }\n    key_equal key_eq() const { return m_ht.key_eq(); }\n    \n    \n    /*\n     * Other\n     */\n    \n    /**\n     * Convert a const_iterator to an iterator.\n     */\n    iterator mutable_iterator(const_iterator pos) {\n        return m_ht.mutable_iterator(pos);\n    }\n    \n    size_type overflow_size() const noexcept { return m_ht.overflow_size(); }\n    \n    friend bool operator==(const hopscotch_set& lhs, const hopscotch_set& rhs) {\n        if(lhs.size() != rhs.size()) {\n            return false;\n        }\n        \n        for(const auto& element_lhs : lhs) {\n            const auto it_element_rhs = rhs.find(element_lhs);\n            if(it_element_rhs == rhs.cend()) {\n                return false;\n            }\n        }\n        \n        return true;\n    }\n\n    friend bool operator!=(const hopscotch_set& lhs, const hopscotch_set& rhs) {\n        return !operator==(lhs, rhs);\n    }\n\n    friend void swap(hopscotch_set& lhs, hopscotch_set& rhs) {\n        lhs.swap(rhs);\n    }\n    \nprivate:\n    ht m_ht;    \n};\n\n\n/**\n * Same as `tsl::hopscotch_set<Key, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.\n */\ntemplate<class Key, \n         class Hash = std::hash<Key>,\n         class KeyEqual = std::equal_to<Key>,\n         class Allocator = std::allocator<Key>,\n         unsigned int NeighborhoodSize = 62,\n         bool StoreHash = false>\nusing hopscotch_pg_set = hopscotch_set<Key, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;\n\n} // end namespace tsl\n\n#endif\n"
  },
  {
    "path": "NEWorld.Base/Common/Console.cpp",
    "content": "// \n// Core: Console.cpp\n// NEWorld: A Free Game with Similar Rules to Minecraft.\n// Copyright (C) 2015-2018 NEWorld Team\n// \n// NEWorld is free software: you can redistribute it and/or modify it \n// under the terms of the GNU Lesser General Public License as published\n// by the Free Software Foundation, either version 3 of the License, or \n// (at your option) any later version.\n// \n// NEWorld is distributed in the hope that it will be useful, but WITHOUT\n// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY \n// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General \n// Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public License\n// along with NEWorld.  If not, see <http://www.gnu.org/licenses/>.\n// \n\n#include \"Console.h\"\n\n#if (__CYGWIN__ || _WIN32)\n\n#include <windows.h>\n#include <winbase.h>\n#include <consoleapi2.h>\n\nnamespace LColorFunc {\n    // Microsoft Windows\n    static HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);\n\n    std::ostream& black(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, 0);\n        return s;\n    }\n\n    std::ostream& lblack(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& red(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED);\n        return s;\n    }\n\n    std::ostream& lred(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& green(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN);\n        return s;\n    }\n\n    std::ostream& lgreen(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& blue(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_BLUE);\n        return s;\n    }\n\n    std::ostream& lblue(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& yellow(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_GREEN);\n        return s;\n    }\n\n    std::ostream& lyellow(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& magenta(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_BLUE);\n        return s;\n    }\n\n    std::ostream& lmagenta(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& cyan(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN | FOREGROUND_BLUE);\n        return s;\n    }\n\n    std::ostream& lcyan(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);\n        return s;\n    }\n\n    std::ostream& white(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);\n        return s;\n    }\n\n    std::ostream& lwhite(std::ostream& s) noexcept {\n        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);\n        return s;\n    }\n}\n\n#else\n    // *nix\nnamespace LColorFunc {\n    std::ostream& black(std::ostream& s) noexcept { return s << \"\\033[21;30m\"; }\n\n    std::ostream& lblack(std::ostream& s) noexcept { return s << \"\\033[1;30m\"; }\n\n    std::ostream& red(std::ostream& s) noexcept { return s << \"\\033[21;31m\"; }\n\n    std::ostream& lred(std::ostream& s) noexcept { return s << \"\\033[1;31m\"; }\n\n    std::ostream& green(std::ostream& s) noexcept { return s << \"\\033[21;32m\"; }\n\n    std::ostream& lgreen(std::ostream& s) noexcept { return s << \"\\033[1;32m\"; }\n\n    std::ostream& blue(std::ostream& s) noexcept { return s << \"\\033[21;34m\"; }\n\n    std::ostream& lblue(std::ostream& s) noexcept { return s << \"\\033[1;34m\"; }\n\n    std::ostream& yellow(std::ostream& s) noexcept { return s << \"\\033[21;33m\"; }\n\n    std::ostream& lyellow(std::ostream& s) noexcept { return s << \"\\033[1;33m\"; }\n\n    std::ostream& magenta(std::ostream& s) noexcept { return s << \"\\033[21;35m\"; }\n\n    std::ostream& lmagenta(std::ostream& s) noexcept { return s << \"\\033[1;35m\"; }\n\n    std::ostream& cyan(std::ostream& s) noexcept { return s << \"\\033[21;36m\"; }\n\n    std::ostream& lcyan(std::ostream& s) noexcept { return s << \"\\033[1;36m\"; }\n\n    std::ostream& white(std::ostream& s) noexcept { return s << \"\\033[21;37m\"; }\n\n    std::ostream& lwhite(std::ostream& s) noexcept { return s << \"\\033[1;37m\"; }\n\n}\n#endif"
  },
  {
    "path": "NEWorld.Base/Common/Console.h",
    "content": "// \n// Core: Console.h\n// NEWorld: A Free Game with Similar Rules to Minecraft.\n// Copyright (C) 2015-2018 NEWorld Team\n// \n// NEWorld is free software: you can redistribute it and/or modify it \n// under the terms of the GNU Lesser General Public License as published\n// by the Free Software Foundation, either version 3 of the License, or \n// (at your option) any later version.\n// \n// NEWorld is distributed in the hope that it will be useful, but WITHOUT\n// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY \n// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General \n// Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public License\n// along with NEWorld.  If not, see <http://www.gnu.org/licenses/>.\n// \n\n#pragma once\n\n#include <ostream>\n\nnamespace LColorFunc {\n    using ColorFunc = std::ostream& (*)(std::ostream& s) noexcept;\n\n    std::ostream& black(std::ostream& s) noexcept;\n\n    std::ostream& lblack(std::ostream& s) noexcept;\n\n    std::ostream& red(std::ostream& s) noexcept;\n\n    std::ostream& lred(std::ostream& s) noexcept;\n\n    std::ostream& green(std::ostream& s) noexcept;\n\n    std::ostream& lgreen(std::ostream& s) noexcept;\n\n    std::ostream& blue(std::ostream& s) noexcept;\n\n    std::ostream& lblue(std::ostream& s) noexcept;\n\n    std::ostream& yellow(std::ostream& s) noexcept;\n\n    std::ostream& lyellow(std::ostream& s) noexcept;\n\n    std::ostream& magenta(std::ostream& s) noexcept;\n\n    std::ostream& lmagenta(std::ostream& s) noexcept;\n\n    std::ostream& cyan(std::ostream& s) noexcept;\n\n    std::ostream& lcyan(std::ostream& s) noexcept;\n\n    std::ostream& white(std::ostream& s) noexcept;\n\n    std::ostream& lwhite(std::ostream& s) noexcept;\n}\n\nnamespace LColor {\n    constexpr const char* black = \"&0\";\n    constexpr const char* red = \"&1\";\n    constexpr const char* yellow = \"&2\";\n    constexpr const char* green = \"&3\";\n    constexpr const char* cyan = \"&4\";\n    constexpr const char* blue = \"&5\";\n    constexpr const char* magenta = \"&6\";\n    constexpr const char* white = \"&7\";\n    constexpr const char* lblack = \"&8\";\n    constexpr const char* lred = \"&9\";\n    constexpr const char* lyellow = \"&a\";\n    constexpr const char* lgreen = \"&b\";\n    constexpr const char* lcyan = \"&c\";\n    constexpr const char* lblue = \"&d\";\n    constexpr const char* lmagenta = \"&e\";\n    constexpr const char* lwhite = \"&f\";\n}"
  },
  {
    "path": "NEWorld.Base/Common/Logger.cpp",
    "content": "// \n// Core: Logger.cpp\n// NEWorld: A Free Game with Similar Rules to Minecraft.\n// Copyright (C) 2015-2018 NEWorld Team\n// \n// NEWorld is free software: you can redistribute it and/or modify it \n// under the terms of the GNU Lesser General Public License as published\n// by the Free Software Foundation, either version 3 of the License, or \n// (at your option) any later version.\n// \n// NEWorld is distributed in the hope that it will be useful, but WITHOUT\n// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY \n// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General \n// Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public License\n// along with NEWorld.  If not, see <http://www.gnu.org/licenses/>.\n// \n\n#include <map>\n#include <ctime>\n#include <mutex>\n#include <array>\n#include <fstream>\n#include <iostream>\n#include <filesystem>\n#include \"Logger.h\"\n#include \"Console.h\"\n\nnamespace {\n    constexpr std::array<const char*, 6> levelTags = {\n            \"[verbose]\", \"[debug]\", \"[info]\", \"[warning]\", \"[error]\", \"[fatal]\"\n    };\n\n    std::vector<std::ofstream> fSink{};\n\n    Logger::Level coutLevel = Logger::Level::verbose;\n    Logger::Level cerrLevel = Logger::Level::fatal;\n    Logger::Level fileLevel = Logger::Level::info;\n    Logger::Level lineLevel = Logger::Level::error;\n\n    template <size_t length>\n    std::string convert(int arg) {\n        char arr[13];\n        int siz = 0u;\n        while (arg) {\n            arr[siz++] = arg % 10 + '0'; // NOLINT\n            arg /= 10;\n        }\n        std::string ret(length - siz, '0');\n        ret.reserve(length);\n        for (int i = siz - 1; i >= 0; i--) ret += arr[i];\n        return ret;\n    }\n\n    std::string getTimeString(const char dateSplit, const char midSplit, const char timeSplit) {\n        time_t timer = time(nullptr);\n        tm currtime;\n#if _MSC_VER\n        localtime_s(&currtime, &timer); // MSVC\n#else\n        localtime_r(&timer, &currtime); // POSIX\n#endif\n        return convert<4u>(currtime.tm_year + 1900) + dateSplit + convert<2u>(currtime.tm_mon)\n            + dateSplit + convert<2u>(currtime.tm_mday) + midSplit + convert<2u>(currtime.tm_hour)\n            + timeSplit + convert<2u>(currtime.tm_min) + timeSplit + convert<2u>(currtime.tm_sec);\n    }\n\n    void setLogColor(const Logger::Level& level, std::stringstream& content) {\n        switch (level) {\n        case Logger::Level::verbose:\n        case Logger::Level::debug: return (content << LColor::white, (void)0);\n        case Logger::Level::info: return (content << LColor::lwhite, (void)0);\n        case Logger::Level::warning: return (content << LColor::lyellow, (void)0);\n        case Logger::Level::error: return (content << LColor::lred, (void)0);\n        case Logger::Level::fatal: return (content << LColor::red, (void)0);\n        default: return;\n        }\n    }\n\n    constexpr char styleChar = '&';\n\n    LColorFunc::ColorFunc queryColorFunc(const char style) {\n        using namespace LColorFunc;\n        static std::map<char, ColorFunc> map = {\n                {'0', black}, {'1', red}, {'2', yellow}, {'3', green},\n                {'4', cyan}, {'5', blue}, {'6', magenta}, {'7', white},\n                {'8', lblack}, {'9', lred}, {'a', lyellow}, {'b', lgreen},\n                {'c', lcyan}, {'d', lblue}, {'e', lmagenta}, {'f', lwhite},\n        };\n        const auto chNorm = (style >= 'A' && style <= 'F') ? style - 'A' + 'a' : style;\n        const auto sRes = map.find(chNorm);\n        return (sRes != map.end()) ? sRes->second : ColorFunc();\n    }\n\n    void chConsumeStyled(std::ostream& ostream, const char ch) {\n        if (const auto cf = queryColorFunc(ch); cf)\n            return (ostream << cf, (void)0);\n        if (ch == styleChar) ostream << styleChar; // Escaped to `stylechar`\n        else ostream << styleChar << ch; // Wrong color code\n    }\n\n    template <class Fn>\n    auto transitStyleString(const std::string& str, Fn fn) {\n        std::string_view vw{ str };\n        std::string::size_type pos1 = 0;\n        std::stringstream ss{};\n        for (;;) {\n            const auto pos2 = vw.find(styleChar, pos1);\n            if (std::string::npos == pos2) return (ss << vw.substr(pos1, str.size()), ss.str());\n            ss << vw.substr(pos1, pos2 - pos1);\n            if (pos2 < str.size()) fn(ss, str[pos2 + 1]);\n            pos1 = pos2 + 2;\n        }\n    }\n\n    void lockedFlush(std::ostream& stream, const std::string& string) {\n        static std::mutex mutex;\n        std::lock_guard<std::mutex> lock(mutex);\n        stream << string;\n    }\n\n    void flushConsole(const Logger::Level level, const std::string& string) {\n        const auto line = transitStyleString(string, chConsumeStyled);\n        if (level >= cerrLevel)\n            lockedFlush(std::cerr, line);\n        else if (level >= coutLevel)\n            lockedFlush(std::cout, line);\n    }\n\n    void flushFiles(const Logger::Level level, const std::string& string) {\n        if (level >= fileLevel) {\n            const auto line = transitStyleString(string, [](auto&&, auto&&) {});\n            for (auto& it : fSink) {\n                lockedFlush(it, line);\n                if (level >= cerrLevel) it.flush();\n            }\n        }\n    }\n}\n\nvoid Logger::addFileSink(const std::string& path, const std::string& prefix) {\n    std::filesystem::create_directory(path);\n    fSink.emplace_back(path + prefix + \"_\" + getTimeString('-', '_', '-') + \".log\");\n}\n\nLogger::Logger(const char* fileName, const char* funcName, int lineNumber, Level level, const char* mgr)\n    :mLevel(level), mFileName(fileName), mFuncName(funcName), mLineNumber(lineNumber) {\n    mContent << LColor::white << getTimeString('-', ' ', ':') << \" [\" << mgr << ']';\n    setLogColor(level, mContent);\n    mContent << levelTags[static_cast<size_t>(level)] << ' ';\n}\n\nLogger::~Logger() {\n    if (mLevel >= lineLevel) {\n        mContent << std::endl\n            << \"\\tSource :\\t\" << mFileName << std::endl\n            << \"\\tAt Line :\\t\" << mLineNumber << std::endl\n            << \"\\tFunction :\\t\" << mFuncName << std::endl;\n    }\n    mContent << std::endl;\n    const auto string = mContent.str();\n    if (!fileOnly) flushConsole(mLevel, string);\n    flushFiles(mLevel, string);\n}"
  },
  {
    "path": "NEWorld.Base/Common/Logger.h",
    "content": "// \n// Core: Logger.h\n// NEWorld: A Free Game with Similar Rules to Minecraft.\n// Copyright (C) 2015-2018 NEWorld Team\n// \n// NEWorld is free software: you can redistribute it and/or modify it \n// under the terms of the GNU Lesser General Public License as published\n// by the Free Software Foundation, either version 3 of the License, or \n// (at your option) any later version.\n// \n// NEWorld is distributed in the hope that it will be useful, but WITHOUT\n// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY \n// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General \n// Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public License\n// along with NEWorld.  If not, see <http://www.gnu.org/licenses/>.\n// \n\n#pragma once\n\n#include <sstream>\n#include <vector>\n#include <string>\n\nclass Logger {\npublic:\n    enum class Level {\n        verbose,\n        debug,\n        info,\n        warning,\n        error,\n        fatal\n    };\n\n    Logger(const char* fileName, const char* funcName, int lineNumber, Level level, const char* mgr);\n    ~Logger();\n\n    template <typename T>\n    Logger& operator<<(const T& rhs) {\n        mContent << rhs;\n        return *this;\n    }\n\n    template <typename U>\n    Logger& operator<<(const std::vector<U>& rhs) {\n        for (auto& item : rhs)\n            mContent << item << \" \";\n        return *this;\n    }\n\n    static void addFileSink(const std::string& path, const std::string& prefix);\nprivate:\n    Level mLevel;\n    int mLineNumber;\n    const char* mFileName;\n    const char* mFuncName;\n    bool fileOnly{ false };\n    std::stringstream mContent;\n};\n\n#define loggerstream(level) Logger(__FILE__, __FUNCTION__, __LINE__, Logger::Level::level, \"NEWorld\")\n// Information for tracing\n#define verbosestream loggerstream(verbose)\n// Information for developers\n#define debugstream loggerstream(debug)\n// Information for engine users\n#define infostream loggerstream(info)\n// Problems that may affect facility, performance or stability but may not lead the Game to crash immediately\n#define warningstream loggerstream(warning)\n// The Game crashes, but may be resumed by ways such as reloading the world which don't restart the program\n#define errorstream loggerstream(error)\n// Unrecoverable error and program termination is required\n#define fatalstream loggerstream(fatal)"
  },
  {
    "path": "NEWorld.Base/Math/Vector2.h",
    "content": "#pragma once\n\n#include <cmath>\n#include <algorithm>\n#include <type_traits>\n\ntemplate<class T, class = std::enable_if<std::is_arithmetic_v<T>>>\nstruct Vec2 {\n    union {\n        T Data[2];\n        struct {\n            T X, Y;\n        };\n    };\n\n    constexpr Vec2() noexcept = default;\n\n    constexpr Vec2(T x, T y) noexcept\n            : X(x), Y(y) {}\n\n    constexpr explicit Vec2(T v) noexcept\n            : X(v), Y(v) {}\n\n    template<typename U, class = std::enable_if_t<std::is_convertible_v<T, U>>>\n    constexpr Vec2(const Vec2<U> &r) noexcept // NOLINT\n            : X(T(r.X)), Y(T(r.Y)) {}\n\n    constexpr Vec2 operator+(const Vec2 &r) const noexcept { return {X + r.X, Y + r.Y}; }\n\n    constexpr Vec2 operator-(const Vec2 &r) const noexcept { return {X - r.X, Y - r.Y}; }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec2 operator*(U r) const noexcept { return {X * r, Y * r}; }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec2 operator/(U r) const noexcept { return {X / r, Y / r}; }\n\n    constexpr Vec2 &operator+=(const Vec2 &r) noexcept { return (X += r.X, Y += r.Y, *this); }\n\n    constexpr Vec2 &operator-=(const Vec2 &r) noexcept { return (X -= r.X, Y -= r.Y, *this); }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec2 &operator*=(U r) noexcept { return (X *= r, Y *= r, *this); }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec2 &operator/=(U r) noexcept { return (X /= r, Y /= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator<<(T r) const noexcept { return {X << r, Y << r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator<<(const Vec2 &r) const noexcept { return {X << r.X, Y << r.Y}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator<<=(T r) noexcept { return (X <<= r, Y <<= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator<<=(const Vec2 &r) noexcept { return (X <<= r.X, Y <<= r.Y, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator>>(T r) const noexcept { return {X >> r, Y >> r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator>>(const Vec2 &r) const noexcept { return {X >> r.X, Y >> r.Y}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator>>=(T r) noexcept { return (X >>= r, Y >>= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator>>=(const Vec2 &r) noexcept { return (X >>= r.X, Y >>= r.Y, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator|(T r) const noexcept { return {X | r, Y | r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator|(const Vec2 &r) const noexcept { return {X | r.X, Y | r.Y}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator|=(T r) noexcept { return (X |= r, Y |= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator|=(const Vec2 &r) noexcept { return (X |= r.X, Y |= r.Y, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator&(T r) const noexcept { return {X & r, Y & r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator&(const Vec2 &r) const noexcept { return {X & r.X, Y & r.Y}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator&=(T r) noexcept { return (X &= r, Y &= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator&=(const Vec2 &r) noexcept { return (X &= r.X, Y &= r.Y, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator^(T r) const noexcept { return {X ^ r, Y ^ r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 operator^(const Vec2 &r) const noexcept { return {X ^ r.X, Y ^ r.Y}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator^=(T r) noexcept { return (X ^= r, Y ^= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec2 &operator^=(const Vec2 &r) noexcept { return (X ^= r.X, Y ^= r.Y, *this); }\n\n    constexpr Vec2 operator-() const noexcept { return {-X, -Y}; }\n\n    [[nodiscard]] constexpr T LengthSquared() const noexcept { return X * X + Y * Y; }\n\n    constexpr bool operator==(const Vec2 &r) const noexcept { return (X == r.X) && (Y == r.Y); }\n\n    constexpr bool operator<(const Vec2 &r) const noexcept { return LengthSquared() < r.LengthSquared(); }\n\n    constexpr bool operator>(const Vec2 &r) const noexcept { return LengthSquared() > r.LengthSquared(); }\n\n    constexpr bool operator<=(const Vec2 &r) const noexcept { return LengthSquared() <= r.LengthSquared(); }\n\n    constexpr bool operator>=(const Vec2 &r) const noexcept { return LengthSquared() >= r.LengthSquared(); }\n\n    [[nodiscard]] constexpr T Dot(const Vec2 &r) const noexcept { return X * r.X + Y * r.Y; }\n\n    [[nodiscard]] T Length() const noexcept { return std::sqrt(LengthSquared()); }\n\n    void Normalize() noexcept { (*this) /= Length(); }\n};\n\ntemplate<class T>\nconstexpr T Dot(const Vec2<T> &l, const Vec2<T> &r) noexcept { return l.Dot(r); }\n\ntemplate<class T, class U, class = std::enable_if<std::is_arithmetic_v<U>>>\nconstexpr Vec2<T> operator*(U l, const Vec2<T> &r) noexcept { return r * l; }\n\ntemplate<class T>\ndouble EuclideanDistanceSquared(const Vec2<T> &l, const Vec2<T> &r) noexcept {\n    return (l - r).LengthSquared();\n}\n\ntemplate<class T>\ndouble EuclideanDistance(const Vec2<T> &l, const Vec2<T> &r) noexcept {\n    return (l - r).Length();\n}\n\ntemplate<class T>\ndouble DistanceSquared(const Vec2<T> &l, const Vec2<T> &r) noexcept {\n    return (l - r).LengthSquared();\n}\n\ntemplate<class T>\ndouble Distance(const Vec2<T> &l, const Vec2<T> &r) noexcept {\n    return (l - r).Length();\n}\n\ntemplate<class T>\nconstexpr T ChebyshevDistance(const Vec2<T> &l, const Vec2<T> &r) noexcept {\n    return std::max(std::abs(l.X - r.X), std::abs(l.Y - r.Y));\n}\n\ntemplate<class T>\nconstexpr T ManhattanDistance(const Vec2<T> &l, const Vec2<T> &r) noexcept {\n    return std::abs(l.X - r.X) + std::abs(l.Y - r.Y);\n}\n\nusing Char2 = Vec2<int8_t>;\nusing Byte2 = Vec2<uint8_t>;\nusing Short2 = Vec2<int16_t>;\nusing UShort2 = Vec2<uint16_t>;\nusing Int2 = Vec2<int32_t>;\nusing UInt2 = Vec2<uint32_t>;\nusing Long2 = Vec2<int64_t>;\nusing ULong2 = Vec2<uint64_t>;\nusing Float2 = Vec2<float>;\nusing Double2 = Vec2<double>;\n"
  },
  {
    "path": "NEWorld.Base/Math/Vector3.h",
    "content": "#pragma once\n\n#include <cmath>\n#include <algorithm>\n#include <type_traits>\n\ntemplate<class T, class = std::enable_if<std::is_arithmetic_v<T>>>\nstruct Vec3 {\n    union {\n        T Data[3];\n        struct {\n            T X, Y, Z;\n        };\n    };\n\n    constexpr Vec3() noexcept = default;\n\n    constexpr Vec3(T x, T y, T z) noexcept\n            : X(x), Y(y), Z(z) {}\n\n    constexpr explicit Vec3(T v) noexcept\n            : X(v), Y(v), Z(v) {}\n\n    template<typename U, class = std::enable_if_t<std::is_convertible_v<T, U>>>\n    constexpr Vec3(const Vec3<U> &r) noexcept // NOLINT\n            : X(T(r.X)), Y(T(r.Y)), Z(T(r.Z)) {}\n\n    template<typename U, class F>\n    constexpr Vec3(const Vec3<U> &r, F transform) noexcept // NOLINT\n            : X(transform(r.X)), Y(transform(r.Y)), Z(transform(r.Z)) {}\n\n\n    constexpr Vec3 operator+(const Vec3 &r) const noexcept { return {X + r.X, Y + r.Y, Z + r.Z}; }\n\n    constexpr Vec3 operator-(const Vec3 &r) const noexcept { return {X - r.X, Y - r.Y, Z - r.Z}; }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec3 operator*(U r) const noexcept { return {X * r, Y * r, Z * r}; }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec3 operator/(U r) const noexcept { return {X / r, Y / r, Z / r}; }\n\n    constexpr Vec3 &operator+=(const Vec3 &r) noexcept { return (X += r.X, Y += r.Y, Z += r.Z, *this); }\n\n    constexpr Vec3 &operator-=(const Vec3 &r) noexcept { return (X -= r.X, Y -= r.Y, Z -= r.Z, *this); }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec3 &operator*=(U r) noexcept { return (X *= r, Y *= r, Z *= r, *this); }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec3 &operator/=(U r) noexcept { return (X /= r, Y /= r, Z /= r, *this); }\n\n    constexpr Vec3 operator*(const Vec3 &r) const noexcept {\n        return {Y * r.Z - Z * r.Y, Z * r.X - X * r.Z, X * r.Y - Y * r.X};\n    }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator<<(T r) const noexcept { return {X << r, Y << r, Z << r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator<<(const Vec3 &r) const noexcept { return {X << r.X, Y << r.Y, Z << r.Z}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator<<=(T r) noexcept { return (X <<= r, Y <<= r, Z <<= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator<<=(const Vec3 &r) noexcept { return (X <<= r.X, Y <<= r.Y, Z <<= r.Z, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator>>(T r) const noexcept { return {X >> r, Y >> r, Z >> r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator>>(const Vec3 &r) const noexcept { return {X >> r.X, Y >> r.Y, Z >> r.Z}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator>>=(T r) noexcept { return (X >>= r, Y >>= r, Z >>= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator>>=(const Vec3 &r) noexcept { return (X >>= r.X, Y >>= r.Y, Z >>= r.Z, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator|(T r) const noexcept { return {X | r, Y | r, Z | r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator|(const Vec3 &r) const noexcept { return {X | r.X, Y | r.Y, Z | r.Z}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator|=(T r) noexcept { return (X |= r, Y |= r, Z |= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator|=(const Vec3 &r) noexcept { return (X |= r.X, Y |= r.Y, Z |= r.Z, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator&(T r) const noexcept { return {X & r, Y & r, Z & r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator&(const Vec3 &r) const noexcept { return {X & r.X, Y & r.Y, Z & r.Z}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator&=(T r) noexcept { return (X &= r, Y &= r, Z &= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator&=(const Vec3 &r) noexcept { return (X &= r.X, Y &= r.Y, Z &= r.Z, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator^(T r) const noexcept { return {X ^ r, Y ^ r, Z ^ r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 operator^(const Vec3 &r) const noexcept { return {X ^ r.X, Y ^ r.Y, Z ^ r.Z}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator^=(T r) noexcept { return (X ^= r, Y ^= r, Z ^= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec3 &operator^=(const Vec3 &r) noexcept { return (X ^= r.X, Y ^= r.Y, Z ^= r.Z, *this); }\n\n    constexpr Vec3 operator-() const noexcept { return {-X, -Y, -Z}; }\n\n    [[nodiscard]] constexpr T LengthSquared() const noexcept { return X * X + Y * Y + Z * Z; }\n\n    constexpr bool operator==(const Vec3 &r) const noexcept { return (X == r.X) && (Y == r.Y) && (Z == r.Z); }\n\n    constexpr bool operator!=(const Vec3& r) const noexcept { return !(*this == r); }\n\n    constexpr bool operator<(const Vec3 &r) const noexcept { return LengthSquared() < r.LengthSquared(); }\n\n    constexpr bool operator>(const Vec3 &r) const noexcept { return LengthSquared() > r.LengthSquared(); }\n\n    constexpr bool operator<=(const Vec3 &r) const noexcept { return LengthSquared() <= r.LengthSquared(); }\n\n    constexpr bool operator>=(const Vec3 &r) const noexcept { return LengthSquared() >= r.LengthSquared(); }\n\n    [[nodiscard]] constexpr T Dot(const Vec3 &r) const noexcept { return X * r.X + Y * r.Y + Z * r.Z; }\n\n    [[nodiscard]] T Length() const noexcept { return std::sqrt(LengthSquared()); }\n\n    void Normalize() noexcept { (*this) /= Length(); }\n};\n\ntemplate<class T>\nconstexpr T Dot(const Vec3<T> &l, const Vec3<T> &r) noexcept { return l.Dot(r); }\n\ntemplate<class T, class U, class = std::enable_if<std::is_arithmetic_v<U>>>\nconstexpr Vec3<T> operator*(U l, const Vec3<T> &r) noexcept { return r * l; }\n\ntemplate<class T>\ndouble EuclideanDistanceSquared(const Vec3<T> &l, const Vec3<T> &r) noexcept {\n    return (l - r).LengthSquared();\n}\n\ntemplate<class T>\ndouble EuclideanDistance(const Vec3<T> &l, const Vec3<T> &r) noexcept {\n    return (l - r).Length();\n}\n\ntemplate<class T>\ndouble DistanceSquared(const Vec3<T> &l, const Vec3<T> &r) noexcept {\n    return (l - r).LengthSquared();\n}\n\ntemplate<class T>\ndouble Distance(const Vec3<T> &l, const Vec3<T> &r) noexcept {\n    return (l - r).Length();\n}\n\ntemplate<class T>\nconstexpr T ChebyshevDistance(const Vec3<T> &l, const Vec3<T> &r) noexcept {\n    return std::max(std::max(std::abs(l.X - r.X), std::abs(l.Y - r.Y)), std::abs(l.Z - r.Z));\n}\n\ntemplate<class T>\nconstexpr T ManhattanDistance(const Vec3<T> &l, const Vec3<T> &r) noexcept {\n    return std::abs(l.X - r.X) + std::abs(l.Y - r.Y) + std::abs(l.Z - r.Z);\n}\n\ntemplate<class T, class F>\nvoid Cursor(const Vec3<T> &min, const Vec3<T> &max, F func) noexcept {\n    Vec3<T> vec{};\n    for (vec.X = min.X; vec.X < max.X; ++vec.X)\n        for (vec.Y = min.Y; vec.Y < max.Y; ++vec.Y)\n            for (vec.Z = min.Z; vec.Z < max.Z; ++vec.Z)\n                func(vec);\n}\n\ntemplate<class T, class F>\nvoid CursorEQ(const Vec3<T> &min, const Vec3<T> &max, F func) noexcept {\n    Vec3<T> vec{};\n    for (vec.X = min.X; vec.X <= max.X; ++vec.X)\n        for (vec.Y = min.Y; vec.Y <= max.Y; ++vec.Y)\n            for (vec.Z = min.Z; vec.Z <= max.Z; ++vec.Z)\n                func(vec);\n}\n\nusing Char3 = Vec3<int8_t>;\nusing Byte3 = Vec3<uint8_t>;\nusing Short3 = Vec3<int16_t>;\nusing UShort3 = Vec3<uint16_t>;\nusing Int3 = Vec3<int32_t>;\nusing UInt3 = Vec3<uint32_t>;\nusing Long3 = Vec3<int64_t>;\nusing ULong3 = Vec3<uint64_t>;\nusing Float3 = Vec3<float>;\nusing Double3 = Vec3<double>;\n"
  },
  {
    "path": "NEWorld.Base/Math/Vector4.h",
    "content": "#pragma once\n\n#include <cmath>\n#include <algorithm>\n#include <type_traits>\n\ntemplate<class T, class = std::enable_if<std::is_arithmetic_v<T>>>\nstruct Vec4 {\n    union {\n        T Data[4];\n        struct {\n            T X, Y, Z, W;\n        };\n    };\n\n    constexpr Vec4() noexcept = default;\n\n    constexpr Vec4(T x, T y, T z, T w) noexcept\n            : X(x), Y(y), Z(z), W(w) {}\n\n    constexpr explicit Vec4(T v) noexcept\n            : X(v), Y(v), Z(v), W(v) {}\n\n    template<typename U, class = std::enable_if_t<std::is_convertible_v<T, U>>>\n    constexpr Vec4(const Vec4<U> &r) noexcept // NOLINT\n            : X(T(r.X)), Y(T(r.Y)), Z(T(r.Z)), W(T(r.W)) {}\n\n    template<typename U, class F>\n    constexpr Vec4(const Vec4<U> &r, F transform) noexcept // NOLINT\n            : X(transform(r.X)), Y(transform(r.Y)), Z(transform(r.Z)), W(transform(r.W)) {}\n\n\n    constexpr Vec4 operator+(const Vec4 &r) const noexcept { return {X + r.X, Y + r.Y, Z + r.Z, W + r.W}; }\n\n    constexpr Vec4 operator-(const Vec4 &r) const noexcept { return {X - r.X, Y - r.Y, Z - r.Z, W - r.W}; }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec4 operator*(U r) const noexcept { return {X * r, Y * r, Z * r, W * r}; }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec4 operator/(U r) const noexcept { return {X / r, Y / r, Z / r, W / r}; }\n\n    constexpr Vec4 &operator+=(const Vec4 &r) noexcept { return (X += r.X, Y += r.Y, Z += r.Z, W += r.W, *this); }\n\n    constexpr Vec4 &operator-=(const Vec4 &r) noexcept { return (X -= r.X, Y -= r.Y, Z -= r.Z, W -= r.W, *this); }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec4 &operator*=(U r) noexcept { return (X *= r, Y *= r, Z *= r, W *= r, *this); }\n\n    template<class U, class = std::enable_if<std::is_arithmetic_v<U>>>\n    constexpr Vec4 &operator/=(U r) noexcept { return (X /= r, Y /= r, Z /= r, W /= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator<<(T r) const noexcept { return {X << r, Y << r, Z << r, W << r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator<<(const Vec4 &r) const noexcept { return {X << r.X, Y << r.Y, Z << r.Z, W << r.W}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator<<=(T r) noexcept { return (X <<= r, Y <<= r, Z <<= r, W <<= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator<<=(const Vec4 &r) noexcept { return (X <<= r.X, Y <<= r.Y, Z <<= r.Z, W <<= r.W, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator>>(T r) const noexcept { return {X >> r, Y >> r, Z >> r, W >> r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator>>(const Vec4 &r) const noexcept { return {X >> r.X, Y >> r.Y, Z >> r.Z, W >> r.W}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator>>=(T r) noexcept { return (X >>= r, Y >>= r, Z >>= r, W >>= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator>>=(const Vec4 &r) noexcept { return (X >>= r.X, Y >>= r.Y, Z >>= r.Z, W >>= r.W, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator|(T r) const noexcept { return {X | r, Y | r, Z | r, W | r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator|(const Vec4 &r) const noexcept { return {X | r.X, Y | r.Y, Z | r.Z, W | r.W}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator|=(T r) noexcept { return (X |= r, Y |= r, Z |= r, W |= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator|=(const Vec4 &r) noexcept { return (X |= r.X, Y |= r.Y, Z |= r.Z, W |= r.W, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator&(T r) const noexcept { return {X & r, Y & r, Z & r, W & r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator&(const Vec4 &r) const noexcept { return {X & r.X, Y & r.Y, Z & r.Z, W & r.W}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator&=(T r) noexcept { return (X &= r, Y &= r, Z &= r, W &= r, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator&=(const Vec4 &r) noexcept { return (X &= r.X, Y &= r.Y, Z &= r.Z, W &= r.W, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator^(T r) const noexcept { return {X ^ r, Y ^ r, Z ^ r, W ^ r}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 operator^(const Vec4 &r) const noexcept { return {X ^ r.X, Y ^ r.Y, Z ^ r.Z, W ^ r.W}; }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator^=(T r) noexcept { return (X ^= r, Y ^= r, Z ^= r, W ^= r.W, *this); }\n\n    template<class = std::enable_if<std::is_integral_v<T>>>\n    constexpr Vec4 &operator^=(const Vec4 &r) noexcept { return (X ^= r.X, Y ^= r.Y, Z ^= r.Z, W ^= r.W, *this); }\n\n    constexpr Vec4 operator-() const noexcept { return {-X, -Y, -Z, -W}; }\n\n    [[nodiscard]] constexpr T LengthSquared() const noexcept { return X * X + Y * Y + Z * Z + W * W; }\n\n    constexpr bool operator==(const Vec4 &r) const noexcept {\n        return (X == r.X) && (Y == r.Y) && (Z == r.Z) && (W == r.W);\n    }\n\n    constexpr bool operator<(const Vec4 &r) const noexcept { return LengthSquared() < r.LengthSquared(); }\n\n    constexpr bool operator>(const Vec4 &r) const noexcept { return LengthSquared() > r.LengthSquared(); }\n\n    constexpr bool operator<=(const Vec4 &r) const noexcept { return LengthSquared() <= r.LengthSquared(); }\n\n    constexpr bool operator>=(const Vec4 &r) const noexcept { return LengthSquared() >= r.LengthSquared(); }\n\n    [[nodiscard]] constexpr T Dot(const Vec4 &r) const noexcept { return X * r.X + Y * r.Y + Z * r.Z + W * r.W; }\n\n    [[nodiscard]] T Length() const noexcept { return std::sqrt(LengthSquared()); }\n\n    void Normalize() noexcept { (*this) /= Length(); }\n};\n\ntemplate<class T>\nconstexpr T Dot(const Vec4<T> &l, const Vec4<T> &r) noexcept { return l.Dot(r); }\n\ntemplate<class T, class U, class = std::enable_if<std::is_arithmetic_v<U>>>\nconstexpr Vec4<T> operator*(U l, const Vec4<T> &r) noexcept { return r * l; }\n\ntemplate<class T>\ndouble EuclideanDistanceSquared(const Vec4<T> &l, const Vec4<T> &r) noexcept {\n    return (l - r).LengthSquared();\n}\n\ntemplate<class T>\ndouble EuclideanDistance(const Vec4<T> &l, const Vec4<T> &r) noexcept {\n    return (l - r).Length();\n}\n\ntemplate<class T>\ndouble DistanceSquared(const Vec4<T> &l, const Vec4<T> &r) noexcept {\n    return (l - r).LengthSquared();\n}\n\ntemplate<class T>\ndouble Distance(const Vec4<T> &l, const Vec4<T> &r) noexcept {\n    return (l - r).Length();\n}\n\ntemplate<class T>\nconstexpr T ChebyshevDistance(const Vec4<T> &l, const Vec4<T> &r) noexcept {\n    return std::max(std::max(std::max(std::abs(l.X - r.X), std::abs(l.Y - r.Y)),std::abs(l.Z - r.Z)), std::abs(l.W - r.W));\n}\n\ntemplate<class T>\nconstexpr T ManhattanDistance(const Vec4<T> &l, const Vec4<T> &r) noexcept {\n    return std::abs(l.X - r.X) + std::abs(l.Y - r.Y) + std::abs(l.Z - r.Z) + std::abs(l.W - r.W);\n}\n\ntemplate<class T, class F>\nvoid Cursor(const Vec4<T> &min, const Vec4<T> &max, F func) noexcept {\n    Vec4<T> vec{};\n    for (vec.X = min.X; vec.X < max.X; ++vec.X)\n        for (vec.Y = min.Y; vec.Y < max.Y; ++vec.Y)\n            for (vec.Z = min.Z; vec.Z < max.Z; ++vec.Z)\n                for (vec.W = min.W; vec.W < max.W; ++vec.W)\n                    func(vec);\n}\n\nusing Char4 = Vec4<int8_t>;\nusing Byte4 = Vec4<uint8_t>;\nusing Short4 = Vec4<int16_t>;\nusing UShort4 = Vec4<uint16_t>;\nusing Int4 = Vec4<int32_t>;\nusing UInt4 = Vec4<uint32_t>;\nusing Long4 = Vec4<int64_t>;\nusing ULong4 = Vec4<uint64_t>;\nusing Float4 = Vec4<float>;\nusing Double4 = Vec4<double>;\n"
  },
  {
    "path": "NEWorld.Base/System/FileSystem.h",
    "content": "#pragma once\n\n#if __has_include(<filesystem>)\n#include <filesystem>\nnamespace NEWorld {\n    namespace filesystem = std::filesystem;\n}\n#elif __has_include(<boost/filesystem.hpp>)\n#include <boost/filesystem.hpp>\nnamespace NEWorld {\n    namespace filesystem = boost::filesystem;\n}\n#else\n#error No available filesystem library configured\n#endif\n"
  },
  {
    "path": "NEWorld.Base/System/MessageBus.cpp",
    "content": "#include \"MessageBus.h\"\n\nstd::vector<std::shared_ptr<kls::PmrBase>> MessageBus::SlotBase::PrepareInvokeList() {\n    std::vector<std::shared_ptr<kls::PmrBase>> invokes{};\n    std::lock_guard<std::mutex> lk{mLock};\n    invokes.reserve(mListeners.size());\n    for (auto& x : mListeners) {\n        if (auto ptr = x.lock(); ptr) { invokes.push_back(std::move(ptr)); }\n    }\n    if (mListeners.size() > 4 * invokes.size()) {\n        mListeners.clear();\n        if (mListeners.capacity() > 8 * invokes.size()) { mListeners.shrink_to_fit(); }\n        for (auto& x : invokes) { mListeners.push_back(x); }\n    }\n    return invokes;\n}\n\nMessageBus& MessageBus::Default() {\n    static MessageBus defaultBus{};\n    return defaultBus;\n}\n"
  },
  {
    "path": "NEWorld.Base/System/MessageBus.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <string>\n#include <memory>\n#include <vector>\n#include <functional>\n#include <unordered_map>\n#include <kls/Object.h>\n\nclass MessageBus {\n    class SlotBase : public kls::PmrBase {\n    protected:\n        std::vector<std::shared_ptr<PmrBase>> PrepareInvokeList();\n        std::mutex mLock;\n        std::vector<std::weak_ptr<PmrBase>> mListeners;\n    };\n\npublic:\n    template<class Tm>\n    class Slot final : public SlotBase {\n        class ListenerBase : public kls::PmrBase {\n            using Invoke = void (*)(ListenerBase *, void *, const Tm &) noexcept;\n        public:\n            explicit ListenerBase(const Invoke fn) noexcept\n                    : Function(fn) {}\n\n            Invoke Function;\n        };\n\n        template<class Fn>\n        class Listener final : public ListenerBase {\n        public:\n            explicit Listener(Fn package)\n                    : ListenerBase(Invoke), mPackage(std::move(package)) {}\n\n        private:\n            static void Invoke(ListenerBase *ths, void *s, const Tm &arg) noexcept {\n                static_cast<Listener *>(ths)->mPackage(s, arg);\n            }\n\n            Fn mPackage;\n        };\n\n    public:\n        void Send(void *sender, const Tm &arg) {\n            const auto invokes = PrepareInvokeList();\n            for (const auto &x: invokes) {\n                const auto x2 = static_cast<ListenerBase *>(x.get());\n                if (x2->Function) { std::invoke(x2->Function, x2, sender, arg); }\n            }\n        }\n\n        template<class Fn>\n        std::shared_ptr<PmrBase> Listen(Fn fn) {\n            const auto s = std::make_shared<Listener<Fn>>(std::move(fn));\n            {\n                std::lock_guard<std::mutex> lk{mLock};\n                mListeners.push_back(s);\n            }\n            return s;\n        }\n    };\n\n    template<class Tm>\n    Slot<Tm> *Get(const std::string &name) noexcept {\n        const auto search = mSlots.find(name);\n        if (search != mSlots.end()) { return dynamic_cast<Slot<Tm> *>(search->second.get()); }\n        auto ptr = std::make_unique<Slot<Tm>>();\n        const auto ret = ptr.get();\n        mSlots.insert_or_assign(name, std::move(ptr));\n        return ret;\n    }\n\n    static MessageBus &Default();\nprivate:\n    std::unordered_map<std::string, std::unique_ptr<kls::PmrBase>> mSlots;\n};\n\nusing NullArg = int;\n"
  },
  {
    "path": "NEWorld.Game/Audio/Audio.cpp",
    "content": "#include \"Audio.h\"\n"
  },
  {
    "path": "NEWorld.Game/Audio/Audio.h",
    "content": "#pragma once\n\nnamespace NEWorld::Audio {\n\n}\n"
  },
  {
    "path": "NEWorld.Game/AudioSystem.cpp",
    "content": "#include\"AudioSystem.h\"\n\nnamespace AudioSystem {\n    //ALDevice Device;\n    //Gain\n    ALfloat BGMGain = 0.1f;//背景音乐\n    ALfloat SoundGain = 0.17f;//音效\n    //Set\n    ALenum DopplerModel = AL_INVERSE_DISTANCE_CLAMPED;//设置OpenAL的距离模型\n    ALfloat DopplerFactor = 1.0f;//多普勒因子\n    ALfloat SpeedOfSound = Air_SpeedOfSound;//声速\n    //Update\n    bool FallBefore = false;//OnGround\n    bool DownWaterBefore = false;//InWater\n    int BGMNum = 0;\n    //Buffer\n    ALuint BGM[10];\n    ALuint Run = -1;\n    ALuint Click = -1;\n    ALuint Fall = -1;\n    ALuint BlockClick = -1;\n    ALuint DownWater = -1;\n    //Source\n    ALuint SBGM = -1;\n    ALuint SRun = -1;\n    ALuint SClick = -1;\n    ALuint SFall = -1;\n    ALuint SBlockClick = -1;\n    ALuint SDownWater = -1;\n\n    void Init() {\n        //初始化设备\n        /*ALDeviceList *DL = Device.GetALDeviceList();\n        Device.InitAL(DL->GetDeviceName(DL->GetDefaultDevice()));\n        delete DL;\n        //开启所有功能\n        alEnable(AL_DOPPLER_FACTOR);\n        alEnable(AL_DISTANCE_MODEL);\n        alEnable(AL_SPEED_OF_SOUND);\n        //背景音乐\n        char BGMName[256];\n        for (size_t i = 0; i < 10; i++)\n        {\n            BGM[i] = -1;\n        }\n        for (size_t i = 0; i < 10; i++)\n        {\n            sprintf_s(BGMName, \"Audio\\\\BGM%d.wav\", i);\n            if (Device.load(BGMName, &BGM[BGMNum])) {\n                BGMNum++;\n            }\n        }\n        //行走and跑步声音\n        if (!Device.load(\"Audio\\\\Run.wav\", &Run))Run = -1;\n        //鼠标单击\n        if (!Device.load(\"Audio\\\\Click.wav\", &Click))Click = -1;\n        //掉落\n        if (!Device.load(\"Audio\\\\Fall.wav\", &Fall))Fall = -1;\n        //击打方块\n        if (!Device.load(\"Audio\\\\BlockClick.wav\", &BlockClick))BlockClick = -1;\n        //下水\n        if (!Device.load(\"Audio\\\\DownWater.wav\", &DownWater))DownWater = -1;\n        //播放BGM\n        int size = GetTickCount64() % BGMNum;\n        ALfloat Pos[] = { 0.0,0.0,0.0 };\n        ALfloat Vel[] = { 0.0,0.0,0.0 };\n        SBGM = Device.Play(BGM[size], false, BGMGain, Pos, Vel);*/\n    }\n\n    void Update(ALfloat PlayerPos[3], bool BFall, bool BBlockClick, ALfloat BlockPos[3], int BRun, bool BDownWater) {\n        //设置全局常量\n        /*alDopplerFactor(DopplerFactor);\n        alDistanceModel(DopplerModel);\n        alSpeedOfSound(SpeedOfSound);\n        //更新音量\n        if (SBGM != -1)alSourcef(SBGM,AL_GAIN,BGMGain);\n        if (SRun != -1)alSourcef(SRun, AL_GAIN, SoundGain);\n        if (SClick != -1)alSourcef(SClick, AL_GAIN, SoundGain);\n        if (SFall != -1)alSourcef(SFall, AL_GAIN, SoundGain);\n        if (SBlockClick != -1)alSourcef(SBlockClick, AL_GAIN, SoundGain);\n        if (SDownWater != -1)alSourcef(SDownWater, AL_GAIN, SoundGain);\n        //更新环境\n        if (SBGM != -1)EFX::set(SBGM);\n        if (SRun != -1)EFX::set(SRun);\n        if (SClick != -1)EFX::set(SClick);\n        if (SFall != -1)EFX::set(SFall);\n        if (SBlockClick != -1)EFX::set(SBlockClick);\n        if (SDownWater != -1)EFX::set(SDownWater);\n        //更新玩家位置\n        PlayerPos[1] += 0.74;\n        ALfloat Vel[] = { 0.0,0.0,0.0 };\n        ALfloat Ori[] = { 0.0,0.0,-1.0, 0.0,1.0,0.0 };\n        Device.Updatelistener(PlayerPos, Vel, Ori);\n        //更新BGM位置\n        ALint state;\n        alGetSourcei(SBGM, AL_SOURCE_STATE, &state);\n        if (state == AL_STOPPED)\n        {\n            Device.Stop(SBGM);\n            int size = GetTickCount64() % BGMNum;\n            ALfloat Pos[] = { 0.0,0.0,0.0 };\n            ALfloat Vel[] = { 0.0,0.0,0.0 };\n            SBGM = Device.Play(BGM[size], false, BGMGain, Pos, Vel);\n        }\n        Device.Updatesource(SBGM, PlayerPos, Vel);\n        //下落\n        PlayerPos[1] -= 1.54;\n        if (BFall != FallBefore)\n        {\n            if (BFall) {\n                SFall = Device.Play(Fall, false, SoundGain, PlayerPos, Vel);\n            }\n            FallBefore = BFall;\n        }\n        else\n        {\n            if(SFall!=-1)Device.Stop(SFall);\n            SFall = -1;\n        }\n        //击打方块\n        if (BBlockClick)\n        {\n            if (SBlockClick==-1) {\n                SBlockClick = Device.Play(BlockClick, true, SoundGain, BlockPos, Vel);\n            }\n        }\n        else\n        {\n            if(SBlockClick!=-1)Device.Stop(SBlockClick);\n            SBlockClick = -1;\n        }\n        //奔跑\n        if ((BRun!=0)&&BFall)\n        {\n            if (SRun == -1)\n            {\n                SRun = Device.Play(Run, true, SoundGain, PlayerPos, Vel);\n            }\n            Device.Updatesource(SRun, PlayerPos, Vel);\n            alSourcef(SRun, AL_PITCH, BRun*0.5f);\n        }\n        else\n        {\n            if(SRun!=-1)Device.Stop(SRun);\n            SRun = -1;\n        }\n        //下水\n        if (BDownWater != DownWaterBefore)\n        {\n            if (SDownWater == -1)SDownWater = Device.Play(DownWater, false, SoundGain, PlayerPos, Vel);\n            DownWaterBefore = BDownWater;\n        }\n        else\n        {\n            if (SDownWater != -1) {\n                ALint state;\n                alGetSourcei(SDownWater, AL_SOURCE_STATE, &state);\n                if (state == AL_STOPPED)\n                {\n                    Device.Stop(SDownWater);\n                    SDownWater = -1;\n                }\n            }\n        }*/\n    }\n\n    void ClickEvent() {\n        /*ALfloat Pos[] = { 0.0,0.0,0.0 };\n        ALfloat Vel[] = { 0.0,0.0,0.0 };\n        SClick = Device.Play(Click, false, SoundGain, Pos, Vel);\n        SleepMs(50);\n        Device.Stop(SClick);\n        SClick = -1;*/\n    }\n\n    void UnInit() {\n        /*if (SBGM != -1)Device.Stop(SBGM);\n        if (SRun != -1)Device.Stop(SRun);\n        if (SClick != -1)Device.Stop(SClick);\n        if (SFall != -1)Device.Stop(SFall);\n        if (SBlockClick != -1)Device.Stop(SBlockClick);\n        if (SDownWater != -1)Device.Stop(SDownWater);\n\n        for (size_t i = 0; i < 10; i++)\n        {\n            if (BGM[i] != -1)Device.unload(BGM[i]);\n        }\n        if (Run != -1)Device.unload(Run);\n        if (Click != -1)Device.unload(Click);\n        if (Fall != -1)Device.unload(Fall);\n        if (BlockClick != -1)Device.unload(BlockClick);\n        if (DownWater != -1)Device.unload(DownWater);\n\n        Device.ShutdownAL();*/\n    }\n}"
  },
  {
    "path": "NEWorld.Game/AudioSystem.h",
    "content": "#pragma once\n\n#include <al.h>\n\nnamespace AudioSystem {\n    //extern ALDevice Device;\n    //Gain\n    extern ALfloat BGMGain;//背景音乐\n    extern ALfloat SoundGain;//音效\n    //Set\n    extern ALenum DopplerModel;//设置OpenAL的距离模型\n    extern ALfloat DopplerFactor;//多普勒因子\n    extern ALfloat SpeedOfSound;//声速\n    const ALfloat Air_SpeedOfSound = 343.3f;\n    const ALfloat Water_SpeedOfSound = 1473.0f;\n    //Update\n    extern bool FallBefore;//OnGround\n    extern bool DownWaterBefore;//InWater\n    extern int BGMNum;\n    //Buffer\n    extern ALuint BGM[10];\n    extern ALuint Run;\n    extern ALuint Click;\n    extern ALuint Fall;\n    extern ALuint BlockClick;\n    extern ALuint DownWater;\n    //Source\n    extern ALuint SBGM;\n    extern ALuint SRun;\n    extern ALuint SClick;\n    extern ALuint SFall;\n    extern ALuint SBlockClick;\n    extern ALuint SDownWater;\n\n    void Init();\n\n    void Update(ALfloat PlayerPos[3], bool BFall, bool BBlockClick, ALfloat BlockPos[3], int Run, bool BDownWater);\n\n    void ClickEvent();\n\n    void UnInit();\n}"
  },
  {
    "path": "NEWorld.Game/Command.h",
    "content": "#pragma once\n\n#include <utility>\n#include <functional>\n\nclass Command {\npublic:\n    Command(std::string _identifier, std::function<bool(const std::vector<std::string> &)> _execute) : identifier(\n            std::move(std::move(_identifier))), execute(std::move(std::move(_execute))) {};\n\n    std::string identifier;\n    std::function<bool(const std::vector<std::string> &)> execute;\n};\n"
  },
  {
    "path": "NEWorld.Game/ControlContext.h",
    "content": "#pragma once\n#include \"stdinclude.h\"\n#include <GLFW/glfw3.h>\n#include <array>\n#include \"Math/Vector2.h\"\n#include \"FunctionsKit.h\"\n#include \"Common/Logger.h\"\n#include \"System/MessageBus.h\"\n\nclass ControlContext {\npublic:\n\tstruct KeyState {\n\t\tbool Pressed : 1;\n\t\tuint16_t LastPressedFrame : 15; // 0 represents never pressed\n\t};\n\n\tenum class Action { PLACE_BLOCK, PICK_BLOCK };\n\tstruct Frame {\n\t\tbool LeftMouse{}, MiddleMouse{}, RightMouse{};\n\t\tdouble MouseScroll{};\n\t\tDouble2 MousePosition{};\n\t\tdouble Time{};\n\t\tstd::array<KeyState, GLFW_KEY_LAST> KeyStates{};\n\t};\n\n\tControlContext(GLFWwindow* window) :mWindow(window) {\n\t\tif (!Listener) Listener = MessageBus::Default().Get<std::pair<int, int>>(\"KeyEvents\")->Listen(\n\t\t\t[this](void*, std::pair<int, int> keyAndAction) {\n\t\t\t\tauto [key, action] = keyAndAction;\n\t\t\t\tKeyStates[key].Pressed = action != GLFW_RELEASE;\n\t\t\t\tif (KeyStates[key].Pressed)\n\t\t\t\t\tKeyStates[key].LastPressedFrame = mFrameCounter;\n\t\t\t});\n\t}\n\tControlContext(ControlContext&) = delete;\n\tControlContext operator=(ControlContext&) = delete;\n\n\tFrame Current, Last;\n\n\tvoid Update() {\n\t\tLast = Current;\n\t\tmFrameCounter++;\n\t\tif (mFrameCounter > 1<<15) mFrameCounter = 1; // loop back to squeeze into 7 bits\n\n\t\tglfwGetCursorPos(mWindow, &Current.MousePosition.X, &Current.MousePosition.Y);\n\t\tCurrent.LeftMouse = glfwGetMouseButton(mWindow, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;\n\t\tCurrent.RightMouse = glfwGetMouseButton(mWindow, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS;\n\t\tCurrent.MiddleMouse = glfwGetMouseButton(mWindow, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS;\n\t\tCurrent.Time = timer();\n\t\tCurrent.MouseScroll = MouseScroll;\n\t\tstd::copy(KeyStates.begin(), KeyStates.end(), Current.KeyStates.begin());\n\t}\n\n\t[[nodiscard]] bool KeyPressed(int key) const noexcept {\n\t\treturn Current.KeyStates[key].Pressed;\n\t}\n\n\t[[nodiscard]] bool KeyJustDoublePressed(int key, int intervalInFrames = 8) const noexcept {\n\t\tassert(intervalInFrames <= 1<<15);\n\n\t\tif (!Current.KeyStates[key].Pressed || Last.KeyStates[key].Pressed) return false;\n\n\t\tauto lastPressed = Last.KeyStates[key].LastPressedFrame;\n\t\tif (lastPressed == 0) return false;\n\n\t\tauto currentFrame = mFrameCounter;\n\t\tif (currentFrame < lastPressed) currentFrame += 1<<15;\n\t\treturn currentFrame - lastPressed < intervalInFrames;\n\t}\n\n\t[[nodiscard]] bool KeyJustPressed(int key) const noexcept {\n\t\treturn Current.KeyStates[key].Pressed && !Last.KeyStates[key].Pressed;\n\t}\n\n\t[[nodiscard]] bool ShouldDo(Action action) {\n\t\tswitch (action) {\n\t\tcase Action::PLACE_BLOCK:\n\t\t\treturn (Current.RightMouse && !Last.RightMouse) || KeyPressed(GLFW_KEY_TAB);\n\t\tcase Action::PICK_BLOCK:\n\t\t\treturn Current.LeftMouse || KeyPressed(GLFW_KEY_ENTER);\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tstatic void MouseScrollCallback(GLFWwindow*, double, double yOffset) {\n\t\tMouseScroll += yOffset;\n\t}\n\nprivate:\n\tGLFWwindow* mWindow;\n\tuint16_t mFrameCounter = 1;\n\n\tinline static std::shared_ptr<kls::PmrBase> Listener;\n\tinline static double MouseScroll = 0;\n\tinline static std::array<KeyState, GLFW_KEY_LAST> KeyStates;\n};\n"
  },
  {
    "path": "NEWorld.Game/Definitions.cpp",
    "content": "#include \"Definitions.h\"\n\n//Global Vars\nfloat FOVyNormal = 60.0f;\nfloat mousemove = 0.2f;\nint viewdistance = 8;\nint cloudwidth = 10;\ndouble selectPrecision = 32;\nint selectDistance = 8;\nfloat walkspeed = 0.15f;\nfloat runspeed = 0.3f;\nint MaxAirJumps = 3 - 1;\nbool SmoothLighting = true;\nbool NiceGrass = true;\nint linelength = 10;\nint linedist = 30;\nfloat skycolorR = 0.7f;\nfloat skycolorG = 1.0f;\nfloat skycolorB = 1.0f;\nfloat FOVyRunning = 8.0f;\nfloat FOVyExt;\ndouble stretch = 1.0f;\nint Multisample = 0;\nbool vsync = false;\nint gametime = 0;\n//float daylight;\n\nint windowwidth;\nint windowheight;\n\n//������Ϸ\nbool multiplayer = false;\nstd::string serverip;\nunsigned short port = 30001;\n\nTextureID BlockTextures;\nTextureID tex_select, tex_unselect, tex_mainmenu[6];\nTextureID DestroyImage[11];\nTextureID DefaultSkin;\n\nMutex_t Mutex;\ndouble lastUpdate;\nbool updateThreadRun, updateThreadPaused;\n\nbool shouldGetScreenshot;\nbool shouldGetThumbnail;\nbool FirstUpdateThisFrame;\ndouble SpeedupAnimTimer;\ndouble TouchdownAnimTimer;\ndouble screenshotAnimTimer;\ndouble bagAnimTimer;\ndouble bagAnimDuration = 0.5;\n\n//OpenGL\nint GLVersionMajor, GLVersionMinor, GLVersionRev;\n//GLFW\nGLFWwindow *MainWindow;\nGLFWcursor *MouseCursor;\n\n#ifdef NEWORLD_DEBUG_PERFORMANCE_REC\nint c_getChunkPtrFromCPA;\nint c_getChunkPtrFromSearch;\nint c_getHeightFromHMap;\nint c_getHeightFromWorldGen;\n#endif\n\n"
  },
  {
    "path": "NEWorld.Game/Definitions.h",
    "content": "#pragma once\n#ifdef NDEBUG\n#pragma comment(linker, \"/SUBSYSTEM:\\\"WINDOWS\\\" /ENTRY:\\\"mainCRTStartup\\\"\")\n#endif\n\n#include \"Typedefs.h\"\n#include \"FunctionsKit.h\"\n\n#include <string>\n\n//Global Vars\nconst unsigned int VERSION = 39;\nconstexpr double MaxUpdateFPS = 30;\nconstexpr const char* MAJOR_VERSION = \"Alpha 0.\";\nconstexpr const char* MINOR_VERSION = \"5\";\nconstexpr const char* EXT_VERSION = \" Technical Perview\";\nconstexpr int DefaultWindowWidth = 852; //默认窗口宽度\nconstexpr int DefaultWindowHeight = 480; //默认窗口高度\nextern float FOVyNormal;\nextern float mousemove;\nextern int viewdistance;\nextern int cloudwidth;\nextern double selectPrecision;\nextern int selectDistance;\nextern float walkspeed;\nextern float runspeed;\nextern int MaxAirJumps;\nextern bool SmoothLighting;\nextern bool NiceGrass;\nextern int linelength;\nextern int linedist;\nextern float skycolorR;\nextern float skycolorG;\nextern float skycolorB;\nextern float FOVyRunning;\nextern float FOVyExt;\nextern int Multisample;\nextern bool vsync;\nextern double stretch;\nextern int gametime;\nconst int gameTimeMax = 43200;\n\nextern int windowwidth;\nextern int windowheight;\n\nextern TextureID BlockTextures;\nextern TextureID tex_select, tex_unselect, tex_title, tex_mainmenu[6];\nextern TextureID DestroyImage[11];\nextern TextureID DefaultSkin;\n\nextern Mutex_t Mutex;\nextern double lastUpdate;\nextern bool updateThreadRun, updateThreadPaused;\n\nextern bool mpclient, mpserver;\nextern bool shouldGetScreenshot;\nextern bool shouldGetThumbnail;\nextern bool FirstUpdateThisFrame;\nextern double SpeedupAnimTimer;\nextern double TouchdownAnimTimer;\nextern double screenshotAnimTimer;\nextern double bagAnimTimer;\nextern double bagAnimDuration;\n\nextern int GLVersionMajor, GLVersionMinor, GLVersionRev;\nextern GLFWwindow *MainWindow;\nextern GLFWcursor *MouseCursor;\n\nvoid AppCleanUp();\n\n"
  },
  {
    "path": "NEWorld.Game/Dispatch.cpp",
    "content": "#include \"Dispatch.h\"\n#include <thread>\n\nnamespace {\n\tauto sessDefault = kls::coroutine::CreateScalingFIFOExecutor(1, std::thread::hardware_concurrency(), 5000);\n}\n\nkls::coroutine::IExecutor * GetSessionDefault() { return sessDefault.get(); }\n"
  },
  {
    "path": "NEWorld.Game/Dispatch.h",
    "content": "#pragma once\n\n#include \"kls/coroutine/Executor.h\"\n\nkls::coroutine::IExecutor * GetSessionDefault();"
  },
  {
    "path": "NEWorld.Game/Frustum.cpp",
    "content": "#include \"Frustum.h\"\n\n#define _USE_MATH_DEFINES\n\n#include <cmath>\n#include <cstring>\n\nvoid Frustum::LoadIdentity() {\n    memset(proj, 0, sizeof(proj));\n    memset(modl, 0, sizeof(modl));\n    modl[0] = modl[5] = modl[10] = modl[15] = 1.0f;\n}\n\ninline void Frustum::MultMatrix(float *a, float *b) {\n    float sum[16];\n    MultMatrixTo(sum, a, b);\n    memcpy(a, sum, sizeof(sum));\n}\n\nvoid Frustum::SetPerspective(float FOV, float aspect, float Znear, float Zfar) {\n    const auto ViewAngleH = FOV * static_cast<float>(M_PI) / 180.0f;\n    const auto ViewAngleV = atan(tan(ViewAngleH / 2.0f) * aspect) * 2.0f;\n    proj[0] = 1.0f / tan(ViewAngleV / 2);\n    proj[5] = proj[0] * aspect;\n    proj[10] = -(Zfar + Znear) / (Zfar - Znear);\n    proj[11] = -1;\n    proj[14] = -2 * Zfar * Znear / (Zfar - Znear);\n}\n\nvoid Frustum::SetOrtho(float left, float right, float top, float bottom, float Znear, float Zfar) {\n    proj[0] = 2 / (right - left);\n    proj[5] = 2 / (bottom - top);\n    proj[10] = 2 / (Znear - Zfar);\n    proj[15] = 1.0f;\n}\n\nvoid Frustum::MultRotate(float angle, float x, float y, float z) {\n    float m[16], sum[16];\n    memset(m, 0, sizeof(m));\n    const auto length = sqrtf(x * x + y * y + z * z);\n    x /= length;\n    y /= length;\n    z /= length;\n    const auto alpha = angle * static_cast<float>(M_PI) / 180.0f;\n    const auto s = sin(alpha);\n    const auto c = cos(alpha);\n    const auto t = 1.0f - c;\n    m[0] = t * x * x + c;\n    m[1] = t * x * y + s * z;\n    m[2] = t * x * z - s * y;\n    m[4] = t * x * y - s * z;\n    m[5] = t * y * y + c;\n    m[6] = t * y * z + s * x;\n    m[8] = t * x * z + s * y;\n    m[9] = t * y * z - s * x;\n    m[10] = t * z * z + c;\n    m[15] = 1.0f;\n    MultMatrixTo(sum, m, modl);\n    memcpy(modl, sum, sizeof(sum));\n}\n\ninline void Frustum::normalize(int side) {\n    const auto magnitude = sqrtf(\n            frus[side + 0] * frus[side + 0] + frus[side + 1] * frus[side + 1] + frus[side + 2] * frus[side + 2]);\n    frus[side + 0] /= magnitude;\n    frus[side + 1] /= magnitude;\n    frus[side + 2] /= magnitude;\n    frus[side + 3] /= magnitude;\n}\n\nvoid Frustum::update() {\n    MultMatrixTo(clip, modl, proj);\n\n    frus[0] = clip[3] - clip[0];\n    frus[1] = clip[7] - clip[4];\n    frus[2] = clip[11] - clip[8];\n    frus[3] = clip[15] - clip[12];\n    normalize(0);\n\n    frus[4] = clip[3] + clip[0];\n    frus[5] = clip[7] + clip[4];\n    frus[6] = clip[11] + clip[8];\n    frus[7] = clip[15] + clip[12];\n    normalize(4);\n\n    frus[8] = clip[3] + clip[1];\n    frus[9] = clip[7] + clip[5];\n    frus[10] = clip[11] + clip[9];\n    frus[11] = clip[15] + clip[13];\n    normalize(8);\n\n    frus[12] = clip[3] - clip[1];\n    frus[13] = clip[7] - clip[5];\n    frus[14] = clip[11] - clip[9];\n    frus[15] = clip[15] - clip[13];\n    normalize(12);\n\n    frus[16] = clip[3] - clip[2];\n    frus[17] = clip[7] - clip[6];\n    frus[18] = clip[11] - clip[10];\n    frus[19] = clip[15] - clip[14];\n    normalize(16);\n\n    frus[20] = clip[3] + clip[2];\n    frus[21] = clip[7] + clip[6];\n    frus[22] = clip[11] + clip[10];\n    frus[23] = clip[15] + clip[14];\n    normalize(20);\n}\n\nbool Frustum::FrustumTest(const ChunkBox &aabb) {\n    for (auto i = 0; i < 24; i += 4) {\n        if (frus[i] * aabb.xmin + frus[i + 1] * aabb.ymin + frus[i + 2] * aabb.zmin + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmax + frus[i + 1] * aabb.ymin + frus[i + 2] * aabb.zmin + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmin + frus[i + 1] * aabb.ymax + frus[i + 2] * aabb.zmin + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmax + frus[i + 1] * aabb.ymax + frus[i + 2] * aabb.zmin + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmin + frus[i + 1] * aabb.ymin + frus[i + 2] * aabb.zmax + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmax + frus[i + 1] * aabb.ymin + frus[i + 2] * aabb.zmax + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmin + frus[i + 1] * aabb.ymax + frus[i + 2] * aabb.zmax + frus[i + 3] <= 0.0f &&\n            frus[i] * aabb.xmax + frus[i + 1] * aabb.ymax + frus[i + 2] * aabb.zmax + frus[i + 3] <= 0.0f) {\n            return false;\n        }\n    }\n    return true;\n}"
  },
  {
    "path": "NEWorld.Game/Frustum.h",
    "content": "#pragma once\n\nclass Frustum {\nprivate:\n    float frus[24], clip[16];\n    float proj[16], modl[16];\n\npublic:\n    //AABB with Float32 coords\n    struct ChunkBox {\n        float xmin, ymin, zmin;\n        float xmax, ymax, zmax;\n    };\n\n    float *getProjMatrix() { return proj; }\n\n    float *getModlMatrix() { return modl; }\n\n    void LoadIdentity();\n\n    void MultMatrixTo(float *sum, float *a, float *b) {\n        sum[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];\n        sum[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];\n        sum[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];\n        sum[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];\n        sum[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];\n        sum[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];\n        sum[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];\n        sum[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];\n        sum[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];\n        sum[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];\n        sum[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];\n        sum[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];\n        sum[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];\n        sum[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];\n        sum[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];\n        sum[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];\n    }\n\n    inline void MultMatrix(float *a, float *b);\n\n    void SetPerspective(float FOV, float aspect, float Znear, float Zfar);\n\n    void SetOrtho(float left, float right, float top, float bottom, float Znear, float Zfar);\n\n    void MultRotate(float angle, float x, float y, float z);\n\n    inline void normalize(int side);\n\n    void update();\n\n    bool FrustumTest(const ChunkBox &aabb);\n};"
  },
  {
    "path": "NEWorld.Game/FunctionsKit.cpp",
    "content": "#include \"FunctionsKit.h\"\n\n#if __has_include(<Windows.h>)\n#define WIN32_LEAN_AND_MEAN\n#define NOMINMAX\n#define NEWORLD_WIN32\n#include <Windows.h>\n#endif\n\nunsigned int g_seed = 0;\n\nstd::vector<std::string> split(const std::string &str, const std::string &pattern) {\n    std::vector<std::string> ret;\n    if (pattern.empty()) return ret;\n    size_t start = 0, index = str.find_first_of(pattern, 0);\n    while (index != str.npos) {\n        if (start != index)\n            ret.push_back(str.substr(start, index - start));\n        start = index + 1;\n        index = str.find_first_of(pattern, start);\n    }\n    if (!str.substr(start).empty())\n        ret.push_back(str.substr(start));\n    return ret;\n}\n\nunsigned int MByteToWChar(wchar_t *dst, const char *src, unsigned int n) {\n#ifdef NEWORLD_WIN32\n    return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, n, dst, n);\n#else\n    return mbstowcs(dst, src, n);\n#endif\n}\n\nunsigned int WCharToMByte(char *dst, const wchar_t *src, unsigned int n) {\n#ifdef NEWORLD_WIN32\n    return WideCharToMultiByte(CP_UTF8, WC_COMPOSITECHECK, src, n, dst, n, nullptr, nullptr);\n#else\n    return wcstombs(dst, src, n);\n#endif\n}\n"
  },
  {
    "path": "NEWorld.Game/FunctionsKit.h",
    "content": "#pragma once\n\n#include \"stdinclude.h\"\n#include \"Typedefs.h\"\n#include <chrono>\n#include <vector>\n#include <sstream>\n#include <string_view>\n\nextern double stretch;\n\n//常用函数\nextern unsigned int g_seed;\n\ninline unsigned int fastRand() {\n    g_seed = (214013 * g_seed + 2531011);\n    return (g_seed >> 16u) & 0x7FFFu;\n}\n\ninline void fastSrand(int seed) { g_seed = seed; }\n\nstd::vector<std::string> split(const std::string &str, const std::string &pattern);\n\ninline std::string boolstr(bool b) { return b ? \"True\" : \"False\"; }\n\ninline double rnd() { return static_cast<double>(fastRand()) / static_cast<double>(0x7FFFu); }\n\ninline int RoundInt(double d) { return static_cast<int>(lround(d)); }\n\ninline Mutex_t MutexCreate() { return new std::mutex; }\n\ninline void MutexDestroy(Mutex_t _hMutex) { delete _hMutex; }\n\ninline void MutexLock(Mutex_t _hMutex) { _hMutex->lock(); }\n\ninline void MutexUnlock(Mutex_t _hMutex) { _hMutex->unlock(); }\n\nunsigned int MByteToWChar(wchar_t *dst, const char *src, unsigned int n);\n\nunsigned int WCharToMByte(char *dst, const wchar_t *src, unsigned int n);\n\ninline unsigned int wstrlen(const wchar_t *wstr) { return wcslen(wstr); }\n\ninline void SleepMs(unsigned int ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }\n\ninline double timer() {\n    return static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(\n            std::chrono::high_resolution_clock::now().time_since_epoch()).count()) / 1000.0;\n}\n\n//计算距离的平方\ninline int DistanceSquare(int ix, int iy, int iz, int x, int y, int z) {\n    return (ix - x) * (ix - x) + (iy - y) * (iy - y) + (iz - z) * (iz - z);\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/GUI.cpp",
    "content": "#include <deque>\n#include \"GUI.h\"\n#include <NsRender/GLFactory.h>\n#include <NoesisPCH.h>\n#include \"Noesis.h\"\n#include \"System/MessageBus.h\"\n#include \"Common/Logger.h\"\n#include <kls/coroutine/Operation.h>\n\nnamespace GUI {\n    static Noesis::Key mapKey(int glfwKey) {\n        using namespace Noesis;\n        static std::unordered_map<int, Noesis::Key> keyTable = {\n\t\t\t{GLFW_KEY_SPACE ,Key_Space},\n\t\t\t{GLFW_KEY_MINUS ,Key_Subtract},\n            {GLFW_KEY_0, Key_D0},\n            {GLFW_KEY_1, Key_D1},\n            {GLFW_KEY_2, Key_D2},\n            {GLFW_KEY_3, Key_D3},\n            {GLFW_KEY_4, Key_D4},\n            {GLFW_KEY_5, Key_D5},\n            {GLFW_KEY_6, Key_D6},\n            {GLFW_KEY_7, Key_D7},\n            {GLFW_KEY_8, Key_D8},\n            {GLFW_KEY_9, Key_D9},\n            {GLFW_KEY_A, Key_A},\n            {GLFW_KEY_B, Key_B},\n            {GLFW_KEY_C, Key_C},\n            {GLFW_KEY_D, Key_D},\n            {GLFW_KEY_E, Key_E},\n            {GLFW_KEY_F, Key_F},\n            {GLFW_KEY_G, Key_G},\n            {GLFW_KEY_H, Key_H},\n            {GLFW_KEY_I, Key_I},\n            {GLFW_KEY_J, Key_J},\n            {GLFW_KEY_K, Key_K},\n            {GLFW_KEY_L, Key_L},\n            {GLFW_KEY_M, Key_M},\n            {GLFW_KEY_N, Key_N},\n            {GLFW_KEY_O, Key_O},\n            {GLFW_KEY_P, Key_P},\n            {GLFW_KEY_Q, Key_Q},\n            {GLFW_KEY_R, Key_R},\n            {GLFW_KEY_S, Key_S},\n            {GLFW_KEY_T, Key_T},\n            {GLFW_KEY_U, Key_U},\n            {GLFW_KEY_V, Key_V},\n            {GLFW_KEY_W, Key_W},\n            {GLFW_KEY_X, Key_X},\n            {GLFW_KEY_Y, Key_Y},\n            {GLFW_KEY_Z, Key_Z}\n        };\n        return keyTable[glfwKey];\n    }\n\n    void Scene::update() {\n        // TODO: change to use glfw callback + message bus?\n        if (mView) {\n            static bool leftPressed = false, rightPressed = false;\n            static double lastPressedTime = 0;\n            double xpos, ypos;\n            glfwGetCursorPos(MainWindow, &xpos, &ypos);\n            mView->MouseMove(xpos, ypos);\n            mView->SetSize(windowwidth, windowheight);\n\n            const int leftCurrentlyPressed = glfwGetMouseButton(MainWindow, GLFW_MOUSE_BUTTON_LEFT);\n            if (leftCurrentlyPressed == GLFW_PRESS && !leftPressed) {\n                mView->MouseButtonDown(xpos, ypos, Noesis::MouseButton_Left);\n                auto curTime = timer();\n                if (curTime - lastPressedTime < 0.25) {\n                    mView->MouseDoubleClick(xpos, ypos, Noesis::MouseButton_Left);\n                }\n                leftPressed = true;\n                lastPressedTime = curTime;\n            }\n            else if (leftCurrentlyPressed != GLFW_PRESS && leftPressed) {\n                leftPressed = false;\n                mView->MouseButtonUp(xpos, ypos, Noesis::MouseButton_Left);\n            }\n\n            const int rightCurrentlyPressed = glfwGetMouseButton(MainWindow, GLFW_MOUSE_BUTTON_RIGHT);\n            if (rightCurrentlyPressed == GLFW_PRESS && !rightPressed) {\n                mView->MouseButtonDown(xpos, ypos, Noesis::MouseButton_Right);\n                rightPressed = true;\n            }\n            else if (rightCurrentlyPressed != GLFW_PRESS && rightPressed) {\n                rightPressed = false;\n                mView->MouseButtonUp(xpos, ypos, Noesis::MouseButton_Right);\n            }\n        }\n\n        onUpdate();\n    }\n\n    kls::coroutine::ValueAsync<void> Scene::render() {\n        mFPS.update();\n\n        if (mView) {\n            // Update view (layout, animations, ...)\n            mView->Update(timer() - mEnterTimeInSec);\n\n            glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_DEPTH_BUFFER_BIT);\n            // Offscreen rendering phase populates textures needed by the on-screen rendering\n            mView->GetRenderer()->UpdateRenderTree();\n            mView->GetRenderer()->RenderOffscreen();\n            glPopAttrib();\n            glBindFramebuffer(GL_FRAMEBUFFER, 0);\n            glViewport(0, 0, windowwidth, windowheight);\n\n            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);\n            glClearStencil(0);\n            glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);\n        }\n\n        co_await onRender();\n\n        if (mView) {\n            glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_DEPTH_BUFFER_BIT);\n            // Rendering is done in the active framebuffer\n            mView->GetRenderer()->Render();\n            glPopAttrib();\n        }\n    }\n\n    Scene::~Scene() {\n        if (mView) {\n            mView->GetRenderer()->Shutdown();\n            mView.Reset();\n        }\n    }\n\n    void Scene::loadView() {\n        mRoot = Noesis::GUI::LoadXaml<Noesis::Grid>(mXamlPath);\n        if (!mRoot) {\n            errorstream << \"UI failed to load!\";\n            return;\n        }\n        onViewBinding();\n        mView = Noesis::GUI::CreateView(mRoot);\n        mView->SetFlags(Noesis::RenderFlags_PPAA | Noesis::RenderFlags_LCD);\n        mView->GetRenderer()->Init(GUI::renderDevice);\n        // high dpi support\n        float scale;\n        glfwGetWindowContentScale(MainWindow, &scale, nullptr);\n        mView->SetScale(scale);\n    }\n\n    kls::coroutine::ValueAsync<void> Scene::singleLoop() {\n        update();\n        co_await render();\n        glfwSwapBuffers(MainWindow);\n        glfwPollEvents();\n    }\n\n    void Scene::load() {\n    \tloadView();\n\n        glfwSetInputMode(MainWindow, GLFW_CURSOR, mHasCursor ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);\n        if (mXamlPath) {\n            mListeners.push_back(MessageBus::Default().Get<std::pair<int, int>>(\"KeyEvents\")->Listen([this](void*, std::pair<int, int> keyAndAction) {\n                auto [key, action] = keyAndAction;\n                if (action == GLFW_PRESS) mView->KeyDown(mapKey(key));\n                else if (action == GLFW_RELEASE) mView->KeyUp(mapKey(key));\n\n                if (key == GLFW_KEY_F5 && action == GLFW_PRESS) {\n                    // reload view. might leak memory but it's for debug only\n                    infostream << \"Reloading View\";\n                    loadView();\n                }\n            }));\n            mListeners.push_back(MessageBus::Default().Get<int>(\"InputEvents\")->Listen([this](void*, int k) {\n                mView->Char(k);\n            }));\n        }\n\n        onLoad();\n    }\n\n    std::deque<std::unique_ptr<Scene>> scenes;\n    \n    void pushScene(std::unique_ptr<Scene> scene) {\n        scene->load();\n        scenes.emplace_back(std::move(scene));\n    }\n\n    void popScene() {\n        scenes.pop_back();\n    }\n\n    void clearScenes() {\n        while (!scenes.empty()) popScene();\n    }\n\n    void appStart() {\n        glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n        glClearColor(0.0, 0.0, 0.0, 1.0);\n        glDisable(GL_CULL_FACE);\n        kls::coroutine::run_blocking([]() -> kls::coroutine::ValueAsync<> {\n            while (!scenes.empty()) {\n                auto &currentScene = scenes.back();\n                co_await currentScene->singleLoop();\n                if (currentScene->shouldLeave()) GUI::popScene();\n                if (glfwWindowShouldClose(MainWindow)) {\n                    clearScenes();\n                }\n            }\n        });\n        AppCleanUp();\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/GUI.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n#include <memory>\n#include <NsCore/Ptr.h>\n#include <NsGui/IView.h>\n#include <NsGui/Grid.h>\n#include <kls/Object.h>\n#include <kls/coroutine/Async.h>\n\nnamespace GUI {\n    class FpsCounter {\n    public:\n        int getFPS() const noexcept { return mFPS; }\n        void update() noexcept {\n            check();\n            frame();\n        }\n        void check() noexcept {\n            if (timer() - mLastTimeInSec >= 1.0) {\n                mFPS = mFPSCounter;\n                mFPSCounter = 0;\n                mLastTimeInSec = timer();\n            }\n        }\n        void frame() noexcept { mFPSCounter++; }\n    private:\n        int mFPSCounter = 0;\n        int mFPS = 0;\n        double mLastTimeInSec = 0;\n    };\n\n    class Scene {\n    public:\n        Scene(const char* xaml, bool hasCursor = true) :\n            mXamlPath(xaml), mHasCursor(hasCursor), mEnterTimeInSec(timer()) {}\n\n        virtual ~Scene();\n\n        void load();\n        kls::coroutine::ValueAsync<void> singleLoop();\n\n        void requestLeave() noexcept { mShouldLeave = true; }\n        bool shouldLeave() const noexcept { return mShouldLeave; }\n\n    protected:\n        virtual kls::coroutine::ValueAsync<void> onRender() { co_return; }\n        virtual void onUpdate() {}\n        virtual void onLoad() {}\n        virtual void onViewBinding() {}\n\n        Noesis::Ptr<Noesis::Grid> mRoot;\n        Noesis::Ptr<Noesis::IView> mView;\n\n        FpsCounter mFPS;\n\n    private:\n        kls::coroutine::ValueAsync<void> render();\n        void update();\n        void loadView();\n\n        bool mShouldLeave = false;\n        const char* mXamlPath;\n        bool mHasCursor;\n        double mEnterTimeInSec;\n\n        std::vector<std::shared_ptr<kls::PmrBase>> mListeners;\n    };\n\n    void pushScene(std::unique_ptr<Scene> scene);\n\n    void popScene();\n\n    void clearScenes();\n\n    void appStart();\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/InventorySlot.cpp",
    "content": "#include \"InventorySlot.h\"\n\n#include <unordered_map>\n\n#include \"Definitions.h\"\n#include \"Textures.h\"\n#include \"Typedefs.h\"\n#include \"Universe/World/Blocks.h\"\n\n#include <NsDrawing/Int32Rect.h>\n#include <NsGui/CroppedBitmap.h>\n#include <NsGui/TextureSource.h>\n#include <NsRender/GLFactory.h>\n#include <NsRender/Texture.h>\n\nconst Noesis::DependencyProperty* InventorySlot::AmountProperty;\nconst Noesis::DependencyProperty* InventorySlot::SelectedProperty;\n\nNoesis::Ptr<Noesis::TextureSource> InventorySlot::CachedBlockTextures;\nstd::unordered_map<Item, Noesis::Ptr<Noesis::CroppedBitmap>> InventorySlot::CachedItemTextures;\n\nInventorySlot::InventorySlot() {\n    Noesis::GUI::LoadComponent(this, \"InventorySlot.xaml\");\n    if (!CachedBlockTextures) {\n        CachedBlockTextures = Noesis::MakePtr<Noesis::TextureSource>(NoesisApp::GLFactory::WrapTexture(\n            BlockTextures, 256, 256, 0, false, true\n        ));\n    }\n}\n\nvoid InventorySlot::clearCache() {\n    CachedItemTextures.clear();\n}\n\nNoesis::ImageSource* InventorySlot::getTextureForItem(Item i) {\n    if (i == Blocks::ENV) return nullptr;\n    // find from cache first\n    auto itemTextureIter = CachedItemTextures.find(i);\n\n    if (itemTextureIter != CachedItemTextures.end())\n        return (*itemTextureIter).second.GetPtr();\n\n    const auto tcX = Textures::getTexcoordX(i, 1) * 256;\n    const auto tcY = Textures::getTexcoordY(i, 1) * 256;\n    return CachedItemTextures[i] = Noesis::MakePtr<Noesis::CroppedBitmap>(\n        CachedBlockTextures.GetPtr(), Noesis::Int32Rect(tcX, tcY, 32, 32)  // TODO: refactor\n        );\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/InventorySlot.h",
    "content": "#include <unordered_map>\n\n#include \"Typedefs.h\"\n#include \"NsGui/CroppedBitmap.h\"\n#include \"NsGui/Image.h\"\n#include \"NsGui/ImageSource.h\"\n#include \"NsGui/IntegrationAPI.h\"\n#include \"NsGui/TextureSource.h\"\n#include \"NsGui/UIElementData.h\"\n#include \"NsGui/UserControl.h\"\n#include \"Universe/Entity/PlayerEntity.h\"\n\nclass InventorySlot : public Noesis::UserControl\n{\npublic:\n    InventorySlot();\n\n    int getAmount() const noexcept { return mQuantity; }\n    void setAmount(int value) { mQuantity = value; SetValue<int>(AmountProperty, value); }\n    Item getItem() const noexcept { return mItem; }\n    void setItem(Item i) {\n        if (mItem == i) return;\n\t    mItem = i;\n        FindName<Noesis::Image>(\"ItemTexture\")->SetSource(getTextureForItem(i));\n    }\n    void setItemStack(ItemStack stack) {\n        setItem(stack.item);\n        setAmount(stack.amount);\n    } \n    bool isSelected() const noexcept { return mSelected; }\n    void setSelected(bool selected) { mSelected = selected; SetValue<bool>(SelectedProperty, selected); }\n\n    static const Noesis::DependencyProperty* AmountProperty;\n    static const Noesis::DependencyProperty* SelectedProperty;\n    static void clearCache();\n\nprivate:\n    static Noesis::ImageSource* getTextureForItem(Item i);\n    static Noesis::Ptr<Noesis::TextureSource> CachedBlockTextures;\n    static std::unordered_map<Item, Noesis::Ptr<Noesis::CroppedBitmap>> CachedItemTextures;\n\n    Item mItem;\n    bool mSelected;\n    int mQuantity;\n\n    NS_IMPLEMENT_INLINE_REFLECTION(InventorySlot, UserControl, \"NEWorld.InventorySlot\") {\n        Noesis::UIElementData* data = NsMeta<Noesis::UIElementData>(Noesis::TypeOf<SelfClass>());\n        data->RegisterProperty<int>(AmountProperty, \"Amount\", Noesis::PropertyMetadata::Create(0));\n        data->RegisterProperty<bool>(SelectedProperty, \"Selected\", Noesis::PropertyMetadata::Create(false));\n    }\n};\n"
  },
  {
    "path": "NEWorld.Game/GUI/Menus/MainMenu.cpp",
    "content": "#include \"Menus.h\"\n#include \"../GUI.h\"\n#include \"NsRender/GLFactory.h\"\n#include \"NsGui/Grid.h\"\n#include \"NsGui/Button.h\"\n#include \"GameView.h\"\n#include \"Frustum.h\"\n#include \"NsApp/NotifyPropertyChangedBase.h\"\n#include \"NsGui/ObservableCollection.h\"\n#include <filesystem>\n\n#include \"GameSettings.h\"\n#include \"Universe/World/Chunk.h\"\n#include \"NsGui/TextBox.h\"\n#include \"NsGui/ListView.h\"\n#include \"Renderer/Renderer.h\"\n\nnamespace Menus {\n    class WorldModel : public NoesisApp::NotifyPropertyChangedBase\n    {\n    public:\n        WorldModel(std::string name) : mName(std::move(name)) {}\n        const char* name() const { return mName.c_str(); }\n\n    private:\n        std::string mName;\n\n        NS_IMPLEMENT_INLINE_REFLECTION(WorldModel, NotifyPropertyChangedBase) {\n            NsProp(\"Name\", &WorldModel::name);\n        }\n    };\n\n    class GameMenuViewModel : public NoesisApp::NotifyPropertyChangedBase {\n    public:\n        GameMenuViewModel() {\n            for (auto&& x : std::filesystem::directory_iterator(\"./Worlds/\")) {\n                if (is_directory(x)) {\n                    mWorlds.Add(Noesis::MakePtr<WorldModel>(x.path().filename().string()));\n                }\n            }\n        }\n        enum class State { MAIN_MENU, SETTINGS, SELECT_WORLD };\n        const char* getState() const {\n            switch (mState) {\n            case State::MAIN_MENU: return \"MainMenu\";\n            case State::SETTINGS: return \"Settings\";\n            case State::SELECT_WORLD: return \"SelectWorld\";\n            default: assert(false);\n            }\n            return nullptr;\n        }\n        void setState(State state) noexcept {\n            if(mState!=state) {\n                mState = state;\n                OnPropertyChanged(\"State\");\n            }\n        }\n\n#define SETTING_ITEM(TYPE, MEMBER_NAME, PROP_NAME) \\\n        TYPE get##PROP_NAME() const noexcept { return MEMBER_NAME; }\\\n        void set##PROP_NAME(TYPE value) noexcept {\\\n            if (value == MEMBER_NAME) return;\\\n            MEMBER_NAME = value;\\\n            OnPropertyChanged(#PROP_NAME);\\\n        }\n\n        SETTING_ITEM(int, mFOV, FOV);\n        SETTING_ITEM(float, mMouseSensitivity, MouseSensitivity);\n        SETTING_ITEM(bool, mNiceGrass, NiceGrass);\n        SETTING_ITEM(bool, mSmoothLighting, SmoothLighting);\n        SETTING_ITEM(bool, mVSync, VSync);\n        SETTING_ITEM(bool, mShadows, Shadows);\n\n#undef SETTING_ITEM\n\n        int getRenderDistance() const { return mRenderDistance; }\n        int getRenderDistanceTick() const noexcept { return mRenderDistanceTick; }\n        void setRenderDistanceTick(int renderDistanceTick) noexcept {\n            if (mRenderDistanceTick != renderDistanceTick) {\n                mRenderDistanceTick = renderDistanceTick;\n                mRenderDistance = 1 << renderDistanceTick; // 2^renderDistanceTick\n                OnPropertyChanged(\"RenderDistanceTick\");\n                OnPropertyChanged(\"RenderDistance\");\n            }\n        }\n\n        const Noesis::ObservableCollection<WorldModel>* getWorlds() const {\n            return &mWorlds;\n        }\n\n    private:\n        State mState = State::MAIN_MENU;\n        float& mFOV = FOVyNormal;\n        float& mMouseSensitivity = mousemove;\n        int& mRenderDistance = viewdistance;\n        int mRenderDistanceTick = int(std::log2(viewdistance));\n        bool& mNiceGrass = NiceGrass;\n        bool& mVSync = vsync;\n        bool& mSmoothLighting = SmoothLighting;\n        bool& mShadows  = Renderer::AdvancedRender;\n\n        Noesis::ObservableCollection<WorldModel> mWorlds;\n\n        NS_IMPLEMENT_INLINE_REFLECTION(GameMenuViewModel, NotifyPropertyChangedBase) {\n            NsProp(\"State\", &GameMenuViewModel::getState);\n            // settings \n            NsProp(\"FOV\", &GameMenuViewModel::getFOV, &GameMenuViewModel::setFOV);\n            NsProp(\"RenderDistanceTick\", &GameMenuViewModel::getRenderDistanceTick, &GameMenuViewModel::setRenderDistanceTick);\n            NsProp(\"RenderDistance\", &GameMenuViewModel::getRenderDistance);\n            NsProp(\"MouseSensitivity\", &GameMenuViewModel::getMouseSensitivity, &GameMenuViewModel::setMouseSensitivity);\n            NsProp(\"NiceGrass\", &GameMenuViewModel::getNiceGrass, &GameMenuViewModel::setNiceGrass);\n            NsProp(\"VSync\", &GameMenuViewModel::getVSync, &GameMenuViewModel::setVSync);\n            NsProp(\"SmoothLighting\", &GameMenuViewModel::getSmoothLighting, &GameMenuViewModel::setSmoothLighting);\n            NsProp(\"Shadows\", &GameMenuViewModel::getShadows, &GameMenuViewModel::setShadows);\n            // Select World\n            NsProp(\"Worlds\", &GameMenuViewModel::getWorlds);\n        }\n    };\n\n    class MainMenu : public GUI::Scene {\n    public:\n        MainMenu() : Scene(\"MainMenu.xaml\"){}\n    private:\n        Noesis::Ptr<GameMenuViewModel> mViewModel;\n\n        void onViewBinding() override {\n            if(!mViewModel) mViewModel = Noesis::MakePtr<GameMenuViewModel>();\n            mRoot->SetDataContext(mViewModel);\n            // Main Menu\n            mRoot->FindName<Noesis::Button>(\"startGame\")->Click() += [this](Noesis::BaseComponent*, const Noesis::RoutedEventArgs&) {\n                mViewModel->setState(GameMenuViewModel::State::SELECT_WORLD);\n            };\n            mRoot->FindName<Noesis::Button>(\"settings\")->Click() += [this](Noesis::BaseComponent*, const Noesis::RoutedEventArgs&) {\n                mViewModel->setState(GameMenuViewModel::State::SETTINGS);\n            };\n            mRoot->FindName<Noesis::Button>(\"exit\")->Click() += [this](Noesis::BaseComponent*, const Noesis::RoutedEventArgs&) {\n                requestLeave();\n            };\n            // Settings\n            mRoot->FindName<Noesis::Button>(\"Save\")->Click() += [this](Noesis::BaseComponent*, const Noesis::RoutedEventArgs&) {\n                mViewModel->setState(GameMenuViewModel::State::MAIN_MENU);\n                GameSettings::getInstance().saveOptions();\n            };\n            // Select World View\n            mRoot->FindName<Noesis::Button>(\"Back\")->Click() += [this](Noesis::BaseComponent*, const Noesis::RoutedEventArgs&) {\n                mViewModel->setState(GameMenuViewModel::State::MAIN_MENU);\n            };\n            mRoot->FindName<Noesis::Button>(\"Create\")->Click() += [this](Noesis::BaseComponent*, const Noesis::RoutedEventArgs&) {\n                World::worldname = mRoot->FindName<Noesis::TextBox>(\"NewWorldNameTextBox\")->GetText();\n                pushGameView();\n            };\n            auto worldList = mRoot->FindName<Noesis::ListView>(\"WorldList\");\n            worldList->MouseDoubleClick() += [this, worldList](Noesis::BaseComponent*, const Noesis::MouseButtonEventArgs&) {\n                World::worldname = static_cast<WorldModel*>(worldList->GetSelectedItem())->name();\n                pushGameView();\n            };\n        }\n\n        kls::coroutine::ValueAsync<void> onRender() override {\n            co_return drawBackground();\n        }\n\n        void drawBackground() {\n            static Frustum frus;\n            static auto startTimer = timer();\n            const auto elapsed = timer() - startTimer;\n            frus.LoadIdentity();\n            frus.SetPerspective(90.0f, static_cast<float>(windowwidth) / windowheight, 0.1f, 10.0f);\n\n            glMatrixMode(GL_PROJECTION);\n            glLoadIdentity();\n            glMultMatrixf(frus.getProjMatrix());\n            glMatrixMode(GL_MODELVIEW);\n            glLoadIdentity();\n            glRotated(elapsed * 4.0, 0.1, 1.0, 0.1);\n            glClearColor(0.0, 0.0, 0.0, 0.0);\n            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n            glDepthFunc(GL_LEQUAL);\n            glDisable(GL_CULL_FACE);\n            glEnable(GL_TEXTURE_2D);\n            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);\n\n            //Begin to draw a cube\n            glBindTexture(GL_TEXTURE_2D, tex_mainmenu[0]);\n            glBegin(GL_QUADS);\n            glTexCoord2f(0.0f, 1.0f);\n            glVertex3f(-1.0f, 1.0f, -1.0f);\n            glTexCoord2f(1.0f, 1.0f);\n            glVertex3f(1.0f, 1.0f, -1.0f);\n            glTexCoord2f(1.0f, 0.0f);\n            glVertex3f(1.0f, -1.0f, -1.0f);\n            glTexCoord2f(0.0f, 0.0f);\n            glVertex3f(-1.0f, -1.0f, -1.0f);\n            glEnd();\n            glBindTexture(GL_TEXTURE_2D, tex_mainmenu[1]);\n            glBegin(GL_QUADS);\n            glTexCoord2f(0.0f, 1.0f);\n            glVertex3f(1.0f, 1.0f, -1.0f);\n            glTexCoord2f(1.0f, 1.0f);\n            glVertex3f(1.0f, 1.0f, 1.0f);\n            glTexCoord2f(1.0f, 0.0f);\n            glVertex3f(1.0f, -1.0f, 1.0f);\n            glTexCoord2f(0.0f, 0.0f);\n            glVertex3f(1.0f, -1.0f, -1.0f);\n            glEnd();\n            glBindTexture(GL_TEXTURE_2D, tex_mainmenu[2]);\n            glBegin(GL_QUADS);\n            glTexCoord2f(0.0f, 1.0f);\n            glVertex3f(1.0f, 1.0f, 1.0f);\n            glTexCoord2f(1.0f, 1.0f);\n            glVertex3f(-1.0f, 1.0f, 1.0f);\n            glTexCoord2f(1.0f, 0.0f);\n            glVertex3f(-1.0f, -1.0f, 1.0f);\n            glTexCoord2f(0.0f, 0.0f);\n            glVertex3f(1.0f, -1.0f, 1.0f);\n            glEnd();\n            glBindTexture(GL_TEXTURE_2D, tex_mainmenu[3]);\n            glBegin(GL_QUADS);\n            glTexCoord2f(0.0f, 1.0f);\n            glVertex3f(-1.0f, 1.0f, 1.0f);\n            glTexCoord2f(1.0f, 1.0f);\n            glVertex3f(-1.0f, 1.0f, -1.0f);\n            glTexCoord2f(1.0f, 0.0f);\n            glVertex3f(-1.0f, -1.0f, -1.0f);\n            glTexCoord2f(0.0f, 0.0f);\n            glVertex3f(-1.0f, -1.0f, 1.0f);\n            glEnd();\n            glBindTexture(GL_TEXTURE_2D, tex_mainmenu[4]);\n            glBegin(GL_QUADS);\n            glTexCoord2f(0.0f, 1.0f);\n            glVertex3f(-1.0f, -1.0f, 1.0f);\n            glTexCoord2f(1.0f, 1.0f);\n            glVertex3f(-1.0f, -1.0f, -1.0f);\n            glTexCoord2f(1.0f, 0.0f);\n            glVertex3f(1.0f, -1.0f, -1.0f);\n            glTexCoord2f(0.0f, 0.0f);\n            glVertex3f(1.0f, -1.0f, 1.0f);\n            glEnd();\n            glBindTexture(GL_TEXTURE_2D, tex_mainmenu[5]);\n            glBegin(GL_QUADS);\n            glTexCoord2f(0.0f, 1.0f);\n            glVertex3f(1.0f, 1.0f, 1.0f);\n            glTexCoord2f(1.0f, 1.0f);\n            glVertex3f(1.0f, 1.0f, -1.0f);\n            glTexCoord2f(1.0f, 0.0f);\n            glVertex3f(-1.0f, 1.0f, -1.0f);\n            glTexCoord2f(0.0f, 0.0f);\n            glVertex3f(-1.0f, 1.0f, 1.0f);\n            glEnd();\n        }\n    };\n\n    std::unique_ptr<GUI::Scene> startMenu() {\n        return std::make_unique<MainMenu>();\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/Menus/Menus.h",
    "content": "#include <memory>\n#include \"GUI/GUI.h\"\n\nnamespace Menus\n{\n    std::unique_ptr<GUI::Scene> startMenu();\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/Noesis.cpp",
    "content": "#include \"Noesis.h\"\n\n#define GLEW_NO_GLU\n#include <GL/glew.h>\n#include <NsApp/StyleInteraction.h>\n#include <NsApp/EventTrigger.h>\n#include <NsApp/TriggerCollection.h>\n#include <NsCore/RegisterComponent.inl>\n#include <NsApp/GoToStateAction.h>\n#include <NsApp/KeyTrigger.h>\n#include <NsApp/MouseDragElementBehavior.h>\n#include <NsApp/ComparisonCondition.h>\n#include <NsCore/EnumConverter.h>\n#include <NsGui/IntegrationAPI.h>\n#include <NsApp/ThemeProviders.h>\n#include <NsApp/LocalTextureProvider.h>\n#include <NsApp/LocalFontProvider.h>\n#include <NsApp/LocalXamlProvider.h>\n#include <NsApp/BehaviorCollection.h>\n#include <NsApp/Interaction.h>\n#include \"Common/Logger.h\"\n#include \"NsGui/BaseValueConverter.h\"\n#include \"NsRender/GLFactory.h\"\n#include \"GUI/InventorySlot.h\"\n#include \"NsApp/ChangePropertyAction.h\"\n\nNoesis::Ptr<Noesis::RenderDevice> GUI::renderDevice;\n\nvoid GUI::noesisSetup() {\n    Noesis::SetLogHandler([](const char* file, uint32_t line, uint32_t level, const char* channel, const char* msg) {\n        Logger::Level prefixes[] = {\n            Logger::Level::debug,\n            Logger::Level::debug,\n            Logger::Level::info,\n            Logger::Level::warning,\n            Logger::Level::error\n        };\n        Logger(file, \"\", line, prefixes[level], \"Noesis\") << msg;\n        });\n\n    // Sets the active license\n    Noesis::GUI::SetLicense(NS_LICENSE_NAME, NS_LICENSE_KEY);\n    if (std::string(NS_LICENSE_NAME).empty()) {\n        errorstream << \"Noesis License not set.\";\n        errorstream << \"Please add a valid license into External/Noesis/Include/NoesisLicense.h\";\n        errorstream << \"You can get one from https://www.noesisengine.com/trial/\";\n    }\n\n    // Noesis initialization. This must be the first step before using any NoesisGUI functionality\n    Noesis::GUI::Init();\n\n    Noesis::Ptr<Noesis::XamlProvider> xamlProvider =\n        *new NoesisApp::LocalXamlProvider(\"Assets/GUI\");\n    Noesis::Ptr<Noesis::FontProvider> fontProvider =\n        *new NoesisApp::LocalFontProvider(\"Assets/Fonts\");\n    Noesis::Ptr<Noesis::TextureProvider> textureProvider =\n        *new NoesisApp::LocalTextureProvider(\"Assets/Textures\");\n    NoesisApp::SetThemeProviders(xamlProvider, fontProvider, textureProvider);\n\n    Noesis::GUI::LoadApplicationResources(\"Theme/NEWorld.xaml\");\n    Noesis::RegisterComponent<NoesisApp::BehaviorCollection>();\n    Noesis::RegisterComponent<NoesisApp::TriggerCollection>();\n    Noesis::RegisterComponent<NoesisApp::EventTrigger>();\n    Noesis::RegisterComponent<NoesisApp::DataTrigger>();\n    Noesis::RegisterComponent<NoesisApp::MouseDragElementBehavior>();\n    Noesis::RegisterComponent<NoesisApp::GoToStateAction>();\n    Noesis::RegisterComponent<NoesisApp::ChangePropertyAction>();\n    Noesis::RegisterComponent<Noesis::EnumConverter<NoesisApp::ComparisonConditionType>>();\n    Noesis::RegisterComponent<InventorySlot>();\n    Noesis::TypeOf<NoesisApp::Interaction>(); // Force the creation of its reflection type\n    Noesis::TypeOf<NoesisApp::StyleInteraction>(); // Force the creation of its reflection type\n\n    // Setup Render Device\n    glPushAttrib(GL_ALL_ATTRIB_BITS);\n    renderDevice = NoesisApp::GLFactory::CreateDevice(false);\n    glPopAttrib();\n}\n\n\nvoid GUI::noesisShutdown() {\n    InventorySlot::clearCache();\n    GUI::renderDevice.Reset();\n    Noesis::GUI::Shutdown();\n}\n"
  },
  {
    "path": "NEWorld.Game/GUI/Noesis.h",
    "content": "#pragma once\n#include \"NsCore/Ptr.h\"\n#include \"NsRender/RenderDevice.h\"\n\nnamespace GUI {\n    void noesisSetup();\n    void noesisShutdown();\n\n\textern Noesis::Ptr<Noesis::RenderDevice> renderDevice;\n}\n"
  },
  {
    "path": "NEWorld.Game/GameSettings.cpp",
    "content": "#include \"GameSettings.h\"\n#include <sstream>\n#include <fstream>\n#include <map>\n#include \"Definitions.h\"\n#include \"Globalization.h\"\n#include \"AudioSystem.h\"\n#include \"Renderer/Renderer.h\"\n\nconstexpr const char* SettingsFile = \"./Configs/options.ini\";\n\nvoid GameSettings::loadOptions() {\n\tstd::unordered_map<std::string, std::string> options;\n\tstd::ifstream filein(\"./Configs/options.ini\", std::ios::in);\n\tif (!filein.is_open()) return;\n\tstd::string name, value;\n\twhile (!filein.eof()) {\n\t\tfilein >> name >> value;\n\t\toptions[name] = value;\n\t}\n\tfilein.close();\n\tloadOption(options, \"Language\", Globalization::Cur_Lang);\n\tloadOption(options, \"FOV\", FOVyNormal);\n\tloadOption(options, \"RenderDistance\", viewdistance);\n\tloadOption(options, \"Sensitivity\", mousemove);\n\tloadOption(options, \"CloudWidth\", cloudwidth);\n\tloadOption(options, \"SmoothLighting\", SmoothLighting);\n\tloadOption(options, \"FancyGrass\", NiceGrass);\n\tloadOption(options, \"MultiSample\", Multisample);\n\tloadOption(options, \"AdvancedRender\", Renderer::AdvancedRender);\n\tloadOption(options, \"ShadowMapRes\", Renderer::ShadowRes);\n\tloadOption(options, \"ShadowDistance\", Renderer::MaxShadowDist);\n\tloadOption(options, \"VerticalSync\", vsync);\n\tloadOption(options, \"GainOfBGM\", AudioSystem::BGMGain);\n\tloadOption(options, \"GainOfSound\", AudioSystem::SoundGain);\n}\n\nvoid GameSettings::saveOptions() {\n\tstd::map<std::string, std::string> options;\n\tstd::ofstream fileout(\"./Configs/options.ini\", std::ios::out);\n\tif (!fileout.is_open()) return;\n\tsaveOption(fileout, \"Language\", Globalization::Cur_Lang);\n\tsaveOption(fileout, \"FOV\", FOVyNormal);\n\tsaveOption(fileout, \"RenderDistance\", viewdistance);\n\tsaveOption(fileout, \"Sensitivity\", mousemove);\n\tsaveOption(fileout, \"CloudWidth\", cloudwidth);\n\tsaveOption(fileout, \"SmoothLighting\", SmoothLighting);\n\tsaveOption(fileout, \"FancyGrass\", NiceGrass);\n\tsaveOption(fileout, \"MultiSample\", Multisample);\n\tsaveOption(fileout, \"AdvancedRender\", Renderer::AdvancedRender);\n\tsaveOption(fileout, \"ShadowMapRes\", Renderer::ShadowRes);\n\tsaveOption(fileout, \"ShadowDistance\", Renderer::MaxShadowDist);\n\tsaveOption(fileout, \"VerticalSync\", vsync);\n\tsaveOption(fileout, \"GainOfBGM\", AudioSystem::BGMGain);\n\tsaveOption(fileout, \"GainOfSound\", AudioSystem::SoundGain);\n\tfileout.close();\n}\n"
  },
  {
    "path": "NEWorld.Game/GameSettings.h",
    "content": "#pragma once\n#include <string>\n#include <sstream>\n#include <unordered_map>\n\nclass GameSettings {\npublic:\n\tGameSettings() {\n        loadOptions();\n\t}\n\n\tstatic GameSettings& getInstance() {\n\t\tstatic GameSettings settings;\n\t\treturn settings;\n\t}\n\n    void loadOptions();\n\n\tvoid saveOptions();\n\nprivate:\n    template<typename T>\n    void loadOption(std::unordered_map<std::string, std::string>& m, const char* name, T& value) {\n        if (m.find(name) == m.end()) return;\n        std::stringstream ss;\n        ss << m[name];\n        ss >> value;\n    }\n\n    template<typename T>\n    void saveOption(std::ostream& out, const char* name, T& value) {\n        out << std::string(name) << \" \" << value << std::endl;\n    }\n};\n"
  },
  {
    "path": "NEWorld.Game/GameView.cpp",
    "content": "#include \"GameView.h\"\n\n#include <optional>\n#include <utility>\n#include <Renderer/World/ShadowMaps.h>\n#include \"Universe/World/Blocks.h\"\n#include \"Textures.h\"\n#include \"Renderer/Renderer.h\"\n#include \"Universe/World/World.h\"\n#include \"Renderer/World/WorldRenderer.h\"\n#include \"Particles.h\"\n#include \"GUI/GUI.h\"\n#include \"Command.h\"\n#include \"Setup.h\"\n#include \"Universe/Game.h\"\n#include \"Common/Logger.h\"\n#include \"NsApp/NotifyPropertyChangedBase.h\"\n#include \"NsGui/Button.h\"\n#include \"NsGui/Image.h\"\n#include \"GUI/InventorySlot.h\"\n#include \"NsGui/TextBlock.h\"\n#include \"GUI/Menus/Menus.h\"\n#include \"NsGui/StackPanel.h\"\n#include \"NsGui/UIElementCollection.h\"\n#include \"NsGui/WrapPanel.h\"\n#include \"ControlContext.h\"\n\nnamespace NoesisApp {\n    class Window;\n}\n\nclass GameView;\n\n// pretty hacky. try to remove later.\nGameView *currentGame = nullptr;\n\nclass GameViewViewModel : public NoesisApp::NotifyPropertyChangedBase {\npublic:\n    const char *getDebugInfo() const {\n        return mDebugInfo.c_str();\n    }\n\n    void setDebugInfo(std::string debugInfo) {\n        if (debugInfo != mDebugInfo) {\n            mDebugInfo = std::move(debugInfo);\n            OnPropertyChanged(\"DebugInfo\");\n        }\n    }\n\n    bool getGamePaused() const {\n        return mGamePaused;\n    }\n\n    void setGamePaused(bool gamePaused) {\n        if (gamePaused != mGamePaused) {\n            mGamePaused = gamePaused;\n            OnPropertyChanged(\"GamePaused\");\n        }\n    }\n\n    bool getBagOpen() const {\n        return mBagOpen;\n    }\n\n    void setBagOpen(bool bagOpen) {\n        if (bagOpen != mBagOpen) {\n            mBagOpen = bagOpen;\n            OnPropertyChanged(\"BagOpen\");\n        }\n    }\n\n    double getHP() const { return mHealth; }\n\n    double getHPMax() const { return mHealthMax; }\n\n    void notifyHPChanges(PlayerEntity *player) {\n        if (mHealth != player->getHealth() || mHealthMax != player->getMaxHealth()) {\n            mHealth = player->getHealth();\n            mHealthMax = player->getMaxHealth();\n\n            OnPropertyChanged(\"HP\");\n            OnPropertyChanged(\"HPMax\");\n        }\n    }\n\nprivate:\n    std::string mDebugInfo;\n    bool mGamePaused = false;\n    bool mBagOpen = false;\n    double mHealth, mHealthMax;\n\nNS_IMPLEMENT_INLINE_REFLECTION(GameViewViewModel, NotifyPropertyChangedBase) {\n        NsProp(\"DebugInfo\", &GameViewViewModel::getDebugInfo);\n        NsProp(\"GamePaused\", &GameViewViewModel::getGamePaused);\n        NsProp(\"BagOpen\", &GameViewViewModel::getBagOpen);\n        NsProp(\"HP\", &GameViewViewModel::getHP);\n        NsProp(\"HPMax\", &GameViewViewModel::getHPMax);\n    }\n};\n\nclass GameView : public virtual GUI::Scene, public Game {\nprivate:\n    ControlContext mControls{MainWindow};\n    GUI::FpsCounter mUpsCounter;\n    InventorySlot *mHotBar[10];\n    InventorySlot *mInventory[4][10];\n    Noesis::Ptr<GameViewViewModel> mViewModel;\n    std::thread mUpdateThread;\n    Frustum mFrustum;\n    WorldRenderer::ChunksRenderer& mChunksRenderer = WorldRenderer::ChunksRenderer::Default();\n\n    struct ItemMoveContext {\n        int row, col;\n        int quantity;\n    };\n    std::optional<ItemMoveContext> mInventoryMoveFrom;\n\npublic:\n    GameView() : Scene(\"InGame.xaml\", false), mViewModel(Noesis::MakePtr<GameViewViewModel>()) {}\n\n    void gameThread() {\n        //Wait until start...\n        MutexLock(Mutex);\n        while (!updateThreadRun) {\n            MutexUnlock(Mutex);\n            SleepMs(1);\n            MutexLock(Mutex);\n        }\n        MutexUnlock(Mutex);\n\n        //Thread start\n        MutexLock(Mutex);\n        lastUpdate = timer();\n\n        while (updateThreadRun) {\n            MutexUnlock(Mutex);\n            std::this_thread::yield();\n            MutexLock(Mutex);\n\n            while (updateThreadPaused) {\n                MutexUnlock(Mutex);\n                std::this_thread::yield();\n                MutexLock(Mutex);\n                lastUpdate = timer();\n            }\n\n            FirstUpdateThisFrame = true;\n            double currentTime = timer();\n            if (currentTime - lastUpdate >= 5.0) lastUpdate = currentTime;\n\n            while (currentTime - lastUpdate >= 1.0 / MaxUpdateFPS && mUpsCounter.getFPS() < 60) {\n                lastUpdate += 1.0 / MaxUpdateFPS;\n                mUpsCounter.frame();\n                updateGame();\n                FirstUpdateThisFrame = false;\n            }\n\n            mUpsCounter.check();\n        }\n        MutexUnlock(Mutex);\n    }\n\n    kls::coroutine::ValueAsync<void> gameRender() {\n        //画场景\n        const auto currentTime = timer();\n\n        const auto camera = mPlayer->renderUpdate(\n                mControls,\n                mBagOpened || mViewModel->getGamePaused(),\n                mControlsForUpdate.Current.Time\n        );\n\n        const double xpos = camera.position.X, ypos = camera.position.Y, zpos = camera.position.Z;\n\n        if (mPlayer->isRunning()) {\n            if (FOVyExt < 9.8) {\n                FOVyExt =\n                        10.0f - (10.0f - FOVyExt) * static_cast<float>(pow(0.8, (currentTime - SpeedupAnimTimer) * 30));\n                SpeedupAnimTimer = currentTime;\n            } else FOVyExt = 10.0;\n        } else {\n            if (FOVyExt > 0.2) {\n                FOVyExt *= static_cast<float>(pow(0.8, (currentTime - SpeedupAnimTimer) * 30));\n                SpeedupAnimTimer = currentTime;\n            } else FOVyExt = 0.0;\n        }\n        SpeedupAnimTimer = currentTime;\n\n        //更新区块VBO\n        mFrustum.LoadIdentity();\n        mFrustum.SetPerspective(FOVyNormal + FOVyExt, static_cast<float>(windowwidth) / windowheight, 0.05f,\n                                viewdistance * 16.0f);\n        mFrustum.MultRotate(static_cast<float>(camera.lookUpDown), 1, 0, 0);\n        mFrustum.MultRotate(360.0f - static_cast<float>(camera.heading), 0, 1, 0);\n        mFrustum.update();\n        mChunksRenderer.FrustumUpdate({xpos, ypos, zpos}, mFrustum);\n        co_await mChunksRenderer.Update(\n                Int3{\n                        RoundInt(mPlayer->getPosition().X),\n                        RoundInt(mPlayer->getPosition().Y),\n                        RoundInt(mPlayer->getPosition().Z)\n                }\n        );\n        MutexUnlock(Mutex);\n\n        glFlush();\n\n        glDepthFunc(GL_LEQUAL);\n        glEnable(GL_CULL_FACE);\n        glCullFace(GL_BACK);\n\n        //daylight = clamp((1.0 - cos((double)gametime / gameTimeMax * 2.0 * M_PI) * 2.0) / 2.0, 0.05, 1.0);\n        //Renderer::sunlightXrot = 90 * daylight;\n        if (Renderer::AdvancedRender) {\n            //Build shadow map\n            if (!DebugShadow) ShadowMaps::BuildShadowMap(mChunksRenderer, xpos, ypos, zpos, currentTime);\n            else ShadowMaps::RenderShadowMap(mChunksRenderer, xpos, ypos, zpos, currentTime);\n        }\n        glClearColor(skycolorR, skycolorG, skycolorB, 1.0);\n        if (!DebugShadow) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        glEnable(GL_TEXTURE_2D);\n\n        MutexLock(Mutex);\n        glMatrixMode(GL_PROJECTION);\n        glLoadIdentity();\n        glMultMatrixf(mFrustum.getProjMatrix());\n        glMatrixMode(GL_MODELVIEW);\n        auto playerChunk = mPlayer->getChunkPosition();\n        MutexUnlock(Mutex);\n\n        auto frameRenderer = mChunksRenderer.List(playerChunk, viewdistance); // currentTime\n        glBindTexture(GL_TEXTURE_2D, BlockTextures);\n\n        // 渲染层1\n        glLoadIdentity();\n        glRotated(camera.lookUpDown, 1, 0, 0);\n        glRotated(360.0 - camera.heading, 0, 1, 0);\n        glDisable(GL_BLEND);\n        Renderer::EnableShaders();\n        if (!DebugShadow) frameRenderer.Render(xpos, ypos, zpos, 0);\n        Renderer::DisableShaders();\n        glEnable(GL_BLEND);\n\n        MutexLock(Mutex);\n\n        if (mBlockDestructionProgress > 0.0) {\n            auto breakingBlock = mCurrentSelection->second;\n            glTranslated(breakingBlock.X - xpos, breakingBlock.Y - ypos, breakingBlock.Z - zpos);\n            renderDestroy(mBlockDestructionProgress, 0, 0, 0);\n            glTranslated(-breakingBlock.X + xpos, -breakingBlock.Y + ypos, -breakingBlock.Z + zpos);\n        }\n        glBindTexture(GL_TEXTURE_2D, BlockTextures);\n        Particles::renderall(xpos, ypos, zpos);\n\n        RenderEntities();\n\n        glDisable(GL_TEXTURE_2D);\n        if (mShouldRenderGUI && mCurrentSelection) {\n            auto selectingBlock = mCurrentSelection->second;\n            glTranslated(selectingBlock.X - xpos, selectingBlock.Y - ypos, selectingBlock.Z - zpos);\n            drawBorder(0, 0, 0);\n            glTranslated(-selectingBlock.X + xpos, -selectingBlock.Y + ypos, -selectingBlock.Z + zpos);\n        }\n\n        MutexUnlock(Mutex);\n\n        // 渲染层2&3\n        glLoadIdentity();\n        glRotated(camera.lookUpDown, 1, 0, 0);\n        glRotated(360.0 - camera.heading, 0, 1, 0);\n        glEnable(GL_TEXTURE_2D);\n        glEnable(GL_CULL_FACE);\n        glBindTexture(GL_TEXTURE_2D, BlockTextures);\n        Renderer::EnableShaders();\n        if (!DebugShadow) frameRenderer.Render(xpos, ypos, zpos, 1);\n        glDisable(GL_CULL_FACE);\n        if (!DebugShadow) frameRenderer.Render(xpos, ypos, zpos, 2);\n        Renderer::DisableShaders();\n\n        glLoadIdentity();\n        glRotated(camera.lookUpDown, 1, 0, 0);\n        glRotated(360.0 - camera.heading, 0, 1, 0);\n        glTranslated(-xpos, -ypos, -zpos);\n\n        MutexLock(Mutex);\n\n        glEnable(GL_CULL_FACE);\n        glEnable(GL_TEXTURE_2D);\n\n        //Time_renderscene = timer() - Time_renderscene;\n        //Time_renderGUI_ = timer();\n\n        glMatrixMode(GL_PROJECTION);\n        glLoadIdentity();\n        glOrtho(0, windowwidth, windowheight, 0, -1.0, 1.0);\n        glMatrixMode(GL_MODELVIEW);\n        glLoadIdentity();\n\n        if (World::GetBlock({RoundInt(xpos), RoundInt(ypos), RoundInt(zpos)}) == Blocks::WATER) {\n            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);\n            glBindTexture(GL_TEXTURE_2D, BlockTextures);\n            const auto tcX = Textures::getTexcoordX(Blocks::WATER, 1);\n            const auto tcY = Textures::getTexcoordY(Blocks::WATER, 1);\n            glBegin(GL_QUADS);\n            glTexCoord2d(tcX, tcY + 1 / 8.0);\n            glVertex2i(0, 0);\n            glTexCoord2d(tcX, tcY);\n            glVertex2i(0, windowheight);\n            glTexCoord2d(tcX + 1 / 8.0, tcY);\n            glVertex2i(windowwidth, windowheight);\n            glTexCoord2d(tcX + 1 / 8.0, tcY + 1 / 8.0);\n            glVertex2i(windowwidth, 0);\n            glEnd();\n        }\n\n        glDisable(GL_TEXTURE_2D);\n        if (currentTime - screenshotAnimTimer <= 1.0 && !shouldGetScreenshot) {\n            const auto col = 1.0f - static_cast<float>(currentTime - screenshotAnimTimer);\n            glColor4f(1.0f, 1.0f, 1.0f, col);\n            glBegin(GL_QUADS);\n            glVertex2i(0, 0);\n            glVertex2i(0, windowheight);\n            glVertex2i(windowwidth, windowheight);\n            glVertex2i(windowwidth, 0);\n            glEnd();\n        }\n        glEnable(GL_TEXTURE_2D);\n\n        if (shouldGetScreenshot) {\n            shouldGetScreenshot = false;\n            screenshotAnimTimer = currentTime;\n            auto t = time(nullptr);\n            char tmp[64];\n            const auto timeinfo = localtime(&t);\n            strftime(tmp, sizeof(tmp), \"%Y年%m月%d日%H时%M分%S秒\", timeinfo);\n            std::stringstream ss;\n            ss << \"Screenshots/\" << tmp << \".bmp\";\n            saveScreenshot(0, 0, windowwidth, windowheight, ss.str());\n        }\n        if (shouldGetThumbnail) {\n            shouldGetThumbnail = false;\n            createThumbnail();\n        }\n    }\n\n    void RenderEntities() {\n        for (auto &entity: mEntities) entity->render();\n    }\n\n    kls::coroutine::ValueAsync<void> onRender() override {\n        MutexLock(Mutex);\n        co_await gameRender();\n        MutexUnlock(Mutex);\n    }\n\n    static void drawBorder(int x, int y, int z) {\n        //绘制选择边框\n        static auto eps = 0.002f; //实际上这个边框应该比方块大一些，否则很难看\n        glEnable(GL_LINE_SMOOTH);\n        glLineWidth(1);\n        glColor3f(0.2f, 0.2f, 0.2f);\n        glBegin(GL_LINES);\n        // Left Face\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        // Front Face\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        // Right Face\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        // Back Face\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glEnd();\n        glDisable(GL_LINE_SMOOTH);\n    }\n\n    void debugInfo() const {\n        std::stringstream ss;\n\n        if (DebugMode) {\n            ss << \"NEWorld v\" << VERSION << \" [OpenGL \" << GLVersionMajor << \".\" << GLVersionMinor << \" \"\n               << GLVersionRev << \"]\" << std::endl\n               << \"Fps:\" << mFPS.getFPS() << \" Ups:\" << mUpsCounter.getFPS() << std::endl\n               << \"Debug Mode:\" << boolstr(DebugMode) << std::endl;\n            if (Renderer::AdvancedRender) {\n                ss << \"Shadow View:\" << boolstr(DebugShadow) << std::endl;\n            }\n            ss << \"X: \" << mPlayer->getPosition().X << \" Y: \" << mPlayer->getPosition().Y << \" Z: \"\n               << mPlayer->getPosition().Z << std::endl\n               << \"Direction:\" << mPlayer->getHeading() << \" Head:\" << mPlayer->getLookUpDown() << std::endl\n               << \"Jump speed:\" << mPlayer->getCurrentJumpSpeed() << std::endl\n               << \"Stats:\";\n            if (mPlayer->isFlying()) ss << \" Flying\";\n            if (mPlayer->isOnGround()) ss << \" On_ground\";\n            if (mPlayer->isNearWall()) ss << \" Near_wall\";\n            if (mPlayer->isCrossWall()) ss << \" Cross_Wall\";\n            ss << std::endl;\n            auto h = gametime / (30 * 60);\n            auto m = gametime % (30 * 60) / 30;\n            auto s = gametime % 30 * 2;\n            ss << \"Time: \"\n               << (h < 10 ? \"0\" : \"\") << h << \":\"\n               << (m < 10 ? \"0\" : \"\") << m << \":\"\n               << (s < 10 ? \"0\" : \"\") << s\n               << \" (\" << gametime << \"/\" << gameTimeMax << \")\" << std::endl;\n            ss << \"load:\" << World::chunks.size() << \" unload:\" << World::unloadedChunks\n               << \" render:\" << WorldRenderer::chunkBuildRenders\n               << \" update:\" << World::updatedChunks;\n\n#ifdef NEWORLD_DEBUG_PERFORMANCE_REC\n            ss << c_getChunkPtrFromCPA << \" CPA requests\" << std::endl;\n            ss << c_getChunkPtrFromSearch << \" search requests\" << std::endl;\n            ss << c_getHeightFromHMap << \" heightmap requests\" << std::endl;\n            ss << c_getHeightFromWorldGen << \" worldgen requests\" << std::endl;\n#endif\n        } else {\n            ss << \"v\" << VERSION << \"  Fps:\" << mFPS.getFPS();\n        }\n        mViewModel->setDebugInfo(ss.str());\n    }\n\n    static void renderDestroy(float level, int x, int y, int z) {\n        static auto eps = 0.002f;\n        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);\n\n        if (level < 100.0) glBindTexture(GL_TEXTURE_2D, DestroyImage[int(level / 10) + 1]);\n        else glBindTexture(GL_TEXTURE_2D, DestroyImage[10]);\n\n        glBegin(GL_QUADS);\n        glTexCoord2f(0.0f, 0.0f);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(1.0f, 0.0f);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(1.0f, 1.0f);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(0.0f, 1.0f);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n\n        glTexCoord2f(1.0f, 0.0f);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(1.0f, 1.0f);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(0.0f, 1.0f);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(0.0f, 0.0f);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n\n        glTexCoord2f(1.0f, 0.0f);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(1.0f, 1.0f);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(0.0f, 1.0f);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(0.0f, 0.0f);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n\n        glTexCoord2f(0.0f, 0.0f);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(1.0f, 0.0f);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(1.0f, 1.0f);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(0.0f, 1.0f);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n\n        glTexCoord2f(0.0f, 1.0f);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(0.0f, 0.0f);\n        glVertex3f(-(0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(1.0f, 0.0f);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(1.0f, 1.0f);\n        glVertex3f((0.5f + eps) + x, (0.5f + eps) + y, -(0.5f + eps) + z);\n\n        glTexCoord2f(1.0f, 1.0f);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(0.0f, 1.0f);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, -(0.5f + eps) + z);\n        glTexCoord2f(0.0f, 0.0f);\n        glVertex3f((0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glTexCoord2f(1.0f, 0.0f);\n        glVertex3f(-(0.5f + eps) + x, -(0.5f + eps) + y, (0.5f + eps) + z);\n        glEnd();\n    }\n\n    static void saveScreenshot(int x, int y, int w, int h, std::string filename) {\n        Textures::TEXTURE_RGB scrBuffer;\n        auto bufw = w, bufh = h;\n        while (bufw % 4 != 0) { bufw += 1; }\n        while (bufh % 4 != 0) { bufh += 1; }\n        scrBuffer.sizeX = bufw;\n        scrBuffer.sizeY = bufh;\n        scrBuffer.buffer = std::unique_ptr<ubyte[]>(new ubyte[bufw * bufh * 3]);\n        glReadPixels(x, y, bufw, bufh, GL_RGB, GL_UNSIGNED_BYTE, scrBuffer.buffer.get());\n        Textures::SaveRGBImage(std::move(filename), scrBuffer);\n    }\n\n    void createThumbnail() {\n        std::stringstream ss;\n        ss << \"Worlds/\" << World::worldname << \"/Thumbnail.bmp\";\n        saveScreenshot(0, 0, windowwidth, windowheight, ss.str());\n    }\n\n    void onViewBinding() override {\n        mRoot->SetDataContext(mViewModel);\n        mRoot->FindName<Noesis::Button>(\"Resume\")->Click() += [this](Noesis::BaseComponent *,\n                                                                     const Noesis::RoutedEventArgs &) {\n            mViewModel->setGamePaused(false);\n            updateThreadPaused = false;\n            glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n        };\n        mRoot->FindName<Noesis::Button>(\"Exit\")->Click() += [this](Noesis::BaseComponent *,\n                                                                   const Noesis::RoutedEventArgs &) {\n            mViewModel->setGamePaused(false);\n            updateThreadPaused = false;\n            requestLeave();\n            pushScene(Menus::startMenu());\n        };\n        auto hotbar = mRoot->FindName<Noesis::StackPanel>(\"Hotbar\");\n        for (auto &slot: mHotBar) {\n            hotbar->GetChildren()->Add(slot = new InventorySlot());\n        }\n        auto inventory = mRoot->FindName<Noesis::WrapPanel>(\"Inventory\");\n        for (int row = 0; row < 4; ++row) {\n            for (int i = 0; i < 10; ++i) {\n                inventory->GetChildren()->Add(mInventory[row][i] = new InventorySlot());\n\n                mInventory[row][i]->PreviewMouseUp() += [this, row, i](Noesis::BaseComponent *,\n                                                                       const Noesis::MouseButtonEventArgs &args) {\n                    auto playerInventory = mPlayer->getInventory();\n                    auto &thisAmount = playerInventory[row][i].amount;\n                    auto &thisItem = playerInventory[row][i].item;\n                    bool rightClick = args.changedButton == Noesis::MouseButton_Right;\n                    if (mInventoryMoveFrom.has_value()) { // if has already selected one\n                        auto &from = mInventoryMoveFrom.value();\n                        auto &fromAmount = playerInventory[from.row][from.col].amount;\n                        auto &fromItem = playerInventory[from.row][from.col].item;\n                        if (thisItem != fromItem && thisItem != Blocks::ENV) { // different item - swap\n                            std::swap(fromAmount, thisAmount);\n                            std::swap(fromItem, thisItem);\n                            from.quantity = 0;\n                        } else { // same item or empty - stack\n                            const auto moveAmount = rightClick ? 1 : std::min(255 - thisAmount,\n                                                                              std::min(from.quantity, int(fromAmount)));\n                            thisItem = fromItem;\n                            fromAmount -= moveAmount;\n                            thisAmount += moveAmount;\n                            from.quantity -= moveAmount;\n                        }\n                        if (fromAmount == 0) fromItem = Blocks::ENV;\n                        if (from.quantity == 0) mInventoryMoveFrom.reset(); // done transfer\n                    } else if (thisAmount != 0) { // if not selected and this one is selectable\n                        mInventoryMoveFrom = ItemMoveContext{\n                                row,\n                                i,\n                                args.changedButton == Noesis::MouseButton_Left ? thisAmount : std::max(thisAmount / 2,\n                                                                                                       1)\n                        };\n                    }\n                };\n            }\n        }\n    }\n\n    void onLoad() override {\n        glEnable(GL_LINE_SMOOTH);\n        glEnable(GL_TEXTURE_2D);\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        glfwSwapBuffers(MainWindow);\n        glfwPollEvents();\n\n        Mutex = MutexCreate();\n        //MutexLock(Mutex);\n        currentGame = this;\n        mUpdateThread = std::thread([this] { gameThread(); });\n        //初始化游戏状态\n        InitGame();\n        infostream << \"Init world...\";\n        World::Init();\n        registerCommands();\n\n        mShouldRenderGUI = true;\n        glDepthFunc(GL_LEQUAL);\n        glEnable(GL_CULL_FACE);\n        setupNormalFog();\n\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        glfwSwapBuffers(MainWindow);\n        glfwPollEvents();\n        infostream << \"Game start!\";\n\n        //这才是游戏开始!\n        glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n        infostream << \"Main loop started\";\n        updateThreadRun = true;\n        lastUpdate = timer();\n    }\n\n    void onUpdate() override {\n        mControls.Update();\n\n        debugInfo();\n        auto inventory = mPlayer->getInventory();\n        for (int i = 0; i < 10; ++i) {\n            mHotBar[i]->setItemStack(inventory[3][i]);\n            mHotBar[i]->setSelected(i == mPlayer->getCurrentHotbarSelection());\n        }\n        for (int row = 0; row < 4; ++row) {\n            for (int i = 0; i < 10; ++i) {\n                mInventory[row][i]->setItemStack(inventory[row][i]);\n                mInventory[row][i]->setSelected(mInventoryMoveFrom.has_value() &&\n                                                row == mInventoryMoveFrom.value().row &&\n                                                i == mInventoryMoveFrom.value().col);\n            }\n        }\n        mViewModel->notifyHPChanges(mPlayer);\n        mViewModel->setBagOpen(mBagOpened);\n\n        static bool wasBagOpen = false;\n        if (mViewModel->getBagOpen()) {\n            if (!wasBagOpen) {\n                wasBagOpen = true;\n                glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n            }\n        } else if (wasBagOpen) {\n            wasBagOpen = false;\n            glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n        }\n\n        if (glfwGetKey(MainWindow, GLFW_KEY_ESCAPE) == 1) {\n            mViewModel->setGamePaused(true);\n            updateThreadPaused = true;\n            glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n            createThumbnail();\n        }\n    }\n\n    ~GameView() override {\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        glfwSwapBuffers(MainWindow);\n        glfwPollEvents();\n        infostream << \"Terminate threads\";\n        updateThreadRun = false;\n        //MutexUnlock(Mutex);\n        mUpdateThread.join();\n        currentGame = nullptr;\n        MutexDestroy(Mutex);\n        saveGame();\n        World::destroyAllChunks();\n        commands.clear();\n    }\n};\n\nvoid pushGameView() { GUI::pushScene(std::make_unique<GameView>()); }\n"
  },
  {
    "path": "NEWorld.Game/GameView.h",
    "content": "#pragma once\n\nvoid pushGameView();\n"
  },
  {
    "path": "NEWorld.Game/Globalization.cpp",
    "content": "#include <fstream>\n#include \"Globalization.h\"\n#include <map>\n#include <nlohmann/json.hpp>\n#include <iostream>\n\nnamespace Globalization {\n\n    int count;\n    std::string Cur_Lang = \"zh_CN\", Cur_Symbol = \"\", Cur_Name = \"\";\n    std::map<int, Line> Lines;\n    std::map<std::string, int> keys;\n\n    bool LoadLang(const std::string& lang) {\n        std::ifstream f(\"./Assets/Translations/\"+lang+\".lang\");\n        if (f.bad()) {\n            return false;\n        }\n        Lines.clear();\n        Cur_Lang = lang;\n        getline(f, Cur_Symbol);\n        getline(f, Cur_Name);\n        for (auto i = 0; i<count; i++) {\n            getline(f, Lines[i].str);\n        }\n        f.close();\n        return true;\n    }\n\n    bool Load() {\n        std::ifstream f(\"./Assets/Translations/Keys.json\");\n        if (f.good()) {\n            try {\n                nlohmann::json file{};\n                f >> file;\n                auto i = 0;\n                for (auto& v: file) { keys[v] = i++; }\n                count = i;\n            }\n            catch (...) {\n                return false;\n            }\n            return LoadLang(Cur_Lang);\n        }\n        return false;\n    }\n\n    std::string GetStrbyid(int id) {\n        return Lines[id].str;\n    }\n\n    std::string GetStrbyKey(std::string key) {\n        return Lines[keys[key]].str;\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Globalization.h",
    "content": "#pragma once\n\n#include <string>\n\nnamespace Globalization {\n    struct Line {\n        std::string str;\n        int id;\n    };\n\n    extern std::string Cur_Lang;\n\n    bool LoadLang(const std::string& lang);\n\n    bool Load();\n\n    std::string GetStrbyid(int id);\n\n    std::string GetStrbyKey(std::string key);\n}"
  },
  {
    "path": "NEWorld.Game/Items.cpp",
    "content": "#include \"Items.h\"\n#include \"Textures.h\"\n\nItemInfo itemsinfo[] = {STICK, APPLE};\n\nvoid loadItemsTextures() {\n    itemsinfo[BuiltInItems::STICK - theFirstItem].texture =\n            Textures::LoadRGBTexture(\"./Assets/Textures/Items/stick.bmp\");\n    itemsinfo[BuiltInItems::APPLE - theFirstItem].texture =\n            Textures::LoadRGBTexture(\"./Assets/Textures/Items/apple.bmp\");\n\n}\n"
  },
  {
    "path": "NEWorld.Game/Items.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n\nclass ItemInfo {\npublic:\n    ItemInfo(Item itemid, TextureID itemtexture = 0) : id(itemid), texture(itemtexture) {}\n\n    Item id;\n    TextureID texture;\n};\n\nenum BuiltInItems {\n    STICK = 30000, APPLE\n};\n\nextern ItemInfo itemsinfo[];\nconst Item theFirstItem = STICK;\n\nvoid loadItemsTextures();\n\ninline bool isBlock(Item i) {\n    return i < theFirstItem;\n}\n\ninline TextureID getItemTexture(Item i) {\n    if (isBlock(i)) return BlockTextures;\n    else return itemsinfo[i - theFirstItem].texture;\n}"
  },
  {
    "path": "NEWorld.Game/Menus.h",
    "content": "#pragma once\n\n#include \"Globalization.h\"\n\nusing Globalization::GetStrbyKey;\nnamespace Menus {\n    void options();\n\n    void Renderoptions();\n\n    void Shaderoptions();\n\n    void GUIoptions();\n\n    void worldmenu();\n\n    void createworldmenu();\n\n    void multiplayermenu();\n\n    void languagemenu();\n\n    void Information();\n\n    void Soundmenu();\n}"
  },
  {
    "path": "NEWorld.Game/NEWorld.cpp",
    "content": "﻿//==============================   Initialize   ================================//\n//==============================初始化(包括闪屏)================================//\n\n#include \"Definitions.h\"\n#include \"Renderer/Renderer.h\"\n#include \"Universe/World/World.h\"\n#include \"Globalization.h\"\n#include \"Setup.h\"\n#include \"GUI/GUI.h\"\n#include \"AudioSystem.h\"\n#include <iostream>\n#include \"System/MessageBus.h\"\n#include \"System/FileSystem.h\"\n#include \"GUI/Menus/Menus.h\"\n#include \"Common/Logger.h\"\n#include \"GUI/Noesis.h\"\n#include \"GameSettings.h\"\nvoid loadOptions();\n\nvoid saveOptions();\n\n//==============================  Main Program  ================================//\n//==============================     主程序     ================================//\n\nvoid ApplicationBeforeLaunch() {\n    setlocale(LC_ALL, \"zh_CN.UTF-8\");\n    GameSettings::getInstance().loadOptions();\n    Globalization::Load();\n    NEWorld::filesystem::create_directories(\"./Configs\");\n    NEWorld::filesystem::create_directories(\"./Worlds\");\n    NEWorld::filesystem::create_directories(\"./Screenshots\");\n    NEWorld::filesystem::create_directories(\"./Mods\");\n}\n\nvoid ApplicationAfterLaunch() {\n    loadTextures();\n    //初始化音频系统\n    AudioSystem::Init();\n}\n#include \"Universe/Entity/Entity.h\"\nint main() {\n    ApplicationBeforeLaunch();\n    windowwidth = DefaultWindowWidth;\n    windowheight = DefaultWindowHeight;\n    if (glfwInit() == 1) {\n        infostream << \"Initialize GLFW\";\n    } else {\n        infostream << \"GLFW initialization failed\";\n    }\n    createWindow();\n    setupScreen();\n\n    GUI::noesisSetup();\n\n    glDisable(GL_CULL_FACE);\n    //splashScreen();\n    ApplicationAfterLaunch();\n    glMatrixMode(GL_PROJECTION);\n    glLoadIdentity();\n    glMatrixMode(GL_MODELVIEW);\n    glLoadIdentity();\n\n    glDisable(GL_LINE_SMOOTH);\n    GUI::pushScene(Menus::startMenu());\n    GUI::appStart();\n    //结束程序，删了也没关系 ←_←（吐槽FB和glfw中）\n    //不对啊这不是FB！！！这是正宗的C++！！！！！！\n    //楼上的楼上在瞎说！！！别信他的！！！\n    //……所以你是不是应该说“吐槽C艹”中？——地鼠\n    GUI::noesisShutdown();\n    glfwTerminate();\n    //反初始化音频系统\n    AudioSystem::UnInit();\n    return 0;\n}\n\nvoid AppCleanUp() {\n    World::saveAllChunks();\n    World::destroyAllChunks();\n}\n"
  },
  {
    "path": "NEWorld.Game/Particles.cpp",
    "content": "#include \"Particles.h\"\n#include \"Universe/World/World.h\"\n#include \"Textures.h\"\n#include \"Renderer/BufferBuilder.h\"\n#include \"Renderer/GL/Pipeline.h\"\n\nnamespace Particles {\n    std::vector<Particle> ptcs;\n    int ptcsrendered;\n    double pxpos, pypos, pzpos;\n\n    Renderer::Pipeline &GetPipeline() {\n        static auto pipeline = []() {\n            using namespace Renderer;\n            constexpr int sof = sizeof(float);\n            PipelineBuilder builder{Topology::TriangleList};\n            builder.SetBinding(1, 7 * sof, 0);\n            builder.AddAttribute(DataType::Float32, 1, 1, 2, 0);\n            builder.AddAttribute(DataType::Float32, 1, 2, 2, 2 * sof);\n            builder.AddAttribute(DataType::Float32, 1, 3, 3, 4 * sof);\n            builder.SetShader(ShaderType::Vertex,\n                              CompileFile(ShaderType::Vertex, \"./Assets/Shaders/Particle.vsh\", {}));\n            builder.SetShader(ShaderType::Fragment,\n                              CompileFile(ShaderType::Fragment, \"./Assets/Shaders/Particle.fsh\", {}));\n            auto result = builder.Build();\n            result->SetUniform(0, 0);\n            result->BindIndexBuffer(GetDefaultQuadIndex(), IndexType::U32);\n            return result;\n        }();\n        return pipeline;\n    }\n\n    void update(Particle &ptc) {\n        const auto psz = ptc.psize;\n        ptc.hb.min.values[0] = ptc.xpos - psz;\n        ptc.hb.max.values[0] = ptc.xpos + psz;\n        ptc.hb.min.values[1] = ptc.ypos - psz;\n        ptc.hb.min.values[1] = ptc.ypos + psz;\n        ptc.hb.min.values[2] = ptc.zpos - psz;\n        ptc.hb.min.values[2] = ptc.zpos + psz;\n\n        double dx = ptc.xsp;\n        double dy = ptc.ysp;\n        double dz = ptc.zsp;\n\n        auto Hitboxes = World::getHitboxes(ptc.hb.extend(AABB::Move(ptc.hb, { dx, dy, dz })));\n        const int hitnum = Hitboxes.size();\n        for (auto i = 0; i < hitnum; i++) {\n            dy = AABB::MaxMove(ptc.hb, Hitboxes[i], dy, 1);\n        }\n        ptc.hb = AABB::Move(ptc.hb, { 0.0, dy, 0.0 });\n        for (auto i = 0; i < hitnum; i++) {\n            dx = AABB::MaxMove(ptc.hb, Hitboxes[i], dx,0);\n        }\n        ptc.hb = AABB::Move(ptc.hb, { dx, 0.0, 0.0 });\n        for (auto i = 0; i < hitnum; i++) {\n            dz = AABB::MaxMove(ptc.hb, Hitboxes[i], dz, 2);\n        }\n        ptc.hb = AABB::Move(ptc.hb, { 0.0, 0.0, dz });\n\n        ptc.xpos += dx;\n        ptc.ypos += dy;\n        ptc.zpos += dz;\n        if (dy != ptc.ysp) ptc.ysp = 0.0;\n        ptc.xsp *= 0.6f;\n        ptc.zsp *= 0.6f;\n        ptc.ysp -= 0.01f;\n        ptc.lasts -= 1;\n\n    }\n\n    void updateall() {\n        for (auto iter = ptcs.begin(); iter < ptcs.end();) {\n            if (!iter->exist) continue;\n            update(*iter);\n            if (iter->lasts <= 0) {\n                iter->exist = false;\n                iter = ptcs.erase(iter);\n            } else {\n                iter++;\n            }\n        }\n    }\n\n    void render(Renderer::BufferBuilder<> &builder, Particle &ptc) {\n        //if (!Frustum::aabbInFrustum(ptc.hb)) return;\n        ptcsrendered++;\n        const auto size =\n                static_cast<float>(BLOCKTEXTURE_UNITSIZE) / BLOCKTEXTURE_SIZE * (ptc.psize <= 1.0f ? ptc.psize : 1.0f);\n        const auto col = World::getbrightness(RoundInt(ptc.xpos), RoundInt(ptc.ypos), RoundInt(ptc.zpos)) /\n                         static_cast<float>(World::BRIGHTNESSMAX);\n        const auto col1 = col * 0.5f;\n        const auto col2 = col * 0.7f;\n        const auto tcx = ptc.tcX;\n        const auto tcy = ptc.tcY;\n        const auto psize = ptc.psize;\n        const auto palpha = (ptc.lasts < 30 ? ptc.lasts / 30.0 : 1.0);\n        const auto xpos = ptc.xpos - pxpos;\n        const auto ypos = ptc.ypos - pypos;\n        const auto zpos = ptc.zpos - pzpos;\n\n        builder.put<24>(\n                tcx, tcy, col1, palpha, xpos - psize, ypos - psize, zpos + psize,\n                tcx + size, tcy, col1, palpha, xpos + psize, ypos - psize, zpos + psize,\n                tcx + size, tcy + size, col1, palpha, xpos + psize, ypos + psize, zpos + psize,\n                tcx, tcy + size, col1, palpha, xpos - psize, ypos + psize, zpos + psize,\n\n                tcx, tcy, col1, palpha, xpos - psize, ypos + psize, zpos - psize,\n                tcx + size, tcy, col1, palpha, xpos + psize, ypos + psize, zpos - psize,\n                tcx + size, tcy + size, col1, palpha, xpos + psize, ypos - psize, zpos - psize,\n                tcx, tcy + size, col1, palpha, xpos - psize, ypos - psize, zpos - psize,\n\n                tcx, tcy, col, palpha, xpos + psize, ypos + psize, zpos - psize,\n                tcx + size, tcy, col, palpha, xpos - psize, ypos + psize, zpos - psize,\n                tcx + size, tcy + size, col, palpha, xpos - psize, ypos + psize, zpos + psize,\n                tcx, tcy + size, col, palpha, xpos + psize, ypos + psize, zpos + psize,\n\n                tcx, tcy, col, palpha, xpos - psize, ypos - psize, zpos - psize,\n                tcx + size, tcy, col, palpha, xpos + psize, ypos - psize, zpos - psize,\n                tcx + size, tcy + size, col, palpha, xpos + psize, ypos - psize, zpos + psize,\n                tcx, tcy + size, col, palpha, xpos - psize, ypos - psize, zpos + psize,\n\n                tcx, tcy, col2, palpha, xpos + psize, ypos + psize, zpos - psize,\n                tcx + size, tcy, col2, palpha, xpos + psize, ypos + psize, zpos + psize,\n                tcx + size, tcy + size, col2, palpha, xpos + psize, ypos - psize, zpos + psize,\n                tcx, tcy + size, col2, palpha, xpos + psize, ypos - psize, zpos - psize,\n\n                tcx, tcy, col2, palpha, xpos - psize, ypos - psize, zpos - psize,\n                tcx + size, tcy, col2, palpha, xpos - psize, ypos - psize, zpos + psize,\n                tcx + size, tcy + size, col2, palpha, xpos - psize, ypos + psize, zpos + psize,\n                tcx, tcy + size, col2, palpha, xpos - psize, ypos + psize, zpos - psize\n        );\n    }\n\n    void renderall(double xpos, double ypos, double zpos) {\n        pxpos = xpos;\n        pypos = ypos;\n        pzpos = zpos;\n        ptcsrendered = 0;\n        Renderer::BufferBuilder builder{};\n        for (auto &ptc: ptcs) {\n            if (!ptc.exist) continue;\n            render(builder, ptc);\n        }\n        VBOID vbo{0};\n        vtxCount vts{0};\n        builder.flush(vbo, vts);\n        if (vts != 0) {\n            GetPipeline()->Use();\n            GetPipeline()->BindVertexBuffer(1, vbo, 0);\n            GetPipeline()->DrawIndexed(vts + vts / 2, 0);\n            glDeleteBuffers(1, &vbo);\n            glBindVertexArray(0);\n        }\n    }\n\n    void throwParticle(Block pt, float x, float y, float z, float xs, float ys, float zs, float psz, int last) {\n        const auto tcX1 = static_cast<float>(Textures::getTexcoordX(pt, 2));\n        const auto tcY1 = static_cast<float>(Textures::getTexcoordY(pt, 2));\n        Particle ptc;\n        ptc.exist = true;\n        ptc.xpos = x;\n        ptc.ypos = y;\n        ptc.zpos = z;\n        ptc.xsp = xs;\n        ptc.ysp = ys;\n        ptc.zsp = zs;\n        ptc.psize = psz;\n        ptc.hb.min.values[0] = x - psz;\n        ptc.hb.max.values[0] = x + psz;\n        ptc.hb.min.values[1] = y - psz;\n        ptc.hb.max.values[1] = y + psz;\n        ptc.hb.min.values[2] = z - psz;\n        ptc.hb.max.values[2] = z + psz;\n        ptc.lasts = last;\n        ptc.tcX = tcX1 + static_cast<float>(rnd()) * (static_cast<float>(BLOCKTEXTURE_UNITSIZE) / BLOCKTEXTURE_SIZE) *\n                         (1.0f - psz);\n        ptc.tcY = tcY1 + static_cast<float>(rnd()) * (static_cast<float>(BLOCKTEXTURE_UNITSIZE) / BLOCKTEXTURE_SIZE) *\n                         (1.0f - psz);\n        ptcs.push_back(ptc);\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Particles.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n#include \"Universe/Entity/bvh.h\"\n\nnamespace Particles {\n    const int PARTICALE_MAX = 4096;\n    struct Particle {\n        bool exist = false;\n        double xpos, ypos, zpos;\n        float xsp, ysp, zsp, psize, tcX, tcY;\n        int lasts;\n        BoundingBox hb;\n    };\n    extern std::vector<Particle> ptcs;\n    extern int ptcsrendered;\n\n    void update(Particle &ptc);\n\n    void updateall();\n\n    void renderall(double xpos, double ypos, double zpos);\n\n    void throwParticle(Block pt, float x, float y, float z, float xs, float ys, float zs, float psz, int last);\n\n    inline void throwParticle(Block pt, Int3 pos) {\n        throwParticle(\n            pt,\n            float(pos.X + rnd() - 0.5f), float(pos.Y + rnd() - 0.2f),\n            float(pos.Z + rnd() - 0.5f),\n            float(rnd() * 0.2f - 0.1f), float(rnd() * 0.2f - 0.1f),\n            float(rnd() * 0.2f - 0.1f),\n            float(rnd() * 0.01f + 0.02f), int(rnd() * 30) + 30\n        );\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/BufferBuilder.h",
    "content": "#pragma once\n\n#include <array>\n#include <algorithm>\n#include \"Definitions.h\"\n#include <kls/temp/STL.h>\n#include <kls/coroutine/Async.h>\n#include <kls/coroutine/Operation.h>\n#include \"Dispatch.h\"\n\nnamespace Renderer {\n    enum class BufferType {\n        Vertex,\n        Index,\n        Indirect\n    };\n\n    template<class B = float>\n    class BufferBuilder {\n        static constexpr ptrdiff_t size = 1024;\n        static constexpr ptrdiff_t mask = size - 1;\n        using Array = std::array<B, size>;\n    public:\n        template<int count, class T = float, class... Elem>\n        void put(Elem... elem) noexcept {\n            std::initializer_list<B> v{static_cast<B>(std::forward<Elem>(elem))...};\n            const auto off = mTail & mask;\n            const auto remain = size - off;\n            if (off == 0) make_sector();\n            if (remain >= static_cast<ptrdiff_t>(v.size())) {\n                std::copy(v.begin(), v.end(), mSectors.back()->begin() + off);\n            } else {\n                std::copy(v.begin(), v.begin() + remain, mSectors.back()->begin() + off);\n                make_sector();\n                std::copy(v.begin() + remain, v.end(), mSectors.back()->begin());\n            }\n            mTail += static_cast<ptrdiff_t>(v.size());\n            mVts += count;\n        }\n\n        // TODO: this is a temporary method\n        kls::coroutine::ValueAsync<void> flushAsync(VBOID& buffer, vtxCount& vts) noexcept {\n            vts = static_cast<vtxCount>(mVts);\n            if (mVts != 0) {\n                if (buffer != 0) glDeleteBuffers(1, &buffer);\n                glCreateBuffers(1, &buffer);\n                const auto segmentSize = static_cast<GLsizeiptr>(mSectors.size() * size * sizeof(B));\n                glNamedBufferStorage(buffer, segmentSize, nullptr, GL_MAP_WRITE_BIT);\n                co_await CopyAndRelease(reinterpret_cast<B*>(glMapNamedBufferRange(\n                    buffer, 0, segmentSize,\n                    GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT\n                )));\n                glFlushMappedNamedBufferRange(buffer, 0, segmentSize);\n                glUnmapNamedBuffer(buffer);\n            }\n        }\n\n        // TODO: this is a temporary method\n        void flush(VBOID &buffer, vtxCount &vts) const noexcept {\n            vts = static_cast<vtxCount>(mVts);\n            if (mVts != 0) {\n                if (buffer != 0) glDeleteBuffers(1, &buffer);\n                glCreateBuffers(1, &buffer);\n                const auto segmentSize = static_cast<GLsizeiptr>(mSectors.size() * size * sizeof(B));\n                glNamedBufferStorage(buffer, segmentSize, nullptr, GL_MAP_WRITE_BIT);\n                auto target = reinterpret_cast<B *>(glMapNamedBufferRange(\n                        buffer, 0, segmentSize,\n                        GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT\n                ));\n                for (auto &s: mSectors) target = std::copy(s->begin(), s->end(), target);\n                glFlushMappedNamedBufferRange(buffer, 0, segmentSize);\n                glUnmapNamedBuffer(buffer);\n            }\n        }\n\n    private:\n        void make_sector() noexcept { mSectors.emplace_back(kls::temp::make_unique<Array>()); }\n\n        kls::coroutine::ValueAsync<void> CopyAndRelease(B* target) {\n            co_await kls::coroutine::SwitchTo(GetSessionDefault());\n            for (auto& s : mSectors) target = std::copy(s->begin(), s->end(), target);\n            mSectors.clear();\n        }\n\n        kls::temp::vector<kls::pmr::unique_ptr<Array>> mSectors{};\n        ptrdiff_t mTail{0u}, mVts{0u};\n    };\n}"
  },
  {
    "path": "NEWorld.Game/Renderer/GL/Objects.cpp",
    "content": ""
  },
  {
    "path": "NEWorld.Game/Renderer/GL/Objects.h",
    "content": "#pragma once\n\n#include \"stdinclude.h\"\n#include \"Temp/Vector.h\"\n#include \"Temp/Unordered.h\"\n\nnamespace Renderer {\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/GL/Pipeline.cpp",
    "content": "#include <fstream>\n#include \"Pipeline.h\"\n#include \"FunctionsKit.h\"\n#include <kls/temp/STL.h>\n#include <Common/Logger.h>\n#include \"Renderer/BufferBuilder.h\"\n\nnamespace {\n    auto CollapseDefines(const std::vector<std::string> &defines) noexcept {\n        kls::temp::set<std::string_view> result{};\n        for (const auto &s: defines) result.insert(s);\n        return result;\n    }\n\n    void PrintDebug(GLuint program) {\n        GLint uniform_count = 0;\n        glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniform_count);\n        if (uniform_count != 0) {\n            GLint max_name_len = 0;\n            GLsizei length = 0;\n            GLsizei count = 0;\n            GLenum type = GL_NONE;\n            glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_len);\n            auto uName = std::make_unique<char[]>(max_name_len);\n            for (GLint i = 0; i < uniform_count; ++i) {\n                glGetActiveUniform(program, i, max_name_len, &length, &count, &type, uName.get());\n                infostream << uName.get() << \":\"\n                          << glGetUniformLocation(program, uName.get()) << \" count:\" << count;\n            }\n        }\n    }\n\n    kls::temp::string Preprocess(std::string_view text, kls::temp::set<std::string_view> defines) {\n        kls::temp::istringstream input{kls::temp::string(text)};\n        kls::temp::stringstream output{};\n        kls::temp::istringstream line{};\n        kls::temp::string cur, var, macro;\n        while (!input.eof()) {\n            std::getline(input, cur);\n            std::string_view lineView{cur};\n            if (cur.empty()) continue;\n            if (lineView.starts_with( \"#\")) { //处理NEWorld预处理器标志\n                line.str(cur);\n                line >> macro;\n                if (macro == \"##NEWORLD_SHADER_DEFINES\") {\n                    line >> var >> macro;\n                    if (defines.find(var) != defines.end()) cur = \"#define \" + macro;\n                    else continue;\n                }\n            }\n            output << cur << '\\n';\n        }\n        return output.str();\n    }\n\n    GLenum MapShaderTypes(Renderer::ShaderType type) noexcept {\n        switch (type) {\n            case Renderer::ShaderType::Vertex:\n                return GL_VERTEX_SHADER;\n            case Renderer::ShaderType::Geometry:\n                return GL_GEOMETRY_SHADER;\n            case Renderer::ShaderType::Fragment:\n                return GL_FRAGMENT_SHADER;\n            case Renderer::ShaderType::Compute:\n                return GL_COMPUTE_SHADER;\n        }\n        return GL_INVALID_ENUM;\n    }\n\n    void CheckErrorShader(GLuint res, const std::string &eMsg) {\n        auto st = GL_TRUE;\n        glGetShaderiv(res, GL_COMPILE_STATUS, &st);\n        if (st == GL_FALSE) debugstream << eMsg; else return;\n        int logLength, charsWritten;\n        glGetShaderiv(res, GL_INFO_LOG_LENGTH, &logLength);\n        if (logLength > 1) {\n            const auto log = kls::temp::make_unique<GLchar[]>(logLength);\n            glGetShaderInfoLog(res, logLength, &charsWritten, log.get());\n            throw std::runtime_error(log.get());\n        }\n    }\n\n    void CheckErrorProgram(GLuint res, const std::string &eMsg) {\n        auto st = GL_TRUE;\n        glGetProgramiv(res, GL_LINK_STATUS, &st);\n        if (st == GL_FALSE) debugstream << eMsg; else return;\n        int logLength, charsWritten;\n        glGetProgramiv(res, GL_INFO_LOG_LENGTH, &logLength);\n        if (logLength > 1) {\n            const auto log = kls::temp::make_unique<GLchar[]>(logLength);\n            glGetProgramInfoLog(res, logLength, &charsWritten, log.get());\n            throw std::runtime_error(log.get());\n        }\n    }\n\n    Renderer::Internal::Object *ToFakePtr(GLuint hdc) noexcept {\n        return reinterpret_cast<Renderer::Internal::Object *>(static_cast<uintptr_t>(hdc));\n    }\n\n    GLuint FromFakePtr(Renderer::Internal::Object *ptr) noexcept {\n        return static_cast<GLuint>(reinterpret_cast<uintptr_t>(ptr));\n    }\n\n    using GLObject = Renderer::Internal::Object;\n\n    auto MakeProgram(const kls::temp::unordered_map<Renderer::ShaderType, Renderer::GLShader> &stages) {\n        auto program = glCreateProgram();\n        auto deleter = [](GLObject *ptr) noexcept {\n            if (ptr) {\n                glDeleteProgram(FromFakePtr(ptr));\n            }\n        };\n        auto result = std::unique_ptr<GLObject, decltype(deleter)>(ToFakePtr(program), deleter);\n        // TODO(implement checks)\n        for (auto&&[type, shader]: stages) {\n            glAttachShader(program, FromFakePtr(shader.get()));\n        }\n        glLinkProgram(program);\n        //PrintDebug(program);\n        CheckErrorProgram(program, \"Shader linking error!\");\n        return result;\n    }\n\n    GLenum MapDataType(Renderer::DataType type) {\n        switch (type) {\n            case Renderer::DataType::Int32:\n                return GL_INT;\n            case Renderer::DataType::Float16:\n                return GL_HALF_FLOAT;\n            case Renderer::DataType::Float32:\n                return GL_FLOAT;\n            case Renderer::DataType::Float64:\n                return GL_DOUBLE;\n        }\n        throw std::runtime_error(\"Unknown DataType Enum\");\n    }\n\n    auto MakeVAO(const kls::temp::vector<Renderer::Internal::AttributeSpec> &attributes) {\n        GLuint vao;\n        glCreateVertexArrays(1, &vao);\n        auto deleter = [](GLObject *ptr) noexcept {\n            if (ptr) {\n                auto vao = FromFakePtr(ptr);\n                glDeleteVertexArrays(1, &vao);\n            }\n        };\n        auto result = std::unique_ptr<GLObject, decltype(deleter)>(ToFakePtr(vao), deleter);\n        // TODO(implement checks)\n        for (auto&&[type, binding, location, width, offset]: attributes) {\n            glEnableVertexArrayAttrib(vao, location);\n            glVertexArrayAttribFormat(vao, location, width, MapDataType(type), GL_FALSE, offset);\n            glVertexArrayAttribBinding(vao, location, binding);\n        }\n        return result;\n    }\n\n    auto ZipBinds(GLuint vao, const kls::temp::unordered_map<int, std::pair<int, int>> &binds) {\n        std::vector<int> result{};\n        for (auto&&[bind, zip]: binds) {\n            if (result.size() <= bind) result.resize(bind + 1, 0);\n            result[bind] = zip.first;\n            glVertexArrayBindingDivisor(vao, bind, zip.second);\n        }\n        return result;\n    }\n\n    GLenum CastTopology(Renderer::Topology topology) {\n        switch (topology) {\n            case Renderer::Topology::PointList:\n                return GL_POINTS;\n            case Renderer::Topology::LineList:\n                return GL_LINES;\n            case Renderer::Topology::LineStrip:\n                return GL_LINE_STRIP;\n            case Renderer::Topology::TriangleList:\n                return GL_TRIANGLES;\n            case Renderer::Topology::TriangleStrip:\n                return GL_TRIANGLE_STRIP;\n            case Renderer::Topology::TriangleFan:\n                return GL_TRIANGLE_FAN;\n            case Renderer::Topology::Quad:\n                return GL_QUADS;\n        }\n        throw std::runtime_error(\"Unknown Topology\");\n    }\n\n    GLenum CastIndexType(Renderer::IndexType index) {\n        switch (index) {\n            case Renderer::IndexType::U8:\n                return GL_UNSIGNED_BYTE;\n            case Renderer::IndexType::U16:\n                return GL_UNSIGNED_SHORT;\n            case Renderer::IndexType::U32:\n                return GL_UNSIGNED_INT;\n        }\n        throw std::runtime_error(\"Unknown IndexType\");\n    }\n\n    class PipelineOGL : public Renderer::IPipeline {\n    public:\n        PipelineOGL(GLenum mode, std::vector<int> strides, GLuint vao, GLuint program) noexcept:\n                mMode(mode), mStrides(std::move(strides)), mVAO(vao), mProgram(program) {}\n\n        ~PipelineOGL() noexcept {\n            glDeleteProgram(mProgram);\n            glDeleteVertexArrays(1, &mVAO);\n        }\n\n        void Use() override {\n            glUseProgram(mProgram);\n            glBindVertexArray(mVAO);\n        }\n\n        void BindVertexBuffer(int bind, GLuint buffer, int offset) override {\n            glVertexArrayVertexBuffer(mVAO, bind, buffer, offset, mStrides[bind]);\n        }\n\n        void BindIndexBuffer(GLuint buffer, Renderer::IndexType type) override {\n            mElement = CastIndexType(type);\n            glVertexArrayElementBuffer(mVAO, buffer);\n        }\n\n        void Draw(int count, int first, int instance) override {\n            glDrawArraysInstancedBaseInstance(mMode, first, count, instance, 0);\n        }\n\n        void DrawIndexed(int count, int first, int instance) override {\n            intptr_t offset = first;\n            if (mElement == GL_UNSIGNED_SHORT) offset *= 2;\n            if (mElement == GL_UNSIGNED_INT) offset *= 4;\n            glDrawElementsInstancedBaseVertexBaseInstance(mMode, count, mElement, nullptr, instance, 0, 0);\n        }\n\n        void SetUniform(GLint loc, float value) override {\n            glProgramUniform1f(mProgram, loc, value);\n        }\n\n        void SetUniform(GLint loc, int value) override {\n            glProgramUniform1i(mProgram, loc, value);\n        }\n\n        void SetUniform(GLint loc, float v0, float v1, float v2, float v3) override {\n            glProgramUniform4f(mProgram, loc, v0, v1, v2, v3);\n        }\n\n        void SetUniform(GLint loc, float *value) override {\n            glProgramUniformMatrix4fv(mProgram, loc, 1, GL_FALSE, value);\n        }\n\n    private:\n        GLenum mMode;\n        GLuint mVAO;\n        GLuint mProgram;\n        GLenum mElement{};\n        std::vector<int> mStrides;\n    };\n\n    kls::temp::string LoadFile(const std::string &path) {\n        auto ss = kls::temp::ostringstream{};\n        std::ifstream input_file(path);\n        if (!input_file.is_open()) {\n            throw std::runtime_error(\"No such file:\" + path);\n        }\n        ss << input_file.rdbuf();\n        return ss.str();\n    }\n}\n\nnamespace Renderer {\n    GLShader Compile(ShaderType type, std::string_view program, const std::vector<std::string> &defines) {\n        const auto preprocessed = Preprocess(program, CollapseDefines(defines));\n        //创建shader\n        const auto mode = MapShaderTypes(type);\n        if (mode == GL_INVALID_ENUM) throw std::runtime_error(\"Bad Shader Type Enum\");\n        const auto res = glCreateShader(mode);\n        const auto dataP = preprocessed.c_str();\n        const auto dataL = static_cast<int>(preprocessed.size());\n        glShaderSource(res, 1, reinterpret_cast<const GLchar *const *>(&dataP), &dataL);\n        glCompileShader(res);\n        CheckErrorShader(res, \"Shader compilation error! Shader: \" + std::string(program));\n        return {ToFakePtr(res), [](auto ptr) noexcept { glDeleteShader(FromFakePtr(ptr)); }};\n    }\n\n    GLShader CompileFile(ShaderType type, const std::string &path, const std::vector<std::string> &defines) {\n        const auto source = LoadFile(path);\n        return Compile(type, source, defines);\n    }\n\n    Pipeline PipelineBuilder::Build() {\n        auto program = MakeProgram(mStages);\n        auto vao = MakeVAO(mSpecs);\n        auto strides = ZipBinds(FromFakePtr(vao.get()), mBindings);\n        auto object = std::make_shared<PipelineOGL>(\n                CastTopology(mTopology), std::move(strides),\n                FromFakePtr(vao.release()), FromFakePtr(program.release())\n        );\n        return std::static_pointer_cast<IPipeline>(std::move(object));\n    }\n\n    GLuint GetDefaultQuadIndex() {\n        static auto buffer = []() {\n            BufferBuilder<uint32_t> builder{};\n            for (auto i = 0; i < 262144; i += 4) builder.put<1>(i, i + 1, i + 2, i, i + 2, i + 3);\n            GLuint ibo{0};\n            vtxCount count{0};\n            builder.flush(ibo, count);\n            return ibo;\n        }();\n        return buffer;\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/GL/Pipeline.h",
    "content": "#pragma once\n\n#include <string_view>\n#include \"stdinclude.h\"\n#include <kls/temp/STL.h>\n\nnamespace Renderer {\n    enum class DataType {\n        Int32, Float16, Float32, Float64\n    };\n\n    enum class ShaderType {\n        Vertex, Geometry, Fragment, Compute\n    };\n\n    enum class IndexType {\n        U8, U16, U32\n    };\n\n    namespace Internal {\n        struct AttributeSpec {\n            DataType type;\n            int binding, location, width, offset;\n\n            constexpr AttributeSpec(DataType type, int binding, int location, int width, int offset) noexcept:\n                    type(type), binding(binding), location(location), width(width), offset(offset) {}\n        };\n\n        class Object {\n        };\n    }\n\n    using GLShader = std::shared_ptr<Internal::Object>;\n\n    GLShader Compile(ShaderType type, std::string_view program, const std::vector<std::string> &defines);\n    GLShader CompileFile(ShaderType type, const std::string &path, const std::vector<std::string> &defines);\n\n    struct IPipeline {\n        virtual void Use() = 0;\n\n        virtual void BindVertexBuffer(int bind, GLuint buffer, int offset = 0) = 0;\n\n        virtual void BindIndexBuffer(GLuint buffer, Renderer::IndexType type) = 0;\n\n        virtual void Draw(int count, int first = 0, int instance = 1) = 0;\n\n        virtual void DrawIndexed(int count, int first = 0, int instance = 1) = 0;\n\n        virtual void SetUniform(GLint loc, float value) = 0;\n\n        virtual void SetUniform(GLint loc, int value) = 0;\n\n        virtual void SetUniform(GLint loc, float v0, float v1, float v2, float v3) = 0;\n\n        virtual void SetUniform(GLint loc, float *value) = 0;\n    };\n\n    enum class Topology {\n        PointList, LineList, LineStrip, TriangleList, TriangleStrip, TriangleFan, Quad\n    };\n\n    using Pipeline = std::shared_ptr<IPipeline>;\n\n    class PipelineBuilder {\n    public:\n        explicit PipelineBuilder(Topology topology) noexcept: mTopology(topology) {}\n\n        void SetBinding(int location, int stride, int divisor) noexcept {\n            mBindings.insert_or_assign(location, std::pair(stride, divisor));\n        }\n\n        bool AddAttribute(DataType type, int binding, int location, int width, int offset) noexcept {\n            const auto binds = mBindings.find(binding);\n            if (binds == mBindings.end()) return false; // 没有对应binding记录\n            const auto stride = binds->second.first;\n            switch (type) {\n                case DataType::Float16:\n                    if (offset + width * 2 > stride) return false; //超出stride长度\n                    break;\n                case DataType::Int32:\n                case DataType::Float32:\n                    if (offset + width * 4 > stride) return false; //超出stride长度\n                    break;\n                case DataType::Float64:\n                    if (offset + width * 8 > stride) return false; //超出stride长度\n                    break;\n                default:\n                    return false; //未定义的数据类别\n            }\n            mSpecs.emplace_back(type, binding, location, width, offset);\n            return true;\n        }\n\n        void SetShader(ShaderType type, GLShader stage) noexcept {\n            mStages.insert_or_assign(type, std::move(stage));\n        }\n\n        Pipeline Build();\n\n    private:\n        Topology mTopology;\n        kls::temp::vector<Internal::AttributeSpec> mSpecs;\n        kls::temp::unordered_map<ShaderType, GLShader> mStages;\n        kls::temp::unordered_map<int, std::pair<int, int>> mBindings;\n    };\n\n    GLuint GetDefaultQuadIndex();\n}"
  },
  {
    "path": "NEWorld.Game/Renderer/Renderer.cpp",
    "content": "#include \"Renderer.h\"\n#include <algorithm>\n#include <Common/Logger.h>\n#include \"Frustum.h\"\n#include \"BufferBuilder.h\"\n\nnamespace Renderer {\n\n    /*\n    好纠结啊好纠结，“高级”渲染模式里的所有数据要不要都用VertexAttribArray啊。。。\n    然而我还是比较懒。。。所以除了【附加】的顶点属性之外，其他属性（比如颜色、纹理坐标）都保留原来的算了。。。\n\n    说到为啥要用【附加】的顶点属性。。。这是由于Shadow Map的精度问题。。。\n    有的时候背光面的外圈会有亮光。。。很难看。。。所以要用Shader把背光面弄暗。。。\n    于是如何让shader知道这个面朝哪里呢？懒得用NormalArray的我就用了一个附加的顶点属性。。。\n    0.0f表示前面(z+)，1.0f表示后面(z-)，2.0f表示右面(x+)，3.0f表示左面(x-)，4.0f表示上面(y+)，5.0f表示下面(y-)\n\n        你没有看错。。。这些值。。。全都是\n\n            浮！\n                点！\n                    型！\n                        的！！！！！！！\n\n    坑爹的GLSL不支持整型作为顶点属性。。。只好用浮点型代替了(╯‵□′)╯︵┻━┻\n    然后为了解决浮点数的精度问题，我在shader里写了个四舍五入取整。。。\n    不说了。。。\n\n    等等我还没有签名呢。。。\n    --qiaozhanrong\n\n    ====================================================\n    留言板：\n\n    1楼. qiaozhanrong: 自己抢个沙发先\n    2楼. Null: 这就是你在源码里写这么一长串的理由？23333333333\n    3楼. qiaozhanrong: 无聊啊233333333333\n\n    4楼. [请输入姓名]: [请输入回复内容]\n\n    [回复]\n    ====================================================\n    */\n\n    bool AdvancedRender;\n    int ShadowRes = 4096;\n    int MaxShadowDist = 4;\n    int shadowdist;\n    float sunlightXrot, sunlightYrot;\n    std::vector<Pipeline> pipelines;\n    int ActivePipeline;\n    int index = 0, size = 0;\n    unsigned int ShadowFBO, DepthTexture;\n\n\n    struct IndirectBaseVertex {\n        uint32_t count;\n        uint32_t instanceCount;\n        uint32_t firstIndex;\n        uint32_t baseVertex;\n        uint32_t baseInstance;\n    };\n\n    void RenderBufferDirect(VBOID buffer, vtxCount vtxs) {\n        pipelines[ActivePipeline]->BindVertexBuffer(1, buffer, 0);\n        pipelines[ActivePipeline]->DrawIndexed(vtxs + vtxs / 2, 0);\n    }\n\n    Pipeline BuildPipeline(\n            const std::string &vshPath, const std::string &fshPath,\n            int tc, int cc, int ac, bool useAc, const std::vector<std::string> &defines = {}\n    ) noexcept {\n        constexpr int sof = sizeof(float);\n        PipelineBuilder builder{Topology::TriangleList};\n        builder.SetBinding(1, (tc + cc + ac + 3) * sof, 0);\n        if (ac && useAc) builder.AddAttribute(DataType::Float32, 1, 1, ac, 0);\n        if (tc) builder.AddAttribute(DataType::Float32, 1, 2, tc, ac * sof);\n        if (cc) builder.AddAttribute(DataType::Float32, 1, 3, cc, (ac + tc) * sof);\n        builder.AddAttribute(DataType::Float32, 1, 4, 3, (ac + tc + cc) * sof);\n        builder.SetShader(ShaderType::Vertex, CompileFile(ShaderType::Vertex, vshPath, defines));\n        builder.SetShader(ShaderType::Fragment, CompileFile(ShaderType::Fragment, fshPath, defines));\n        auto result = builder.Build();\n        result->BindIndexBuffer(GetDefaultQuadIndex(), IndexType::U32);\n        return result;\n    }\n\n    void initShaders() {\n        std::vector<std::string> defines{};\n        sunlightXrot = 30.0f;\n        sunlightYrot = 60.0f;\n        shadowdist = std::min(MaxShadowDist, viewdistance);\n        pipelines = {\n                BuildPipeline(\"./Assets/Shaders/Main.vsh\", \"./Assets/Shaders/Main.fsh\", 2, 3, 1, true),\n                BuildPipeline(\"./Assets/Shaders/Shadow.vsh\", \"./Assets/Shaders/Shadow.fsh\", 0, 0, 0, false),\n                BuildPipeline(\"./Assets/Shaders/Depth.vsh\", \"./Assets/Shaders/Depth.fsh\", 0, 0, 0, false, defines),\n                BuildPipeline(\"./Assets/Shaders/Simple.vsh\", \"./Assets/Shaders/Simple.fsh\", 2, 3, 1, false)\n        };\n\n        glGenTextures(1, &DepthTexture);\n        glBindTexture(GL_TEXTURE_2D, DepthTexture);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n        glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);\n        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, ShadowRes, ShadowRes, 0, GL_DEPTH_COMPONENT,\n                     GL_UNSIGNED_INT, NULL);\n\n        glActiveTexture(GL_TEXTURE1);\n        glBindTexture(GL_TEXTURE_2D, DepthTexture);\n        glActiveTexture(GL_TEXTURE0);\n\n        glGenFramebuffersEXT(1, &ShadowFBO);\n        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ShadowFBO);\n        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, DepthTexture, 0);\n        if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {\n            debugstream << \"Frame buffer creation error!\";\n        }\n        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);\n\n        pipelines[MainShader]->SetUniform(0, 0);\n        pipelines[MainShader]->SetUniform(1, 1);\n        pipelines[MainShader]->SetUniform(5, skycolorR, skycolorG, skycolorB, 1.0f);\n        pipelines[SimpleShader]->SetUniform(0, 0);\n    }\n\n    void destroyShaders() {\n        glDeleteTextures(1, &DepthTexture);\n        glDeleteFramebuffersEXT(1, &ShadowFBO);\n    }\n\n    void BindPipeline(int shaderID) {\n        pipelines[shaderID]->Use();\n        ActivePipeline = shaderID;\n    }\n\n    void EnableAdvancedShaders() {\n        shadowdist = std::min(MaxShadowDist, viewdistance);\n        //Enable pipeline\n        auto &pipeline = pipelines[MainShader];\n        BindPipeline(MainShader);\n\n        //Calc matrix\n        const auto scale = 16.0f * sqrt(3.0f);\n        const auto length = shadowdist * scale;\n        Frustum frus;\n        frus.LoadIdentity();\n        frus.SetOrtho(-length, length, -length, length, -length, length);\n        frus.MultRotate(sunlightXrot, 1.0f, 0.0f, 0.0f);\n        frus.MultRotate(sunlightYrot, 0.0f, 1.0f, 0.0f);\n\n        //Set uniform\n        pipeline->SetUniform(6, viewdistance * 16.0f);\n        pipeline->SetUniform(2, frus.getProjMatrix());\n        pipeline->SetUniform(3, frus.getModlMatrix());\n    }\n\n    void EnableSimpleShaders() {\n        BindPipeline(SimpleShader);\n    }\n\n    void EnableShaders() {\n        if (AdvancedRender) EnableAdvancedShaders(); else EnableSimpleShaders();\n    }\n\n    void DisableShaders() {\n        glBindVertexArray(0);\n        glUseProgram(0);\n    }\n\n    void StartShadowPass() {\n        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ShadowFBO);\n        glDrawBuffer(GL_NONE);\n        glReadBuffer(GL_NONE);\n        BindPipeline(ShadowShader);\n        glViewport(0, 0, ShadowRes, ShadowRes);\n    }\n\n    void EndShadowPass() {\n        glDrawBuffer(GL_NONE);\n        glReadBuffer(GL_NONE);\n        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);\n        glDrawBuffer(GL_BACK);\n        glReadBuffer(GL_BACK);\n        glBindVertexArray(0);\n        glViewport(0, 0, windowwidth, windowheight);\n    }\n\n    Pipeline &GetPipeline() {\n        return pipelines[ActivePipeline];\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/Renderer.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n#include \"GL/Pipeline.h\"\n#include <cstring>\n\nnamespace Renderer {\n    //我猜你肯定不敢看Renderer.cpp  --qiaozhanrong\n    //猜对了  --Null\n\n    enum {\n        MainShader, ShadowShader, DepthShader, SimpleShader\n    };\n\n    const int ArraySize = 2621440;\n    extern bool AdvancedRender;\n    extern int ShadowRes;\n    extern int MaxShadowDist;\n    extern int shadowdist;\n    extern float sunlightXrot, sunlightYrot;\n    extern unsigned int DepthTexture;\n    extern int ActivePipeline;\n\n    void RenderBufferDirect(VBOID buffer, vtxCount vtxs);\n\n    Pipeline& GetPipeline();\n\n    void initShaders();\n\n    void destroyShaders();\n\n    void EnableShaders();\n\n    void DisableShaders();\n\n    void StartShadowPass();\n\n    void EndShadowPass();\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/World/ChunkRenderer.cpp",
    "content": "#include \"ChunkRenderer.h\"\n#include \"Renderer/Renderer.h\"\n#include \"Universe/World/World.h\"\n#include \"Renderer/BufferBuilder.h\"\n#include \"Math/Vector4.h\"\n#include \"Dispatch.h\"\n\nnamespace WorldRenderer {\n    class ChunkRenderContext {\n        static constexpr auto MX = 18 * 18;\n        static constexpr auto MY = 18;\n    public:\n        ChunkRenderContext(World::Chunk *chunk) {\n            Block *bF = mBStates;\n            std::uint8_t *lF = mLumin;\n            const auto cPos = chunk->GetPosition() * 16;\n            Cursor(cPos - Int3{1}, cPos + Int3{17}, [&bF, &lF, chunk](const auto &v) {\n                *(bF++) = World::GetBlock(v, Blocks::ROCK, chunk);\n                *(lF++) = World::GetBrightness(v);\n            });\n        }\n\n        void Rebase(int x, int y, int z) noexcept { mBase = (x + 1) * MX + (y + 1) * MY + (z + 1); }\n\n        auto State(int dx, int dy, int dz) noexcept { return mBStates[mBase + dx * MX + dy * MY + dz]; }\n\n        Brightness Lumin(int dx, int dy, int dz) noexcept { return mLumin[mBase + dx * MX + dy * MY + dz]; }\n\n    private:\n        int mBase{0};\n        Block mBStates[18 * 18 * 18];\n        std::uint8_t mLumin[18 * 18 * 18]{0};\n    };\n\n    enum Face {\n        Front, Back, Right, Left, Top, Bottom\n    };\n\n    //TODO(simplify this function)\n    static void renderblock(Renderer::BufferBuilder<> &builder, ChunkRenderContext &ctx, int x, int y, int z) {\n        double tcx, tcy, size, EPS = 0.0;\n        Block blk[7] = {ctx.State(0, 0, 0), ctx.State(0, 0, 1), ctx.State(0, 0, -1),\n                        ctx.State(1, 0, 0), ctx.State(-1, 0, 0), ctx.State(0, 1, 0), ctx.State(0, -1, 0)};\n        Brightness brt[7] = {ctx.Lumin(0, 0, 0), ctx.Lumin(0, 0, 1), ctx.Lumin(0, 0, -1),\n                             ctx.Lumin(1, 0, 0), ctx.Lumin(-1, 0, 0), ctx.Lumin(0, 1, 0), ctx.Lumin(0, -1, 0)};\n\n        size = 1 / 8.0f - EPS;\n\n        if (NiceGrass && blk[0] == Blocks::GRASS && ctx.State(0, -1, 1) == Blocks::GRASS) {\n            tcx = Textures::getTexcoordX(blk[0], 1) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 1) + EPS;\n        } else {\n            tcx = Textures::getTexcoordX(blk[0], 2) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 2) + EPS;\n        }\n\n        // Front Face\n        if (!(BlockInfo(blk[1]).isOpaque() || (blk[1] == blk[0] && !BlockInfo(blk[0]).isOpaque())) ||\n            blk[0] == Blocks::LEAF) {\n\n            Double4 col{double(brt[1])};\n\n            if (blk[0] != Blocks::GLOWSTONE && SmoothLighting)\n                col = (col + Double4(\n                        ctx.Lumin(0, -1, 1) + ctx.Lumin(-1, 0, 1) + ctx.Lumin(-1, -1, 1),\n                        ctx.Lumin(0, -1, 1) + ctx.Lumin(1, 0, 1) + ctx.Lumin(1, -1, 1),\n                        ctx.Lumin(0, 1, 1) + ctx.Lumin(1, 0, 1) + ctx.Lumin(1, 1, 1),\n                        ctx.Lumin(0, 1, 1) + ctx.Lumin(-1, 0, 1) + ctx.Lumin(-1, 1, 1)\n                )) / 4.0;\n\n            col /= World::BRIGHTNESSMAX;\n\n            if (blk[0] != Blocks::GLOWSTONE && !Renderer::AdvancedRender) col *= 0.5;\n            // att, tex, col vert\n            builder.put<4>(\n                    0.0f, tcx, tcy, col.X, col.X, col.X, -0.5 + x, -0.5 + y, 0.5 + z,\n                    0.0f, tcx + size, tcy, col.Y, col.Y, col.Y, 0.5 + x, -0.5 + y, 0.5 + z,\n                    0.0f, tcx + size, tcy + size, col.Z, col.Z, col.Z, 0.5 + x, 0.5 + y, 0.5 + z,\n                    0.0f, tcx, tcy + size, col.W, col.W, col.W, -0.5 + x, 0.5 + y, 0.5 + z\n            );\n        }\n\n        if (NiceGrass && blk[0] == Blocks::GRASS && ctx.State(0, -1, -1) == Blocks::GRASS) {\n            tcx = Textures::getTexcoordX(blk[0], 1) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 1) + EPS;\n        } else {\n            tcx = Textures::getTexcoordX(blk[0], 2) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 2) + EPS;\n        }\n\n        // Back Face\n        if (!(BlockInfo(blk[2]).isOpaque() || (blk[2] == blk[0] && !BlockInfo(blk[0]).isOpaque())) ||\n            blk[0] == Blocks::LEAF) {\n\n            Double4 col{double(brt[2])};\n\n            if (blk[0] != Blocks::GLOWSTONE && SmoothLighting)\n                col = (col + Double4(\n                        ctx.Lumin(0, -1, -1) + ctx.Lumin(-1, 0, -1) + ctx.Lumin(-1, -1, -1),\n                        ctx.Lumin(0, 1, -1) + ctx.Lumin(-1, 0, -1) + ctx.Lumin(-1, 1, -1),\n                        ctx.Lumin(0, 1, -1) + ctx.Lumin(1, 0, -1) + ctx.Lumin(1, 1, -1),\n                        ctx.Lumin(0, -1, -1) + ctx.Lumin(1, 0, -1) + ctx.Lumin(1, -1, -1)\n                )) / 4.0;\n\n            col /= World::BRIGHTNESSMAX;\n            if (blk[0] != Blocks::GLOWSTONE && !Renderer::AdvancedRender) col *= 0.5;\n            // att, tex, col vert\n            builder.put<4>(\n                    1.0f, tcx + size * 1.0, tcy + size * 0.0, col.X, col.X, col.X, -0.5 + x, -0.5 + y, -0.5 + z,\n                    1.0f, tcx + size * 1.0, tcy + size * 1.0, col.Y, col.Y, col.Y, -0.5 + x, 0.5 + y, -0.5 + z,\n                    1.0f, tcx + size * 0.0, tcy + size * 1.0, col.Z, col.Z, col.Z, 0.5 + x, 0.5 + y, -0.5 + z,\n                    1.0f, tcx + size * 0.0, tcy + size * 0.0, col.W, col.W, col.W, 0.5 + x, -0.5 + y, -0.5 + z\n            );\n        }\n\n        if (NiceGrass && blk[0] == Blocks::GRASS && ctx.State(1, -1, 0) == Blocks::GRASS) {\n            tcx = Textures::getTexcoordX(blk[0], 1) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 1) + EPS;\n        } else {\n            tcx = Textures::getTexcoordX(blk[0], 2) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 2) + EPS;\n        }\n\n        // Right face\n        if (!(BlockInfo(blk[3]).isOpaque() || (blk[3] == blk[0] && !BlockInfo(blk[0]).isOpaque())) ||\n            blk[0] == Blocks::LEAF) {\n\n            Double4 col{double(brt[3])};\n\n            if (blk[0] != Blocks::GLOWSTONE && SmoothLighting)\n                col = (col + Double4(\n                        ctx.Lumin(1, -1, 0) + ctx.Lumin(1, 0, -1) + ctx.Lumin(1, -1, -1),\n                        ctx.Lumin(1, 1, 0) + ctx.Lumin(1, 0, -1) + ctx.Lumin(1, 1, -1),\n                        ctx.Lumin(1, 1, 0) + ctx.Lumin(1, 0, 1) + ctx.Lumin(1, 1, 1),\n                        ctx.Lumin(1, -1, 0) + ctx.Lumin(1, 0, 1) + ctx.Lumin(1, -1, 1)\n                )) / 4.0;\n\n            col /= World::BRIGHTNESSMAX;\n            if (blk[0] != Blocks::GLOWSTONE && !Renderer::AdvancedRender) col *= 0.7;\n\n            // att, tex, col vert\n            builder.put<4>(\n                    2.0f, tcx + size * 1.0, tcy + size * 0.0, col.X, col.X, col.X, 0.5 + x, -0.5 + y, -0.5 + z,\n                    2.0f, tcx + size * 1.0, tcy + size * 1.0, col.Y, col.Y, col.Y, 0.5 + x, 0.5 + y, -0.5 + z,\n                    2.0f, tcx + size * 0.0, tcy + size * 1.0, col.Z, col.Z, col.Z, 0.5 + x, 0.5 + y, 0.5 + z,\n                    2.0f, tcx + size * 0.0, tcy + size * 0.0, col.W, col.W, col.W, 0.5 + x, -0.5 + y, 0.5 + z\n            );\n        }\n\n        if (NiceGrass && blk[0] == Blocks::GRASS && ctx.State(-1, -1, 0) == Blocks::GRASS) {\n            tcx = Textures::getTexcoordX(blk[0], 1) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 1) + EPS;\n        } else {\n            tcx = Textures::getTexcoordX(blk[0], 2) + EPS;\n            tcy = Textures::getTexcoordY(blk[0], 2) + EPS;\n        }\n\n        // Left Face\n        if (!(BlockInfo(blk[4]).isOpaque() || (blk[4] == blk[0] && !BlockInfo(blk[0]).isOpaque())) ||\n            blk[0] == Blocks::LEAF) {\n\n            Double4 col{double(brt[4])};\n\n            if (blk[0] != Blocks::GLOWSTONE && SmoothLighting)\n                col = (col + Double4(\n                        ctx.Lumin(-1, -1, 0) + ctx.Lumin(-1, 0, -1) + ctx.Lumin(-1, -1, -1),\n                        ctx.Lumin(-1, -1, 0) + ctx.Lumin(-1, 0, 1) + ctx.Lumin(-1, -1, 1),\n                        ctx.Lumin(-1, 1, 0) + ctx.Lumin(-1, 0, 1) + ctx.Lumin(-1, 1, 1),\n                        ctx.Lumin(-1, 1, 0) + ctx.Lumin(-1, 0, -1) + ctx.Lumin(-1, 1, -1)\n                )) / 4.0;\n\n            col /= World::BRIGHTNESSMAX;\n            if (blk[0] != Blocks::GLOWSTONE && !Renderer::AdvancedRender) col *= 0.7;\n\n            // att, tex, col vert\n            builder.put<4>(\n                    3.0f, tcx + size * 0.0, tcy + size * 0.0, col.X, col.X, col.X, -0.5 + x, -0.5 + y, -0.5 + z,\n                    3.0f, tcx + size * 1.0, tcy + size * 0.0, col.Y, col.Y, col.Y, -0.5 + x, -0.5 + y, 0.5 + z,\n                    3.0f, tcx + size * 1.0, tcy + size * 1.0, col.Z, col.Z, col.Z, -0.5 + x, 0.5 + y, 0.5 + z,\n                    3.0f, tcx + size * 0.0, tcy + size * 1.0, col.W, col.W, col.W, -0.5 + x, 0.5 + y, -0.5 + z\n            );\n        }\n\n        tcx = Textures::getTexcoordX(blk[0], 1);\n        tcy = Textures::getTexcoordY(blk[0], 1);\n\n        // Top Face\n        if (!(BlockInfo(blk[5]).isOpaque() || (blk[5] == blk[0] && !BlockInfo(blk[0]).isOpaque())) ||\n            blk[0] == Blocks::LEAF) {\n\n            Double4 col{double(brt[5])};\n\n            if (blk[0] != Blocks::GLOWSTONE && SmoothLighting)\n                col = (col + Double4(\n                        ctx.Lumin(0, 1, -1) + ctx.Lumin(-1, 1, 0) + ctx.Lumin(-1, 1, -1),\n                        ctx.Lumin(0, 1, 1) + ctx.Lumin(-1, 1, 0) + ctx.Lumin(-1, 1, 1),\n                        ctx.Lumin(0, 1, 1) + ctx.Lumin(1, 1, 0) + ctx.Lumin(1, 1, 1),\n                        ctx.Lumin(0, 1, -1) + ctx.Lumin(1, 1, 0) + ctx.Lumin(1, 1, -1)\n                )) / 4.0;\n\n            col /= World::BRIGHTNESSMAX;\n\n            // att, tex, col vert\n            builder.put<4>(\n                    4.0f, tcx + size * 0.0, tcy + size * 1.0, col.X, col.X, col.X, -0.5 + x, 0.5 + y, -0.5 + z,\n                    4.0f, tcx + size * 0.0, tcy + size * 0.0, col.Y, col.Y, col.Y, -0.5 + x, 0.5 + y, 0.5 + z,\n                    4.0f, tcx + size * 1.0, tcy + size * 0.0, col.Z, col.Z, col.Z, 0.5 + x, 0.5 + y, 0.5 + z,\n                    4.0f, tcx + size * 1.0, tcy + size * 1.0, col.W, col.W, col.W, 0.5 + x, 0.5 + y, -0.5 + z\n            );\n        }\n\n        tcx = Textures::getTexcoordX(blk[0], 3);\n        tcy = Textures::getTexcoordY(blk[0], 3);\n\n        // Bottom Face\n        if (!(BlockInfo(blk[6]).isOpaque() || (blk[6] == blk[0] && !BlockInfo(blk[0]).isOpaque())) ||\n            blk[0] == Blocks::LEAF) {\n\n            Double4 col{double(brt[6])};\n\n            if (blk[0] != Blocks::GLOWSTONE && SmoothLighting)\n                col = (col + Double4(\n                        ctx.Lumin(0, -1, -1) + ctx.Lumin(-1, -1, 0) + ctx.Lumin(-1, -1, -1),\n                        ctx.Lumin(0, -1, -1) + ctx.Lumin(1, -1, 0) + ctx.Lumin(1, -1, -1),\n                        ctx.Lumin(0, -1, 1) + ctx.Lumin(1, -1, 0) + ctx.Lumin(1, -1, 1),\n                        ctx.Lumin(0, -1, 1) + ctx.Lumin(-1, -1, 0) + ctx.Lumin(-1, -1, 1)\n                )) / 4.0;\n\n            col /= World::BRIGHTNESSMAX;\n\n            // att, tex, col vert\n            builder.put<4>(\n                    5.0f, tcx + size * 1.0, tcy + size * 1.0, col.X, col.X, col.X, -0.5 + x, -0.5 + y, -0.5 + z,\n                    5.0f, tcx + size * 0.0, tcy + size * 1.0, col.Y, col.Y, col.Y, 0.5 + x, -0.5 + y, -0.5 + z,\n                    5.0f, tcx + size * 0.0, tcy + size * 0.0, col.Z, col.Z, col.Z, 0.5 + x, -0.5 + y, 0.5 + z,\n                    5.0f, tcx + size * 1.0, tcy + size * 0.0, col.W, col.W, col.W, -0.5 + x, -0.5 + y, 0.5 + z\n            );\n        }\n    }\n\n    static void RenderPrimitive_Depth(Renderer::BufferBuilder<> &builder, QuadPrimitive_Depth &p) {\n        const auto x = p.x, y = p.y, z = p.z, length = p.length;\n        switch (p.direction) {\n            case 0:\n                return builder.put<4>(x + 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z - 0.5,\n                                      x + 0.5, y + 0.5, z + length + 0.5, x + 0.5, y - 0.5, z + length + 0.5);\n            case 1:\n                return builder.put<4>(x - 0.5, y + 0.5, z - 0.5, x - 0.5, y - 0.5, z - 0.5,\n                                      x - 0.5, y - 0.5, z + length + 0.5, x - 0.5, y + 0.5, z + length + 0.5);\n            case 2:\n                return builder.put<4>(x + 0.5, y + 0.5, z - 0.5, x - 0.5, y + 0.5, z - 0.5,\n                                      x - 0.5, y + 0.5, z + length + 0.5, x + 0.5, y + 0.5, z + length + 0.5);\n            case 3:\n                return builder.put<4>(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y - 0.5, z - 0.5,\n                                      x + 0.5, y - 0.5, z + length + 0.5, x - 0.5, y - 0.5, z + length + 0.5);\n            case 4:\n                return builder.put<4>(x - 0.5, y + 0.5, z + 0.5, x - 0.5, y - 0.5, z + 0.5,\n                                      x + length + 0.5, y - 0.5, z + 0.5, x + length + 0.5, y + 0.5, z + 0.5);\n            case 5:\n                return builder.put<4>(x - 0.5, y - 0.5, z - 0.5, x - 0.5, y + 0.5, z - 0.5,\n                                      x + length + 0.5, y + 0.5, z - 0.5, x + length + 0.5, y - 0.5, z - 0.5);\n        }\n    }\n\n    static kls::coroutine::ValueAsync<void> RenderDepthModelEvaluate(World::Chunk *c, Renderer::BufferBuilder<> &builder) {\n        co_await kls::coroutine::SwitchTo(GetSessionDefault());\n        const auto cp = c->GetPosition();\n        auto x = 0, y = 0, z = 0;\n        QuadPrimitive_Depth cur;\n        Block bl, neighbour;\n        auto valid = false;\n        int cur_l_mx = bl = neighbour = 0;\n        //Linear merge for depth model\n        for (auto d = 0; d < 6; d++) {\n            cur.direction = d;\n            for (auto i = 0; i < 16; i++)\n                for (auto j = 0; j < 16; j++) {\n                    for (auto k = 0; k < 16; k++) {\n                        //Get position\n                        if (d < 2) x = i, y = j, z = k;\n                        else if (d < 4) x = i, y = j, z = k;\n                        else x = k, y = i, z = j;\n                        //Get block ID\n                        bl = c->GetBlock({x, y, z});\n                        //Get neighbour ID\n                        const auto xx = x + delta[d][0], yy = y + delta[d][1], zz = z + delta[d][2];\n                        if (xx < 0 || xx >= 16 || yy < 0 || yy >= 16 || zz < 0 || zz >= 16) {\n                            neighbour = World::GetBlock(cp * 16 + Int3(xx, yy, zz));\n                        } else {\n                            neighbour = c->GetBlock({(xx), (yy), (zz)});\n                        }\n                        //Render\n                        if (bl == Blocks::ENV || bl == Blocks::GLASS || bl == neighbour && bl != Blocks::LEAF ||\n                            BlockInfo(neighbour).isOpaque() || BlockInfo(bl).isTranslucent()) {\n                            //Not valid block\n                            if (valid) {\n                                if (BlockInfo(neighbour).isOpaque()) {\n                                    if (cur_l_mx < cur.length) cur_l_mx = cur.length;\n                                    cur_l_mx++;\n                                } else {\n                                    RenderPrimitive_Depth(builder, cur);\n                                    valid = false;\n                                }\n                            }\n                            continue;\n                        }\n                        if (valid) {\n                            if (cur_l_mx > cur.length) cur.length = cur_l_mx;\n                            cur.length++;\n                        } else {\n                            valid = true;\n                            cur.x = x;\n                            cur.y = y;\n                            cur.z = z;\n                            cur.length = cur_l_mx = 0;\n                        }\n                    }\n                    if (valid) {\n                        RenderPrimitive_Depth(builder, cur);\n                        valid = false;\n                    }\n                }\n        }\n    }\n\n    static kls::coroutine::ValueAsync<void> RenderDepthModel(World::Chunk *c, ChunkRender &r) {\n        if (Renderer::AdvancedRender) co_return;\n        Renderer::BufferBuilder builder{};\n        co_await RenderDepthModelEvaluate(c, builder);\n        co_await builder.flushAsync(r.Renders[3].Buffer, r.Renders[3].Count);\n    }\n\n    static kls::coroutine::ValueAsync<void> BuildRenderEvaluate(World::Chunk *&c, Renderer::BufferBuilder<> b[]) {\n        co_await kls::coroutine::SwitchTo(GetSessionDefault());\n        auto context = kls::temp::make_unique<ChunkRenderContext>(c);\n        for (int x = 0; x < 16; x++) {\n            for (int y = 0; y < 16; y++) {\n                for (int z = 0; z < 16; z++) {\n                    context->Rebase(x, y, z);\n                    const auto curr = context->State(0, 0, 0);\n                    if (curr == Blocks::ENV) continue;\n                    if (!BlockInfo(curr).isTranslucent()) renderblock(b[0], *context, x, y, z);\n                    if (BlockInfo(curr).isTranslucent() && BlockInfo(curr).isSolid())\n                        renderblock(b[1], *context, x, y, z);\n                    if (!BlockInfo(curr).isSolid()) renderblock(b[2], *context, x, y, z);\n                }\n            }\n        }\n    }\n\n    static kls::coroutine::ValueAsync<void> RenderChunk(World::Chunk *c, ChunkRender &r) {\n        Renderer::BufferBuilder<> b[3]{};\n        co_await BuildRenderEvaluate(c, b);\n        co_await kls::coroutine::awaits(\n                b[0].flushAsync(r.Renders[0].Buffer, r.Renders[0].Count),\n                b[1].flushAsync(r.Renders[1].Buffer, r.Renders[1].Count),\n                b[2].flushAsync(r.Renders[2].Buffer, r.Renders[2].Count)\n        );\n    }\n\n    bool ChunkRender::CheckBuild(const std::shared_ptr<World::Chunk> &c) {\n        for (auto x = -1; x <= 1; x++) {\n            for (auto y = -1; y <= 1; y++) {\n                for (auto z = -1; z <= 1; z++) {\n                    if (x == 0 && y == 0 && z == 0) continue;\n                    if (World::ChunkOutOfBound(c->GetPosition() + Int3{x, y, z})) continue;\n                    if (!World::ChunkLoaded(c->GetPosition() + Int3{x, y, z})) return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    kls::coroutine::ValueAsync<void> ChunkRender::Rebuild(std::shared_ptr<World::Chunk> c) {\n        World::rebuiltChunks++;\n        World::updatedChunks++;\n        co_await kls::coroutine::awaits(\n                RenderChunk(c.get(), *this),\n                RenderDepthModel(c.get(), *this)\n        );\n        c->updated = false;\n        Built = true;\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/World/ChunkRenderer.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n#include \"Textures.h\"\n#include \"Universe/World/World.h\"\n#include <kls/coroutine/Async.h>\n\nnamespace WorldRenderer {\n    const int delta[6][3] = { {1,  0,  0},\n                             {-1, 0,  0},\n                             {0,  1,  0},\n                             {0,  -1, 0},\n                             {0,  0,  1},\n                             {0,  0,  -1} };\n\n\n    struct RenderPair {\n        VBOID Buffer{ 0 };\n        vtxCount Count{ 0 };\n    };\n\n    struct ChunkRender {\n        bool Built;\n        bool Visiable;\n        Int3 Position;\n        double LoadAnim;\n        RenderPair Renders[4];\n        BoundingBox BaseBounds;\n        std::weak_ptr<World::Chunk> Ref;\n\n        explicit ChunkRender(const std::shared_ptr<World::Chunk>& c) :\n            Built{ false }, Position(c->GetPosition()),\n            LoadAnim(static_cast<float>(Position.Y) * 16.0f + 16.0f),\n            BaseBounds(c->getBaseAABB()), Ref(c) {}\n\n        bool CheckBuild(const std::shared_ptr<World::Chunk>& c);\n\n        kls::coroutine::ValueAsync<void> Rebuild(std::shared_ptr<World::Chunk> c);\n\n        Frustum::ChunkBox getRelativeAABB(const Double3& camera) {\n            Frustum::ChunkBox ret{};\n            ret.xmin = static_cast<float>(BaseBounds.min.values[0] - camera.X);\n            ret.xmax = static_cast<float>(BaseBounds.max.values[0] - camera.X);\n            ret.ymin = static_cast<float>(BaseBounds.min.values[1] /*- loadAnim*/ - camera.Y);\n            ret.ymax = static_cast<float>(BaseBounds.max.values[1] /*- loadAnim*/ - camera.Y);\n            ret.zmin = static_cast<float>(BaseBounds.min.values[2] - camera.Z);\n            ret.zmax = static_cast<float>(BaseBounds.max.values[2] - camera.Z);\n            return ret;\n        }\n    };\n\n    //深度模型的面 | Face in depth model\n    struct QuadPrimitive_Depth {\n        int x, y, z, length, direction;\n\n        QuadPrimitive_Depth() : x(0), y(0), z(0), length(0), direction(0) {}\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/World/ShadowMaps.cpp",
    "content": "#include \"ShadowMaps.h\"\n#include \"Universe/World/World.h\"\n#include \"Renderer/Renderer.h\"\n\nnamespace ShadowMaps {\n    void BuildShadowMap(\n            WorldRenderer::ChunksRenderer &chunksRenderer,\n            double xpos, double ypos, double zpos, double curtime\n    ) {\n        const auto cx = World::GetChunkPos(static_cast<int>(xpos)), cy = World::GetChunkPos(\n                static_cast<int>(ypos)), cz = World::GetChunkPos(static_cast<int>(zpos));\n\n        Renderer::StartShadowPass();\n        glClear(GL_DEPTH_BUFFER_BIT);\n        glEnableVertexAttribArray(4);\n        glDisable(GL_TEXTURE_2D);\n        glDisable(GL_FOG);\n        glDisable(GL_BLEND);\n        const auto scale = 16.0f * sqrt(3.0f);\n        const auto length = Renderer::shadowdist * scale;\n        glMatrixMode(GL_PROJECTION);\n        glLoadIdentity();\n        glOrtho(-length, length, -length, length, -length, length);\n        glMatrixMode(GL_MODELVIEW);\n        glLoadIdentity();\n        glRotated(Renderer::sunlightXrot, 1.0, 0.0, 0.0);\n        glRotated(Renderer::sunlightYrot, 0.0, 1.0, 0.0);\n\n        auto frame = chunksRenderer.List({cx, cy, cz}, Renderer::shadowdist + 1, /*curtime,*/ false);\n        frame.Render(xpos, ypos, zpos, 3);\n\n        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);\n        glDisableVertexAttribArray(4);\n        Renderer::EndShadowPass();\n\n        glEnable(GL_FOG);\n        glEnable(GL_BLEND);\n    }\n\n    void RenderShadowMap(\n            WorldRenderer::ChunksRenderer &chunksRenderer,\n            double xpos, double ypos, double zpos, double curtime\n    ) {\n        const auto cx = World::GetChunkPos(static_cast<int>(xpos)), cy = World::GetChunkPos(\n                static_cast<int>(ypos)), cz = World::GetChunkPos(static_cast<int>(zpos));\n\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        glEnableVertexAttribArray(4);\n        glDisable(GL_TEXTURE_2D);\n        glDisable(GL_FOG);\n        glDisable(GL_BLEND);\n        const auto scale = 16.0f * sqrt(3.0f);\n        const auto length = Renderer::shadowdist * scale;\n        glMatrixMode(GL_PROJECTION);\n        glLoadIdentity();\n        glOrtho(-length, length, -length, length, -length, length);\n        glMatrixMode(GL_MODELVIEW);\n        glLoadIdentity();\n        glRotated(Renderer::sunlightXrot, 1.0, 0.0, 0.0);\n        glRotated(Renderer::sunlightYrot, 0.0, 1.0, 0.0);\n\n        auto frame = chunksRenderer.List({cx, cy, cz}, Renderer::shadowdist + 1, /*curtime,*/ false);\n        frame.Render(xpos, ypos, zpos, 3);\n\n        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);\n        glDisableVertexAttribArray(4);\n\n        glEnable(GL_FOG);\n        glEnable(GL_BLEND);\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Renderer/World/ShadowMaps.h",
    "content": "#pragma once\n\n#include \"Renderer/World/WorldRenderer.h\"\n\nnamespace ShadowMaps {\n    void BuildShadowMap(\n            WorldRenderer::ChunksRenderer &chunksRenderer,\n            double xpos, double ypos, double zpos, double curtime\n    );\n\n    void RenderShadowMap(\n            WorldRenderer::ChunksRenderer &chunksRenderer,\n            double xpos, double ypos, double zpos, double curtime\n    );\n    //void BuildCascadedShadowMaps(double xpos, double ypos, double zpos, double curtime);\n    //void DrawShadowMap(int xi, int yi, int xa, int ya);\n}"
  },
  {
    "path": "NEWorld.Game/Renderer/World/WorldRenderer.cpp",
    "content": "#include <queue>\n#include \"WorldRenderer.h\"\n#include <kls/coroutine/Operation.h>\n\nnamespace WorldRenderer {\n    int MaxChunkRenders = 64;\n    int chunkBuildRenders;\n\n    static constexpr auto ccOffset = Int3(7); // offset to a chunk center\n\n    void ChunksRenderer::FrustumUpdate(Double3 camera, Frustum &frus) {\n        for (auto &entry: mChunks) entry.Visiable = frus.FrustumTest(entry.getRelativeAABB(camera));\n    }\n\n    // TODO(make it better, the function is bad)\n    kls::coroutine::ValueAsync<void> ChunksRenderer::Update(Int3 position) {\n        struct SortEntry {\n            int Distance;\n            ChunkRender *Render;\n            std::shared_ptr<World::Chunk> Locked;\n        };\n        struct SortCmp {\n            [[nodiscard]] constexpr bool operator()(const SortEntry &l, const SortEntry &r) const {\n                return l.Distance > r.Distance;\n            }\n        };\n        // Pull in the added part\n        {\n            std::lock_guard lk{mMAdd};\n            mChunks.insert(mChunks.end(), mLAdd.begin(), mLAdd.end());\n            mLAdd.clear();\n        }\n        // Sort the update priority list, also update the frustum results\n        int invalidated = 0;\n        {\n            const auto cp = World::GetChunkPos(position);\n            std::priority_queue<SortEntry, kls::temp::vector<SortEntry>, SortCmp> candidate;\n            for (auto &entry: mChunks) {\n                if (auto lock = entry.Ref.lock(); lock) {\n                    if (!lock->updated) continue;\n                    const auto c = lock->GetPosition();\n                    if (ChebyshevDistance(c, cp) > viewdistance) continue;\n                    const auto distance = static_cast<int>(DistanceSquared(c * 16 + ccOffset, position));\n                    candidate.push(SortEntry{distance, &entry, std::move(lock)});\n                } else ++invalidated;\n            }\n            // walk the candidates and update max elements\n            int released = 0;\n            {\n                kls::temp::vector<kls::coroutine::ValueAsync<void>> operations{};\n                while ((released < MaxChunkRenders) && (!candidate.empty())) {\n                    auto& top = candidate.top();\n                    if (top.Render->CheckBuild(top.Locked)) {\n                        ++released;\n                        operations.push_back(top.Render->Rebuild(top.Locked));\n                    }\n                    candidate.pop();\n                }\n                co_await kls::coroutine::await_all(std::move(operations));\n            }\n            chunkBuildRenders = released;\n        }\n        // purge the table if there are too many dead items.\n        PurgeTable(invalidated);\n    }\n\n    void ChunksRenderer::PurgeTable(int invalidated) {\n        kls::temp::vector<VBOID> toRelease;\n        if (invalidated * 4 > mChunks.size()) {\n            std::vector<ChunkRender> swap;\n            for (auto& entry : mChunks) {\n                if (!entry.Ref.expired()) {\n                    swap.push_back(std::move(entry));\n                }\n                else if (entry.Built) {\n                    if (entry.Renders[0].Buffer != 0) toRelease.push_back(entry.Renders[0].Buffer);\n                    if (entry.Renders[1].Buffer != 0) toRelease.push_back(entry.Renders[1].Buffer);\n                    if (entry.Renders[2].Buffer != 0) toRelease.push_back(entry.Renders[2].Buffer);\n                    if (entry.Renders[3].Buffer != 0) toRelease.push_back(entry.Renders[3].Buffer);\n                }\n            }\n            mChunks.swap(swap);\n        }\n        if (!toRelease.empty()) glDeleteBuffers(toRelease.size(), toRelease.data());\n    }\n\n    FrameChunksRenderer::FrameChunksRenderer(\n            const std::vector<ChunkRender> &list,\n            Int3 cPos, int renderDist, bool frus\n    ) {\n        auto renderedChunks = 0;\n        for (auto &entry: list) {\n            if (!entry.Built) continue;\n            if (ChebyshevDistance(cPos, entry.Position) <= renderDist) {\n                if (!frus || entry.Visiable) {\n                    renderedChunks++;\n                    mFiltered.push_back(&entry);\n                    //RenderChunkList.emplace_back(chunk.get(), (curtime - lastUpdate) * 30.0);\n                    // position(c->GetPosition()), loadAnim(c->loadAnim * pow(0.6, TimeDelta)) {\n                }\n            }\n        }\n    }\n\n    void FrameChunksRenderer::Render(double x, double y, double z, int buffer) {\n        float m[16];\n        if (buffer != 3) {\n            memset(m, 0, sizeof(m));\n            m[0] = m[5] = m[10] = m[15] = 1.0f;\n        }\n        for (auto cr: mFiltered) {\n            if (cr->Renders[buffer].Count == 0) continue;\n            const auto offset = Double3(cr->Position) * 16.0 - Double3(x, /*cr.loadAnim*/ +y, z);\n            glPushMatrix();\n            glTranslated(offset.X, offset.Y, offset.Z);\n            if (Renderer::AdvancedRender && buffer != 3) {\n                m[12] = static_cast<float>(offset.X);\n                m[13] = static_cast<float>(offset.Y);\n                m[14] = static_cast<float>(offset.Z);\n                Renderer::GetPipeline()->SetUniform(4, m);\n            }\n            Renderer::RenderBufferDirect(cr->Renders[buffer].Buffer, cr->Renders[buffer].Count);\n            glPopMatrix();\n        }\n\n        glFlush();\n    }\n}"
  },
  {
    "path": "NEWorld.Game/Renderer/World/WorldRenderer.h",
    "content": "#pragma once\n\n#include \"Renderer/Renderer.h\"\n#include \"ChunkRenderer.h\"\n#include <kls/temp/STL.h>\n#include <kls/thread/SpinLock.h>\n\nnamespace WorldRenderer {\n    class FrameChunksRenderer {\n    public:\n        explicit FrameChunksRenderer(\n                const std::vector<ChunkRender> &list,\n                Int3 cPos, int renderDist, bool frus = true\n        );\n\n        void Render(double x, double y, double z, int buffer);\n\n    private:\n        kls::temp::vector<const ChunkRender *> mFiltered;\n    };\n\n    class ChunksRenderer {\n    public:\n        kls::coroutine::ValueAsync<void> Update(Int3 position);\n\n        void FrustumUpdate(Double3 camera, Frustum &frus);\n\n        void PurgeTable(int invalidated);\n\n        auto List(Int3 cPos, int renderDist, bool frus = true) {\n            return FrameChunksRenderer(mChunks, cPos, renderDist, frus);\n        }\n\n        void Add(const std::shared_ptr<World::Chunk> &c) {\n            std::lock_guard lk{mMAdd};\n            mLAdd.emplace_back(c);\n        }\n\n        // TODO(Try to remove this)\n        static ChunksRenderer &Default() {\n            static ChunksRenderer instance{};\n            return instance;\n        }\n\n    private:\n        kls::thread::SpinLock mMAdd;\n        std::vector<ChunkRender> mLAdd;\n        std::vector<ChunkRender> mChunks;\n    };\n\n    extern int chunkBuildRenders;\n}"
  },
  {
    "path": "NEWorld.Game/Setup.cpp",
    "content": "#include <iostream>\n#include \"Setup.h\"\n\n#include \"ControlContext.h\"\n#include \"Definitions.h\"\n#include \"Textures.h\"\n#include \"Renderer/Renderer.h\"\n#include \"Universe/World/World.h\"\n#include \"Items.h\"\n#include \"Common/Logger.h\"\n#include \"System/MessageBus.h\"\n\nvoid splashScreen() {\n    auto splTex = Textures::LoadRGBTexture(\"./Assets/Textures/GUI/SplashScreen.bmp\");\n    glEnable(GL_TEXTURE_2D);\n    for (auto i = 0; i < 256; i += 2) {\n        glfwSwapBuffers(MainWindow);\n        glfwPollEvents();\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        glBindTexture(GL_TEXTURE_2D, splTex);\n        glColor4f(static_cast<float>(i) / 256, static_cast<float>(i) / 256, static_cast<float>(i) / 256, 1.0);\n        glBegin(GL_QUADS);\n        glTexCoord2f(0.0, 1.0);\n        glVertex2i(-1, 1);\n        glTexCoord2f(850.0f / 1024.0f, 1.0);\n        glVertex2i(1, 1);\n        glTexCoord2f(850.0f / 1024.0f, 1.0 - 480.0f / 1024.0f);\n        glVertex2i(1, -1);\n        glTexCoord2f(0.0, 1.0 - 480.0f / 1024.0f);\n        glVertex2i(-1, -1);\n        glEnd();\n        SleepMs(10);\n    }\n    glDeleteTextures(1, &splTex);\n    glfwSwapBuffers(MainWindow);\n    glfwPollEvents();\n}\n\nvoid createWindow() {\n    glfwSetErrorCallback([](int, const char *desc) { std::cout << desc << std::endl; });\n    std::stringstream title;\n    title << \"NEWorld \" << MAJOR_VERSION << MINOR_VERSION << EXT_VERSION;\n    if (Multisample != 0) glfwWindowHint(GLFW_SAMPLES, Multisample);\n    MainWindow = glfwCreateWindow(windowwidth, windowheight, title.str().c_str(), NULL, NULL);\n\n    // high dpi screens deserve a larger window\n    float widthScale, heightScale;\n    glfwGetWindowContentScale(MainWindow, &widthScale, &heightScale);\n    windowwidth *= widthScale;\n    windowheight *= heightScale;\n    glfwSetWindowSize(MainWindow, windowwidth, windowheight);\n    glfwSwapBuffers(MainWindow);\n\n    MouseCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);\n    glfwMakeContextCurrent(MainWindow);\n    glewInit();\n    glfwSetCursor(MainWindow, MouseCursor);\n    glfwSetInputMode(MainWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n    glfwSetWindowSizeCallback(MainWindow, &WindowSizeFunc);\n    glfwSetMouseButtonCallback(MainWindow, &MouseButtonFunc);\n    glfwSetScrollCallback(MainWindow, &ControlContext::MouseScrollCallback);\n    glfwSetCharCallback(MainWindow, &CharInputFunc);\n    glfwSetKeyCallback(MainWindow, [](GLFWwindow* window, int key, int scancode, int action, int mods){\n        MessageBus::Default().Get<std::pair<int, int>>(\"KeyEvents\")->Send(nullptr, std::make_pair(key, action));\n    });\n}\n\nvoid message_callback(GLenum source, GLenum type, GLuint id, GLenum severity,\n                      GLsizei length, GLchar const *message, void const *user_param) {\n    auto const src_str = [source]() {\n        switch (source) {\n            case GL_DEBUG_SOURCE_API:\n                return \"API\";\n            case GL_DEBUG_SOURCE_WINDOW_SYSTEM:\n                return \"WINDOW SYSTEM\";\n            case GL_DEBUG_SOURCE_SHADER_COMPILER:\n                return \"SHADER COMPILER\";\n            case GL_DEBUG_SOURCE_THIRD_PARTY:\n                return \"THIRD PARTY\";\n            case GL_DEBUG_SOURCE_APPLICATION:\n                return \"APPLICATION\";\n            case GL_DEBUG_SOURCE_OTHER:\n                return \"OTHER\";\n        }\n        return \"UNKNOWN\";\n    }();\n\n    auto const type_str = [type]() {\n        switch (type) {\n            case GL_DEBUG_TYPE_ERROR:\n                return \"ERROR\";\n            case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:\n                return \"DEPRECATED_BEHAVIOR\";\n            case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:\n                return \"UNDEFINED_BEHAVIOR\";\n            case GL_DEBUG_TYPE_PORTABILITY:\n                return \"PORTABILITY\";\n            case GL_DEBUG_TYPE_PERFORMANCE:\n                return \"PERFORMANCE\";\n            case GL_DEBUG_TYPE_MARKER:\n                return \"MARKER\";\n            case GL_DEBUG_TYPE_OTHER:\n                return \"OTHER\";\n        }\n        return \"UNKNOWN\";\n    }();\n\n    auto const severity_str = [severity]() {\n        switch (severity) {\n            case GL_DEBUG_SEVERITY_NOTIFICATION:\n                return \"NOTIFICATION\";\n            case GL_DEBUG_SEVERITY_LOW:\n                return \"LOW\";\n            case GL_DEBUG_SEVERITY_MEDIUM:\n                return \"MEDIUM\";\n            case GL_DEBUG_SEVERITY_HIGH:\n                return \"HIGH\";\n        }\n        return \"UNKNOWN\";\n    }();\n    infostream << src_str << \", \" << type_str << \", \" << severity_str << \", \" << id << \": \" << message;\n}\n\nvoid setupScreen() {\n    glEnable(GL_DEBUG_OUTPUT);\n    glDebugMessageCallback(message_callback, nullptr);\n    glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE);\n    // TODO(this actually does matter, silenced for other debugging issues)\n    glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, GL_DONT_CARE, 0, nullptr, GL_FALSE);\n    //获取OpenGL版本\n    GLVersionMajor = glfwGetWindowAttrib(MainWindow, GLFW_CONTEXT_VERSION_MAJOR);\n    GLVersionMinor = glfwGetWindowAttrib(MainWindow, GLFW_CONTEXT_VERSION_MINOR);\n    GLVersionRev = glfwGetWindowAttrib(MainWindow, GLFW_CONTEXT_REVISION);\n    //获取OpenGL函数地址\n    infostream << \"GL Version: \" << GLVersionMajor << '.' << GLVersionMinor << '.' << GLVersionRev;\n    infostream << \"GL Vendor: \" << glGetString(GL_VENDOR);\n    infostream << \"GL Renderer: \" << glGetString(GL_RENDERER);\n    //渲染参数设置\n    glViewport(0, 0, windowwidth, windowheight);\n    glMatrixMode(GL_PROJECTION);\n    glLoadIdentity();\n    glMatrixMode(GL_MODELVIEW);\n    glLoadIdentity();\n    glShadeModel(GL_SMOOTH);\n    glDisable(GL_DITHER);\n    glEnable(GL_CULL_FACE);\n    glEnable(GL_TEXTURE_2D);\n    glEnable(GL_DEPTH_TEST);\n    glEnable(GL_ALPHA_TEST);\n    glEnable(GL_BLEND);\n    glEnable(GL_LINE_SMOOTH);\n    glDepthFunc(GL_LEQUAL);\n    glAlphaFunc(GL_GREATER, 0.0); //<--这家伙在卖萌？(往后面看看，卖萌的多着呢)\n    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);\n    glHint(GL_FOG_HINT, GL_FASTEST);\n    glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);\n    if (Multisample != 0) glEnable(GL_MULTISAMPLE_ARB);\n    glPixelStorei(GL_PACK_ALIGNMENT, 4);\n    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);\n    glColor4f(0.0, 0.0, 0.0, 1.0);\n    glClearColor(0.0, 0.0, 0.0, 1.0);\n    glClearDepth(1.0);\n    Renderer::initShaders();\n    if (vsync) glfwSwapInterval(1);\n    else glfwSwapInterval(0);\n}\n\nvoid setupNormalFog() {\n    float fogColor[4] = {skycolorR, skycolorG, skycolorB, 1.0f};\n    glEnable(GL_FOG);\n    glFogi(GL_FOG_MODE, GL_LINEAR);\n    glFogfv(GL_FOG_COLOR, fogColor);\n    glFogf(GL_FOG_START, viewdistance * 16.0f - 32.0f);\n    glFogf(GL_FOG_END, viewdistance * 16.0f);\n}\n\nvoid loadTextures() {\n    //载入纹理\n    Textures::Init();\n\n    tex_select = Textures::LoadRGBATexture(\"./Assets/Textures/GUI/select.bmp\", \"\");\n    tex_unselect = Textures::LoadRGBATexture(\"./Assets/Textures/GUI/unselect.bmp\", \"\");\n    for (auto i = 0; i < 6; i++) {\n        std::stringstream ss;\n        ss << \"./Assets/Textures/GUI/mainmenu\" << i << \".bmp\";\n        tex_mainmenu[i] = Textures::LoadRGBTexture(ss.str());\n    }\n\n    DefaultSkin = Textures::LoadRGBATexture(\"./Assets/Textures/Player/skin_xiaoqiao.bmp\",\n                                            \"./Assets/Textures/Player/skinmask_xiaoqiao.bmp\");\n\n    for (auto gloop = 1; gloop <= 10; gloop++) {\n        std::string result;\n        result = std::to_string(gloop);\n        const auto path = \"./Assets/Textures/Blocks/destroy_\" + result + \".bmp\";\n        DestroyImage[gloop] = Textures::LoadRGBATexture(path, path);\n    }\n\n    BlockTextures = Textures::LoadRGBATexture(\"./Assets/Textures/Blocks/Terrain.bmp\",\n                                              \"./Assets/Textures/Blocks/Terrainmask.bmp\");\n    loadItemsTextures();\n}\n\nvoid WindowSizeFunc(GLFWwindow *win, int width, int height) {\n    if (width < 640) width = 640;\n    if (height < 360) height = 360;\n    windowwidth = width;\n    windowheight = height > 0 ? height : 1;\n    glfwSetWindowSize(win, width, height);\n    setupScreen();\n}\n\nvoid MouseButtonFunc(GLFWwindow *, int button, int action, int) {\n    MessageBus::Default().Get<std::pair<int, int>>(\"Mouse\")->Send(nullptr, std::make_pair(button, action));\n}\n\nvoid CharInputFunc(GLFWwindow *, unsigned int c) {\n    MessageBus::Default().Get<int>(\"InputEvents\")->Send(nullptr, c);\n    if (c >= 128) {\n        const auto pwszUnicode = new wchar_t[2];\n        pwszUnicode[0] = static_cast<wchar_t>(c);\n        pwszUnicode[1] = '\\0';\n        auto pszMultiByte = static_cast<char *>(malloc(static_cast<unsigned int>(4)));\n        pszMultiByte = static_cast<char *>(realloc(pszMultiByte, WCharToMByte(pszMultiByte, pwszUnicode, 4)));\n        free(pszMultiByte);\n        delete[] pwszUnicode;\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Setup.h",
    "content": "#pragma once\n\nstruct GLFWwindow;\n\nvoid splashScreen();\n\nvoid createWindow();\n\nvoid setupScreen();\n\nvoid setupNormalFog();\n\nvoid loadTextures();\n\nvoid WindowSizeFunc(GLFWwindow *win, int width, int height);\n\nvoid MouseButtonFunc(GLFWwindow *, int button, int action, int);\n\nvoid CharInputFunc(GLFWwindow *, unsigned int c);\n\nvoid MouseScrollFunc(GLFWwindow *, double, double yoffset);\n"
  },
  {
    "path": "NEWorld.Game/Textures.cpp",
    "content": "#include \"Textures.h\"\n#include \"Items.h\"\n#include \"Universe/World/Blocks.h\"\n#include <cstring>\n#include <fstream>\n#include \"Common/Logger.h\"\n\nint BLOCKTEXTURE_SIZE, BLOCKTEXTURE_UNITSIZE, BLOCKTEXTURE_UNITS;\n\nnamespace Textures {\n\n    void Init() {\n        BLOCKTEXTURE_SIZE = 256;\n        BLOCKTEXTURE_UNITSIZE = 32;\n        BLOCKTEXTURE_UNITS = 8;\n    }\n\n    ubyte getTextureIndex(Block blockname, ubyte side) {\n        switch (blockname) {\n            case Blocks::ROCK:\n                return ROCK;\n            case Blocks::GRASS:\n                switch (side) {\n                    case 1:\n                        return GRASS_TOP;\n                    case 2:\n                        return GRASS_SIDE;\n                    case 3:\n                        return DIRT;\n                }\n            case Blocks::DIRT:\n                return DIRT;\n            case Blocks::STONE:\n                return STONE;\n            case Blocks::PLANK:\n                return PLANK;\n            case Blocks::WOOD:\n                switch (side) {\n                    case 1:\n                        return WOOD_TOP;\n                    case 2:\n                        return WOOD_SIDE;\n                    case 3:\n                        return WOOD_TOP;\n                }\n            case Blocks::BEDROCK:\n                return BEDROCK;\n            case Blocks::LEAF:\n                return LEAF;\n            case Blocks::GLASS:\n                return GLASS;\n            case Blocks::WATER:\n                return WATER;\n            case Blocks::LAVA:\n                return LAVA;\n            case Blocks::GLOWSTONE:\n                return GLOWSTONE;\n            case Blocks::SAND:\n                return SAND;\n            case Blocks::CEMENT:\n                return CEMENT;\n            case Blocks::ICE:\n                return ICE;\n            case Blocks::COAL:\n                return COAL;\n            case Blocks::IRON:\n                return IRON;\n            case Blocks::TNT:\n                return TNT;\n            default:\n                return NULLBLOCK;\n        }\n    }\n\n    double getTexcoordX(Item item, ubyte side) {\n        if (isBlock(item)) //如果为方块\n            return (getTextureIndex(item, side) & 7) / 8.0;\n        else\n            return NULLBLOCK;\n    }\n\n    double getTexcoordY(Item item, ubyte side) {\n        if (isBlock(item)) //如果为方块\n            return (getTextureIndex(item, side) >> 3) / 8.0;\n        else\n            return NULLBLOCK;\n    }\n\n    void LoadRGBImage(TEXTURE_RGB &tex, std::string Filename) {\n        unsigned int ind = 0;\n        auto& bitmap = tex; //返回位图\n        bitmap.buffer = nullptr;\n        bitmap.sizeX = bitmap.sizeY = 0;\n        std::ifstream bmpfile(Filename, std::ios::binary | std::ios::in); //位图文件（二进制）\n        if (!bmpfile.is_open()) {\n            warningstream << \"Cannot load \" << Filename;\n            return;\n        }\n        BITMAPINFOHEADER bih; //各种关于位图的参数\n        BITMAPFILEHEADER bfh; //各种关于文件的参数\n        //开始读取\n        bmpfile.read((char *) &bfh, sizeof(BITMAPFILEHEADER));\n        bmpfile.read((char *) &bih, sizeof(BITMAPINFOHEADER));\n        bitmap.sizeX = bih.biWidth;\n        bitmap.sizeY = bih.biHeight;\n        bitmap.buffer = std::unique_ptr<ubyte[]>(new unsigned char[bitmap.sizeX * bitmap.sizeY * 3]);\n        bmpfile.read((char *) bitmap.buffer.get(), bitmap.sizeX * bitmap.sizeY * 3);\n        bmpfile.close();\n        for (unsigned int i = 0; i < bitmap.sizeX * bitmap.sizeY; i++) {\n            const auto t = bitmap.buffer[ind];\n            bitmap.buffer[ind] = bitmap.buffer[ind + 2];\n            bitmap.buffer[ind + 2] = t;\n            ind += 3;\n        }\n    }\n\n    void LoadRGBAImage(TEXTURE_RGBA &tex, std::string Filename, std::string MkFilename) {\n        unsigned char *rgb = nullptr, *a = nullptr;\n        unsigned int ind = 0;\n        auto noMaskFile = (MkFilename == \"\");\n        auto& bitmap = tex; //·µ»ØÎ»Í¼\n        bitmap.buffer = nullptr;\n        bitmap.sizeX = bitmap.sizeY = 0;\n        std::ifstream bmpfile(Filename, std::ios::binary | std::ios::in);\n        std::ifstream maskfile;\n        if (!noMaskFile)maskfile.open(MkFilename, std::ios::binary | std::ios::in);\n        if (!bmpfile.is_open()) {\n            warningstream << \"Cannot load bitmap \" << Filename;\n            return;\n        }\n        if (!noMaskFile && !maskfile.is_open()) {\n            warningstream << \"Cannot load bitmap \" << MkFilename;\n            return;\n        }\n        BITMAPFILEHEADER bfh;\n        BITMAPINFOHEADER bih;\n\n        if (!noMaskFile) {\n            maskfile.read((char *) &bfh, sizeof(BITMAPFILEHEADER));\n            maskfile.read((char *) &bih, sizeof(BITMAPINFOHEADER));\n        }\n        bmpfile.read((char *) &bfh, sizeof(BITMAPFILEHEADER));\n        bmpfile.read((char *) &bih, sizeof(BITMAPINFOHEADER));\n        bitmap.sizeX = bih.biWidth;\n        bitmap.sizeY = bih.biHeight;\n        bitmap.buffer = std::unique_ptr<ubyte[]>(new unsigned char[bitmap.sizeX * bitmap.sizeY * 4]);\n        //¶ÁÈ¡Êý¾Ý\n        rgb = new unsigned char[bitmap.sizeX * bitmap.sizeY * 3];\n        bmpfile.read((char *) rgb, bitmap.sizeX * bitmap.sizeY * 3);\n        bmpfile.close();\n        if (!noMaskFile) {\n            a = new unsigned char[bitmap.sizeX * bitmap.sizeY * 3];\n            maskfile.read((char *) a, bitmap.sizeX * bitmap.sizeY * 3);\n            maskfile.close();\n        }\n        //ºÏ²¢Óë×ª»»\n        for (unsigned int i = 0; i < bitmap.sizeX * bitmap.sizeY; i++) {\n            //°ÑBGR¸ñÊ½×ª»»ÎªRGB¸ñÊ½\n            bitmap.buffer[ind] = rgb[i * 3 + 2];\n            bitmap.buffer[ind + 1] = rgb[i * 3 + 1];\n            bitmap.buffer[ind + 2] = rgb[i * 3];\n            //Alpha\n            if (noMaskFile) bitmap.buffer[ind + 3] = 255;\n            else bitmap.buffer[ind + 3] = 255 - a[i * 3];\n            ind += 4;\n        }\n    }\n\n    TextureID LoadRGBTexture(std::string Filename) {\n        TEXTURE_RGB image;\n        TextureID ret;\n        LoadRGBImage(image, Filename);\n        glGenTextures(1, &ret);\n        glBindTexture(GL_TEXTURE_2D, ret);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);\n        Build2DMipmaps(GL_RGB, image.sizeX, image.sizeY, static_cast<int>(log2(image.sizeX)), image.buffer.get());\n        return ret;\n    }\n\n    TextureID LoadFontTexture(std::string Filename) {\n        TEXTURE_RGBA Texture;\n        TEXTURE_RGB image;\n        TextureID ret;\n        LoadRGBImage(image, Filename);\n        Texture.sizeX = image.sizeX;\n        Texture.sizeY = image.sizeY;\n        Texture.buffer = std::unique_ptr<ubyte[]>(new unsigned char[image.sizeX * image.sizeY * 4]);\n        if (Texture.buffer == nullptr) {\n            printf(\"[console][Warning] Cannot alloc memory when loading %s\\n\", Filename.c_str());\n            return 0;\n        }\n        auto ip = image.buffer.get();\n        auto tp = Texture.buffer.get();\n        for (unsigned int i = 0; i != image.sizeX * image.sizeY; i++) {\n            *tp = 255;\n            tp++;\n            *tp = 255;\n            tp++;\n            *tp = 255;\n            tp++;\n            *tp = 255 - *ip;\n            tp++;\n            ip += 3;\n        }\n        glGenTextures(1, &ret);\n        glBindTexture(GL_TEXTURE_2D, ret);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Texture.sizeX, Texture.sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE,\n                     Texture.buffer.get());\n        return ret;\n    }\n\n    TextureID LoadRGBATexture(std::string Filename, std::string MkFilename) {\n        TextureID ret;\n        TEXTURE_RGBA image;\n        LoadRGBAImage(image, Filename, MkFilename);\n        glGenTextures(1, &ret);\n        glBindTexture(GL_TEXTURE_2D, ret);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);\n        Build2DMipmaps(GL_RGBA, image.sizeX, image.sizeY, static_cast<int>(log2(BLOCKTEXTURE_UNITSIZE)), image.buffer.get());\n        return ret;\n    }\n\n    void SaveRGBImage(std::string filename, TEXTURE_RGB &image) {\n        BITMAPFILEHEADER bitmapfileheader;\n        BITMAPINFOHEADER bitmapinfoheader;\n        bitmapfileheader.bfSize = image.sizeX * image.sizeY * 3 + 54;\n        bitmapinfoheader.biWidth = image.sizeX;\n        bitmapinfoheader.biHeight = image.sizeY;\n        bitmapinfoheader.biSizeImage = image.sizeX * image.sizeY * 3;\n        for (unsigned int i = 0; i != image.sizeX * image.sizeY * 3; i += 3) {\n            const auto t = image.buffer.get()[i];\n            image.buffer.get()[i] = image.buffer.get()[i + 2];\n            image.buffer.get()[i + 2] = t;\n        }\n        std::ofstream ofs(filename, std::ios::out | std::ios::binary);\n        ofs.write((char *) &bitmapfileheader, sizeof(bitmapfileheader));\n        ofs.write((char *) &bitmapinfoheader, sizeof(bitmapinfoheader));\n        ofs.write((char *) image.buffer.get(), sizeof(ubyte) * image.sizeX * image.sizeY * 3);\n        ofs.close();\n    }\n\n    void Build2DMipmaps(GLenum format, int w, int h, int level, const ubyte *src) {\n        auto sum = 0, scale = 1, cur_w = 0, cur_h = 0, cc = 0;\n        if (format == GL_RGBA) cc = 4;\n        else if (format == GL_RGB) cc = 3;\n        const auto cur = new ubyte[w * h * cc];\n        memset(cur, 0, w * h * cc);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, level);\n        glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f);\n        glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, src);\n        for (auto i = 1; i <= level; i++) {\n            scale <<= 1;\n            cur_w = w / scale;\n            cur_h = h / scale;\n            for (auto y = 0; y < cur_h; y++)\n                for (auto x = 0; x < cur_w; x++) {\n                    for (auto col = 0; col < cc; col++) {\n                        sum = 0;\n                        for (auto yy = 0; yy < scale; yy++)\n                            for (auto xx = 0; xx < scale; xx++) {\n                                sum += src[((y * scale + yy) * w + x * scale + xx) * cc + col];\n                            }\n                        cur[(y * cur_w + x) * cc + col] = static_cast<ubyte>(sum / (scale * scale));\n                    }\n                }\n            glTexImage2D(GL_TEXTURE_2D, i, format, cur_w, cur_h, 0, format, GL_UNSIGNED_BYTE, cur);\n        }\n        delete[] cur;\n    }\n\n}"
  },
  {
    "path": "NEWorld.Game/Textures.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n\nextern int BLOCKTEXTURE_SIZE, BLOCKTEXTURE_UNITSIZE, filter;\nconst short BITMAP_ID = 0x4D42;\n\nnamespace Textures {\n\n#pragma pack(push)\n#pragma pack(1)\n    struct TEXTURE_RGB {\n        unsigned int sizeX;\n        unsigned int sizeY;\n        std::unique_ptr<ubyte[]> buffer;\n    };\n\n    struct TEXTURE_RGBA {\n        unsigned int sizeX;\n        unsigned int sizeY;\n        std::unique_ptr<ubyte[]> buffer;\n    };\n\n    struct BITMAPINFOHEADER {\n        int biSize = 40, biWidth, biHeight;\n        short biPlanes = 1, biBitCount = 24;\n        int biCompression = 0, biSizeImage, biXPelsPerMeter = 0, biYPelsPerMeter = 0, biClrUsed = 0, biClrImportant = 0;\n    };\n\n    struct BITMAPFILEHEADER {\n        short bfType = BITMAP_ID;\n        int bfSize;\n        short bfReserved1 = 0, bfReserved2 = 0;\n        int bfOffBits = 54;\n    };\n#pragma pack(pop)\n\n    enum BlockTextureID {\n        ROCK, GRASS_TOP, GRASS_SIDE, DIRT, STONE, PLANK, WOOD_TOP, WOOD_SIDE, BEDROCK, LEAF,\n        GLASS, WATER, LAVA, GLOWSTONE, SAND, CEMENT, ICE, COAL, IRON, TNT, UNKNOWN\n    };\n    const int NULLBLOCK = 63;\n\n    void Init();\n\n    ubyte getTextureIndex(Block blockname, ubyte side);\n\n    double getTexcoordX(Item item, ubyte side);\n\n    double getTexcoordY(Item item, ubyte side);\n\n    void LoadRGBImage(TEXTURE_RGB &tex, std::string Filename);\n\n    void LoadRGBAImage(TEXTURE_RGBA &tex, std::string Filename, std::string MkFilename);\n\n    TextureID LoadRGBTexture(std::string Filename);\n\n    TextureID LoadFontTexture(std::string Filename);\n\n    TextureID LoadRGBATexture(std::string Filename, std::string MkFilename);\n\n    void SaveRGBImage(std::string filename, TEXTURE_RGB &image);\n\n    void Build2DMipmaps(GLenum format, int w, int h, int level, const ubyte *src);\n\n}\n"
  },
  {
    "path": "NEWorld.Game/Tick.cpp",
    "content": "#include \"Tick.h\"\n#include <unordered_map>\n#include <kls/temp/STL.h>\n#include <kls/coroutine/Operation.h>\n\nstd::shared_ptr<TickPipeline> CreateTickPipeline(std::vector<std::shared_ptr<TickComponent>> c) {\n\tstruct ComponentHolder;\n\n\tstruct ComponentConstructionInfo {\n\t\tint ActualDependencyCount{};\n\t\tComponentHolder* FutureHolder{};\n\t\tstd::vector<ComponentHolder*> ComponentsToRelease{};\n\t};\n\n\tstruct ComponentHolder {\n\t\tconst int ActualDependencyCount{};\n\t\tstd::shared_ptr<TickComponent> Component{};\n\t\tstd::vector<ComponentHolder*> ComponentsToRelease{};\n\t\tstd::atomic_int AwaitingDependencies{ 0 };\n\n\t\tconstexpr ComponentHolder() noexcept = default;\n\n\t\tComponentHolder(ComponentConstructionInfo&& construct, std::shared_ptr<TickComponent> c) noexcept :\n\t\t\tActualDependencyCount(construct.ActualDependencyCount), Component(std::move(c)),\n\t\t\tComponentsToRelease(std::move(construct.ComponentsToRelease)) {}\n\n\t\tComponentHolder(ComponentHolder&& o) noexcept :\n\t\t\tActualDependencyCount(o.ActualDependencyCount), Component(std::move(o.Component)),\n\t\t\tComponentsToRelease(std::move(o.ComponentsToRelease)) {}\n\n        kls::coroutine::ValueAsync<void> Launch() {\n\t\t\tco_await kls::coroutine::Redispatch();\n\t\t\tco_await Component->Evaluate();\n            kls::temp::vector<kls::coroutine::ValueAsync<void>> childern{};\n\t\t\tchildern.reserve(ComponentsToRelease.size()); // reserve the max size\n\t\t\t// decrease the counter for all released component and spawn if necessary\n\t\t\tfor (auto next : ComponentsToRelease) {\n\t\t\t\tif (next->AwaitingDependencies.fetch_sub(1) == 1) childern.push_back(next->Launch());\n\t\t\t}\n\t\t\tco_await kls::coroutine::await_all(std::move(childern));\n\t\t}\n\t};\n\n\tclass Pipeline : public TickPipeline {\n\tpublic:\n\t\texplicit Pipeline(std::vector<std::shared_ptr<TickComponent>> components) {\n\t\t\tstd::unordered_map<TickComponent*, ComponentConstructionInfo> info{};\n\t\t\t// construct the sorting base\n\t\t\tmComponents.reserve(components.size()); // this is done to ensure the references remains valid\n\t\t\tfor (auto& c : components) {\n\t\t\t\tinfo.insert_or_assign(c.get(), ComponentConstructionInfo{ 0, &mComponents.emplace_back(), {} });\n\t\t\t}\n\t\t\t// construct dependency graph\n\t\t\tfor (auto& c : components) {\n\t\t\t\tfor (auto& b : c->GetToEvalBefore()) {\n\t\t\t\t\tinfo[c.get()].ActualDependencyCount++;\n\t\t\t\t\tinfo[b].ComponentsToRelease.push_back(info[c.get()].FutureHolder);\n\t\t\t\t}\n\t\t\t\tfor (auto& a : c->GetToEvalAfter()) {\n\t\t\t\t\tinfo[c.get()].ComponentsToRelease.push_back(info[a].FutureHolder);\n\t\t\t\t\tinfo[a].ActualDependencyCount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// consolidates the component graph information\n\t\t\tfor (auto& c : components) {\n\t\t\t\tconst auto ptr = c.get();\n\t\t\t\tauto& construct = info[ptr];\n\t\t\t\tauto& holder = *construct.FutureHolder;\n\t\t\t\t// we have to reuse the address for constructing the new holder\n\t\t\t\tstd::destroy_at(&holder);\n\t\t\t\tstd::construct_at(&holder, std::move(construct), std::move(c));\n\t\t\t\tif (holder.ActualDependencyCount == 0) mComponentRoots.push_back(&holder);\n\t\t\t}\n\t\t}\n\n        kls::coroutine::ValueAsync<void> Evaluate() override {\n\t\t\t// reset all component counters\n\t\t\tfor (auto& c : mComponents) c.AwaitingDependencies.store(c.ActualDependencyCount);\n            kls::temp::vector<kls::coroutine::ValueAsync<void>> childern{};\n\t\t\tchildern.reserve(mComponents.size()); // reserve the exact size\n\t\t\t// launch all the roots and await for the result\n\t\t\tfor (auto root : mComponentRoots) childern.push_back(root->Launch());\n\t\t\tco_await kls::coroutine::await_all(std::move(childern));\n\t\t}\n\tprivate:\n\t\tstd::vector<ComponentHolder> mComponents{};\n\t\tstd::vector<ComponentHolder*> mComponentRoots{};\n\n\t};\n\n\treturn std::make_shared<Pipeline>(std::move(c));\n}\n"
  },
  {
    "path": "NEWorld.Game/Tick.h",
    "content": "#pragma once\n\n#include <vector>\n#include <kls/Object.h>\n#include <kls/coroutine/Async.h>\n\nstruct TickComponent: kls::PmrBase {\n\tvirtual std::vector<TickComponent*> GetToEvalAfter() = 0;\n\tvirtual std::vector<TickComponent*> GetToEvalBefore() = 0;\n\tvirtual kls::coroutine::ValueAsync<void> Evaluate() = 0;\n};\n\nstruct TickPipeline: kls::PmrBase {\n\tvirtual kls::coroutine::ValueAsync<void> Evaluate() = 0;\n};\n\nstd::shared_ptr<TickPipeline> CreateTickPipeline(std::vector<std::shared_ptr<TickComponent>>);\n"
  },
  {
    "path": "NEWorld.Game/Typedefs.h",
    "content": "#pragma once\n//Types/constants define\n\n#include <cstdint>\n#include <thread>\n#include <mutex>\n\ntypedef unsigned char ubyte;\n\ntypedef unsigned char blockprop;\ntypedef unsigned char Brightness;\ntypedef unsigned int TextureID;\ntypedef uint32_t Item;\ntypedef unsigned int VBOID;\ntypedef int vtxCount;\ntypedef uint64_t chunkid;\ntypedef unsigned int onlineid;\n#ifdef NEWORLD_GAME\ntypedef std::mutex *Mutex_t;\n\n#endif\n\nusing Block = uint32_t;\n"
  },
  {
    "path": "NEWorld.Game/Universe/CommandHandler.h",
    "content": "#pragma once\n\n#include \"Command.h\"\n#include \"Universe/World/Blocks.h\"\n#include \"Universe/World/World.h\"\n#include \"Items.h\"\n\nstd::vector<Command> commands;\n\nclass CommandHandler {\n\npublic:\n    static bool doCommand(const std::vector<std::string> &command) {\n        for (auto &i: commands) {\n            if (command[0] == i.identifier) return i.execute(command);\n        }\n        return false;\n    }\n\n    static void registerCommands() {\n        commands.emplace_back(\"/setblock\", [](const std::vector<std::string> &command) {\n            if (command.size() != 5) return false;\n            const auto x = std::stoi(command[1]);\n            const auto y = std::stoi(command[2]);\n            const auto z = std::stoi(command[3]);\n            const Block b = std::stoll(command[4]);\n            World::SetBlock({x, y, z}, b);\n            return true;\n        });\n        commands.emplace_back(\"/tree\", [](const std::vector<std::string> &command) {\n            if (command.size() != 4) return false;\n            const auto x = std::stoi(command[1]);\n            const auto y = std::stoi(command[2]);\n            const auto z = std::stoi(command[3]);\n            World::buildtree({x, y, z});\n            return true;\n        });\n        commands.emplace_back(\"/explode\", [](const std::vector<std::string> &command) {\n            if (command.size() != 5) return false;\n            const auto x = std::stoi(command[1]);\n            const auto y = std::stoi(command[2]);\n            const auto z = std::stoi(command[3]);\n            const auto r = std::stoi(command[4]);\n            World::explode(x, y, z, r);\n            return true;\n        });\n        commands.emplace_back(\"/time\", [](const std::vector<std::string> &command) {\n            if (command.size() != 2) return false;\n            const auto time = std::stoi(command[4]);\n            if (time < 0 || time > gameTimeMax) return false;\n            gametime = time;\n            return true;\n        });\n    }\n};\n"
  },
  {
    "path": "NEWorld.Game/Universe/Entity/Entity.cpp",
    "content": "#include \"Entity.h\"\n\n#include \"Universe/World/World.h\"\n\nInt3 Entity::getChunkPosition() const noexcept\n{ return Int3(mPosition, World::GetChunkPos<int>); }\n\nvoid Entity::move(const EntityBVH& bvh) {\n\tif (!doCollisionCheck()) {\n\t\tmVelocityForRendering = mVelocity;\n\t\tmPosition += mVelocity;\n\t\treturn;\n\t}\n\tauto currentHitbox = bounding_box();\n\tauto currentMovementHitbox = movement_bounding_box();\n\tauto hitboxes = World::getHitboxes(currentMovementHitbox);\n\n\tconst auto collisions = bvh.intersect(currentMovementHitbox);\n\tfor (const auto& entity : collisions) {\n\t\tif (entity == this) continue;\n\t\thitboxes.push_back(entity->bounding_box());\n\t}\n\n\tauto actualMovement = getVelocity();\n\tfor (const auto& box : hitboxes) {\n\t\tactualMovement.Y = AABB::MaxMove(currentHitbox, box, actualMovement.Y, 1);\n\t}\n\tcurrentHitbox.min += Vector3(0.0, actualMovement.Y, 0.0);\n\tcurrentHitbox.max += Vector3(0.0, actualMovement.Y, 0.0);\n\tfor (const auto& box : hitboxes) {\n\t\tactualMovement.X = AABB::MaxMove(currentHitbox, box, actualMovement.X, 0);\n\t}\n\tcurrentHitbox.min += Vector3(actualMovement.X, 0.0, 0.0);\n\tcurrentHitbox.max += Vector3(actualMovement.X, 0.0, 0.0);\n\tfor (const auto& box : hitboxes) {\n\t\tactualMovement.Z = AABB::MaxMove(currentHitbox, box, actualMovement.Z, 2);\n\t}\n\tmPosition += actualMovement;\n\tmVelocityForRendering = actualMovement;\n\tafterMove(actualMovement);\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/Entity/Entity.h",
    "content": "#pragma once\n\n#include \"bvh.h\"\n#include \"Definitions.h\"\n#include \"Math/Vector3.h\"\n\nstruct RenderProperties {\n    Double3 position;\n    double heading, lookUpDown;\n};\n\nclass Entity {\npublic:\n    Entity(Double3 position, Double3 size) : mPosition(position), mSize(size), mVelocity() {}\n    virtual ~Entity() = default;\n\n    [[nodiscard]] Vector3 center() const { return toBvhVec(mPosition); }\n    [[nodiscard]] BoundingBox bounding_box() const {\n\t    return { toBvhVec(mPosition - mSize / 2), toBvhVec(mPosition + mSize / 2) };\n    }\n    [[nodiscard]] BoundingBox movement_bounding_box() const {\n        return bounding_box().extend(AABB::Move(bounding_box(), mVelocity));\n    }\n\n    [[nodiscard]] bool intersect(const BoundingBox& box) const {\n        return AABB::Intersect(bounding_box(), box);\n    }\n\n    [[nodiscard]] Double3 getPosition() const noexcept { return mPosition; }\n    [[nodiscard]] Double3 getSize() const noexcept { return mSize; }\n    [[nodiscard]] Double3 getVelocity() const noexcept { return mVelocity; }\n    [[nodiscard]] virtual bool doCollisionCheck() const noexcept { return true; }\n    Int3 getChunkPosition() const noexcept;\n\n    // Move the position by velocity. Will use World for collision check\n    void move(const EntityBVH& bvh);\n    virtual void afterMove(Double3 actualMovement) {}\n\n    virtual void update() = 0;\n    virtual void render() = 0;\n    \n    [[nodiscard]] double getHeading() const noexcept { return mHeading; }\n    [[nodiscard]] double getLookUpDown() const noexcept { return mLookUpDown; }\n\n    [[nodiscard]] virtual RenderProperties getPropertiesForRender(double timeDelta) const noexcept {\n        // timeDelta is the time since the last update frame.\n        auto cameraPosition = mPosition + (timeDelta * MaxUpdateFPS - 1) * mVelocityForRendering;\n        return{ cameraPosition, mHeading + mXLookSpeed, std::clamp(mLookUpDown + mYLookSpeed, -90.0, 90.0) };\n    }\n\nprotected:\n    Double3 mPosition, mSize, mVelocity;\n\n    double mLookUpDown = 90, mHeading = 0;\n    double mXLookSpeed = 0, mYLookSpeed = 0;\nprivate:\n    // The actual velocity used for movement in this frame\n    // can be used for inter-frame interpolation.\n    Double3 mVelocityForRendering;\n};\n"
  },
  {
    "path": "NEWorld.Game/Universe/Entity/PlayerEntity.cpp",
    "content": "#include \"PlayerEntity.h\"\n#include <fstream>\n#include <sstream>\n\n#include \"ControlContext.h\"\n#include \"Universe/World/Blocks.h\"\n#include \"Universe/World/World.h\"\n\nvoid PlayerEntity::update() {\n    // Process player's health\n    if (mGameMode != GameMode::Creative) {\n        if (mHealth > 0) {\n            if (mPosition.Y < -100) mHealth -= (-100 - mPosition.Y) / 100;\n            mHealth = std::min(mHealth + mHealSpeed, mMaxHealth);\n        }\n        else {\n            spawn();\n        }\n    }\n\n    if (!mFlying && !mCrossWall && World::inWater(bounding_box())) {\n        mVelocity *= 0.6;\n    }\n}\n\nRenderProperties PlayerEntity::renderUpdate(const ControlContext& control, bool freeze, double lastUpdate) {\n    if (freeze) {\n        mYLookSpeed = mXLookSpeed = 0;\n        return getPropertiesForRender(0);\n    }\n    if (isOnGround()) {\n        //半蹲特效\n        if (mCurrentJumpSpeed < -0.005) {\n            if (mCurrentJumpSpeed <= -(mHeight - 0.5f))\n                mHeightExt = -(mHeight - 0.5f);\n            else\n                mHeightExt = static_cast<float>(mCurrentJumpSpeed);\n            TouchdownAnimTimer = control.Current.Time;\n        }\n        else {\n            if (mHeightExt <= -0.005) {\n                mHeightExt *= static_cast<float>(pow(0.8, (control.Current.Time - TouchdownAnimTimer) * 30));\n                TouchdownAnimTimer = control.Current.Time;\n            }\n        }\n    }\n\n    const auto timeDelta = control.Current.Time - lastUpdate;\n\n    //转头！你治好了我多年的颈椎病！\n    mXLookSpeed -= (control.Current.MousePosition.X - control.Last.MousePosition.X) * mousemove;\n    mYLookSpeed += (control.Current.MousePosition.Y - control.Last.MousePosition.Y) * mousemove;\n    if (control.KeyPressed(GLFW_KEY_RIGHT))\n        mXLookSpeed -= mousemove * 16 * timeDelta * 30.0;\n    if (control.KeyPressed(GLFW_KEY_LEFT))\n        mXLookSpeed += mousemove * 16 * timeDelta * 30.0;\n    if (control.KeyPressed(GLFW_KEY_UP))\n        mYLookSpeed -= mousemove * 16 * timeDelta * 30.0;\n    if (control.KeyPressed(GLFW_KEY_DOWN))\n        mYLookSpeed += mousemove * 16 * timeDelta * 30.0;\n    \n    return getPropertiesForRender(timeDelta);\n}\n\nvoid PlayerEntity::controlUpdate(const ControlContext& control) {\n    //更新方向\n    mHeading = fmod(mHeading + mXLookSpeed, 360.0);\n    mLookUpDown = std::clamp(mLookUpDown + mYLookSpeed, -90.0, 90.0);\n    mXLookSpeed = mYLookSpeed = 0.0;\n\n    ProcessNavigate(control);\n    HotbarItemSelect(control);\n\n    if (control.KeyPressed(GLFW_KEY_SPACE)) StartJump();\n\n    if (control.KeyPressed(GLFW_KEY_LEFT_SHIFT) || control.KeyPressed(GLFW_KEY_RIGHT_SHIFT)) {\n        if (mCrossWall || mFlying) mVelocity.Y -= walkspeed / 2;\n    }\n    mSpeedBoost = control.KeyPressed(GLFW_KEY_F) ? 10 : 1;\n\t//跳跃\n    ProcessJump();\n}\n\nvoid PlayerEntity::ProcessNavigate(const ControlContext& control) {\n    auto speed = getSpeed();\n\n    if (control.KeyJustDoublePressed(GLFW_KEY_W)) {\n        mRunning = true;\n    }\n        \n    if (control.KeyPressed(GLFW_KEY_W)) {\n        mVelocity.X += -sin(mHeading * M_PI / 180.0) * speed;\n        mVelocity.Z += -cos(mHeading * M_PI / 180.0) * speed;\n    }\n    else {\n        mRunning = false;\n    }\n\n    if (control.KeyPressed(GLFW_KEY_S) == GLFW_PRESS) {\n        mVelocity.X += sin(mHeading * M_PI / 180.0) * speed;\n        mVelocity.Z += cos(mHeading * M_PI / 180.0) * speed;\n    }\n\n    if (control.KeyPressed(GLFW_KEY_A) == GLFW_PRESS) {\n        mVelocity.X += sin((mHeading - 90) * M_PI / 180.0) * speed;\n        mVelocity.Z += cos((mHeading - 90) * M_PI / 180.0) * speed;\n    }\n\n    if (control.KeyPressed(GLFW_KEY_D) == GLFW_PRESS) {\n        mVelocity.X += -sin((mHeading - 90) * M_PI / 180.0) * speed;\n        mVelocity.Z += -cos((mHeading - 90) * M_PI / 180.0) * speed;\n    }\n\n    if (!mFlying && !mCrossWall) {\n        const auto horizontalSpeed = sqrt(mVelocity.X * mVelocity.X + mVelocity.Z * mVelocity.Z);\n        if (horizontalSpeed > speed) {\n            mVelocity.X *= speed / horizontalSpeed;\n            mVelocity.Z *= speed / horizontalSpeed;\n        }\n    }\n}\n\nvoid PlayerEntity::ProcessJump() {\n    if (!mInWater) {\n        if (!mFlying && !mCrossWall) {\n            mVelocity.Y = -0.001;\n            if (mOnGround) {\n                mCurrentJumpSpeed = 0.0;\n                mAirJumps = 0;\n            }\n            else {\n                //自由落体计算\n                mCurrentJumpSpeed -= 0.025;\n                mVelocity.Y = mCurrentJumpSpeed + 0.5 * 0.6 / 900.0;\n            }\n        }\n        else {\n            mCurrentJumpSpeed = 0.0;\n            mAirJumps = 0;\n        }\n    }\n    else {\n        mCurrentJumpSpeed = 0.0;\n        mAirJumps = MaxAirJumps;\n        if (mVelocity.Y <= 0.001 && !mFlying && !mCrossWall) {\n            mVelocity.Y = -0.001;\n            if (!mOnGround) mVelocity.Y -= 0.1;\n        }\n    }\n}\n\nvoid PlayerEntity::HotbarItemSelect(const ControlContext& control) {\n    //切换方块\n    if (control.KeyPressed(GLFW_KEY_Z) && mIndexInHand > 0) mIndexInHand--;\n    if (control.KeyPressed(GLFW_KEY_X) && mIndexInHand < 9) mIndexInHand++;\n    auto deltaScroll = control.Last.MouseScroll - control.Current.MouseScroll;\n    if (static_cast<int>(mIndexInHand) + deltaScroll < 0)mIndexInHand = 9;\n    else if (static_cast<int>(mIndexInHand) + deltaScroll > 9)mIndexInHand = 0;\n    else mIndexInHand += static_cast<char>(deltaScroll);\n}\n\nvoid PlayerEntity::StartJump() {\n    if (!mInWater) {\n        if ((mOnGround || mAirJumps < MaxAirJumps) && !mFlying &&\n            !mCrossWall) {\n            if (!mOnGround) {\n                mCurrentJumpSpeed = 0.3;\n                mAirJumps++;\n            }\n            else {\n                mCurrentJumpSpeed = 0.25;\n                mOnGround = false;\n            }\n        }\n        if (mFlying || mCrossWall) {\n            mVelocity.Y += walkspeed / 2;\n        }\n    }\n    else {\n        mVelocity.Y = walkspeed;\n    }\n}\n\nvoid PlayerEntity::spawn() {\n    mPosition = { 0,60,0 };\n    mVelocity = { 0,0,0 };\n    mHealth = mMaxHealth;\n    mCurrentJumpSpeed = 0.0;\n    memset(mInventory, 0, sizeof(mInventory));\n    \n    for (size_t i = 0; i < 255; i++) {\n        addItem(Blocks::ROCK);\n        addItem(Blocks::GRASS);\n        addItem(Blocks::DIRT);\n        addItem(Blocks::STONE);\n        addItem(Blocks::PLANK);\n        addItem(Blocks::WOOD);\n        addItem(Blocks::LEAF);\n        addItem(Blocks::GLASS);\n        addItem(Blocks::WATER);\n        addItem(Blocks::LAVA);\n        addItem(Blocks::GLOWSTONE);\n        addItem(Blocks::SAND);\n        addItem(Blocks::CEMENT);\n        addItem(Blocks::ICE);\n        addItem(Blocks::COAL);\n        addItem(Blocks::IRON);\n        addItem(Blocks::TNT);\n    }\n}\n\nvoid PlayerEntity::afterMove(Double3 actualMovement) {\n\t// if we tried to go down but did not actually go down - we hit the ground/floor.\n    if (actualMovement.Y != mVelocity.Y && mVelocity.Y < 0) {\n        mOnGround = true;\n        // fall damage\n        if (mVelocity.Y < -0.4 && mGameMode == GameMode::Survival) {\n            mHealth += mVelocity.Y * mDropDamage; // mVelocity.Y is negative\n        }\n    }\n    else mOnGround = false;\n\n    // when we hit the ceil\n    if (actualMovement.Y != mVelocity.Y && mVelocity.Y > 0) mCurrentJumpSpeed = 0.0;\n\n    // when we hit walls\n    mNearWall = actualMovement.X != mVelocity.X || actualMovement.Z != mVelocity.Z;\n\n    mVelocity = Double3(Int3(mVelocity * 100000)) / 100000.0;\n\n    mVelocity *= 0.8;\n    if (mFlying || mCrossWall) mVelocity.Y *= 0.8;\n    if (mOnGround) {\n        mVelocity *= .7;\n        mVelocity.Y = 0;\n    }\n}\n\nbool PlayerEntity::placeBlock(Int3 position, Block blockname) {\n    if (!World::ChunkOutOfBound(getChunkPosition())\n        && (!AABB::Intersect(bounding_box(), AABB::BoxForBlock(position)) || mCrossWall || !BlockInfo(blockname).isSolid())\n        && !BlockInfo(World::GetBlock(position)).isSolid()) {\n\n        World::PutBlock(position, blockname);\n        return true;\n\n    }\n    return false;\n}\n\nbool PlayerEntity::addItem(Item itemname, short amount) {\n    const auto InvMaxStack = 255;\n    for (auto i = 3; i >= 0; i--) {\n        for (auto j = 0; j != 10; j++) {\n            if (mInventory[i][j].item == itemname && mInventory[i][j].amount < InvMaxStack) {\n                if (amount + mInventory[i][j].amount <= InvMaxStack) {\n                    mInventory[i][j].amount += amount;\n                    return true;\n                }\n                amount -= InvMaxStack - mInventory[i][j].amount;\n                mInventory[i][j].amount = InvMaxStack;\n            }\n        }\n    }\n    for (auto i = 3; i >= 0; i--) {\n        for (auto j = 0; j != 10; j++) {\n            if (mInventory[i][j].item == Blocks::ENV) {\n                mInventory[i][j].item = itemname;\n                if (amount <= InvMaxStack) {\n                    mInventory[i][j].amount = amount;\n                    return true;\n                }\n                mInventory[i][j].amount = InvMaxStack;\n                amount -= InvMaxStack;\n            }\n        }\n    }\n    return false;\n}\n\nvoid PlayerEntity::setGameMode(GameMode _gamemode) {\n    mGameMode = _gamemode;\n    switch (_gamemode) {\n    case GameMode::Survival:\n        mFlying = false;\n        mCrossWall = false;\n        mCurrentJumpSpeed = 0.0;\n        break;\n\n    case GameMode::Creative:\n        mFlying = true;\n        mCurrentJumpSpeed = 0.0;\n        break;\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/Entity/PlayerEntity.h",
    "content": "#pragma once\n\n#include \"ControlContext.h\"\n#include \"Entity.h\"\n#include \"Universe/World/Blocks.h\"\n\nstruct ItemStack {\n\tItem item;\n\tuint8_t amount;\n};\n\nenum class GameMode { Survival, Creative };\nclass PlayerEntity: public Entity {\npublic:\n\tPlayerEntity(Double3 pos) : Entity(pos, { .6,1.7,.6 }) {}\n\tPlayerEntity() : PlayerEntity({ 0,0,0 }) { spawn(); }\n\n\tvoid afterMove(Double3 velocity) override;\n\n\tvoid render() override {}\n\tvoid update() override;\n\tvoid controlUpdate(const ControlContext& control); // called by update thread\n\tRenderProperties renderUpdate(const ControlContext& control, bool freeze, double lastUpdate); // called by render thread\n\n\tvoid spawn();\n\tbool addItem(Item itemName, short amount = 1);\n\tbool placeBlock(Int3 position, Block blockName);\n\tvoid setGameMode(GameMode gameMode);\n\tGameMode getGameMode() const noexcept { return mGameMode; }\n\tvoid setVelocity(Double3 vel) { mVelocity = vel; }\n\tvoid toggleCrossWall() noexcept { mCrossWall = !mCrossWall; }\n\tauto getInventory() noexcept { return mInventory; }\n\tItemStack& getCurrentSelectedItem() noexcept { return mInventory[3][mIndexInHand]; }\n\tint getCurrentHotbarSelection() noexcept { return mIndexInHand; }\n\tdouble getHealth() const noexcept { return mHealth; }\n\tvoid setHealth(double health) noexcept { mHealth = health; }\n\tdouble getMaxHealth() const noexcept { return mMaxHealth; }\n\tdouble getCurrentJumpSpeed() const noexcept { return mCurrentJumpSpeed; }\n\tbool isRunning() const noexcept { return mRunning; }\n\tbool isOnGround() const noexcept { return mOnGround; }\n\tbool isNearWall() const noexcept { return mNearWall; }\n\tbool isFlying() const noexcept { return mFlying; }\n\tbool isCrossWall() const noexcept { return mCrossWall; }\n\tdouble getSpeed() const noexcept {\n\t\t// TODO: impl super-sprint\n\t\treturn (mRunning ? runspeed : walkspeed) * mSpeedBoost;\n\t}\n\tRenderProperties getPropertiesForRender(double timeDelta) const noexcept override {\n\t\tauto props = Entity::getPropertiesForRender(timeDelta);\n\t\tprops.position.Y += mHeightExt + mHeight - mSize.Y / 2;\n\t\treturn props;\n\t}\n\nprivate:\n\tvoid ProcessInteract(const ControlContext& control);\n\tvoid ProcessJump();\n\tvoid ProcessNavigate(const ControlContext& control);\n\tvoid HotbarItemSelect(const ControlContext& control);\n\tvoid StartJump();\n\n\tint mAirJumps = 0;\n\tdouble mCurrentJumpSpeed = 0;\n\n\tGameMode mGameMode = GameMode::Survival;\n\n\tdouble mHealth = 20, mMaxHealth = 20;\n\tdouble mHealSpeed = 0.01;\n\tdouble mDropDamage = 5.0;\n\n\tdouble mSpeedBoost = 1;\n\tdouble mHeight = 1.7; // height of eyes\n\tdouble mHeightExt = 0;\n\n\t// status\n\tbool mOnGround = false;\n\tbool mRunning = false;\n\tbool mNearWall = false;\n\tbool mInWater = false;\n\tbool mFlying = false;\n\tbool mCrossWall = false;\n\n\tItemStack mInventory[4][10];\n\tubyte mIndexInHand = 0;\n};\n"
  },
  {
    "path": "NEWorld.Game/Universe/Entity/bvh.cpp",
    "content": "#include \"bvh.h\"\n\n#include <bvh/sweep_sah_builder.hpp>\n#include <bvh/locally_ordered_clustering_builder.hpp>\n#include <bvh/primitive_intersectors.hpp>\n#include \"Entity.h\"\n\ntemplate <typename Bvh, typename Primitive, bool Permuted = false>\nstruct AABBIntersector : public bvh::PrimitiveIntersector<Bvh, Primitive, Permuted, false> {\n    AABBIntersector(const Bvh& bvh, const Primitive* primitives)\n        : bvh::PrimitiveIntersector<Bvh, Primitive, Permuted, false>(bvh, primitives) {}\n\n    std::vector<size_t> result_indices;\n\n    bool intersect(size_t index, const BoundingBox& box, bool is_leaf) {\n        auto [p, i] = this->primitive_at(index);\n        if (auto hit = p->intersect(box)) {\n            if (is_leaf) result_indices.push_back(i);\n            return true;\n        }\n        return false;\n    }\n};\n\nstatic void aabb_intersect(const Bvh& bvh, size_t index, AABBIntersector<Bvh, Entity*, true>& intersector, const BoundingBox& box) {\n    const auto& node = bvh.nodes[index];\n    if (node.is_leaf()) {\n        for (int i = 0; i < node.primitive_count; ++i) {\n            intersector.intersect(node.first_child_or_primitive + i, box, true);\n        }\n        return;\n    }\n    if (intersector.intersect(index, box, false)) {\n        auto left = bvh.nodes[index].first_child_or_primitive;\n        aabb_intersect(bvh, left, intersector, box);\n        aabb_intersect(bvh, left + 1, intersector, box);\n    }\n}\n\nEntityBVH::EntityBVH(const std::vector<std::unique_ptr<Entity>>& entities, bool for_movement) {\n    std::vector<BoundingBox> bboxes;\n    std::vector<Vector3> centers;\n\n    for (const auto& e : entities) {\n        bboxes.emplace_back(for_movement ? e->movement_bounding_box() : e->bounding_box());\n        centers.emplace_back(e->center());\n        mEntities.emplace_back(e.get());\n    }\n\n    construct_bvh(bboxes, centers);\n}\n\nstd::vector<Entity*> EntityBVH::intersect(const BoundingBox& box) const {\n    AABBIntersector<Bvh, Entity*, true> intersector(mBvh, mEntities.data());\n    aabb_intersect(mBvh, 0, intersector, box);\n    std::vector<Entity*> entities;\n    for (const auto index : intersector.result_indices) entities.push_back(mEntities[index]);\n    return entities;\n}\n\nvoid EntityBVH::construct_bvh(const std::vector<BoundingBox>& bboxes, const std::vector<Vector3>& centers) {\n    const auto global_bbox =\n        bvh::compute_bounding_boxes_union(bboxes.data(), bboxes.size());\n\n    bvh::SweepSahBuilder builder(mBvh);\n    builder.build(global_bbox, bboxes.data(), centers.data(), bboxes.size());\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/Entity/bvh.h",
    "content": "#pragma once\n\n#include <bvh/bvh.hpp>\n#include <bvh/vector.hpp>\n#include <bvh/ray.hpp>\n\n#include \"Math/Vector3.h\"\n\nusing Scalar = double;\nusing Vector3 = bvh::Vector3<Scalar>;\nusing BoundingBox = bvh::BoundingBox<Scalar>;\nusing Ray = bvh::Ray<Scalar>;\nusing Bvh = bvh::Bvh<Scalar>;\n\nclass Entity;\n\ninline Vector3 toBvhVec(const Double3& v) { return {v.X, v.Y, v.Z}; }\n\nclass EntityBVH {\npublic:\n    EntityBVH(const std::vector<std::unique_ptr<Entity>>& entities, bool for_movement = false);\n    std::vector<Entity*> intersect(const BoundingBox& box) const;\n\nprivate:\n    void construct_bvh(const std::vector<BoundingBox>& bboxes, const std::vector<Vector3>& centers);\n\n    Bvh mBvh;\n    std::vector<Entity*> mEntities;\n};\n\nnamespace AABB {\n    inline bool InClip(const BoundingBox& boxA, const BoundingBox& boxB, int axis) {\n        if (boxA.min.values[axis] > boxB.min.values[axis] && boxA.min.values[axis] < boxB.max.values[axis] ||\n            boxA.max.values[axis] > boxB.min.values[axis] && boxA.max.values[axis] < boxB.max.values[axis])\n            return true;\n        if (boxB.min.values[axis] > boxA.min.values[axis] && boxB.min.values[axis] < boxA.max.values[axis] ||\n            boxB.max.values[axis] > boxA.min.values[axis] && boxB.max.values[axis] < boxA.max.values[axis])\n            return true;\n        return false;\n    }\n\n    inline bool Intersect(const BoundingBox& boxA, const BoundingBox& boxB) {\n        return InClip(boxA, boxB, 0) && InClip(boxA, boxB, 1) && InClip(boxA, boxB, 2);\n    }\n\n    inline double MaxMove(const BoundingBox& boxA, const BoundingBox& boxB, double distance, int axis) {\n        // perform collision from A to B\n        if (!((axis == 0 || InClip(boxA, boxB, 0)) &&\n            (axis == 1 || InClip(boxA, boxB, 1)) &&\n            (axis == 2 || InClip(boxA, boxB, 2))))\n            return distance;\n        if (boxA.min.values[axis] >= boxB.max.values[axis] && distance < 0.0)\n            return std::max(boxB.max.values[axis] - boxA.min.values[axis], distance);\n        if (boxA.max.values[axis] <= boxB.min.values[axis] && distance > 0.0)\n            return std::min(boxB.min.values[axis] - boxA.max.values[axis], distance);\n        return distance;\n    }\n\n    inline BoundingBox Move(BoundingBox box, Double3 direction) {\n        box.min += toBvhVec(direction);\n        box.max += toBvhVec(direction);\n        return box;\n    }\n\n    inline BoundingBox BoxForBlock(Int3 pos) {\n        return { {pos.X - 0.5, pos.Y - 0.5, pos.Z - 0.5},\n        \t{ pos.X + 0.5, pos.Y + 0.5, pos.Z + 0.5 } };\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/Game.h",
    "content": "#pragma once\n\n#include \"CommandHandler.h\"\n#include \"ControlContext.h\"\n#include \"Common/Logger.h\"\n#include \"Entity/Entity.h\"\n#include \"Entity/PlayerEntity.h\"\n\nclass Game : public CommandHandler {\n    Vec3<int> mLastSelectedBlockPos{};\nprotected:\n    std::vector<std::unique_ptr<Entity>> mEntities{};\n    PlayerEntity* mPlayer = nullptr;\n    ControlContext mControlsForUpdate{ MainWindow };\n\n    bool DebugHitbox{};\n    bool DebugMode{};\n    bool DebugShadow{};\n\n    bool mShouldRenderGUI{};\n    float mBlockDestructionProgress{};\n    std::optional<std::pair<Block, Vec3<int>>> mCurrentSelection{};\n\n    bool mBagOpened = false;\npublic:\n    void InitGame() {\n        infostream << \"Init player...\";\n        auto player = std::make_unique<PlayerEntity>(); // TODO: try to load first\n        mPlayer = player.get();\n        mEntities.emplace_back(std::move(player));\n    }\n    \n    void updateGame() {\n        mControlsForUpdate.Update();\n        //时间\n        gametime++;\n        if (glfwGetKey(MainWindow, GLFW_KEY_F8)) gametime += 30;\n        if (gametime > gameTimeMax) gametime = 0;\n\n        World::unloadedChunks = 0;\n        World::rebuiltChunks = 0;\n        World::updatedChunks = 0;\n\n        //cpArray move\n        const auto playerChunk = mPlayer->getChunkPosition();\n        const auto shiftedChunkStart = playerChunk - Int3(viewdistance + 2);\n        World::cpArray.MoveTo(shiftedChunkStart);\n\n        if (FirstUpdateThisFrame) {\n            ChunkLoadUnload();\n        }\n\n        //加载动画\n        for (const auto& cp : World::chunks) {\n            if (cp->loadAnim <= 0.3f) cp->loadAnim = 0.0f;\n            else cp->loadAnim *= 0.6f;\n        }\n\n        //随机状态更新\n        RandomTick();\n\n        if (!mBagOpened) {\n            // Get camera position. Used delta=0 so there might be a little errors but shouldn't be noticeable.\n            const auto props = mPlayer->getPropertiesForRender(0);\n            ProcessInteract(props.position, props.heading, props.lookUpDown);\n            mPlayer->controlUpdate(mControlsForUpdate);\n            HotkeySettingsToggle();\n        }\n\n        if (isPressed(GLFW_KEY_E) && mShouldRenderGUI) {\n            mBagOpened = !mBagOpened;\n            bagAnimTimer = timer();\n            if (mBagOpened) {\n                shouldGetThumbnail = true;\n            }\n        }\n        \n        Particles::updateall();\n        \n        EntitiesUpdate();\n        EntitiesMovement();\n    }\n\n    void EntitiesUpdate() {\n        // update all entities\n        for (auto& entity : mEntities) {\n            entity->update();\n        }\n    }\n\n    void EntitiesMovement() {\n        // movement of entities\n        if (mEntities.empty()) return;\n        EntityBVH bvh(mEntities, true);\n        for (auto& entity : mEntities) {\n            entity->move(bvh);\n        }\n    }\n\n    // handle ray/bounding box collision check to pick/place blocks\n    void ProcessInteract(Double3 startPosition, double heading, double lookUpDown) {\n        auto pos = startPosition;\n        Int3 blockBefore = pos;\n        mCurrentSelection = std::nullopt;\n\n        for (auto i = 0; i < selectPrecision * selectDistance; i++) {\n            //线段延伸\n            pos.X += sin(M_PI / 180 * (heading - 180)) * sin(M_PI / 180 * (lookUpDown + 90)) / selectPrecision;\n            pos.Y += cos(M_PI / 180 * (lookUpDown + 90)) / selectPrecision;\n            pos.Z += cos(M_PI / 180 * (heading - 180)) * sin(M_PI / 180 * (lookUpDown + 90)) / selectPrecision;\n\n            const auto currentBlockPos = Int3(pos, RoundInt);\n            const auto currentBlock = World::GetBlock(currentBlockPos);\n            //碰到方块\n            if (BlockInfo(currentBlock).isSolid()) {\n                mCurrentSelection = std::make_pair(currentBlock, currentBlockPos);\n                break;\n            }\n            blockBefore = currentBlockPos;\n        }\n        if (!mCurrentSelection) return;\n        \n        auto& itemSelection = mPlayer->getCurrentSelectedItem();\n\n    \tif (mControlsForUpdate.ShouldDo(ControlContext::Action::PICK_BLOCK)) {\n            Particles::throwParticle(mCurrentSelection->first, mCurrentSelection->second);\n            // Reset progress if selecting a different block\n            if (mCurrentSelection->second != mLastSelectedBlockPos) mBlockDestructionProgress = 0.0;\n            else {\n                double factor = itemSelection.item == STICK ? 4 : 30.0 / (BlockInfo(itemSelection.item).getHardness() + 0.1);\n\n                factor = std::clamp(factor, 1.0, 1.7);\n\n                mBlockDestructionProgress += BlockInfo(mCurrentSelection->first).getHardness() *\n                    (mPlayer->getGameMode() == GameMode::Creative ? 10.0f : 0.3f) * factor;\n            }\n\n            if (mBlockDestructionProgress >= 100.0) {\n                for (auto j = 1; j <= 25; j++) {\n                    Particles::throwParticle(mCurrentSelection->first, mCurrentSelection->second);\n                }\n                World::PickBlock(mCurrentSelection->second);\n            }\n        } else mBlockDestructionProgress = 0.0;\n\n        if (mControlsForUpdate.ShouldDo(ControlContext::Action::PLACE_BLOCK)) {\n            if (itemSelection.amount > 0 && isBlock(itemSelection.item)) {\n                //放置方块\n                if (mPlayer->placeBlock(blockBefore, itemSelection.item)) {\n                    itemSelection.amount--;\n                    if (itemSelection.amount == 0)\n                        itemSelection.item = Blocks::ENV;\n                }\n            }\n            else {\n                //使用物品\n                if (itemSelection.item == APPLE) {\n                    itemSelection.amount--;\n                    if (itemSelection.amount == 0)\n                        itemSelection.item = Blocks::ENV;\n                    mPlayer->setHealth(mPlayer->getMaxHealth());\n                }\n            }\n        }\n        mLastSelectedBlockPos = mCurrentSelection->second;\n    }\n    void ChunkLoadUnload() const {\n        World::sortChunkLoadUnloadList(mPlayer->getPosition());\n        for (const auto&[_, chunk] : World::ChunkUnloadList) World::DeleteChunk(chunk->GetPosition());\n        for (const auto&[_, pos]: World::ChunkLoadList) World::AddChunk(pos);\n    }\n    \n    void HotkeySettingsToggle() {//各种设置切换\n        if (isPressed(GLFW_KEY_F1)) {\n            mPlayer->setGameMode(mPlayer->getGameMode() == GameMode::Creative ? GameMode::Survival : GameMode::Creative);\n        }\n        if (isPressed(GLFW_KEY_F2)) shouldGetScreenshot = true;\n        if (isPressed(GLFW_KEY_F3)) {\n            DebugMode = !DebugMode;\n            if (isPressed(GLFW_KEY_H)) {\n                DebugHitbox = !DebugHitbox;\n                DebugMode = true;\n            }\n            if (Renderer::AdvancedRender) {\n                if (isPressed(GLFW_KEY_M)) {\n                    DebugShadow = !DebugShadow;\n                    DebugMode = true;\n                }\n            }\n            else DebugShadow = false;\n        }\n        if (isPressed(GLFW_KEY_F4) && mPlayer->getGameMode() == GameMode::Creative)\n            mPlayer->toggleCrossWall();\n        if (isPressed(GLFW_KEY_F5)) mShouldRenderGUI = !mShouldRenderGUI;\n        if (isPressed(GLFW_KEY_F7)) mPlayer->spawn();\n        if (isPressed(GLFW_KEY_L)) World::saveAllChunks();\n    }\n\n    bool isPressed(int key) { return mControlsForUpdate.KeyJustPressed(key); }\n\n    void RandomTick() const {\n        for (auto &chunk : World::chunks) {\n            const auto cPos = chunk->GetPosition();\n            const auto bPos = Int3{std::min(15, int(rnd() * 16)), std::min(15, int(rnd() * 16)), std::min(15, int(rnd() * 16))};\n            const auto gPos = (cPos << World::ChunkEdgeSizeLog2) + bPos;\n            const auto block = chunk->GetBlock(bPos);\n            if (block != Blocks::ENV) {\n                BlockInfo(block).OnRandomTick(gPos, block);\n            }\n        }\n    }\n    \n    static void saveGame() {\n        infostream << \"Saving world\";\n        World::saveAllChunks();\n        // TODO: save player info\n        //if (!Player::save(World::worldname)) {\n        //    warningstream << \"Failed saving player info!\";\n        //}\n    }\n\n    static bool loadGame() {\n        // TODO: load player info\n        //if (!Player::load(World::worldname)) {\n        //    warningstream << \"Failed loading player info!\";\n        //    return false;\n        //}\n        return true;\n    }\n};\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/BlockRegistry.cpp",
    "content": "#include \"BlockRegistry.h\"\n#include <nlohmann/json.hpp>\n#include <unordered_map>\n#include <vector>\n#include <mutex>\n#include <fstream>\n\nnamespace {\n    std::once_flag gEnvBlockInit;\n    std::vector<Blocks::BlockType*> gTable;\n    std::unordered_map<std::string_view, std::pair<Blocks::BlockType*, int>> gTypes;\n    auto gEnvBlock = Blocks::BlockType(\"NEWorld.Blocks.Air\", false, false, false, 0); // NOLINT\n    constexpr const char* gConfigName = \"new_world_block_id_table\";\n\n    void TableExtract(const nlohmann::json& cfgTree) {\n        auto&& entries = cfgTree[gConfigName];\n        gTable.resize(entries.size());\n        for (auto& [key, value] : entries.items()) {\n            std::string name = key;\n            const auto iter = gTypes.find(name);\n            if (iter != gTypes.end()) {\n                const auto index = static_cast<int>(value);\n                iter->second.second = index;\n                gTable[index] = iter->second.first;\n            }\n            else {\n                throw std::runtime_error(\"Required Block: (\" + name + \") Not Found\");\n            }\n        }\n    }\n\n    void TableDump(nlohmann::json& cfgTree) {\n        for (const auto& x : gTypes) {\n            cfgTree[std::string(x.first)] = x.second.second;\n        }\n    }\n}\n\nvoid BlockRegistry::Register(Blocks::BlockType *type) {\n    if (type) {\n        gTypes.insert_or_assign(type->GetId(), std::pair{type, -1});\n    }\n    std::call_once(gEnvBlockInit, []() {\n        gTypes.insert_or_assign(gEnvBlock.GetId(), std::pair{&gEnvBlock, 0});\n    });\n}\n\nbool BlockRegistry::LoadIdTable(const NEWorld::filesystem::path &path) {\n    ClearIdTable();\n    std::ifstream config { path.string() };\n    if (config) {\n        nlohmann::json cfgTree {};\n        config >> cfgTree;\n        TableExtract(cfgTree);\n        return true;\n    }\n    return false;\n}\n\nvoid BlockRegistry::CreateIdTableNull() {\n    ClearIdTable();\n    gTable.emplace_back(&gEnvBlock);\n}\n\nvoid BlockRegistry::CreateIdTableAuto() {\n    CreateIdTableNull();\n    for (auto& [_, pair] : gTypes) {\n        if (pair.second) {\n            pair.second = gTable.size();\n            gTable.emplace_back(pair.first);\n        }\n    }\n}\n\nbool BlockRegistry::AppendIdTable(const std::string_view &name) {\n    const auto iter = gTypes.find(name);\n    if (iter != gTypes.end()) {\n        auto& [type, id] = iter->second;\n        if (id == -1) {\n            id = gTable.size();\n            gTable.emplace_back(type);\n            return true;\n        }\n    }\n    return false;\n}\n\nbool BlockRegistry::SaveIdTable(const NEWorld::filesystem::path &path) {\n    std::ofstream config { path.string() };\n    if (config) {\n        nlohmann::json cfgTree {};\n        TableDump(cfgTree);\n        config << cfgTree;\n        return true;\n    }\n    return false;\n}\n\nBlocks::BlockType *BlockRegistry::QueryTable(int id) noexcept { return gTable[id]; }\n\nint BlockRegistry::QueryId(const std::string_view &name) noexcept {\n    const auto iter = gTypes.find(name);\n    return iter != gTypes.end() ? iter->second.second: -1;\n}\n\nvoid BlockRegistry::ClearIdTable() noexcept {\n    if (!gTable.empty()) {\n        for (auto &x : gTypes) { x.second.second = -1; }\n        gTable.clear();\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/BlockRegistry.h",
    "content": "#pragma once\n\n#include \"Blocks.h\"\n#include \"System/FileSystem.h\"\n\nclass BlockRegistry {\npublic:\n    static void Register(Blocks::BlockType* type);\n\n    static bool LoadIdTable(const NEWorld::filesystem::path& path);\n\n    static void CreateIdTableNull();\n\n    static void CreateIdTableAuto();\n\n    static bool AppendIdTable(const std::string_view &name);\n\n    static bool SaveIdTable(const NEWorld::filesystem::path& path);\n\n    static void ClearIdTable() noexcept;\n\n    static Blocks::BlockType* QueryTable(int id) noexcept;\n\n    static int QueryId(const std::string_view& name) noexcept;\n};\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Blocks.cpp",
    "content": "#include \"Items.h\"\n#include \"Blocks.h\"\n#include \"World.h\"\n\nnamespace Blocks {\n    BlockType::~BlockType() noexcept = default;\n\n    bool BlockType::BeforeBlockPlace(const Int3& position, Block block) noexcept { return true; }\n    void BlockType::AfterBlockPlace(const Int3& position, Block block) noexcept { }\n    bool BlockType::BeforeBlockDestroy(const Int3& position, Block block) noexcept { return true; }\n\n    void BlockType::AfterBlockDestroy(const Int3& position, Block block) noexcept {\n        // should be an item drop here\n        // TODO: replace when entity is implemented\n        //Player::addItem(block);\n    }\n\n    void BlockType::OnRandomTick(const Int3& position, Block block) noexcept { }\n\n    BlockType blockData[BLOCK_DEF_END + 1] = {\n        //\t\t\t方块名称\t\t  固体\t 不透明\t  半透明  可以爆炸  硬度\n        BlockType(\"NEWorld.Blocks.Air\", false, false, false, 0),\n        BlockType(\"NEWorld.Blocks.Rock\", true, true, false, 2),\n        BlockType(\"NEWorld.Blocks.Grass\", true, true, false, 5),\n        BlockType(\"NEWorld.Blocks.Dirt\", true, true, false, 5),\n        BlockType(\"NEWorld.Blocks.Stone\", true, true, false, 2),\n        BlockType(\"NEWorld.Blocks.Plank\", true, true, false, 5),\n        BlockType(\"NEWorld.Blocks.Wood\", true, true, false, 5),\n        BlockType(\"NEWorld.Blocks.Bedrock\", true, true, false, 0),\n        BlockType(\"NEWorld.Blocks.Leaf\", true, false, false, 15),\n        BlockType(\"NEWorld.Blocks.Glass\", true, false, false, 30),\n        BlockType(\"NEWorld.Blocks.Water\", false, false, true, 0),\n        BlockType(\"NEWorld.Blocks.Lava\", false, false, true, 0),\n        BlockType(\"NEWorld.Blocks.GlowStone\", true, true, false, 10),\n        BlockType(\"NEWorld.Blocks.Sand\", true, true, false, 8),\n        BlockType(\"NEWorld.Blocks.Cement\", true, true, false, 0.5f),\n        BlockType(\"NEWorld.Blocks.Ice\", true, false, true, 25),\n        BlockType(\"NEWorld.Blocks.Coal Block\", true, true, false, 1),\n        BlockType(\"NEWorld.Blocks.Iron Block\", true, true, false, 0.5f),\n        BlockType(\"NEWorld.Blocks.TNT\", true, true, false, 25),\n        BlockType(\"NEWorld.Blocks.Null Block\", true, true, false, 0)\n    };\n\n    class Grass : public BlockType {\n    public:\n        Grass()\n            : BlockType(blockData[2]) { }\n\n        void OnRandomTick(const Int3& position, Block) noexcept override {\n            const auto top = position + Int3{0, 1, 0};\n            //草被覆盖\n            if (World::GetBlock(top) != Blocks::ENV) {\n                World::SetBlock(position, Blocks::DIRT);\n                World::updateblock(top.X, top.Y, top.Z, true);\n            }\n        }\n    } gGrass;\n\n    class Dirt : public BlockType {\n    public:\n        Dirt()\n            : BlockType(blockData[3]) { }\n\n        void OnRandomTick(const Int3& position, Block) noexcept override {\n            const auto top = position + Int3{0, 1, 0};\n            static constexpr Int3 direction[] = {\n                {1, 0, 0}, {-1, 0, 0}, {0, 0, 1}, {0, 0, -1},\n                {1, 1, 0}, {-1, 1, 0}, {0, 1, 1}, {0, 1, -1},\n                {1, -1, 0}, {-1, -1, 0}, {0, -1, 1}, {0, -1, -1}\n            };\n            if (World::GetBlock(top, Blocks::NONEMPTY) == Blocks::ENV) {\n                for (const auto& v : direction) {\n                    if (World::GetBlock(position + v) == Blocks::GRASS) {\n                        //长草\n                        World::SetBlock(position, Blocks::GRASS);\n                        World::updateblock(top.X, top.Y, top.Z, true);\n                        return;\n                    }\n                }\n            }\n        }\n    } gDirt;\n\n    class Leaf : public BlockType {\n    public:\n        Leaf()\n            : BlockType(blockData[8]) { }\n\n        void AfterBlockDestroy(const Int3& position, Block) noexcept override {\n            //if (rnd() < 0.2) {\n            //    if (rnd() < 0.5)Player::addItem(APPLE);\n            //    else Player::addItem(STICK);\n            //}\n            //else { Player::addItem(Blocks::LEAF); }\n        }\n    } gLeaf;\n\n    class Tnt : public BlockType {\n    public:\n        Tnt()\n            : BlockType(blockData[18]) { }\n\n        void AfterBlockPlace(const Int3& position, Block) noexcept override {\n            World::explode(position.X, position.Y, position.Z, 8, World::GetChunk(World::GetChunkPos(position)));\n        }\n    } gTnt;\n\n    BlockType* TypeIndex[BLOCK_DEF_END + 1] = {\n        &blockData[0], &blockData[1], &gGrass, &gDirt, &blockData[4], &blockData[5],\n        &blockData[6], &blockData[7], &gLeaf, &blockData[9], &blockData[10], &blockData[11],\n        &blockData[12], &blockData[13], &blockData[14], &blockData[15], &blockData[16], &blockData[17],\n        &gTnt, &blockData[19]\n    };\n}\n\nBlocks::BlockType& BlockInfo(int id) noexcept {\n    return *Blocks::TypeIndex[id >= Blocks::BLOCK_DEF_END || id < 0 ? Blocks::BLOCK_DEF_END : id];\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Blocks.h",
    "content": "﻿#pragma once\n\n#include <utility>\n#include <utility>\n#include <Math/Vector3.h>\n#include \"Definitions.h\"\n#include \"Globalization.h\"\n\nnamespace Blocks {\n    enum BlockID {\n        ENV, ROCK, GRASS, DIRT, STONE, PLANK, WOOD, BEDROCK, LEAF,\n        GLASS, WATER, LAVA, GLOWSTONE, SAND, CEMENT, ICE, COAL, IRON,\n        TNT, BLOCK_DEF_END\n    };\n    const Block NONEMPTY = 1;\n\n    class BlockType {\n    private:\n        std::string name;\n        float Hardness;\n        bool Solid;\n        bool Opaque;\n        bool Translucent;\n\n    public:\n        BlockType(std::string blockName, bool solid, bool opaque, bool translucent, float _hardness)\n                :name(std::move(blockName)), Hardness(_hardness), Solid(solid), Opaque(opaque),\n                 Translucent(translucent) { };\n\n        virtual  ~BlockType() noexcept;\n\n        [[nodiscard]] std::string_view GetId() const noexcept { return std::string_view(name); }\n\n        //获得方块名称\n        [[nodiscard]] std::string getBlockName() const { return Globalization::GetStrbyKey(name); }\n\n        //是否是固体\n        [[nodiscard]] bool isSolid() const { return Solid; }\n\n        //是否不透明\n        [[nodiscard]] bool isOpaque() const { return Opaque; }\n\n        //是否半透明\n        [[nodiscard]] bool isTranslucent() const { return Translucent; }\n\n        //获得硬度（数值越大硬度越小，最大100）\n        [[nodiscard]] float getHardness() const { return Hardness; }\n\n        //方块事件\n        virtual bool BeforeBlockPlace(const Int3& position, Block block) noexcept;\n        virtual void AfterBlockPlace(const Int3& position, Block block) noexcept;\n        virtual bool BeforeBlockDestroy(const Int3& position, Block block) noexcept;\n        virtual void AfterBlockDestroy(const Int3& position, Block block) noexcept;\n        virtual void OnRandomTick(const Int3& position, Block block) noexcept;\n    };\n}\n\nBlocks::BlockType& BlockInfo(int id) noexcept;\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Chunk.cpp",
    "content": "#include \"Chunk.h\"\n#include \"World.h\"\n\nnamespace World {\n    Chunk::~Chunk() {\n        unloadedChunksCount++;\n    }\n\n    BoundingBox Chunk::getBaseAABB() {\n        const auto min = Double3(GetPosition() * 16) - Double3(0.5);\n        const auto max = Double3(GetPosition() * 16) + Double3(16.0 - 0.5);\n        return BoundingBox{toBvhVec(min), toBvhVec(max)};\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Chunk.h",
    "content": "﻿#pragma once\n\n#include \"Definitions.h\"\n#include \"Blocks.h\"\n#include \"Frustum.h\"\n#include <cstring>\n#include <kls/Object.h>\n#include <Math/Vector3.h>\n#include \"Data/ChunkStorage.h\"\n#include \"Universe/Entity/bvh.h\"\n\nclass Object;\n\nnamespace World {\n    extern std::string worldname;\n    extern Brightness BRIGHTNESSMIN;\n    extern Brightness BRIGHTNESSMAX;    //Maximum brightness\n    extern Brightness skylight;\n\n    constexpr chunkid GetChunkId(Int3 vec) noexcept {\n        if (vec.Y == -128) vec.Y = 0;\n        if (vec.Y <= 0) vec.Y = abs(vec.Y) + (1LL << 7);\n        if (vec.X == -134217728) vec.X = 0;\n        if (vec.X <= 0) vec.X = abs(vec.X) + (1LL << 27);\n        if (vec.Z == -134217728) vec.Z = 0;\n        if (vec.Z <= 0) vec.Z = abs(vec.Z) + (1LL << 27);\n        return (chunkid(vec.Y) << 56) + (chunkid(vec.X) << 28) + vec.Z;\n    }\n\n    struct ChunkPosHash {\n        chunkid operator()(Int3 pos) const noexcept {\n            return GetChunkId(pos);\n        }\n    };\n\n    class ChunkData {\n        static constexpr int GetIndex(const Int3 vec) noexcept {\n            const auto v = UInt3(vec);\n            return static_cast<int>((v.X << ChunkPlaneSizeLog2) | (v.Y << ChunkEdgeSizeLog2) | v.Z);\n        }\n    public:\n        ChunkData() = default;\n\n        ChunkData(const ChunkData& o): mBrightness(o.mBrightness) {\n            for (int i = 0; i < ChunkCubicSize; ++i) mBlock.Set(i, o.mBlock.Get(i));\n        }\n\n        Block GetBlock(const Int3 vec) noexcept { return mBlock.Get(GetIndex(vec)); }\n\n        Brightness GetBrightness(const Int3 vec) noexcept { return mBrightness[GetIndex(vec)]; }\n\n        void SetBlock(const Int3 vec, Block block) noexcept {\n            mBlock.Set(GetIndex(vec), block);\n        }\n\n        void SetBrightness(const Int3 vec, Brightness brightness) noexcept {\n            mBrightness[GetIndex(vec)] = brightness;\n        }\n\n    private:\n        std::array<Brightness, 4096> mBrightness{0};\n        Data::ChunkStorage mBlock{4, Data::Init};\n    };\n\n    class Chunk {\n    private:\n        bool mLazy;\n        ChunkData *mData;\n        chunkid mId;\n        const Int3 mPos;\n        BoundingBox mBounds;\n        std::vector<std::shared_ptr<kls::PmrBase>> mAttached {};\n    public:\n        Chunk(Int3 pos, ChunkData *data, bool isShared = false) noexcept:\n                mPos(pos), mId(GetChunkId(pos)), mLazy(isShared), mData(data),\n                Modified(false), Empty(false), updated(false), loadAnim(0.0) {\n            mBounds = getBaseAABB();\n        }\n\n        ~Chunk();\n\n        [[nodiscard]] Int3 GetPosition() const noexcept { return mPos; }\n\n        bool Empty, updated, Modified;\n        double loadAnim;\n\n        [[nodiscard]] chunkid GetId() const noexcept { return mId; }\n\n        Block GetBlock(const Int3 vec) noexcept { return mData->GetBlock(vec); }\n\n        Brightness GetBrightness(const Int3 vec) noexcept { return mData->GetBrightness(vec); }\n\n        void SetBlock(const Int3 vec, Block block) noexcept {\n            if (mLazy) {\n                mLazy = false;\n                mData = new ChunkData(*mData);\n            }\n            mData->SetBlock(vec, block);\n            Modified = true;\n        }\n\n        void SetBrightness(const Int3 vec, Brightness brightness) noexcept {\n            if (mLazy) {\n                mLazy = false;\n                mData = new ChunkData(*mData);\n            }\n            mData->SetBrightness(vec, brightness);\n            Modified = true;\n        }\n\n        auto RawUnsafe() noexcept { return mData; }\n\n        BoundingBox getBaseAABB();\n\n        void Attach(std::shared_ptr<kls::PmrBase> attachment) {\n            mAttached.push_back(std::move(attachment));\n        }\n\n        void Detach(const std::shared_ptr<kls::PmrBase>& attachment) {\n            auto iter = std::find(mAttached.begin(), mAttached.end(), attachment);\n            if (iter != mAttached.end()) mAttached.erase(iter);\n        }\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/ChunkPtrArray.cpp",
    "content": "#include \"ChunkPtrArray.h\"\n#include <cstring>\n\nnamespace World {\n    void ChunkPtrArray::Create(int s) {\n        size = s;\n        size2 = size * size;\n        size3 = size * size * size;\n        man = {size2, size, 1};\n        array = new Chunk *[size3];\n        memset(array, 0, size3 * sizeof(Chunk *));\n    }\n\n    void ChunkPtrArray::Finalize() {\n        delete[] array;\n        array = nullptr;\n    }\n\n    void ChunkPtrArray::Move(const Int3 &delta) {\n        if (delta.X || delta.Y || delta.Z) {\n            Int3 iter{};\n            auto **arrTemp = new Chunk *[size3];\n            for (iter.X = 0; iter.X < size; iter.X++) {\n                for (iter.Y = 0; iter.Y < size; iter.Y++) {\n                    for (iter.Z = 0; iter.Z < size; iter.Z++) {\n                        arrTemp[Dot(iter, man)] = Fetch(iter + delta);\n                    }\n                }\n            }\n            delete[] array;\n            array = arrTemp;\n            origin += delta;\n        }\n    }\n}"
  },
  {
    "path": "NEWorld.Game/Universe/World/ChunkPtrArray.h",
    "content": "#pragma once\n\n#include \"Math/Vector3.h\"\n\nnamespace World {\n    class Chunk;\n\n    class ChunkPtrArray {\n    public:\n        void Create(int s);\n\n        void Finalize();\n\n        void Move(const Int3 &delta);\n\n        void MoveTo(const Int3 &pos) { Move(pos - origin); }\n\n        void Add(Chunk *c, const Int3 &pos) noexcept { Set(pos, c); }\n\n        void Remove(const Int3 &pos) noexcept { Set(pos, nullptr); }\n\n        [[nodiscard]] Chunk *Get(const Int3 &pos) const noexcept { return Fetch(pos - origin); }\n\n        void Set(const Int3 &pos, Chunk *c) noexcept { Write(pos - origin, c); }\n\n    private:\n        [[nodiscard]] bool Has(const Int3 v) const noexcept {\n            return v.X >= 0 && v.X < size && v.Z >= 0 && v.Z < size && v.Y >= 0 && v.Y < size;\n        }\n\n        [[nodiscard]] Chunk *Fetch(const Int3 v) const noexcept { return Has(v) ? array[Dot(v, man)] : nullptr; }\n\n        void Write(const Int3 &pos, Chunk *c) noexcept { if (Has(pos)) array[Dot(pos, man)] = c; }\n\n        Chunk **array = nullptr;\n        Int3 origin{}, man{};\n        int size{}, size2{}, size3{};\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Data/BitStorage.h",
    "content": "#pragma once\n\n#include <cmath>\n#include <memory>\n#include <cstddef>\n#include <cstring>\n#include <cstdint>\n#include <cassert>\n#include <vector>\n\nnamespace World::Data {\n    template<class T, class U>\n    T Exchange(T &v, const U as) noexcept {\n        const auto r = v;\n        v = as;\n        return r;\n    }\n\n    template<class T, class U, class V>\n    void AssertBetween(const T low, const U high, const V val) noexcept { assert(val >= low && val <= high); }\n\n    class BitsDenseView {\n    public:\n        BitsDenseView(const unsigned int bits, const unsigned int size, uint64_t *const data)\n                : mMask((1ull << bits) - 1ull), mData(data),\n                  mBits(bits), mSize(size),\n                  mDataSize(static_cast<unsigned int>(ceil(double(size * bits) / 64.0) * sizeof(uint64_t))) {\n            AssertBetween(1u, 32u, bits);\n        }\n\n        BitsDenseView(const BitsDenseView &r) noexcept = default;\n\n        BitsDenseView(BitsDenseView &&r) noexcept\n                : BitsDenseView(r) { r.mData = nullptr; } // NOLINT\n\n        BitsDenseView &operator=(const BitsDenseView &r) noexcept = default;\n\n        BitsDenseView &operator=(BitsDenseView &&r) noexcept {\n            *this = r, r.mData = nullptr;\n            return *this;\n        }\n\n        void Set(const unsigned int index, const uint64_t val) noexcept {\n            AssertBetween(0u, mSize - 1, index);\n            AssertBetween(0u, mMask, val);\n            const auto bitOffset = index * mBits;\n            const auto hByteBegin = bitOffset >> 6u;\n            const auto hByteEnd = (bitOffset + (mBits - 1u)) >> 6u;\n            const auto headBitLoc = bitOffset & 63u;\n            const auto lowerRemains = mData[hByteBegin] & ~(mMask << headBitLoc);\n            const auto lowerChanged = (val & mMask) << headBitLoc;\n            mData[hByteBegin] = lowerRemains | lowerChanged;\n            if (hByteBegin != hByteEnd) {\n                const auto bitsLower = 64u - headBitLoc;\n                const auto bitsUpper = mBits - bitsLower;\n                const auto upperRemains = (mData[hByteEnd] >> bitsUpper) << bitsUpper;\n                const auto upperChanged = (val & mMask) >> bitsLower;\n                mData[hByteEnd] = upperRemains | upperChanged;\n            }\n        }\n\n        [[nodiscard]] unsigned int Get(const unsigned int index) noexcept {\n            AssertBetween(0u, mSize - 1, index);\n            const auto bitOffset = index * mBits;\n            const auto hByteBegin = bitOffset >> 6u;\n            const auto hByteEnd = (bitOffset + (mBits - 1u)) >> 6u;\n            const auto headBitLoc = bitOffset & 63u;\n            if (hByteBegin == hByteEnd) {\n                return static_cast<unsigned int>((mData[hByteBegin] >> headBitLoc) & mMask);\n            } else {\n                const auto bitsLower = 64u - headBitLoc;\n                const auto upper = (mData[hByteEnd] << bitsLower) & mMask;\n                const auto lower = mData[hByteBegin] >> headBitLoc;\n                return static_cast<unsigned int>(upper | lower);\n            }\n        }\n\n        template<class T>\n        void Pack(const T *sparse) noexcept {\n            uint64_t current = 0u;\n            auto bS = 0u;\n            auto bE = mBits;\n            auto write = mData;\n            for (const auto sE = sparse + mSize; sparse != sE; ++sparse) {\n                const auto v = (*sparse) & mMask;\n                current |= static_cast<uint64_t>(v) << bS;\n                if (bE >= 64u) {\n                    *(write++) = current;\n                    current = (bE -= 64u) ? v >> (64u - bS) : 0u;\n                }\n                bS = bE;\n                bE += mBits;\n            }\n            if (bS) { *write = current; }\n        }\n\n        template<class T>\n        void Unpack(T *sparse) noexcept {\n            auto bS = 0u;\n            auto bE = mBits;\n            auto write = mData;\n            uint64_t current = *(write++);\n            for (const auto sE = sparse + mSize; sparse != sE; ++sparse) {\n                *sparse = (current >> bS) & mMask;\n                if (bE >= 64u) {\n                    bE -= 64u;\n                    current = *(write++);\n                    *sparse |= (current << (64u - bS)) & mMask;\n                }\n                bS = bE;\n                bE += mBits;\n            }\n        }\n\n        [[nodiscard]] auto Bits() const noexcept { return mBits; }\n\n        [[nodiscard]] auto Size() const noexcept { return mSize; }\n\n        [[nodiscard]] auto Raw() noexcept { return mData; }\n\n        [[nodiscard]] auto Raw() const noexcept { return mData; }\n\n        [[nodiscard]] auto RawSize() const noexcept { return mDataSize; }\n\n    private:\n        uint64_t mMask;\n        uint64_t *mData;\n        unsigned int mBits, mSize, mDataSize;\n    };\n\n    class BitsSparseView {\n    public:\n        BitsSparseView(const int bits, const int size, std::byte *const data) noexcept\n                : mFlag(GetFlag(bits)), mSize(size), mData(data) {} // NOLINT\n\n        BitsSparseView(const BitsSparseView &r) noexcept = default;\n\n        BitsSparseView(BitsSparseView &&r) noexcept\n                : BitsSparseView(r) { r.mData = nullptr; } // NOLINT\n\n        BitsSparseView &operator=(const BitsSparseView &r) noexcept = default;\n\n        BitsSparseView &operator=(BitsSparseView &&r) noexcept {\n            *this = r, r.mData = nullptr;\n            return *this;\n        }\n\n        void Set(const unsigned int index, const int val) noexcept {\n            switch (mFlag) {\n                case 0:\n                    A<uint8_t>(index) = val;\n                    return;\n                case 1:\n                    A<uint16_t>(index) = val;\n                    return;\n                case 2:\n                    A<uint32_t>(index) = val;\n            }\n        }\n\n        [[nodiscard]] int Get(const unsigned int index) const noexcept {\n            switch (mFlag) {\n                case 0:\n                    return A<uint8_t>(index);\n                case 1:\n                    return A<uint16_t>(index);\n                case 2:\n                    return A<uint32_t>(index);\n            }\n            return 0;\n        }\n\n        [[nodiscard]] int GetSet(const unsigned int index, const int val) noexcept {\n            switch (mFlag) {\n                case 0:\n                    return Exchange(A<uint8_t>(index), val);\n                case 1:\n                    return Exchange(A<uint16_t>(index), val);\n                case 2:\n                    return Exchange(A<uint32_t>(index), val);\n            }\n            return 0;\n        }\n\n        [[nodiscard]] auto Size() const noexcept { return mSize; }\n\n        [[nodiscard]] auto Raw() noexcept { return mData; }\n\n        [[nodiscard]] auto Raw() const noexcept { return mData; }\n\n        [[nodiscard]] auto MaxBits() const noexcept { return 8 * (1 << mFlag); } // NOLINT\n        [[nodiscard]] auto RawSize() const noexcept { return mSize << mFlag; } // NOLINT\n    protected:\n        static constexpr int GetFlag(const int bits) noexcept {\n            if (bits <= 8) return 0;\n            if (bits <= 16) return 1;\n            return 2;\n        }\n\n    private:\n        template<class T>\n        [[nodiscard]] T &A(const unsigned int x) noexcept { return reinterpret_cast<T *>(mData)[x]; }\n\n        template<class T>\n        [[nodiscard]] const T &A(const unsigned int x) const noexcept { return reinterpret_cast<const T *>(mData)[x]; }\n\n        static_assert(alignof(std::max_align_t) >= 4);\n        static_assert(sizeof(int) >= 4);\n\n        int mFlag, mSize;\n        std::byte *mData;\n    };\n\n    class BitsDense : public BitsDenseView {\n    public:\n        BitsDense(const unsigned int bits, const unsigned int size, const bool init)\n                : BitsDenseView(bits, size, new uint64_t[size_t(ceil(double(size * bits) / 64.0))]) {\n            if (init) std::memset(Raw(), 0, RawSize());\n        }\n\n        BitsDense(const BitsDense &) = delete;\n\n        BitsDense(BitsDense &&r) noexcept\n                : BitsDenseView(std::move(r)) {}\n\n        BitsDense &operator=(const BitsDense &) = delete;\n\n        BitsDense &operator=(BitsDense &&r) noexcept {\n            if (std::addressof(r) != this) {\n                delete[] Raw();\n                static_cast<BitsDenseView &>(*this) = static_cast<BitsDenseView &&>(std::move(r));\n            }\n            return *this;\n        }\n\n        ~BitsDense() noexcept { delete[] Raw(); }\n    };\n\n    class BitsSparse : public BitsSparseView {\n    public:\n        BitsSparse(const int bits, const int size, const bool init)\n                : BitsSparseView(bits, size, new std::byte[size << GetFlag(bits)]) { // NOLINT\n            if (init) std::memset(Raw(), 0, RawSize());\n        }\n\n        BitsSparse(const BitsSparse &) = delete;\n\n        BitsSparse(BitsSparse &&r) noexcept\n                : BitsSparseView(std::move(r)) {}\n\n        BitsSparse &operator=(const BitsSparse &) = delete;\n\n        BitsSparse &operator=(BitsSparse &&r) noexcept {\n            if (std::addressof(r) != this) {\n                delete[] Raw();\n                static_cast<BitsSparseView &>(*this) = static_cast<BitsSparseView &&>(std::move(r));\n            }\n            return *this;\n        }\n\n        ~BitsSparse() noexcept { delete[] Raw(); }\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Data/ChunkStorage.cpp",
    "content": "#include \"ChunkStorage.h\"\n\nnamespace World::Data {\n    ChunkStorage::ChunkStorage(int bits) noexcept\n            :mT(CheckMode(bits)), mBit(bits) {\n        switch (mT) {\n        case 0:\n            new(&mV.b4) BlocksSparse4();\n            new(&mP.p4) BlockPalette4();\n            return;\n        case 1:\n            new(&mV.b8) BlocksSparse8();\n            new(&mP.p8) BlockPalette8(bits);\n            return;\n        case 2:\n            new(&mV.b16) BlocksSparse16();\n            new(&mP.p16) BlockPalette16();\n            return;\n        }\n    }\n\n    ChunkStorage::ChunkStorage(int bits, InitializeT) noexcept\n            :mT(CheckMode(bits)), mBit(bits) {\n        switch (mT) {\n        case 0:\n            new(&mV.b4) BlocksSparse4(Init);\n            new(&mP.p4) BlockPalette4();\n            return;\n        case 1:\n            new(&mV.b8) BlocksSparse8(Init);\n            new(&mP.p8) BlockPalette8(bits);\n            return;\n        case 2:\n            new(&mV.b16) BlocksSparse16(Init);\n            new(&mP.p16) BlockPalette16();\n            return;\n        }\n    }\n\n    ChunkStorage::~ChunkStorage() noexcept {\n        switch (mT) {\n        case 0: (mV.b4.~BlocksSparse4(), mP.p4.~BlockPalette4());\n            return;\n        case 1: (mV.b8.~BlocksSparse8(), mP.p8.~BlockPalette8());\n            return;\n        case 2: (mV.b16.~BlocksSparse16(), mP.p16.~BlockPalette16());\n            return;\n        }\n    }\n\n    bool ChunkStorage::TrySet(const int index, const uintptr_t val) noexcept {\n        switch (mT) {\n        case 0: {\n            auto id = mP.p4.fromVal(val);\n            return (id != -1) && (mV.b4.Set(index, id), true);\n        }\n        case 1: {\n            auto id = mP.p8.fromVal(val);\n            return (id != -1) && (mV.b8.Set(index, id), true);\n        }\n        case 2: {\n            auto id = mP.p16.fromVal(val);\n            return (id != -1) && (mV.b16.Set(index, id), true);\n        }\n        }\n        return false;\n    }\n\n    void ChunkStorage::Scale48() noexcept {\n        BlocksSparse8 b8{Init};\n        BlockPalette8 p8{mP.p4};\n        for (int i = 0; i<4096; ++i) b8.Set(i, mV.b4.Get(i)); // mId is kept\n        // close the 4-bit storage\n        mV.b4.~BlocksSparse4();\n        mP.p4.~BlockPalette4();\n        // switch mode\n        mBit = 5;\n        mT = 1;\n        // enable the 8-bit storage\n        new(&mV.b8) BlocksSparse8(std::move(b8));\n        new(&mP.p8) BlockPalette8(std::move(p8));\n    }\n\n    void ChunkStorage::Scale8H() noexcept {\n        BlocksSparse16 b16{Init};\n        BlockPalette16 p16{};\n        for (int i = 0; i<4096; ++i) b16.Set(i, p16.fromVal(mP.p8.toVal(mV.b8.Get(i))));\n        // close the 8-bit storage\n        mV.b8.~BlocksSparse8();\n        mP.p8.~BlockPalette8();\n        // switch mode\n        mBit = 9;\n        mT = 2;\n        // enable the 16-bit storage\n        new(&mV.b16) BlocksSparse16(std::move(b16));\n        new(&mP.p16) BlockPalette16(p16);\n    }\n\n    void ChunkStorage::UpScale() noexcept {\n        switch (mT) {\n        case 0: Scale48();\n            break;\n        case 1: ((mBit<8) ? Scale88() : Scale8H());\n            break;\n        }\n    }\n\n    void ChunkStorage::Set(const int index, const uintptr_t val) noexcept {\n        if (!TrySet(index, val)) {\n            UpScale();\n            TrySet(index, val);\n        }\n    }\n\n    void ChunkStorage::Scale88() noexcept {\n        (mP.p8.UpScale(), ++mBit);\n    }\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/Data/ChunkStorage.h",
    "content": "#pragma once\n\n#include <array>\n#include <vector>\n#include <algorithm>\n#include \"BitStorage.h\"\n\nnamespace World {\n    constexpr unsigned int ChunkEdgeSizeLog2 = 4;\n    constexpr unsigned int ChunkPlaneSizeLog2 = ChunkEdgeSizeLog2 * 2u;\n    constexpr unsigned int ChunkCubicSizeLog2 = ChunkEdgeSizeLog2 * 3u;\n    constexpr unsigned int ChunkEdgeSize = 1u << ChunkEdgeSizeLog2;\n    constexpr unsigned int ChunkPlaneSize = 1u << ChunkPlaneSizeLog2;\n    constexpr unsigned int ChunkCubicSize = 1u << ChunkCubicSizeLog2;\n}\n\nnamespace World::Data {\n    static constexpr uintptr_t EmptyDataValue = ~uintptr_t(0);\n\n    struct InitializeT {\n    };\n\n    constexpr InitializeT Init;\n\n    class BlocksSparse4 {\n        struct Split {\n            uint8_t upper: 4;\n            uint8_t lower: 4;\n        };\n    public:\n        BlocksSparse4() = default;\n\n        explicit BlocksSparse4(InitializeT) noexcept\n                : mStorage(ChunkCubicSize >> 1, {0, 0}) {}\n\n        [[nodiscard]] int Get(const int index) const noexcept {\n            const auto sec = mStorage[index >> 1u]; // NOLINT\n            return (index & 1) ? sec.upper : sec.lower; // NOLINT\n        }\n\n        void Set(const int index, const int val) noexcept {\n            auto &sec = mStorage[index >> 1u]; // NOLINT\n            if (index & 1) sec.upper = val; else sec.lower = val; // NOLINT\n        }\n\n        [[nodiscard]] auto Raw() noexcept { return mStorage.data(); }\n\n        [[nodiscard]] auto Raw() const noexcept { return mStorage.data(); }\n\n    private:\n        std::vector<Split> mStorage;\n    };\n\n    class BlocksSparse8 {\n    public:\n        BlocksSparse8() = default;\n\n        explicit BlocksSparse8(InitializeT) noexcept\n                : mStorage(ChunkCubicSize, 0) {}\n\n        [[nodiscard]] int Get(const int index) const noexcept { return mStorage[index]; }\n\n        void Set(const int index, const int val) noexcept { mStorage[index] = val; }\n\n        [[nodiscard]] auto Raw() noexcept { return mStorage.data(); }\n\n        [[nodiscard]] auto Raw() const noexcept { return mStorage.data(); }\n\n    private:\n        std::vector<uint8_t> mStorage;\n    };\n\n    class BlocksSparse16 {\n    public:\n        BlocksSparse16() = default;\n\n        explicit BlocksSparse16(InitializeT) noexcept\n                : mStorage(ChunkCubicSize, 0) {}\n\n        [[nodiscard]] int Get(const int index) const noexcept { return mStorage[index]; }\n\n        void Set(const int index, const int val) noexcept { mStorage[index] = val; }\n\n        [[nodiscard]] auto Raw() noexcept { return mStorage.data(); }\n\n        [[nodiscard]] auto Raw() const noexcept { return mStorage.data(); }\n\n    private:\n        std::vector<uint16_t> mStorage;\n    };\n\n    class BlockPalette4 {\n    public:\n        [[nodiscard]] uintptr_t toVal(const int id) const noexcept { return ((id < mSize) ? mT[id] : 0); }\n\n        [[nodiscard]] int tryFromVal(const uintptr_t val) const noexcept {\n            for (int i = 0; i < mSize; ++i) { if (mT[i] == val) return i; }\n            return -1;\n        }\n\n        int fromVal(const uintptr_t val) noexcept {\n            if (const auto v = tryFromVal(val); v == -1) {\n                return (mSize < 16) ? (mT[mSize] = val, mSize++) : -1;\n            } else return v;\n        }\n\n        [[nodiscard]] auto Size() const noexcept { return mSize; }\n\n        [[nodiscard]] auto Raw() const noexcept { return mT.get(); }\n\n    private:\n        std::unique_ptr<uintptr_t[]> mT{new uintptr_t[16]};\n        int mSize{0};\n    };\n\n    class BlockPalette8 {\n        static constexpr int LdRev(int size) noexcept { return (size >> 2) * 5; } // NOLINT, 0.8 Load Factor\n    public:\n        explicit BlockPalette8(int bits = 5) noexcept: mSize(0), mCurMax(1u << bits), mCurMask(mCurMax - 1) { // NOLINT\n            StgCreate();\n        }\n\n        explicit BlockPalette8(const BlockPalette4 &p4) noexcept: BlockPalette8(5) {\n            mSize = p4.Size();\n            std::memcpy(mT.get(), p4.Raw(), p4.Size() * sizeof(uintptr_t));\n            ReHash();\n        }\n\n        [[nodiscard]] uintptr_t toVal(const int id) const noexcept { return ((id < mSize) ? mT[id] : 0); }\n\n        [[nodiscard]] int tryFromVal(const uintptr_t val) const noexcept {\n            for (auto i = Hash(val);; ++i) {\n                const auto m = i & mCurMask; // NOLINT\n                const auto v = rLookUp[m];\n                if (v == val) return rMap[m];\n                if (v == EmptyDataValue) return -1;\n            }\n        }\n\n        int fromVal(const uintptr_t val) noexcept {\n            if (mSize == mCurMax) return -1;\n            for (auto i = Hash(val);; ++i) {\n                const auto m = i & mCurMask; // NOLINT\n                const auto v = rLookUp[m];\n                if (v == val) return rMap[m];\n                if (v == EmptyDataValue) return (rLookUp[m] = val, mT[mSize] = val, rMap[m] = mSize++);\n            }\n        }\n\n        void UpScale() noexcept {\n            mCurMax <<= 1; // NOLINT\n            mCurMask = mCurMax - 1;\n            auto s = std::move(mT);\n            StgCreate();\n            std::memcpy(mT.get(), s.get(), mSize * sizeof(uintptr_t));\n            ReHash();\n        }\n\n    private:\n        [[nodiscard]] uint8_t Hash(const uintptr_t v) const noexcept { return static_cast<uint8_t>(v & mCurMask); }\n\n        void ReHash() noexcept {\n            for (int i = 0; i < mSize; ++i) {\n                for (auto u = Hash(mT[i]);; ++u) {\n                    const auto m = u & mCurMask; // NOLINT\n                    const auto v = rLookUp[m];\n                    if (v == EmptyDataValue) {\n                        (rLookUp[m] = mT[i], rMap[m] = i);\n                        break;\n                    }\n                }\n            }\n        }\n\n        void StgCreate() noexcept {\n            mT = std::unique_ptr<uintptr_t[]>(new uintptr_t[mCurMax + LdRev(mCurMax) + LdRev(mCurMax >> 1)]); // NOLINT\n            rMap = reinterpret_cast<uint8_t *>(mT.get() + mCurMax + LdRev(mCurMax));\n            rLookUp = mT.get() + mCurMax;\n            std::memset(rLookUp, 0xFF, LdRev(mCurMax) * (sizeof(uintptr_t) + sizeof(uint8_t)));\n        }\n\n        std::unique_ptr<uintptr_t[]> mT{};\n        uintptr_t *rLookUp{};\n        uint8_t *rMap{};\n        int mSize, mCurMax, mCurMask;\n    };\n\n    // TODO(Need to be worked on)\n    class BlockPalette16 {\n    public:\n        [[nodiscard]] uintptr_t toVal(const int id) const noexcept { return id; } // NOLINT\n\n        [[nodiscard]] int tryFromVal(const uintptr_t val) const noexcept { return static_cast<int>(val); } // NOLINT\n\n        int fromVal(const uintptr_t val) noexcept { return tryFromVal(val); } // NOLINT\n    };\n\n    class ChunkStorage {\n    public:\n        explicit ChunkStorage(int bits) noexcept;\n\n        ChunkStorage(int bits, InitializeT) noexcept;\n\n        ~ChunkStorage() noexcept;\n\n        [[nodiscard]] uintptr_t Get(const int index) const noexcept {\n            switch (mT) {\n                case 0:\n                    return mP.p4.toVal(mV.b4.Get(index));\n                case 1:\n                    return mP.p8.toVal(mV.b8.Get(index));\n                case 2:\n                    return mP.p16.toVal(mV.b16.Get(index));\n            }\n            return 0;\n        }\n\n        void Set(int index, uintptr_t val) noexcept;\n\n    private:\n        bool TrySet(int index, uintptr_t val) noexcept;\n\n        void Scale48() noexcept;\n\n        void Scale88() noexcept;\n\n        void Scale8H() noexcept;\n\n        void UpScale() noexcept;\n\n        static constexpr int CheckMode(int bits) noexcept {\n            if (bits <= 4) return 0;\n            if (bits <= 8) return 1;\n            return 2;\n        }\n\n        int mT, mBit;\n\n        union V {\n            V() noexcept {} // NOLINT\n            ~V() noexcept {} // NOLINT\n            BlocksSparse4 b4;\n            BlocksSparse8 b8;\n            BlocksSparse16 b16;\n        } mV;\n\n        union P {\n            P() noexcept {} // NOLINT\n            ~P() noexcept {} // NOLINT\n            BlockPalette4 p4;\n            BlockPalette8 p8;\n            BlockPalette16 p16;\n        } mP;\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/OrderedArray.h",
    "content": "#pragma once\n\n#include <array>\n#include <utility>\n#include <algorithm>\n#include <functional>\n#include <type_traits>\n\ntemplate <class Tk, class Td, size_t Size,\n          template<class>class Compare = std::less>\nclass OrderedList {\npublic:\n    static_assert(std::is_trivial_v<Tk> && std::is_trivial_v<Td>);\n\n    OrderedList() noexcept : mEnd(mList.end()), mComp() {}\n    using ArrayType = std::array<std::pair<Tk, Td>, Size>;\n    using Iterator = typename ArrayType::iterator;\n    using ConstIterator = typename ArrayType::const_iterator;\n    [[nodiscard]] Iterator begin() noexcept { return mList.begin(); }\n    [[nodiscard]] ConstIterator begin() const noexcept { return mList.begin(); }\n    [[nodiscard]] Iterator end() noexcept { return mEnd; }\n    [[nodiscard]] ConstIterator end() const noexcept { return mEnd; }\n\n    void Insert(Tk key, Td data) noexcept {\n        const auto first = std::lower_bound(\n\t\t\tbegin(), end(), key,                                \n\t\t\t[this](const auto& l, const auto& r) noexcept { return mComp(l.first, r); }\n\t\t);\n        if (first != mList.end()) {\n            if (first != end()) {\n                const auto last = std::min(mList.end() - 1, end());\n                std::copy_backward(first, last, last + 1);\n            }\n            *first = std::pair<Tk, Td>(key, data);\n            if (end() != mList.end()) ++mEnd;\n        }\n    }\n\n    void Clear() noexcept { mEnd = begin(); }\n\n    [[nodiscard]] auto Count() const noexcept { return end() - begin(); }\nprivate:\n    ArrayType mList {};\n    Iterator mEnd;\n    Compare<Tk> mComp;\n};\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/TerrainGen/Carve.cpp",
    "content": "namespace World::TerrainGen {\n    /*\n    void buildtree(Int3 pos) {\n        auto [x, y, z] = pos.Data;\n        //对生成条件进行更严格的检测\n        //一：正上方五格必须为空气\n        for (auto i = y + 1; i < y + 6; i++) {\n            if (GetBlock({(x), (i), (z)}) != Blocks::ENV)return;\n        }\n        //二：周围五格不能有树\n        for (auto ix = x - 4; ix < x + 4; ix++) {\n            for (auto iy = y - 4; iy < y + 4; iy++) {\n                for (auto iz = z - 4; iz < z + 4; iz++) {\n                    if (GetBlock({(ix), (iy), (iz)}) == Blocks::WOOD ||\n                        GetBlock({(ix), (iy), (iz)}) == Blocks::LEAF)\n                        return;\n                }\n            }\n        }\n        //终于可以开始生成了\n        //设置泥土\n        SetBlock({x, y, z}, Blocks::DIRT);\n        //设置树干\n        auto h = 0;//高度\n        //测算泥土数量\n        auto Dirt = 0;//泥土数\n        for (auto ix = x - 4; ix < x + 4; ix++) {\n            for (auto iy = y - 4; iy < y; iy++) {\n                for (auto iz = z - 4; iz < z + 4; iz++) {\n                    if (GetBlock({(ix), (iy), (iz)}) == Blocks::DIRT)Dirt++;\n                }\n            }\n        }\n        //测算最高高度\n        for (auto i = y + 1; i < y + 16; i++) {\n            if (GetBlock({(x), (i), (z)}) == Blocks::ENV) { h++; }\n            else { break; };\n        }\n        //取最小值\n        h = std::min(h, int(Dirt * 15 / 268 * std::max(rnd(), 0.8)));\n        if (h < 7)return;\n        //开始生成树干\n        for (auto i = y + 1; i < y + h + 1; i++) {\n            SetBlock({(x), (i), (z)}, Blocks::WOOD);\n        }\n        //设置树叶及枝杈\n        //计算树叶起始生成高度\n        const auto leafh = int(double(h) * 0.618) + 1;//黄金分割比大法好！！！\n        const auto distancen2 = int(double((h - leafh + 1) * (h - leafh + 1))) + 1;\n        for (auto iy = y + leafh; iy < y + int(double(h) * 1.382) + 2; iy++) {\n            for (auto ix = x - 6; ix < x + 6; ix++) {\n                for (auto iz = z - 6; iz < z + 6; iz++) {\n                    const auto distancen = DistanceSquare(ix, iy, iz, x, y + leafh + 1, z);\n                    if ((GetBlock({(ix), (iy), (iz)}) == Blocks::ENV) && (distancen < distancen2)) {\n                        if ((distancen <= distancen2 / 9) && (rnd() > 0.3)) {\n                            SetBlock({(ix), (iy), (iz)}, Blocks::WOOD);//生成枝杈\n                        } else {\n                            SetBlock({(ix), (iy), (iz)}, Blocks::LEAF); //生成树叶\n                        }\n                    }\n                }\n            }\n        }\n        // TODO(move this function when terrain carving for terrain generation is possible)\n    }\n    */\n}"
  },
  {
    "path": "NEWorld.Game/Universe/World/TerrainGen/Generate.h",
    "content": "#pragma once\n\n#include \"Heights.h\"\n#include \"Universe/World/Chunk.h\"\n\nnamespace World::TerrainGen {\n    class Generate {\n    public:\n        explicit Generate(int seed) : mHeights(seed) {\n            Cursor(Int3{0}, Int3{15}, [this](auto p) noexcept {\n                mSkyChunk.SetBlock(p, Blocks::ENV);\n                mSkyChunk.SetBrightness(p, skylight);\n                mLavaChunk.SetBlock(p, Blocks::LAVA);\n                mLavaChunk.SetBrightness(p, skylight);\n            });\n        }\n\n        // TODO(remove when system is completed)\n        static Generate &Get() {\n            static Generate instance{3404};\n            return instance;\n        }\n\n        Chunk *Run(Int3 c) {\n            auto result = Draft(c);\n            // TODO(this is a hack, formalize it)\n            if (result->RawUnsafe() == &mSkyChunk) result->Empty = true;\n            if (!result->Empty) result->updated = true;\n            return result;\n        }\n\n    private:\n        Heights mHeights;\n        ChunkData mSkyChunk{}, mLavaChunk{};\n\n        static ChunkData *NoiseTerrain(Int3 pos, Heights::Section &hm) {\n            auto[cx, cy, cz] = pos.Data;\n            auto result = std::make_unique<ChunkData>();\n            int maxh;\n\n            const auto sh = WaterLevel + 2 - (cy << 4);\n            const auto wh = WaterLevel - (cy << 4);\n\n            for (auto x = 0; x < 16; ++x) {\n                for (auto z = 0; z < 16; ++z) {\n                    const auto h = hm.Get(x, z) - (cy << 4);\n                    if (h > sh && h > wh + 1) {\n                        //Grass layer\n                        if (h >= 0 && h < 16) result->SetBlock(Int3{x, h, z}, Blocks::GRASS);\n                        //Dirt layer\n                        maxh = std::min(std::max(0, h), 16);\n                        for (auto y = std::min(std::max(0, h - 5), 16); y < maxh; ++y)\n                            result->SetBlock(Int3{x, y, z}, Blocks::DIRT);\n                    } else {\n                        //Sand layer\n                        maxh = std::min(std::max(0, h + 1), 16);\n                        for (auto y = std::min(std::max(0, h - 5), 16); y < maxh; ++y)\n                            result->SetBlock(Int3{x, y, z}, Blocks::SAND);\n                        //Water layer\n                        const auto minh = std::min(std::max(0, h + 1), 16);\n                        maxh = std::min(std::max(0, wh + 1), 16);\n                        auto cur_br = BRIGHTNESSMAX - (WaterLevel - (maxh - 1 + (cy << 4))) * 2;\n                        if (cur_br < BRIGHTNESSMIN) cur_br = BRIGHTNESSMIN;\n                        for (auto y = maxh - 1; y >= minh; --y) {\n                            result->SetBlock(Int3{x, y, z}, Blocks::WATER);\n                            result->SetBrightness(Int3{x, y, z}, static_cast<Brightness>(cur_br));\n                            cur_br -= 2;\n                            if (cur_br < BRIGHTNESSMIN) cur_br = BRIGHTNESSMIN;\n                        }\n                    }\n                    //Rock layer\n                    maxh = std::min(std::max(0, h - 5), 16);\n                    for (auto y = 0; y < maxh; ++y) result->SetBlock(Int3{x, y, z}, Blocks::ROCK);\n                    //Air layer\n                    for (auto y = std::min(std::max(0, std::max(h + 1, wh + 1)), 16); y < 16; ++y) {\n                        result->SetBlock(Int3{x, y, z}, Blocks::ENV);\n                        result->SetBrightness(Int3{x, y, z}, skylight);\n                    }\n                }\n            }\n            return result.release();\n        }\n\n        static Chunk* Make(Int3 pos, ChunkData *data, bool isShared, std::shared_ptr<kls::PmrBase> hm) {\n            auto result = new Chunk(pos, data, isShared);\n            result->Attach(std::move(hm));\n            return result;\n        }\n\n        Chunk * Draft(Int3 pos) {\n            auto[cx, cy, cz] = pos.Data;\n            //Fast generate parts\n            //Part1 out of the terrain bound\n            if (cy > 4) return new Chunk(pos, &mSkyChunk, true);\n            if (cy < 0) return new Chunk(pos, &mLavaChunk, true);\n\n            //Part2 out of geometry area\n            auto cur = mHeights.Get(cx, cz); // TODO(attach this to chunk)\n            if (cy > cur->High()) return Make(pos, &mSkyChunk, true, std::move(cur));\n            if (cy < cur->Low()) {\n                auto result = std::make_unique<ChunkData>();\n                Cursor(Int3{0}, Int3{15}, [&result](auto p) noexcept {\n                    result->SetBlock(p, Blocks::ROCK);\n                    result->SetBrightness(p, 0);\n                });\n                return Make(pos, result.release(), false, std::move(cur));\n            }\n            const auto terrain = NoiseTerrain(pos, *cur);\n            return Make(pos, terrain, false, std::move(cur));\n        }\n\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/TerrainGen/Heights.h",
    "content": "#pragma once\n\n#include \"Noise.h\"\n#include <bit>\n#include <atomic>\n#include <kls/Object.h>\n#include <tsl/hopscotch_map.h>\n\nnamespace World::TerrainGen {\n    class Heights {\n    public:\n        class Section : public kls::PmrBase {\n        public:\n            void Init(Noise &noise, int cx, int cz) {\n                std::call_once(mLazy, [this, &noise, cx, cz]() {\n                    auto low = std::numeric_limits<int>::max(), high = WaterLevel;\n                    const int bX = cx * 16, bZ = cz * 16;\n                    for (auto x = 0; x < 16; ++x) {\n                        for (auto z = 0; z < 16; ++z) {\n                            const auto height = mHeights[x][z] = noise.Get(x + bX, z + bZ);\n                            if (height < low) low = height;\n                            if (height > high) high = height;\n                        }\n                    }\n                    mCLow = static_cast<int>(std::floor(double(low) / 16.0));\n                    mCHigh = static_cast<int>(std::ceil(double(high) / 16.0));\n                });\n            }\n\n            [[nodiscard]] int Low() const noexcept { return mCLow; }\n\n            [[nodiscard]] int High() const noexcept { return mCHigh; }\n\n            [[nodiscard]] int Get(int x, int z) const noexcept { return mHeights[x][z]; }\n\n        private:\n            int mCLow{}, mCHigh{};\n            int mHeights[16][16]{};\n            std::once_flag mLazy{};\n        };\n\n        explicit Heights(int seed) : mNoise(seed) {}\n\n        std::shared_ptr<Section> Get(int cx, int cz) {\n            auto result = GetEntry(cx, cz);\n            result->Init(mNoise, cx, cz);\n            return result;\n        }\n    private:\n        Noise mNoise;\n        std::mutex mMutex;\n        // TODO(Add a cleanup tick)\n        tsl::hopscotch_map<uint64_t, std::weak_ptr<Section>> mSections{};\n\n        static uint64_t MakeKey(int32_t cx, int32_t cz) noexcept {\n            const auto uCx = std::bit_cast<uint32_t>(cx);\n            const auto uCz = std::bit_cast<uint32_t>(cz);\n            return (static_cast<uint64_t>(uCx) << 32ull) | static_cast<uint64_t>(uCz);\n        }\n\n        std::shared_ptr<Section> GetEntry(int cx, int cz) {\n            std::lock_guard lk{mMutex};\n            const auto key = MakeKey(cx, cz);\n            const auto find = mSections.find(key);\n            if (find != mSections.end()) {\n                auto current = find.value().lock();\n                if (current) return current;\n            }\n            auto result = std::make_shared<Section>();\n            mSections[key] = result;\n            return result;\n        }\n    };\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/TerrainGen/Noise.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n\nnamespace World::TerrainGen {\n    static constexpr int WaterLevel = 30;\n    static constexpr double NoiseScaleX = 64;\n    static constexpr double NoiseScaleZ = 64;\n\n    // TODO(add seed support)\n    class Noise {\n        static constexpr double Base(int x, int y) {\n            auto xx = x + y * 13258953287;\n            xx = (xx >> 13) ^ xx;\n            return ((xx * (xx * xx * 15731 + 789221) + 1376312589) & 0x7fffffff) / 16777216.0;\n        }\n\n        static constexpr double SmoothedNoise(int x, int y) {\n            const auto corners = (Base(x - 1, y - 1) + Base(x + 1, y - 1) + Base(x - 1, y + 1) +\n                                  Base(x + 1, y + 1)) / 8.0;\n            const auto sides = (Base(x - 1, y) + Base(x + 1, y) + Base(x, y - 1) + Base(x, y + 1)) / 4.0;\n            const auto center = Base(x, y);\n            return corners + sides + center;\n        }\n\n        static constexpr double Interpolate(double a, double b, double x) { return a * (1.0 - x) + b * x; }\n\n        static double InterpolatedNoise(double x, double y) {\n            const auto int_X = static_cast<int>(floor(x)); //不要问我为毛用floor，c++默认居然TM的是向零取整的\n            const auto fractional_X = x - int_X;\n            const auto int_Y = static_cast<int>(floor(y));\n            const auto fractional_Y = y - int_Y;\n            const auto v1 = Base(int_X, int_Y);\n            const auto v2 = Base(int_X + 1, int_Y);\n            const auto v3 = Base(int_X, int_Y + 1);\n            const auto v4 = Base(int_X + 1, int_Y + 1);\n            const auto i1 = Interpolate(v1, v2, fractional_X);\n            const auto i2 = Interpolate(v3, v4, fractional_X);\n            return Interpolate(i1, i2, fractional_Y);\n        }\n\n        static double PerlinNoise2D(double x, double y) {\n            double total = 0, frequency = 1, amplitude = 1;\n            for (auto i = 0; i <= 4; i++) {\n                total += InterpolatedNoise(x * frequency, y * frequency) * amplitude;\n                frequency *= 2;\n                amplitude /= 2.0;\n            }\n            return total;\n        }\n    public:\n        explicit Noise(int mapSeed) : seed(mapSeed) {\n            fastSrand(mapSeed);\n            for (auto &i: perm) i = rnd() * 256.0;\n        }\n\n        int Get(int x, int y) {\n            return static_cast<int>(PerlinNoise2D(x / NoiseScaleX + 0.125, y / NoiseScaleZ + 0.125)) >> 2;\n        }\n    private:\n        int seed;\n        double perm[256]{};\n    };\n}"
  },
  {
    "path": "NEWorld.Game/Universe/World/World.cpp",
    "content": "﻿#include \"World.h\"\n#include \"Particles.h\"\n#include <cmath>\n#include <algorithm>\n#include \"System/FileSystem.h\"\n#include \"TerrainGen/Generate.h\"\n#include \"Renderer/World/WorldRenderer.h\"\n\nnamespace World {\n    std::string worldname;\n    Brightness skylight = 15;         //Sky light level\n    Brightness BRIGHTNESSMAX = 15;    //Maximum brightness\n    Brightness BRIGHTNESSMIN = 2;     //Mimimum brightness\n    Brightness BRIGHTNESSDEC = 1;     //Brightness decrease\n\n    std::vector<std::shared_ptr<Chunk>> chunks{};\n    Chunk *cpCachePtr = nullptr;\n    chunkid cpCacheID = 0;\n    ChunkPtrArray cpArray;\n    int cloud[128][128];\n    int rebuiltChunks, rebuiltChunksCount;\n    int updatedChunks, updatedChunksCount;\n    int unloadedChunks, unloadedChunksCount;\n\n    OrderedList<int, Int3, 64> ChunkLoadList{};\n    OrderedList<int, Chunk *, 64, std::greater> ChunkUnloadList{};\n\n    void Init() {\n        std::stringstream ss;\n        ss << \"Worlds/\" << worldname << \"/\";\n        NEWorld::filesystem::create_directories(ss.str());\n        ss.clear();\n        ss.str(\"\");\n        ss << \"Worlds/\" << worldname << \"/chunks\";\n        NEWorld::filesystem::create_directories(ss.str());\n\n        cpCachePtr = nullptr;\n        cpCacheID = 0;\n\n        cpArray.Create((viewdistance + 2) * 2);\n    }\n\n    auto LowerChunkBound(chunkid cid) noexcept {\n        return std::lower_bound(chunks.begin(), chunks.end(), cid, [](auto &left, auto right) noexcept {\n            return left->GetId() < right;\n        });\n    }\n\n    Chunk *AddChunk(Int3 vec) {\n        const auto cid = GetChunkId(vec);  //Chunk ID\n        const auto chunkIter = LowerChunkBound(cid);\n        if (chunkIter != chunks.end()) {\n            if ((*chunkIter)->GetId() == cid) {\n                printf(\"[Console][Error]\");\n                printf(\"Chunk(%d,%d,%d)has been loaded,when adding Chunk.\\n\", vec.X, vec.Y, vec.Z);\n                return chunkIter->get();\n            }\n        }\n        // TODO(Actually try to load from disk)\n        const auto newChunk = TerrainGen::Generate::Get().Run(vec);\n        const auto& ptr = *chunks.insert(chunkIter, std::shared_ptr<Chunk>(newChunk));\n        WorldRenderer::ChunksRenderer::Default().Add(ptr);\n        cpCacheID = cid;\n        cpCachePtr = newChunk;\n        cpArray.Add(newChunk, vec);\n        return newChunk;\n    }\n\n    Chunk *GetChunk(Int3 vec) {\n        const auto cid = GetChunkId(vec);\n        if (cpCacheID == cid && cpCachePtr) return cpCachePtr;\n        auto ret = cpArray.Get(vec);\n        if (ret) {\n            cpCacheID = cid;\n            cpCachePtr = ret;\n            return ret;\n        }\n        if (!chunks.empty()) {\n            const auto iter = LowerChunkBound(cid);\n            if (iter != chunks.end()) {\n                const auto& chunk = *iter;\n                if (chunk->GetId() == cid) {\n                    ret = chunk.get();\n                    cpCacheID = cid;\n                    cpCachePtr = ret;\n                    cpArray.Add(ret, vec);\n                    return ret;\n                }\n            }\n        }\n        return nullptr;\n    }\n\n    void DeleteChunk(Int3 vec) {\n        const auto id = GetChunkId(vec);  //Chunk ID\n        const auto chunkIter = LowerChunkBound(id);\n        if (chunkIter != chunks.end()) {\n            if ((*chunkIter)->GetId() == id) {\n                const auto chunk = chunkIter->get();\n                if (cpCachePtr == chunk) {\n                    cpCacheID = 0;\n                    cpCachePtr = nullptr;\n                }\n                cpArray.Remove(vec);\n                chunks.erase(chunkIter);\n            }\n        }\n    }\n    \n    std::vector<BoundingBox> getHitboxes(const BoundingBox& box) {\n        std::vector<BoundingBox> ret;\n\n        for (auto a = int(box.min.values[0] + 0.5) - 1; a <= int(box.max.values[0] + 0.5) + 1; a++) {\n            for (auto b = int(box.min.values[1] + 0.5) - 1; b <= int(box.max.values[1] + 0.5) + 1; b++) {\n                for (auto c = int(box.min.values[2] + 0.5) - 1; c <= int(box.max.values[2] + 0.5) + 1; c++) {\n                    Int3 pos = { a, b, c };\n                    if (BlockInfo(GetBlock(pos)).isSolid()) {\n                        auto blockBox = AABB::BoxForBlock(pos);\n                        if (AABB::Intersect(box, blockBox)) ret.push_back(blockBox);\n                    }\n                }\n            }\n        }\n        return ret;\n    }\n\n    bool inWater(const BoundingBox &box) {\n        for (auto a = int(box.min.values[0] + 0.5) - 1; a <= int(box.max.values[0] + 0.5) + 1; a++) {\n            for (auto b = int(box.min.values[1] + 0.5) - 1; b <= int(box.max.values[1] + 0.5) + 1; b++) {\n                for (auto c = int(box.min.values[2] + 0.5) - 1; c <= int(box.max.values[2] + 0.5) + 1; c++) {\n                    Int3 pos = { a,b,c };\n                    auto block = GetBlock(pos);\n                    if (block == Blocks::WATER || block == Blocks::LAVA) {\n                        if (AABB::Intersect(box, AABB::BoxForBlock(pos))) return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    void updateblock(int x, int y, int z, bool blockchanged, int depth) {\n        //Blockupdate\n\n        if (depth > 200) return;\n        depth++;\n\n        auto updated = blockchanged;\n        const auto cx = GetChunkPos(x);\n        const auto cy = GetChunkPos(y);\n        const auto cz = GetChunkPos(z);\n\n        if (ChunkOutOfBound({cx, cy, cz})) return;\n\n        const auto b = GetBlockPos(Int3{x, y, z});\n\n        auto cptr = GetChunk({cx, cy, cz});\n        if (cptr != nullptr) {\n            const auto oldbrightness = cptr->GetBrightness(b);\n            auto skylighted = true;\n            auto yi = y + 1;\n            auto cyi = GetChunkPos(yi);\n            if (y < 0) skylighted = false;\n            else {\n                while (!ChunkOutOfBound({(cx), (cyi + 1), (cz)}) && ChunkLoaded({(cx), (cyi + 1), (cz)}) &&\n                       skylighted) {\n                    if (BlockInfo(GetBlock({x, yi, z})).isOpaque() || GetBlock({(x), (yi), (z)}) == Blocks::WATER) {\n                        skylighted = false;\n                    }\n                    yi++;\n                    cyi = GetChunkPos(yi);\n                }\n            }\n\n            if (!BlockInfo(GetBlock({x, y, z})).isOpaque()) {\n                Block blks[7] = {0,\n                                 (GetBlock({(x), (y), (z + 1)})),    //Front face\n                                 (GetBlock({(x), (y), (z - 1)})),    //Back face\n                                 (GetBlock({(x + 1), (y), (z)})),    //Right face\n                                 (GetBlock({(x - 1), (y), (z)})),    //Left face\n                                 (GetBlock({(x), (y + 1), (z)})),    //Top face\n                                 (GetBlock({(x), (y - 1), (z)}))};  //Bottom face\n                Brightness brts[7] = {0,\n                                      getbrightness(x, y, z + 1),    //Front face\n                                      getbrightness(x, y, z - 1),    //Back face\n                                      getbrightness(x + 1, y, z),    //Right face\n                                      getbrightness(x - 1, y, z),    //Left face\n                                      getbrightness(x, y + 1, z),    //Top face\n                                      getbrightness(x, y - 1, z)};  //Bottom face\n                auto maxbrightness = 1;\n                for (auto i = 2; i <= 6; i++) {\n                    if (brts[maxbrightness] < brts[i]) maxbrightness = i;\n                }\n                auto br = brts[maxbrightness];\n                if (blks[maxbrightness] == Blocks::WATER) {\n                    if (br - 2 < BRIGHTNESSMIN) br = BRIGHTNESSMIN; else br -= 2;\n                } else {\n                    if (br - 1 < BRIGHTNESSMIN) br = BRIGHTNESSMIN; else br--;\n                }\n\n                if (skylighted) {\n                    if (br < skylight) br = skylight;\n                }\n                if (br < BRIGHTNESSMIN) br = BRIGHTNESSMIN;\n                //Set brightness\n                cptr->SetBrightness(b, br);\n\n            } else {\n\n                //Opaque block\n                cptr->SetBrightness(b, 0);\n                if (GetBlock({x, y, z}) == Blocks::GLOWSTONE || GetBlock({x, y, z}) == Blocks::LAVA) {\n                    cptr->SetBrightness(b, BRIGHTNESSMAX);\n                }\n\n            }\n\n            if (oldbrightness != cptr->GetBrightness(b)) updated = true;\n\n            if (updated) {\n                updateblock(x, y + 1, z, false, depth);\n                updateblock(x, y - 1, z, false, depth);\n                updateblock(x + 1, y, z, false, depth);\n                updateblock(x - 1, y, z, false, depth);\n                updateblock(x, y, z + 1, false, depth);\n                updateblock(x, y, z - 1, false, depth);\n            }\n\n            setChunkUpdated(cx, cy, cz, true);\n            if (b.X == 15 && cx < worldsize - 1) setChunkUpdated(cx + 1, cy, cz, true);\n            if (b.X == 0 && cx > -worldsize) setChunkUpdated(cx - 1, cy, cz, true);\n            if (b.Y == 15 && cy < worldheight - 1) setChunkUpdated(cx, cy + 1, cz, true);\n            if (b.Y == 0 && cy > -worldheight) setChunkUpdated(cx, cy - 1, cz, true);\n            if (b.Z == 15 && cz < worldsize - 1) setChunkUpdated(cx, cy, cz + 1, true);\n            if (b.Z == 0 && cz > -worldsize) setChunkUpdated(cx, cy, cz - 1, true);\n\n        }\n    }\n\n    bool ChunkHintMatch(const Int3 c, Chunk *const cptr) noexcept { return cptr && cptr->GetPosition() == c; }\n\n    Block GetBlock(const Int3 v, Block mask, Chunk *const hint) {\n        //获取方块\n        const auto c = GetChunkPos(v);\n        if (ChunkOutOfBound(c)) return Blocks::ENV;\n        const auto b = GetBlockPos(v);\n        if (ChunkHintMatch(c, hint)) { return hint->GetBlock(b); }\n        const auto ci = GetChunk(c);\n        if (ci) { return ci->GetBlock(b); }\n        return mask;\n    }\n\n    Brightness GetBrightness(Int3 v, Chunk *const hint) {\n        //获取亮度\n        const auto c = GetChunkPos(v);\n        if (ChunkOutOfBound(c)) return skylight;\n        const auto b = GetBlockPos(v);\n        if (ChunkHintMatch(c, hint)) { return hint->GetBrightness(b); }\n        const auto ci = GetChunk(c);\n        if (ci) { return ci->GetBrightness(b); }\n        return skylight;\n    }\n\n    Chunk *GetChunkNoneLazy(const Int3 c) noexcept {\n        return GetChunk(c);\n    }\n\n    void SetBlock(Int3 v, Block block, Chunk *hint) {\n        //设置方块\n        const auto c = GetChunkPos(v);\n        const auto b = GetBlockPos(v);\n        if (ChunkHintMatch(c, hint)) {\n            hint->SetBlock(b, block);\n            updateblock(v.X, v.Y, v.Z, true);\n        } else if (!ChunkOutOfBound(c)) {\n            if (const auto i = GetChunkNoneLazy(c); i) {\n                i->SetBlock(b, block);\n                updateblock(v.X, v.Y, v.Z, true);\n            }\n        }\n    }\n\n    void SetBrightness(Int3 v, Brightness brightness, Chunk *hint) {\n        //设置亮度\n        const auto c = GetChunkPos(v);\n        const auto b = GetBlockPos(v);\n        if (ChunkHintMatch(c, hint)) {\n            hint->SetBrightness(b, brightness);\n        } else if (!ChunkOutOfBound(c)) {\n            if (const auto i = GetChunkNoneLazy(c); i) {\n                i->SetBrightness(b, brightness);\n            }\n        }\n    }\n\n    bool chunkUpdated(const Int3 vec) {\n        const auto i = GetChunk(vec);\n        if (!i) return false;\n        return i->updated;\n    }\n\n    void setChunkUpdated(int x, int y, int z, bool value) {\n        if (const auto i = GetChunkNoneLazy({x, y, z}); i) {\n            i->updated = value;\n        }\n    }\n\n    static constexpr auto ccOffset = Int3(7); // offset to a chunk center\n\n    void sortChunkLoadUnloadList(Int3 pos) {\n        const auto cp = GetChunkPos(pos);\n\n        ChunkUnloadList.Clear();\n        for (auto &chunk : chunks) {\n            const auto c = chunk->GetPosition();\n            if (ChebyshevDistance(c, cp) > viewdistance)\n                ChunkUnloadList.Insert(DistanceSquared(c * 16 + ccOffset, pos), chunk.get());\n        }\n\n        ChunkLoadList.Clear();\n        const auto diff = Int3(viewdistance + 1);\n        Cursor(cp - diff, cp + diff, [&](const auto &c) noexcept {\n            if (ChunkOutOfBound(c)) return;\n            if (!cpArray.Get(c))\n                ChunkLoadList.Insert(DistanceSquared(c * 16 + ccOffset, pos), c);\n        });\n    }\n\n    void saveAllChunks() {\n    }\n\n    void destroyAllChunks() {\n        chunks.clear();\n        chunks.shrink_to_fit();\n\n        cpArray.Finalize();\n\n        rebuiltChunks = 0;\n        rebuiltChunksCount = 0;\n\n        updatedChunks = 0;\n        updatedChunksCount = 0;\n\n        unloadedChunks = 0;\n        unloadedChunksCount = 0;\n\n        ChunkLoadList.Clear();\n        ChunkUnloadList.Clear();\n    }\n\n    void buildtree(Int3 pos) {\n        auto [x, y, z] = pos.Data;\n        //对生成条件进行更严格的检测\n        //一：正上方五格必须为空气\n        for (auto i = y + 1; i < y + 6; i++) {\n            if (GetBlock({(x), (i), (z)}) != Blocks::ENV)return;\n        }\n        //二：周围五格不能有树\n        for (auto ix = x - 4; ix < x + 4; ix++) {\n            for (auto iy = y - 4; iy < y + 4; iy++) {\n                for (auto iz = z - 4; iz < z + 4; iz++) {\n                    if (GetBlock({(ix), (iy), (iz)}) == Blocks::WOOD ||\n                        GetBlock({(ix), (iy), (iz)}) == Blocks::LEAF)\n                        return;\n                }\n            }\n        }\n        //终于可以开始生成了\n        //设置泥土\n        SetBlock({x, y, z}, Blocks::DIRT);\n        //设置树干\n        auto h = 0;//高度\n        //测算泥土数量\n        auto Dirt = 0;//泥土数\n        for (auto ix = x - 4; ix < x + 4; ix++) {\n            for (auto iy = y - 4; iy < y; iy++) {\n                for (auto iz = z - 4; iz < z + 4; iz++) {\n                    if (GetBlock({(ix), (iy), (iz)}) == Blocks::DIRT)Dirt++;\n                }\n            }\n        }\n        //测算最高高度\n        for (auto i = y + 1; i < y + 16; i++) {\n            if (GetBlock({(x), (i), (z)}) == Blocks::ENV) { h++; }\n            else { break; };\n        }\n        //取最小值\n        h = std::min(h, int(Dirt * 15 / 268 * std::max(rnd(), 0.8)));\n        if (h < 7)return;\n        //开始生成树干\n        for (auto i = y + 1; i < y + h + 1; i++) {\n            SetBlock({(x), (i), (z)}, Blocks::WOOD);\n        }\n        //设置树叶及枝杈\n        //计算树叶起始生成高度\n        const auto leafh = int(double(h) * 0.618) + 1;//黄金分割比大法好！！！\n        const auto distancen2 = int(double((h - leafh + 1) * (h - leafh + 1))) + 1;\n        for (auto iy = y + leafh; iy < y + int(double(h) * 1.382) + 2; iy++) {\n            for (auto ix = x - 6; ix < x + 6; ix++) {\n                for (auto iz = z - 6; iz < z + 6; iz++) {\n                    const auto distancen = DistanceSquare(ix, iy, iz, x, y + leafh + 1, z);\n                    if ((GetBlock({(ix), (iy), (iz)}) == Blocks::ENV) && (distancen < distancen2)) {\n                        if ((distancen <= distancen2 / 9) && (rnd() > 0.3)) {\n                            SetBlock({(ix), (iy), (iz)}, Blocks::WOOD);//生成枝杈\n                        } else {\n                            SetBlock({(ix), (iy), (iz)}, Blocks::LEAF); //生成树叶\n                        }\n                    }\n                }\n            }\n        }\n        // TODO(move this function when terrain carving for terrain generation is possible)\n    }\n\n    void explode(int x, int y, int z, int r, Chunk *c) {\n        const double maxdistsqr = r * r;\n        for (auto fx = x - r - 1; fx < x + r + 1; fx++) {\n            for (auto fy = y - r - 1; fy < y + r + 1; fy++) {\n                for (auto fz = z - r - 1; fz < z + r + 1; fz++) {\n                    const auto distsqr = (fx - x) * (fx - x) + (fy - y) * (fy - y) + (fz - z) * (fz - z);\n                    if (distsqr <= maxdistsqr * 0.75 ||\n                        distsqr <= maxdistsqr && rnd() > (distsqr - maxdistsqr * 0.6) / (maxdistsqr * 0.4)) {\n                        const auto e = GetBlock({(fx), (fy), (fz)});\n                        if (e == Blocks::ENV) continue;\n                        for (auto j = 1; j <= 12; j++) {\n                            Particles::throwParticle(e,\n                                                     float(fx + rnd() - 0.5f), float(fy + rnd() - 0.2f),\n                                                     float(fz + rnd() - 0.5f),\n                                                     float(rnd() * 0.2f - 0.1f), float(rnd() * 0.2f - 0.1f),\n                                                     float(rnd() * 0.2f - 0.1f),\n                                                     float(rnd() * 0.02 + 0.03), int(rnd() * 60) + 30);\n                        }\n                        SetBlock({(fx), (fy), (fz)}, Blocks::ENV, c);\n                    }\n                }\n            }\n        }\n    }\n\n    void PutBlock(const Int3 v, Block block) {\n        auto &blockInfo = BlockInfo(block);\n        if (blockInfo.BeforeBlockPlace(v, block)) {\n            SetBlock(v, block);\n            blockInfo.AfterBlockPlace(v, block);\n        }\n    }\n\n    void PickBlock(const Int3 v) {\n        const auto block = GetBlock(v);\n        auto &type = BlockInfo(block);\n        if (type.BeforeBlockDestroy(v, block)) {\n            SetBlock(v, Blocks::ENV);\n            type.AfterBlockDestroy(v, block);\n        }\n    }\n\n}\n"
  },
  {
    "path": "NEWorld.Game/Universe/World/World.h",
    "content": "#pragma once\n\n#include \"Definitions.h\"\n#include \"ChunkPtrArray.h\"\n#include \"Chunk.h\"\n#include \"Blocks.h\"\n#include \"OrderedArray.h\"\n#include \"Universe/Entity/bvh.h\"\n\nextern int viewdistance;\n\nnamespace World {\n    extern std::string worldname;\n    const int worldsize = 134217728;\n    const int worldheight = 128;\n    extern Brightness skylight;         //Sky light level\n    extern Brightness BRIGHTNESSMAX;    //Maximum brightness\n    extern Brightness BRIGHTNESSMIN;    //Mimimum brightness\n    extern Brightness BRIGHTNESSDEC;    //Brightness decree\n\n    extern std::vector<std::shared_ptr<Chunk>> chunks;\n    extern ChunkPtrArray cpArray;\n\n    extern int cloud[128][128];\n    extern int rebuiltChunks, rebuiltChunksCount;\n    extern int updatedChunks, updatedChunksCount;\n    extern int unloadedChunks, unloadedChunksCount;\n    extern OrderedList<int, Int3, 64> ChunkLoadList;\n    extern OrderedList<int, Chunk*, 64, std::greater> ChunkUnloadList;\n\n    template <class T>\n    constexpr T GetChunkPos(const T n) noexcept { return n >> 4; }\n\n    template <class T>\n    constexpr T GetBlockPos(const T n) noexcept { return n & 15; }\n\n    void Init();\n\n    Chunk *AddChunk(Int3 vec);\n\n    void DeleteChunk(Int3 vec);\n\n    Chunk *GetChunk(Int3 vec);\n\n    constexpr bool ChunkOutOfBound(const Int3 v) noexcept {\n        return v.Y < -worldheight || v.Y > worldheight - 1 ||\n                v.X < -134217728 || v.X > 134217727 || v.Z < -134217728 || v.Z > 134217727;\n    }\n\n    inline bool ChunkLoaded(const Int3 v) noexcept {\n        if (ChunkOutOfBound(v)) return false;\n        return GetChunk(v);\n    }\n\n    std::vector<BoundingBox> getHitboxes(const BoundingBox& box);\n\n    bool inWater(const BoundingBox& box);\n\n    void updateblock(int x, int y, int z, bool blockchanged, int depth = 0);\n\n    Block GetBlock(Int3 v, Block mask = Blocks::ENV, Chunk *hint = nullptr);\n\n    Brightness GetBrightness(Int3 v, Chunk *hint = nullptr);\n\n    void SetBlock(Int3 v, Block block, Chunk *hint = nullptr);\n\n    void SetBrightness(Int3 v, Brightness brightness, Chunk *hint = nullptr);\n\n    inline Brightness getbrightness(int x, int y, int z, Chunk *cptr = nullptr) {\n        return GetBrightness({x, y, z}, cptr);\n    }\n\n    void PutBlock(Int3 v, Block block);\n\n    void PickBlock(Int3 v);\n\n    bool chunkUpdated(Int3 vec);\n\n    void setChunkUpdated(int x, int y, int z, bool value);\n\n    void sortChunkLoadUnloadList(Int3 pos);\n\n    void saveAllChunks();\n\n    void destroyAllChunks();\n\n    void buildtree(Int3 pos);\n\n    void explode(int x, int y, int z, int r, Chunk *c = nullptr);\n}\n"
  },
  {
    "path": "NEWorld.Game/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ 生成的包含文件。\n// 供 resource.rc 使用\n//\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        101\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1000\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "NEWorld.Game/resource.rc",
    "content": "GLFW_ICON ICON \"neworld.ico\""
  },
  {
    "path": "NEWorld.Game/stdinclude.h",
    "content": "#pragma once\n\n#include <thread>\n#include <mutex>\n\n#define _USE_MATH_DEFINES\n#ifndef M_PI\n#define M_PI 3.14159265358979323846264338327950288\n#endif\n\n#include <cmath>\n#include <ctime>\n#include <vector>\n#include <cassert>\n#include <cstdarg>\n\n#ifdef NEWORLD_GAME\n\n#define GLEW_NO_GLU\n#include <GL/glew.h>\n#include <GLFW/glfw3.h>\n\n#endif"
  },
  {
    "path": "NoesisGUI/CMake/FindNoesis.cmake",
    "content": "# modified from https://github.com/rschurade/Ingnomia\n\nfind_path(NOESIS_INCLUDE_DIR\n\tNAMES\n\t\tNoesisPCH.h\n\tHINTS\n\t\t${NOESIS_ROOT}/Include\n)\n\nfind_library(NOESIS_LIBRARY\n\tNAMES\n\t\tlibNoesis Noesis\n\tHINTS\n\t\t${NOESIS_ROOT}/Bin/linux_x86_64\n\t\t${NOESIS_ROOT}/Lib/windows_x86_64\n)\n\nif(WIN32)\n\tfind_file(NOESIS_DLL\n\t\tNAMES\n\t\t\tNoesis.dll\n\t\tHINTS\n\t\t\t${NOESIS_ROOT}/Bin/windows_x86_64\n\t)\nelse()\n\tset(NOESIS_DLL ${NOESIS_LIBRARY})\nendif()\n\ninclude(FindPackageHandleStandardArgs)\n# handle the QUIETLY and REQUIRED arguments and set NOESIS_FOUND to TRUE\n# if all listed variables are TRUE\nfind_package_handle_standard_args(Noesis\n\tDEFAULT_MSG\n\tNOESIS_LIBRARY NOESIS_INCLUDE_DIR NOESIS_DLL)\n\nmark_as_advanced(NOESIS_INCLUDE_DIR NOESIS_LIBRARY NOESIS_DLL)\n\nif(NOESIS_FOUND AND NOT TARGET Noesis)\n\tadd_library(Noesis SHARED IMPORTED)\n\tset_target_properties(Noesis\n\t\tPROPERTIES\n\t\t\tINTERFACE_INCLUDE_DIRECTORIES \"${NOESIS_INCLUDE_DIR}\"\n\t\t\tIMPORTED_LOCATION \"${NOESIS_DLL}\"\n\t\t\tIMPORTED_IMPLIB \"${NOESIS_LIBRARY}\"\n\t)\nendif()"
  },
  {
    "path": "NoesisGUI/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\nproject(NEWorld)\nset(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/CMake)\n\nkls_add_library_module(NoesisGUI NW::Noesis)\n\nset(NOESIS_ROOT \"${CMAKE_CURRENT_SOURCE_DIR}/SDK\" CACHE PATH \"Root to the NoesisGui sdk\")\nfind_package(Noesis REQUIRED)\nfile(GLOB NOESIS_APP_INCLUDE\n        SDK/Src/Packages/*/*/Include\n        SDK/Src/Packages/App/Theme/Data/Theme\n        SDK/Src/Packages/App/Theme/Data/Theme/Fonts\n        )\nfile(GLOB NOESIS_APP_SRCS\n        SDK/Src/Packages/App/Theme/Src/*.cpp\n        SDK/Src/Packages/App/Providers/Src/*.cpp\n        SDK/Src/Packages/App/Interactivity/Src/*.cpp\n        SDK/Src/Packages/App/MediaElement/Src/*.cpp\n        SDK/Src/Packages/App/ApplicationLauncher/Src/NotifyPropertyChangedBase.cpp\n        SDK/Src/Packages/Render/GLRenderDevice/Src/*.cpp\n        )\ntarget_sources(NoesisGUI PRIVATE ${NOESIS_APP_SRCS})\ntarget_compile_definitions(NoesisGUI PUBLIC\n        NS_APP_THEME_API=\n        NS_APP_PROVIDERS_API=\n        NS_APP_INTERACTIVITY_API=\n        NS_APP_MEDIAELEMENT_API=\n        NS_APP_APPLICATIONLAUNCHER_API=\n        NS_RENDER_GLRENDERDEVICE_API=\n        )\n# For some reason we still need to put all these as public\n# TODO(Maybe we should put a wrapper library around it)\ntarget_include_directories(NoesisGUI PUBLIC ${NOESIS_APP_INCLUDE})\ntarget_link_libraries(NoesisGUI PUBLIC Noesis)\nexecute_process(COMMAND python bin2h.py WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})\nadd_custom_command(\n        TARGET NoesisGUI POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy\n        ${NOESIS_DLL} ${KLS_OUT_PRODUCT_DIR}\n)\n"
  },
  {
    "path": "NoesisGUI/bin2h.py",
    "content": "#!/usr/bin/env python\nfrom pathlib import Path\n\ndef bin2h(filein, fileout):\n    with open(filein, \"rb\") as fd:\n        data = bytearray(fd.read())\n\n    with open(fileout, \"w\") as fo:\n        fo.write(\"/* %s, %d bytes */\\n\" % (filein, len(data)))\n        fo.write(\"const uint8_t %s[] =\\n{\\n\" % filein.name.replace(\".\",\"_\").replace(\" \",\"_\"))\n        line = \"\" \n\n        for i in data:\n            num = \"%d\" % i + \",\"\n            if len(line) + len(num) < 100:\n                line += num\n            else:\n                fo.write(\"    \" + line + \"\\n\")\n                line = num\n\n        fo.write(\"    \" + line[:-1] + \"\\n\")\n        fo.write(\"};\\n\")\n\ndef main():\n    targets = list(Path(\".\").glob(\"SDK/Src/Packages/App/Theme/Data/Theme/*.xaml\"))\n    targets.extend(Path(\".\").glob(\"SDK/Src/Packages/App/Theme/Data/Theme/Fonts/*.otf\"))\n    for p in targets:\n        tarp = str(p)+\".bin.h\"\n        print(p, 'to' , tarp)\n        bin2h(p, tarp)\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "README.md",
    "content": "# NEWorld\n\n![Screenshot](https://raw.githubusercontent.com/Infinideastudio/NEWorld/refactor/Docs/old.png)\n\nNEWorld is a 3D sandbox game inspired by Minecraft, licensed under [LGPL v3](http://www.gnu.org/licenses/lgpl.html).\nIt features an engine specifically made for this game and hand-written with C++ for maximized optimization.\nWe welcome all contributions.  \n\nNEWorld 是一个类似于 Minecraft 的 3D 沙盒游戏，采用[LGPLv3许可证](http://www.gnu.org/licenses/lgpl.html)发布并受其保护。\n我们使用 C++ 专为 NEWorld 其自制了游戏引擎，而没有使用任何现成的解决方案，以最大化性能，欢迎感兴趣的人加入到我们的开发中。\n\nThere were attempts to [refactor](https://github.com/Infinideastudio/NEWorld/tree/refactor) or\n[rewrite](https://github.com/Infinideastudio/NEWorld/tree/renew) the game since the codebase was old and somewhat messy.\nWhat you see here is the latest attempt to incrementally clean the codebase while keeping most of its features.\n\n## Features\n\n1. C++20\n2. Optimized for performance\n3. Should be cross-platform (although the development is currently focused in Windows)\n4. Depends on OpenGL 4.5, glew, glfw, openal-soft, leveldb, Noesis GUI.\n5. Re-invented wheels\n6. Written for fun!\n\n## Compilation\n\n1. Clone this repo\n2. Install vcpkg and install dependencies\n3. Download [Noesis](https://www.noesisengine.com/), and put it into `External/Noesis`\n4. Draft the license key into `External/Noesis/Include/NoesisLicense.h`\n5. Compile and run!"
  }
]