From 8ded279ab2392a676c6bfb98881fc6b1437b8367 Mon Sep 17 00:00:00 2001 From: gil zhang Date: Sun, 28 May 2023 16:29:46 +0800 Subject: [PATCH] add 3dmodel load code --- src/ElectronBot.Braincase/App.xaml.cs | 4 + .../ModelLoadCompactOverlayPage.xaml | 32 ++ .../ModelLoadCompactOverlayPage.xaml.cs | 41 +++ .../Controls/ElectronBotModelLoad.xaml | 12 + .../Controls/ElectronBotModelLoad.xaml.cs | 26 ++ .../ElectronBot.Braincase.csproj | 14 +- .../Strings/en-us/Resources.resw | 3 + .../Strings/zh-cn/Resources.resw | 3 + .../ViewModels/MainViewModel.cs | 31 +- .../ModelLoadCompactOverlayViewModel.cs | 324 ++++++++++++++++++ src/ElectronBot.Braincase/Views/MainPage.xaml | 5 + 11 files changed, 493 insertions(+), 2 deletions(-) create mode 100644 src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml create mode 100644 src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml.cs create mode 100644 src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml create mode 100644 src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml.cs create mode 100644 src/ElectronBot.Braincase/ViewModels/ModelLoadCompactOverlayViewModel.cs diff --git a/src/ElectronBot.Braincase/App.xaml.cs b/src/ElectronBot.Braincase/App.xaml.cs index b670ade..dd5f670 100644 --- a/src/ElectronBot.Braincase/App.xaml.cs +++ b/src/ElectronBot.Braincase/App.xaml.cs @@ -26,6 +26,7 @@ using Views; using Windows.ApplicationModel.Background; using Windows.Media.Playback; using Windows.UI.Popups; +using HelixToolkit.SharpDX.Core; using ViewModels; namespace ElectronBot.Braincase; @@ -160,6 +161,9 @@ public partial class App : Application services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); diff --git a/src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml b/src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml new file mode 100644 index 0000000..dc355cd --- /dev/null +++ b/src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + diff --git a/src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml.cs b/src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml.cs new file mode 100644 index 0000000..2842f80 --- /dev/null +++ b/src/ElectronBot.Braincase/Controls/CompactOverlay/ModelLoadCompactOverlayPage.xaml.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using ViewModels; +using Windows.Foundation; +using Windows.Foundation.Collections; +using ElectronBot.Braincase; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Controls.CompactOverlay; +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class ModelLoadCompactOverlayPage : Page +{ + public ModelLoadCompactOverlayViewModel ViewModel + { + get; + } + public ModelLoadCompactOverlayPage() + { + this.InitializeComponent(); + ViewModel = App.GetService(); + } + + private async void ModelLoadCompactOverlayPage_OnLoaded(object sender, RoutedEventArgs e) + { + await ViewModel.Loaded(); + } +} diff --git a/src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml b/src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml new file mode 100644 index 0000000..e300a6a --- /dev/null +++ b/src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml.cs b/src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml.cs new file mode 100644 index 0000000..4db36f0 --- /dev/null +++ b/src/ElectronBot.Braincase/Controls/ElectronBotModelLoad.xaml.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Controls; +public sealed partial class ElectronBotModelLoad : UserControl +{ + public ElectronBotModelLoad() + { + this.InitializeComponent(); + } +} diff --git a/src/ElectronBot.Braincase/ElectronBot.Braincase.csproj b/src/ElectronBot.Braincase/ElectronBot.Braincase.csproj index 6fe746c..74f9059 100644 --- a/src/ElectronBot.Braincase/ElectronBot.Braincase.csproj +++ b/src/ElectronBot.Braincase/ElectronBot.Braincase.csproj @@ -53,6 +53,8 @@ + + @@ -144,7 +146,7 @@ - + @@ -154,6 +156,10 @@ + + + + @@ -205,6 +211,12 @@ PreserveNewest + + MSBuild:Compile + + + MSBuild:Compile + $(DefaultXamlRuntime) diff --git a/src/ElectronBot.Braincase/Strings/en-us/Resources.resw b/src/ElectronBot.Braincase/Strings/en-us/Resources.resw index b042718..162e56f 100644 --- a/src/ElectronBot.Braincase/Strings/en-us/Resources.resw +++ b/src/ElectronBot.Braincase/Strings/en-us/Resources.resw @@ -701,4 +701,7 @@ GaussianBlur Value: + + Emulation + \ No newline at end of file diff --git a/src/ElectronBot.Braincase/Strings/zh-cn/Resources.resw b/src/ElectronBot.Braincase/Strings/zh-cn/Resources.resw index c830397..b614ce8 100644 --- a/src/ElectronBot.Braincase/Strings/zh-cn/Resources.resw +++ b/src/ElectronBot.Braincase/Strings/zh-cn/Resources.resw @@ -698,4 +698,7 @@ 高斯模糊值: + + 电子仿真 + \ No newline at end of file diff --git a/src/ElectronBot.Braincase/ViewModels/MainViewModel.cs b/src/ElectronBot.Braincase/ViewModels/MainViewModel.cs index c65916d..bf033b3 100644 --- a/src/ElectronBot.Braincase/ViewModels/MainViewModel.cs +++ b/src/ElectronBot.Braincase/ViewModels/MainViewModel.cs @@ -67,6 +67,8 @@ public partial class MainViewModel : ObservableRecipient, INavigationAware CanvasImageSource? canvasImageSource = null; + private readonly ElementTheme _elementTheme; + private readonly IntPtr _hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow); public MainViewModel( @@ -77,7 +79,8 @@ public partial class MainViewModel : ObservableRecipient, INavigationAware ObjectPickerService objectPickerService, MediaPlayer mediaPlayer, IActionExpressionProviderFactory actionExpressionProviderFactory, - ISpeechAndTTSService speechAndTTSService) + ISpeechAndTTSService speechAndTTSService, + IThemeSelectorService elementTheme) { _localSettingsService = localSettingsService; @@ -109,6 +112,7 @@ public partial class MainViewModel : ObservableRecipient, INavigationAware ElectronBotHelper.Instance.ClockCanvasStop += Instance_ClockCanvasStop; ElectronBotHelper.Instance.ClockCanvasStart += Instance_ClockCanvasStart; + _elementTheme = elementTheme.Theme; } private void Instance_ClockCanvasStart(object? sender, EventArgs e) @@ -318,6 +322,31 @@ public partial class MainViewModel : ObservableRecipient, INavigationAware } } + [RelayCommand] + private void ElectronEmulation() + { + try + { + WindowEx compactOverlay = new CompactOverlayWindow(); + + compactOverlay.Content = new ModelLoadCompactOverlayPage() + { + RequestedTheme = _elementTheme + }; + + var appWindow = compactOverlay.AppWindow; + + appWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay); + + appWindow.Show(); + + //App.MainWindow.Hide(); + } + catch (Exception) + { + } + } + [RelayCommand] private async void TestVoice() { diff --git a/src/ElectronBot.Braincase/ViewModels/ModelLoadCompactOverlayViewModel.cs b/src/ElectronBot.Braincase/ViewModels/ModelLoadCompactOverlayViewModel.cs new file mode 100644 index 0000000..123db27 --- /dev/null +++ b/src/ElectronBot.Braincase/ViewModels/ModelLoadCompactOverlayViewModel.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using Windows.ApplicationModel; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Controls.CompactOverlay; +using ElectronBot.Braincase; +using ElectronBot.Braincase.Helpers; +using HelixToolkit.SharpDX.Core; +using HelixToolkit.WinUI; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Models; +using SharpDX; +using Assimp; +using HelixToolkit.SharpDX.Core.Assimp; +using HelixToolkit.SharpDX.Core.Model.Scene; +using BoundingBox = SharpDX.BoundingBox; +using Camera = HelixToolkit.WinUI.Camera; + +namespace ViewModels; +public partial class ModelLoadCompactOverlayViewModel : ObservableRecipient +{ + private readonly DispatcherTimer DispatcherTimer = new(); + + public ModelLoadCompactOverlayViewModel(IEffectsManager effectsManager) + { + EffectsManager = effectsManager; + DispatcherTimer.Interval = TimeSpan.FromMilliseconds(500); + + DispatcherTimer.Tick += ModelAcitonDispatcherTimer_Tick; + Material = new DiffuseMaterial() + { + EnableUnLit = false, + DiffuseMap = LoadTexture("eyes-closed.png") + }; + } + + + [RelayCommand] + public async Task Loaded() + { + try + { + var body = new List() + { + "Body1.obj", + "Body2.obj", + }; + + var head = new List() + { + "Face.obj", + "Head1.obj", + "Head2.obj", + "Head3.obj", + }; + + var leftArm = new List() + { + "LeftArm1.obj", + "LeftArm2.obj", + "LeftShoulder.obj", + }; + + var rightArm = new List() + { + "RightArm1.obj", + "RightArm2.obj", + "RightShoulder.obj" + }; + + + var baseBody = new List() + { + "Base.obj", + }; + var importer = new Importer(); + + foreach (var modelName in head) + { + var modelPath = Package.Current.InstalledLocation.Path + $"\\Assets\\ElectronBotModel\\{modelName}"; + + var newScene = importer.Load(modelPath); + + if (newScene != null) + { + /// Pre-attach and calculate all scene info in a separate task. + newScene.Root.Attach(EffectsManager); + + newScene.Root.UpdateAllTransformMatrix(); + + HeadModel.AddNode(newScene.Root); + + if (modelName == "Head3.obj") + { + if (newScene != null && newScene.Root != null) + { + foreach (var node in newScene.Root.Traverse()) + { + if (node is MeshNode meshNode) + { + meshNode.Material = Material; + } + } + } + } + } + } + + + foreach (var modelName in body) + { + var modelPath = Package.Current.InstalledLocation.Path + $"\\Assets\\ElectronBotModel\\{modelName}"; + + var newScene = importer.Load(modelPath); + + if (newScene != null) + { + /// Pre-attach and calculate all scene info in a separate task. + newScene.Root.Attach(EffectsManager); + newScene.Root.UpdateAllTransformMatrix(); + + BodyModel.AddNode(newScene.Root); + + if (modelName == "Body2.obj") + { + if (newScene.Root.TryGetCentroid(out var centroid)) + { + /// Must use UI thread to set value back. + ModelCentroidPoint = centroid; + } + } + } + } + + foreach (var modelName in rightArm) + { + var modelPath = Package.Current.InstalledLocation.Path + $"\\Assets\\ElectronBotModel\\{modelName}"; + var newScene = importer.Load(modelPath); + if (newScene != null) + { + /// Pre-attach and calculate all scene info in a separate task. + newScene.Root.Attach(EffectsManager); + newScene.Root.UpdateAllTransformMatrix(); + + RightArmModel.AddNode(newScene.Root); + + if (newScene.Root.TryGetBound(out var bound)) + { + /// Must use UI thread to set value back. + if (modelName == "RightShoulder.obj") + { + ModelBoundingBox = bound; + } + + } + } + } + + + foreach (var modelName in leftArm) + { + var modelPath = Package.Current.InstalledLocation.Path + $"\\Assets\\ElectronBotModel\\{modelName}"; + var newScene = importer.Load(modelPath); + if (newScene != null) + { + /// Pre-attach and calculate all scene info in a separate task. + newScene.Root.Attach(EffectsManager); + newScene.Root.UpdateAllTransformMatrix(); + + LeftArmModel.AddNode(newScene.Root); + + if (newScene.Root.TryGetBound(out var bound)) + { + /// Must use UI thread to set value back. + if (modelName == "RightShoulder.obj") + { + // ModelBoundingBox = bound; + } + + } + } + } + + foreach (var modelName in baseBody) + { + var modelPath = Package.Current.InstalledLocation.Path + $"\\Assets\\ElectronBotModel\\{modelName}"; + var newScene = importer.Load(modelPath); + if (newScene != null) + { + /// Pre-attach and calculate all scene info in a separate task. + newScene.Root.Attach(EffectsManager); + newScene.Root.UpdateAllTransformMatrix(); + + BaseModel.AddNode(newScene.Root); + } + } + + FocusCameraToScene(); + DispatcherTimer.Start(); + } + catch (Exception) + { + ToastHelper.SendToast("模型加载失败", TimeSpan.FromSeconds(3)); + } + } + + private void FocusCameraToScene() + { + var maxWidth = Math.Max(Math.Max(ModelBoundingBox.Width, ModelBoundingBox.Height), ModelBoundingBox.Depth) + 80; + var pos = ModelBoundingBox.Center + new Vector3(0, 0, maxWidth); + Camera.Position = pos; + Camera.LookDirection = ModelBoundingBox.Center - pos; + Camera.UpDirection = Vector3.UnitY; + if (Camera is OrthographicCamera orthCam) + { + orthCam.Width = maxWidth; + } + } + + private void ModelAcitonDispatcherTimer_Tick(object? sender, object e) + { + try + { + var list = ModelBoundingBox.GetCorners(); + + var average = new SharpDX.Vector3( + (list[1].X + list[5].X) / 2f, + ((list[1].Y + list[5].Y) / 2f) - 8f, + (list[1].Z + list[5].Z) / 2f + ); + + var translationMatrix = Matrix.Translation(-average.X, -average.Y, -average.Z); + var tr2 = RightArmModel.HxTransform3D * translationMatrix; + //var tr3 = tr2 * Matrix.RotationZ(MathUtil.DegreesToRadians(-(DateTime.Now.Second))); + var tr4 = tr2 * Matrix.RotationX(MathUtil.DegreesToRadians(-(DateTime.Now.Second))); + var tr5 = tr4 * Matrix.Translation(average.X, average.Y, average.Z); + + RightArmModel.HxTransform3D = tr5; + } + catch + { + + } + + } + + public IEffectsManager EffectsManager + { + get; + } + + + public SceneNodeGroupModel3D BodyModel + { + get; + } = new SceneNodeGroupModel3D(); + + public SceneNodeGroupModel3D LeftArmModel + { + get; + } = new SceneNodeGroupModel3D(); + + public SceneNodeGroupModel3D RightArmModel + { + get; + } = new SceneNodeGroupModel3D(); + + public SceneNodeGroupModel3D HeadModel + { + get; + } = new SceneNodeGroupModel3D(); + + public SceneNodeGroupModel3D BaseModel + { + get; + } = new SceneNodeGroupModel3D(); + + public DiffuseMaterial Material + { + private set; + get; + } + + [ObservableProperty] bool _showAxis = true; + + [ObservableProperty] + private Vector3 _modelCentroidPoint = default; + [ObservableProperty] + private bool _showWireframe = false; + [ObservableProperty] + private BoundingBox _modelBoundingBox = default; + + public Camera Camera + { + get; + } = new OrthographicCamera() { NearPlaneDistance = 1e-2, FarPlaneDistance = 1e4 }; + + [RelayCommand] + public void CompactOverlay() + { + //WindowEx compactOverlay = new CompactOverlayWindow(); + + //compactOverlay.Content = new DefaultCompactOverlayPage(); + + //var appWindow = compactOverlay.AppWindow; + + //appWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay); + + //appWindow.Destroy(); + + App.MainWindow.Show(); + } + + private TextureModel LoadTexture(string file) + { + var filePath = Package.Current.InstalledLocation.Path + $"\\Assets\\Emoji\\Pic\\{file}"; + + return TextureModel.Create(filePath); + } +} diff --git a/src/ElectronBot.Braincase/Views/MainPage.xaml b/src/ElectronBot.Braincase/Views/MainPage.xaml index bc9824b..a47b90c 100644 --- a/src/ElectronBot.Braincase/Views/MainPage.xaml +++ b/src/ElectronBot.Braincase/Views/MainPage.xaml @@ -92,6 +92,11 @@ +