diff --git a/CUE4Parse b/CUE4Parse index 8d145bb8..068e843f 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 8d145bb84efee7dfc1efe9ed2b2d9e53d8b463c3 +Subproject commit 068e843fb40649559d0aa82e5f2767b2e6a42512 diff --git a/FModel/ViewModels/ExportSessionViewModel.cs b/FModel/ViewModels/ExportSessionViewModel.cs index cc511219..fbeb8f1c 100644 --- a/FModel/ViewModels/ExportSessionViewModel.cs +++ b/FModel/ViewModels/ExportSessionViewModel.cs @@ -14,6 +14,7 @@ using CUE4Parse.Utils; using FModel.Extensions; using FModel.Framework; using FModel.Settings; +using FModel.Views; using FModel.Views.Snooper; using Serilog.Events; @@ -58,7 +59,16 @@ public class ExportSessionViewModel : ViewModel get { if (_session != null) return _session; - _session = new ExportSession(); + _session = new ExportSession((args, ct) => + { + Application.Current.Dispatcher.Invoke(() => + { + var window = new StreamingLevelFilterWindow(new StreamingLevelFilterViewModel(args)); + _stopwatch.Stop(); + window.ShowDialog(); + _stopwatch.Start(); + }, DispatcherPriority.Normal, ct); + }); _session.PropertyChanged += OnSessionPropertyChanged; return _session; } diff --git a/FModel/ViewModels/StreamingLevelFilterViewModel.cs b/FModel/ViewModels/StreamingLevelFilterViewModel.cs new file mode 100644 index 00000000..a77fc448 --- /dev/null +++ b/FModel/ViewModels/StreamingLevelFilterViewModel.cs @@ -0,0 +1,192 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using CUE4Parse_Conversion; +using CUE4Parse_Conversion.Dto; + +namespace FModel.ViewModels; + +public class StreamingLevelFilterViewModel +{ + public string WorldName { get; } + public int TotalCount { get; } + public List Children { get; } = []; + + public StreamingLevelFilterViewModel(StreamingLevelFilterArgs args) + { + WorldName = args.WorldName; + + foreach (var actor in args.Actors) + { + Children.Add(new ActorNodeVm(actor)); + } + + var worldLevels = new ActorNodeVm("Streaming Levels") { IsExpanded = true }; + foreach (var level in args.StreamingLevels) + { + worldLevels.Children.Add(new StreamingLevelNodeVm(level)); + } + if (worldLevels.Children.Count > 0) Children.Add(worldLevels); + + TotalCount = CountLevels(args.Actors) + args.StreamingLevels.Count; + } + + public void SkipAll() + { + foreach (var child in Children) + { + child.IsChecked = false; + } + } + + private int CountLevels(IReadOnlyList actors) + { + var n = 0; + foreach (var a in actors) n += CountFromActor(a); + return n; + } + + private int CountFromActor(ActorDto actor) + { + var n = actor.StreamingLevels?.Count ?? 0; + if (actor.RootComponent is { } comp) n += CountFromComponent(comp); + return n; + } + + private int CountFromComponent(SceneComponentDto comp) + { + var n = 0; + foreach (var a in comp.AttachedActors) n += CountFromActor(a); + foreach (var c in comp.Children) n += CountFromComponent(c); + return n; + } +} + +public class ActorNodeVm : TreeNodeVm +{ + public override string Name { get; } + public bool IsExpanded { get; set; } + public ObservableCollection Children { get; } = []; + + private static int _batch; + + public ActorNodeVm(string name) + { + Name = name; + Children.CollectionChanged += (_, e) => + { + if (e.NewItems is null) return; + + foreach (TreeNodeVm child in e.NewItems) + { + child.PropertyChanged += (_, args) => + { + if (args.PropertyName == nameof(IsChecked) && _batch == 0) + OnPropertyChanged(nameof(IsChecked)); + }; + } + }; + } + + public ActorNodeVm(ActorDto actor) : this(actor.Name) + { + if (actor.StreamingLevels is { Count: > 0 } streamingLevels) + foreach (var level in streamingLevels) + Children.Add(new StreamingLevelNodeVm(level)); + + CollectFromComponent(actor.RootComponent); + } + + private ActorNodeVm(SceneComponentDto component) : this(component.Name) + { + CollectFromComponent(component); + } + + private void NotifyIntermediates() + { + foreach (var child in Children.OfType()) + { + child.NotifyIntermediates(); + child.OnPropertyChanged(nameof(IsChecked)); + } + } + + private void CollectFromComponent(SceneComponentDto? comp) + { + if (comp is null) return; + foreach (var actor in comp.AttachedActors) + Children.Add(new ActorNodeVm(actor)); + foreach (var component in comp.Children) + Children.Add(new ActorNodeVm(component)); + } + + private IEnumerable AllLevels() + { + foreach (var child in Children) + { + switch (child) + { + case StreamingLevelNodeVm sl: + yield return sl; + break; + case ActorNodeVm actor: + { + foreach (var l in actor.AllLevels()) + { + yield return l; + } + break; + } + } + } + } + + public bool? IsChecked + { + get + { + var all = AllLevels().ToList(); + if (all.Count == 0) return false; + + var trueCount = all.Count(l => l.IsChecked); + if (trueCount == all.Count) return true; + if (trueCount == 0) return false; + return null; + } + set + { + var v = value ?? false; + _batch++; + foreach (var l in AllLevels()) + l.IsChecked = v; + NotifyIntermediates(); + _batch--; + OnPropertyChanged(); + } + } +} + +public class StreamingLevelNodeVm(StreamingLevel level) : TreeNodeVm +{ + public override string Name { get; } = level.World.Name; + + public bool IsChecked + { + get => level.IsPersistent; + set + { + level.IsPersistent = value; + OnPropertyChanged(); + } + } +} + +public abstract class TreeNodeVm : INotifyPropertyChanged +{ + public abstract string Name { get; } + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string? name = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); +} diff --git a/FModel/Views/ExportSessionWindow.xaml b/FModel/Views/ExportSessionWindow.xaml index 480b55e7..8e3f6b9d 100644 --- a/FModel/Views/ExportSessionWindow.xaml +++ b/FModel/Views/ExportSessionWindow.xaml @@ -5,7 +5,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignWidth="512" d:DesignHeight="512" d:DataContext="{d:DesignInstance Type=vm:ExportSessionViewModel, IsDesignTimeCreatable=False}" - Background="{DynamicResource {x:Static adonisUi:Brushes.Layer0BackgroundBrush}}" xmlns:vm="clr-namespace:FModel.ViewModels" xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI" diff --git a/FModel/Views/StreamingLevelFilterWindow.xaml b/FModel/Views/StreamingLevelFilterWindow.xaml new file mode 100644 index 00000000..5d2539c4 --- /dev/null +++ b/FModel/Views/StreamingLevelFilterWindow.xaml @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +