From 798e71789ed0f0ba8b67420ccf2422fe155dc113 Mon Sep 17 00:00:00 2001 From: Ashadow <------> Date: Wed, 1 Jan 2025 17:13:31 +0100 Subject: [PATCH 01/13] implemented scenes feature --- Brio/Brio.cs | 5 +- Brio/Brio.csproj | 4 + .../Actor/ActorContainerCapability.cs | 12 ++- .../Posing/ModelPosingCapability.cs | 41 ++++++++- Brio/Capabilities/Posing/PosingCapability.cs | 4 +- Brio/Config/Configuration.cs | 6 ++ Brio/Config/ImportConfiguration.cs | 17 ++++ Brio/Entities/Actor/ActorContainerEntity.cs | 2 +- Brio/Entities/EntityManager.cs | 11 +++ Brio/Files/ActorFile.cs | 33 +++++++ Brio/Files/PoseFile.cs | 1 + Brio/Files/SceneFile.cs | 27 ++++++ Brio/Game/Posing/PoseImporter.cs | 6 +- Brio/Game/Scene/SceneService.cs | 77 ++++++++++++++++ .../UI/Controls/Editors/PosingEditorCommon.cs | 2 - Brio/UI/Controls/Stateless/FileUIHelpers.cs | 49 ++++++++++ Brio/UI/Windows/MainWindow.cs | 25 ++++- Brio/UI/Windows/SettingsWindow.cs | 92 ++++++++++++++++++- .../Specialized/PosingOverlayToolbarWindow.cs | 2 +- 19 files changed, 393 insertions(+), 23 deletions(-) create mode 100644 Brio/Config/ImportConfiguration.cs create mode 100644 Brio/Files/ActorFile.cs create mode 100644 Brio/Files/SceneFile.cs create mode 100644 Brio/Game/Scene/SceneService.cs diff --git a/Brio/Brio.cs b/Brio/Brio.cs index 81708319..c01e9590 100644 --- a/Brio/Brio.cs +++ b/Brio/Brio.cs @@ -24,6 +24,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Diagnostics; +using Brio.Game.Scene; namespace Brio; @@ -113,6 +114,7 @@ private static ServiceCollection SetupServices(DalamudServices dalamudServices) serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); // IPC serviceCollection.AddSingleton(); @@ -155,8 +157,9 @@ private static ServiceCollection SetupServices(DalamudServices dalamudServices) serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Brio/Brio.csproj b/Brio/Brio.csproj index e74b8293..004f09c7 100644 --- a/Brio/Brio.csproj +++ b/Brio/Brio.csproj @@ -76,4 +76,8 @@ + + + + diff --git a/Brio/Capabilities/Actor/ActorContainerCapability.cs b/Brio/Capabilities/Actor/ActorContainerCapability.cs index 45ab7600..bca57705 100644 --- a/Brio/Capabilities/Actor/ActorContainerCapability.cs +++ b/Brio/Capabilities/Actor/ActorContainerCapability.cs @@ -1,6 +1,8 @@ -using Brio.Capabilities.Core; +using System; +using Brio.Capabilities.Core; using Brio.Entities; using Brio.Entities.Actor; +using Brio.Entities.Core; using Brio.Game.Actor; using Brio.Game.Core; using Brio.Game.GPose; @@ -32,7 +34,7 @@ public void SelectActorInHierarchy(ActorEntity entity) _entityManager.SetSelectedEntity(entity); } - public void CreateCharacter(bool enableAttachments, bool targetNewInHierarchy, bool forceSpawnActorWithoutCompanion = false) + public EntityId CreateCharacter(bool enableAttachments, bool targetNewInHierarchy, bool forceSpawnActorWithoutCompanion = false) { SpawnFlags flags = SpawnFlags.Default; if(enableAttachments) @@ -40,11 +42,15 @@ public void CreateCharacter(bool enableAttachments, bool targetNewInHierarchy, b if(_actorSpawnService.CreateCharacter(out var chara, flags, disableSpawnCompanion: forceSpawnActorWithoutCompanion)) { + EntityId characterId = new EntityId(chara); if(targetNewInHierarchy) { - _entityManager.SetSelectedEntity(chara); + _entityManager.SetSelectedEntity(characterId); } + return characterId; } + + throw new Exception("Failed to create character"); } public void DestroyCharacter(ActorEntity entity) diff --git a/Brio/Capabilities/Posing/ModelPosingCapability.cs b/Brio/Capabilities/Posing/ModelPosingCapability.cs index 7719c4e1..3fc2ba5d 100644 --- a/Brio/Capabilities/Posing/ModelPosingCapability.cs +++ b/Brio/Capabilities/Posing/ModelPosingCapability.cs @@ -1,4 +1,6 @@ -using Brio.Capabilities.Actor; +using System.Numerics; +using Brio.Capabilities.Actor; +using Brio.Config; using Brio.Core; using Brio.Entities.Actor; using Brio.Files; @@ -39,8 +41,7 @@ public unsafe Transform OriginalTransform } public Transform? OverrideTransform => _transformOverride; - - + private Transform? _transformOverride = null; private Transform? _originalTransform = null; @@ -68,13 +69,43 @@ public override void Dispose() public void ImportModelPose(PoseFile poseFile, PoseImporterOptions options) { - if(options.ApplyModelTransform) - Transform += poseFile.ModelDifference; + if(!options.ApplyModelTransform) + { + return; + } + + var temp = new Transform + { + Position = options.PositionTransformType switch + { + PoseImportTransformType.Difference => Transform.Position + poseFile.ModelDifference.Position, + PoseImportTransformType.Absolute => poseFile.ModelAbsoluteValues.Position, + _ => Transform.Position + }, + Rotation = options.RotationTransformType switch + { + PoseImportTransformType.Difference => Quaternion.Normalize(Transform.Rotation * poseFile.ModelDifference.Rotation), + PoseImportTransformType.Absolute => poseFile.ModelAbsoluteValues.Rotation, + _ => Transform.Rotation + }, + Scale = options.ScaleTransformType switch + { + PoseImportTransformType.Difference => Transform.Scale + poseFile.ModelDifference.Scale, + PoseImportTransformType.Absolute => poseFile.ModelAbsoluteValues.Scale, + _ => Transform.Scale + } + }; + + Transform = temp; } public void ExportModelPose(PoseFile poseFile) { if(_originalTransform.HasValue) + { poseFile.ModelDifference = Transform.CalculateDiff(_originalTransform.Value); + } + + poseFile.ModelAbsoluteValues = Transform; } } diff --git a/Brio/Capabilities/Posing/PosingCapability.cs b/Brio/Capabilities/Posing/PosingCapability.cs index 03610292..7a64d094 100644 --- a/Brio/Capabilities/Posing/PosingCapability.cs +++ b/Brio/Capabilities/Posing/PosingCapability.cs @@ -2,7 +2,6 @@ using Brio.Config; using Brio.Core; using Brio.Entities.Actor; -using Brio.Entities.Core; using Brio.Files; using Brio.Game.Posing; using Brio.Input; @@ -263,8 +262,7 @@ private void Reconcile(bool reset = true, bool generateSnapshot = true) ImportPose(poseFile, options: all, generateSnapshot: false); }, delayTicks: 2); } - - private PoseFile GeneratePoseFile() + public PoseFile GeneratePoseFile() { var poseFile = new PoseFile(); SkeletonPosing.ExportSkeletonPose(poseFile); diff --git a/Brio/Config/Configuration.cs b/Brio/Config/Configuration.cs index 41bd0191..a322cd47 100644 --- a/Brio/Config/Configuration.cs +++ b/Brio/Config/Configuration.cs @@ -26,6 +26,8 @@ internal class Configuration : IPluginConfiguration // Environment public EnvironmentConfiguration Environment { get; set; } = new EnvironmentConfiguration(); + + public ImportConfiguration Import { get; set; } = new ImportConfiguration(); // Library public LibraryConfiguration Library { get; set; } = new LibraryConfiguration(); @@ -33,9 +35,13 @@ internal class Configuration : IPluginConfiguration public string LastMCDFPath { get; set; } = string.Empty; public string LastExportPath { get; set; } = string.Empty; public string LastXATPath { get; set; } = string.Empty; + + public string LastScenePath { get; set; } = string.Empty; public bool UseLibraryWhenImporting { get; set; } = true; + public bool SceneDestoryActorsBeforeImport { get; set; } = false; + // Input public InputConfiguration Input { get; set; } = new InputConfiguration(); diff --git a/Brio/Config/ImportConfiguration.cs b/Brio/Config/ImportConfiguration.cs new file mode 100644 index 00000000..16983e83 --- /dev/null +++ b/Brio/Config/ImportConfiguration.cs @@ -0,0 +1,17 @@ +namespace Brio.Config; + +internal class ImportConfiguration +{ + public bool ApplyModelTransform { get; set; } = true; + + public PoseImportTransformType PositionTransformType { get; set; } = PoseImportTransformType.Difference; + public PoseImportTransformType RotationTransformType { get; set; } = PoseImportTransformType.Absolute; + public PoseImportTransformType ScaleTransformType { get; set; } = PoseImportTransformType.Absolute; +} + +internal enum PoseImportTransformType +{ + Ignore, + Difference, + Absolute +} diff --git a/Brio/Entities/Actor/ActorContainerEntity.cs b/Brio/Entities/Actor/ActorContainerEntity.cs index e2aa1371..7755d961 100644 --- a/Brio/Entities/Actor/ActorContainerEntity.cs +++ b/Brio/Entities/Actor/ActorContainerEntity.cs @@ -18,7 +18,7 @@ public override void OnAttached() public override void OnChildAttached() => SortChildren(); public override void OnChildDetached() => SortChildren(); - + private void SortChildren() { diff --git a/Brio/Entities/EntityManager.cs b/Brio/Entities/EntityManager.cs index 1e9f617d..f07f7a0e 100644 --- a/Brio/Entities/EntityManager.cs +++ b/Brio/Entities/EntityManager.cs @@ -124,6 +124,17 @@ public bool TryGetEntity(EntityId id, [MaybeNullWhen(false)] out T entity) wh _entityMap.TryGetValue(id, out var entity); return entity; } + + public T? GetEntity(EntityId id) where T : Entity + { + _entityMap.TryGetValue(id, out var entity); + + if(entity is T t) + { + return t; + } + return null; + } public bool EntityExists(EntityId id) { diff --git a/Brio/Files/ActorFile.cs b/Brio/Files/ActorFile.cs new file mode 100644 index 00000000..b07ac5cd --- /dev/null +++ b/Brio/Files/ActorFile.cs @@ -0,0 +1,33 @@ +using System; +using Brio.Capabilities.Actor; +using Brio.Capabilities.Posing; +using Brio.Entities.Actor; + +namespace Brio.Files; + +[Serializable] +internal class ActorFile +{ + public String FriendlyName { get; set; } = ""; + + public required AnamnesisCharaFile AnamnesisCharaFile { get; set; } + public required PoseFile PoseFile { get; set; } + + public static implicit operator ActorFile(ActorEntity actorEntity) + { + + var appearanceCapability = actorEntity.GetCapability(); + var posingCapability = actorEntity.GetCapability(); + + posingCapability.GeneratePoseFile(); + + var actorFile = new ActorFile + { + FriendlyName = actorEntity.FriendlyName, + AnamnesisCharaFile = appearanceCapability.CurrentAppearance, + PoseFile = posingCapability.GeneratePoseFile() + }; + return actorFile; + + } +} diff --git a/Brio/Files/PoseFile.cs b/Brio/Files/PoseFile.cs index 21127af2..7a95cc03 100644 --- a/Brio/Files/PoseFile.cs +++ b/Brio/Files/PoseFile.cs @@ -66,6 +66,7 @@ protected override void Apply(PoseFile file, ActorEntity actor, bool asExpressio internal class PoseFile : JsonDocumentBase { public Bone ModelDifference { get; set; } = Transform.Identity; + public Bone ModelAbsoluteValues { get; set; } = Transform.Identity; public Dictionary Bones { get; set; } = []; public Dictionary MainHand { get; set; } = []; diff --git a/Brio/Files/SceneFile.cs b/Brio/Files/SceneFile.cs new file mode 100644 index 00000000..89f5e4af --- /dev/null +++ b/Brio/Files/SceneFile.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Brio.Resources; +using Dalamud.Interface.Textures.TextureWraps; + + +namespace Brio.Files; + +internal class SceneFileInfo : JsonDocumentBaseFileInfo +{ + public override string Name => "Scene File"; + + public override IDalamudTextureWrap Icon => ResourceProvider.Instance.GetResourceImage("Images.FileIcon_Unknown.png"); + public override string Extension => ".scene"; + +} + +[Serializable] +internal class SceneFile +{ + public List Actors { get; set; } = []; + + public void AddActor(ActorFile actorFile) + { + Actors.Add(actorFile); + } +} diff --git a/Brio/Game/Posing/PoseImporter.cs b/Brio/Game/Posing/PoseImporter.cs index ca95fe4f..27cf003b 100644 --- a/Brio/Game/Posing/PoseImporter.cs +++ b/Brio/Game/Posing/PoseImporter.cs @@ -1,4 +1,4 @@ -using Brio.Capabilities.Posing; +using Brio.Config; using Brio.Core; using Brio.Files; using Brio.Game.Posing.Skeletons; @@ -69,5 +69,7 @@ internal class PoseImporterOptions(BoneFilter filter, TransformComponents transf public BoneFilter BoneFilter { get; set; } = filter; public TransformComponents TransformComponents { get; set; } = transformComponents; public bool ApplyModelTransform { get; set; } = applyModelTransform; + public PoseImportTransformType PositionTransformType { get; set; } = PoseImportTransformType.Difference; + public PoseImportTransformType RotationTransformType { get; set; } = PoseImportTransformType.Difference; + public PoseImportTransformType ScaleTransformType { get; set; } = PoseImportTransformType.Difference; } - diff --git a/Brio/Game/Scene/SceneService.cs b/Brio/Game/Scene/SceneService.cs new file mode 100644 index 00000000..7e5cd62b --- /dev/null +++ b/Brio/Game/Scene/SceneService.cs @@ -0,0 +1,77 @@ +using System; +using Brio.Capabilities.Actor; +using Brio.Capabilities.Posing; +using Brio.Config; +using Brio.Entities; +using Brio.Entities.Actor; +using Brio.Entities.Core; +using Brio.Files; +using Brio.Game.Actor.Appearance; +using Brio.Game.Posing; +using Dalamud.Plugin.Services; + +namespace Brio.Game.Scene; + +internal class SceneService(EntityManager entityManager, PosingService posingService, IFramework framework) +{ + internal static SceneFile BuildSceneFile(EntityManager entityManager) + { + SceneFile sceneFile = new(); + + var entity = entityManager.GetEntity("actorContainer") + ?? throw new NullReferenceException("Error: Entity ActorContainerEntity may not be null!"); + + foreach(var child in entity.Children) + { + if(child is ActorEntity actorEntity) + { + sceneFile.AddActor(actorEntity); + } + } + + return sceneFile; + } + + internal void BuildScene(SceneFile sceneFile) + { + ActorContainerEntity actorContainerEntity = entityManager.GetEntity("actorContainer") + ?? throw new NullReferenceException("Error: Entity ActorContainerEntity may not be null!"); + + var actorCapability = actorContainerEntity.GetCapability(); + + if(ConfigurationService.Instance.Configuration.SceneDestoryActorsBeforeImport) + { + actorCapability.DestroyAll(); + } + + foreach(ActorFile actorFile in sceneFile.Actors) + { + EntityId actorId = actorCapability.CreateCharacter(false, true, forceSpawnActorWithoutCompanion: true); + + framework.RunOnTick(() => + { + ApplyDataToActor(actorId, actorFile); + }, delayTicks: 4); // Waiting 4 frames to give the Actor time to be attached + + } + } + private void ApplyDataToActor(EntityId actorId, ActorFile actorFile) + { + var attachedActor = entityManager.GetEntity(actorId) ?? throw new NullReferenceException("Error: Failed to import Actor"); + var posingCapability = attachedActor.GetCapability(); + var appearanceCapability = attachedActor.GetCapability(); + + attachedActor.FriendlyName = actorFile.FriendlyName; + + var poseOptions = posingService.DefaultImporterOptions; + poseOptions.ApplyModelTransform = ConfigurationService.Instance.Configuration.Import.ApplyModelTransform; + + poseOptions.PositionTransformType = ConfigurationService.Instance.Configuration.Import.PositionTransformType; + poseOptions.RotationTransformType = ConfigurationService.Instance.Configuration.Import.RotationTransformType; + poseOptions.ScaleTransformType = ConfigurationService.Instance.Configuration.Import.ScaleTransformType; + + posingCapability.ImportPose(actorFile.PoseFile, poseOptions); + + _ = appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Default); + } +} diff --git a/Brio/UI/Controls/Editors/PosingEditorCommon.cs b/Brio/UI/Controls/Editors/PosingEditorCommon.cs index 471a04d2..00ada7b8 100644 --- a/Brio/UI/Controls/Editors/PosingEditorCommon.cs +++ b/Brio/UI/Controls/Editors/PosingEditorCommon.cs @@ -15,8 +15,6 @@ internal static class PosingEditorCommon { public static void DrawSelectionName(PosingCapability posing) { - ImGui.Text(posing.Selected.DisplayName); - ImGui.SetWindowFontScale(0.75f); ImGui.TextDisabled(posing.Selected.Subtitle); ImGui.SetWindowFontScale(1.0f); diff --git a/Brio/UI/Controls/Stateless/FileUIHelpers.cs b/Brio/UI/Controls/Stateless/FileUIHelpers.cs index 50e6fd69..eca84e60 100644 --- a/Brio/UI/Controls/Stateless/FileUIHelpers.cs +++ b/Brio/UI/Controls/Stateless/FileUIHelpers.cs @@ -16,6 +16,9 @@ using System.Collections.Generic; using System.IO; using System.Numerics; +using Brio.Entities; +using Brio.Game.Scene; +using Brio.Resources; namespace Brio.UI.Controls.Stateless; @@ -206,4 +209,50 @@ public static void ShowImportMCDFModal(ActorAppearanceCapability capability) } }, 1, ConfigurationService.Instance.Configuration.LastMCDFPath, true); } + public static void ShowExportSceneModal(EntityManager entityManager) + { + UIManager.Instance.FileDialogManager.SaveFileDialog("Export Scene File###export_scene_window", "Scene File (*.scene){.scene}", "scene", "{.scene}", + (success, path) => + { + if(success) + { + Brio.Log.Info("Exporting scene..."); + if(!path.EndsWith(".scene")) + path += ".scene"; + + var directory = Path.GetDirectoryName(path); + if(directory is not null) + { + ConfigurationService.Instance.Configuration.LastScenePath = directory; + ConfigurationService.Instance.Save(); + } + + SceneFile sceneFile = SceneService.BuildSceneFile(entityManager); + ResourceProvider.Instance.SaveFileDocument(path, sceneFile); + Brio.Log.Info("Finished exporting scene"); + } + }, ConfigurationService.Instance.Configuration.LastScenePath, true); + } + + public static void ShowImportSceneModal(SceneService sceneService) + { + List types = [typeof(SceneFile)]; + TypeFilter filter = new TypeFilter("Scenes", [.. types]); + + LibraryManager.GetWithFilePicker(filter, r => + { + Brio.Log.Info("Importing scene..."); + if(r is SceneFile importedFile) + { + sceneService.BuildScene(importedFile); + Brio.Log.Info("Finished imported scene"); + } + else + { + throw new IOException("The file selected is not a valid scene file"); + } + }); + } } + + diff --git a/Brio/UI/Windows/MainWindow.cs b/Brio/UI/Windows/MainWindow.cs index 3e822bfe..b3f2a3ec 100644 --- a/Brio/UI/Windows/MainWindow.cs +++ b/Brio/UI/Windows/MainWindow.cs @@ -9,6 +9,7 @@ using ImGuiNET; using System; using System.Numerics; +using Brio.Game.Scene; namespace Brio.UI.Windows; @@ -19,9 +20,11 @@ internal class MainWindow : Window, IDisposable private readonly LibraryWindow _libraryWindow; private readonly ConfigurationService _configurationService; private readonly InputService _inputService; - private readonly EntityManager _entityManager; private readonly EntityHierarchyView _entitySelector; + private readonly SceneService _sceneService; + + private const int NumberOfButtons = 4; public MainWindow( ConfigurationService configService, @@ -29,7 +32,9 @@ public MainWindow( InfoWindow infoWindow, LibraryWindow libraryWindow, EntityManager entityManager, - InputService input) + InputService input, + SceneService sceneService + ) : base($"{Brio.Name} Scene Manager [{configService.Version}]###brio_main_window", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.AlwaysAutoResize) { Namespace = "brio_main_namespace"; @@ -39,9 +44,9 @@ public MainWindow( _libraryWindow = libraryWindow; _infoWindow = infoWindow; _inputService = input; - _entityManager = entityManager; _entitySelector = new(_entityManager); + _sceneService = sceneService; SizeConstraints = new WindowSizeConstraints { @@ -88,13 +93,25 @@ private void OnPromptWindowToggle() private void DrawHeaderButtons() { float buttonWidths = 25; - float finalWidth = ImBrio.GetRemainingWidth() - ((buttonWidths * 2) + (ImGui.GetStyle().ItemSpacing.X * 2) + ImGui.GetStyle().WindowBorderSize); + float finalWidth = ImBrio.GetRemainingWidth() - ((buttonWidths * NumberOfButtons) + (ImGui.GetStyle().ItemSpacing.X * NumberOfButtons) + ImGui.GetStyle().WindowBorderSize); if(ImBrio.Button("Library", FontAwesomeIcon.Book, new Vector2(finalWidth, 0))) _libraryWindow.Toggle(); if(ImGui.IsItemHovered()) ImGui.SetTooltip("Open the Library"); + + ImGui.SameLine(); + if(ImBrio.FontIconButton("buttonImportScene", FontAwesomeIcon.FileImport, "Import Scene")) + { + FileUIHelpers.ShowImportSceneModal(_sceneService); + } + + ImGui.SameLine(); + if(ImBrio.FontIconButton("buttonExportScene", FontAwesomeIcon.FileExport, "Export Scene")) + { + FileUIHelpers.ShowExportSceneModal(_entityManager); + } ImGui.SameLine(); if(ImBrio.FontIconButton(FontAwesomeIcon.InfoCircle, new(buttonWidths, 0))) diff --git a/Brio/UI/Windows/SettingsWindow.cs b/Brio/UI/Windows/SettingsWindow.cs index ad39a7c7..3e85f9bf 100644 --- a/Brio/UI/Windows/SettingsWindow.cs +++ b/Brio/UI/Windows/SettingsWindow.cs @@ -40,7 +40,7 @@ public SettingsWindow( _brioIPCService = brioIPCService; _mareService = mareService; - Size = new Vector2(400, 450); + Size = new Vector2(450, 450); } private bool _isModal = false; @@ -89,6 +89,7 @@ public override void Draw() DrawIPCTab(); DrawPosingTab(); DrawLibraryTab(); + DrawImportTab(); DrawKeysTab(); DrawAdvancedTab(); } @@ -191,6 +192,17 @@ private void DrawDisplaySettings() } } + private void DrawImportTab() + { + using(var tab = ImRaii.TabItem("Import")) + { + if(tab.Success) + { + DrawImportScene(); + } + } + } + private void DrawIPCTab() { using(var tab = ImRaii.TabItem("IPC")) @@ -260,6 +272,84 @@ private void DrawThirdPartyIPC() } } + private void DrawImportScene() + { + + if(ImGui.CollapsingHeader("Scene", ImGuiTreeNodeFlags.DefaultOpen)) + { + bool destroyActorsBeforeImport = _configurationService.Configuration.SceneDestoryActorsBeforeImport; + if(ImGui.Checkbox("Destroy Actors before Scene import", ref destroyActorsBeforeImport)) + { + _configurationService.Configuration.SceneDestoryActorsBeforeImport = destroyActorsBeforeImport; + _configurationService.ApplyChange(); + } + } + + if(ImGui.CollapsingHeader("Pose", ImGuiTreeNodeFlags.DefaultOpen)) + { + + bool applyModelTransform = _configurationService.Configuration.Import.ApplyModelTransform; + if(ImGui.Checkbox("Apply Model Transform", ref applyModelTransform)) + { + _configurationService.Configuration.Import.ApplyModelTransform = applyModelTransform; + _configurationService.ApplyChange(); + } + + var positionTransformType = _configurationService.Configuration.Import.PositionTransformType; + ImGui.SetNextItemWidth(200); + using(var combo = ImRaii.Combo("Position", positionTransformType.ToString())) + { + if(combo.Success) + { + foreach(var poseImportTransformType in Enum.GetValues()) + { + if(ImGui.Selectable($"{poseImportTransformType}", poseImportTransformType == positionTransformType)) + { + _configurationService.Configuration.Import.PositionTransformType = poseImportTransformType; + _configurationService.ApplyChange(); + } + } + } + } + + + var rotationTransformType = _configurationService.Configuration.Import.RotationTransformType; + ImGui.SetNextItemWidth(200); + using(var combo = ImRaii.Combo("Rotation", rotationTransformType.ToString())) + { + if(combo.Success) + { + foreach(var poseImportTransformType in Enum.GetValues()) + { + if(ImGui.Selectable($"{poseImportTransformType}", poseImportTransformType == rotationTransformType)) + { + _configurationService.Configuration.Import.RotationTransformType = poseImportTransformType; + _configurationService.ApplyChange(); + } + } + } + } + + var scaleTransformType = _configurationService.Configuration.Import.ScaleTransformType; + ImGui.SetNextItemWidth(200); + using(var combo = ImRaii.Combo("Scale", scaleTransformType.ToString())) + { + if(combo.Success) + { + foreach(var poseImportTransformType in Enum.GetValues()) + { + if(ImGui.Selectable($"{poseImportTransformType}", poseImportTransformType == scaleTransformType)) + { + _configurationService.Configuration.Import.ScaleTransformType = poseImportTransformType; + _configurationService.ApplyChange(); + } + } + } + } + + } + } + private void DrawBrioIPC() { diff --git a/Brio/UI/Windows/Specialized/PosingOverlayToolbarWindow.cs b/Brio/UI/Windows/Specialized/PosingOverlayToolbarWindow.cs index 37b0f87e..2d9bcfb4 100644 --- a/Brio/UI/Windows/Specialized/PosingOverlayToolbarWindow.cs +++ b/Brio/UI/Windows/Specialized/PosingOverlayToolbarWindow.cs @@ -320,7 +320,7 @@ private void DrawButtons(PosingCapability posing) using(ImRaii.PushFont(UiBuilder.IconFont)) { if(ImGui.Button($"{FontAwesomeIcon.FileExport.ToIconString()}###export_pose", new Vector2(buttonSize))) - FileUIHelpers.ShowExportPoseModal(posing); ; + FileUIHelpers.ShowExportPoseModal(posing); } if(ImGui.IsItemHovered()) ImGui.SetTooltip("Export Pose"); From 8e9e4e388d406e4873543aa6ce645f08c5a20072 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Wed, 1 Jan 2025 11:52:44 -0600 Subject: [PATCH 02/13] remove empty folder --- Brio/Brio.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Brio/Brio.csproj b/Brio/Brio.csproj index 004f09c7..e74b8293 100644 --- a/Brio/Brio.csproj +++ b/Brio/Brio.csproj @@ -76,8 +76,4 @@ - - - - From 46c33c9e99a8816447d5424ef7dc45b41fa42725 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Wed, 1 Jan 2025 15:14:28 -0600 Subject: [PATCH 03/13] work on scene import/exporter Reworked how the scene is loaded to be more consistent Fixed the Pose importer so it can once again import poses when there are null values on some of the bones Fixed saving the actors name to only save it the name was changed Changed names to some methods and variables Changed file extension to `.brioscn` Restored the bone selection name test in the posing editor --- .../Actor/ActorContainerCapability.cs | 4 +- .../Posing/ModelPosingCapability.cs | 59 +++++++-------- Brio/Capabilities/Posing/PosingCapability.cs | 13 ++-- Brio/Config/Configuration.cs | 2 +- Brio/Config/ImportConfiguration.cs | 17 ----- Brio/Config/SceneImportConfiguration.cs | 17 +++++ Brio/Entities/Actor/ActorEntity.cs | 8 +-- Brio/Files/ActorFile.cs | 4 +- Brio/Files/SceneFile.cs | 2 +- Brio/Game/Posing/PoseImporter.cs | 3 - Brio/Game/Posing/PosingService.cs | 7 +- Brio/Game/Scene/SceneService.cs | 71 ++++++++++--------- .../UI/Controls/Editors/PosingEditorCommon.cs | 2 + Brio/UI/Controls/Stateless/FileUIHelpers.cs | 17 ++--- Brio/UI/Windows/SettingsWindow.cs | 19 +++-- 15 files changed, 132 insertions(+), 113 deletions(-) delete mode 100644 Brio/Config/ImportConfiguration.cs create mode 100644 Brio/Config/SceneImportConfiguration.cs diff --git a/Brio/Capabilities/Actor/ActorContainerCapability.cs b/Brio/Capabilities/Actor/ActorContainerCapability.cs index bca57705..de133405 100644 --- a/Brio/Capabilities/Actor/ActorContainerCapability.cs +++ b/Brio/Capabilities/Actor/ActorContainerCapability.cs @@ -34,7 +34,7 @@ public void SelectActorInHierarchy(ActorEntity entity) _entityManager.SetSelectedEntity(entity); } - public EntityId CreateCharacter(bool enableAttachments, bool targetNewInHierarchy, bool forceSpawnActorWithoutCompanion = false) + public (EntityId, ICharacter) CreateCharacter(bool enableAttachments, bool targetNewInHierarchy, bool forceSpawnActorWithoutCompanion = false) { SpawnFlags flags = SpawnFlags.Default; if(enableAttachments) @@ -47,7 +47,7 @@ public EntityId CreateCharacter(bool enableAttachments, bool targetNewInHierarch { _entityManager.SetSelectedEntity(characterId); } - return characterId; + return (characterId, chara); } throw new Exception("Failed to create character"); diff --git a/Brio/Capabilities/Posing/ModelPosingCapability.cs b/Brio/Capabilities/Posing/ModelPosingCapability.cs index 3fc2ba5d..1cacaa87 100644 --- a/Brio/Capabilities/Posing/ModelPosingCapability.cs +++ b/Brio/Capabilities/Posing/ModelPosingCapability.cs @@ -1,10 +1,10 @@ -using System.Numerics; -using Brio.Capabilities.Actor; +using Brio.Capabilities.Actor; using Brio.Config; using Brio.Core; using Brio.Entities.Actor; using Brio.Files; using Brio.Game.Posing; +using System.Numerics; namespace Brio.Capabilities.Posing; @@ -41,7 +41,7 @@ public unsafe Transform OriginalTransform } public Transform? OverrideTransform => _transformOverride; - + private Transform? _transformOverride = null; private Transform? _originalTransform = null; @@ -67,36 +67,39 @@ public override void Dispose() ResetTransform(); } - public void ImportModelPose(PoseFile poseFile, PoseImporterOptions options) + public void ImportModelPose(PoseFile poseFile, PoseImporterOptions options, bool isLoadingAsScene) { - if(!options.ApplyModelTransform) - { - return; - } - - var temp = new Transform + if(options.ApplyModelTransform) { - Position = options.PositionTransformType switch - { - PoseImportTransformType.Difference => Transform.Position + poseFile.ModelDifference.Position, - PoseImportTransformType.Absolute => poseFile.ModelAbsoluteValues.Position, - _ => Transform.Position - }, - Rotation = options.RotationTransformType switch + if(isLoadingAsScene) { - PoseImportTransformType.Difference => Quaternion.Normalize(Transform.Rotation * poseFile.ModelDifference.Rotation), - PoseImportTransformType.Absolute => poseFile.ModelAbsoluteValues.Rotation, - _ => Transform.Rotation - }, - Scale = options.ScaleTransformType switch + Transform = new Transform + { + Position = ConfigurationService.Instance.Configuration.Import.PositionTransformType switch + { + ScenePoseTransformType.Difference => Transform.Position + poseFile.ModelDifference.Position, + ScenePoseTransformType.Absolute => poseFile.ModelAbsoluteValues.Position, + _ => Transform.Position + }, + Rotation = ConfigurationService.Instance.Configuration.Import.RotationTransformType switch + { + ScenePoseTransformType.Difference => Quaternion.Normalize(Transform.Rotation * poseFile.ModelDifference.Rotation), + ScenePoseTransformType.Absolute => poseFile.ModelAbsoluteValues.Rotation, + _ => Transform.Rotation + }, + Scale = ConfigurationService.Instance.Configuration.Import.ScaleTransformType switch + { + ScenePoseTransformType.Difference => Transform.Scale + poseFile.ModelDifference.Scale, + ScenePoseTransformType.Absolute => poseFile.ModelAbsoluteValues.Scale, + _ => Transform.Scale + } + }; + } + else { - PoseImportTransformType.Difference => Transform.Scale + poseFile.ModelDifference.Scale, - PoseImportTransformType.Absolute => poseFile.ModelAbsoluteValues.Scale, - _ => Transform.Scale + Transform += poseFile.ModelDifference; } - }; - - Transform = temp; + } } public void ExportModelPose(PoseFile poseFile) diff --git a/Brio/Capabilities/Posing/PosingCapability.cs b/Brio/Capabilities/Posing/PosingCapability.cs index 7a64d094..992bf5d3 100644 --- a/Brio/Capabilities/Posing/PosingCapability.cs +++ b/Brio/Capabilities/Posing/PosingCapability.cs @@ -124,14 +124,14 @@ public void ImportPose(string path, PoseImporterOptions? options = null) } } - public void ImportPose(OneOf rawPoseFile, PoseImporterOptions? options = null, bool asExpression = false) + public void ImportPose(OneOf rawPoseFile, PoseImporterOptions? options = null, bool asExpression = false, bool asScene = false) { - ImportPose(rawPoseFile, options, reset: false, reconcile: false, asExpression: asExpression); + ImportPose(rawPoseFile, options, reset: false, reconcile: false, asExpression: asExpression, asScene: asScene); } PoseFile? tempPose; private void ImportPose(OneOf rawPoseFile, PoseImporterOptions? options = null, bool generateSnapshot = true, bool reset = true, bool reconcile = true, - bool asExpression = false, bool expressionPhase2 = false) + bool asExpression = false, bool expressionPhase2 = false, bool asScene = false) { var poseFile = rawPoseFile.Match( poseFile => poseFile, @@ -153,6 +153,11 @@ private void ImportPose(OneOf rawPoseFile, PoseImporte options = _posingService.ExpressionOptions; tempPose = GeneratePoseFile(); } + else if (asScene) + { + options = _posingService.SceneImporterOptions; + options.ApplyModelTransform = ConfigurationService.Instance.Configuration.Import.ApplyModelTransform; + } else { options ??= _posingService.DefaultImporterOptions; @@ -164,7 +169,7 @@ private void ImportPose(OneOf rawPoseFile, PoseImporte SkeletonPosing.ImportSkeletonPose(poseFile, options, expressionPhase2); if(asExpression == false) - ModelPosing.ImportModelPose(poseFile, options); + ModelPosing.ImportModelPose(poseFile, options, asScene); if(generateSnapshot) _framework.RunOnTick(() => Snapshot(reset, reconcile, asExpression: asExpression), delayTicks: 4); diff --git a/Brio/Config/Configuration.cs b/Brio/Config/Configuration.cs index a322cd47..cc4b8125 100644 --- a/Brio/Config/Configuration.cs +++ b/Brio/Config/Configuration.cs @@ -27,7 +27,7 @@ internal class Configuration : IPluginConfiguration // Environment public EnvironmentConfiguration Environment { get; set; } = new EnvironmentConfiguration(); - public ImportConfiguration Import { get; set; } = new ImportConfiguration(); + public SceneImportConfiguration Import { get; set; } = new SceneImportConfiguration(); // Library public LibraryConfiguration Library { get; set; } = new LibraryConfiguration(); diff --git a/Brio/Config/ImportConfiguration.cs b/Brio/Config/ImportConfiguration.cs deleted file mode 100644 index 16983e83..00000000 --- a/Brio/Config/ImportConfiguration.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Brio.Config; - -internal class ImportConfiguration -{ - public bool ApplyModelTransform { get; set; } = true; - - public PoseImportTransformType PositionTransformType { get; set; } = PoseImportTransformType.Difference; - public PoseImportTransformType RotationTransformType { get; set; } = PoseImportTransformType.Absolute; - public PoseImportTransformType ScaleTransformType { get; set; } = PoseImportTransformType.Absolute; -} - -internal enum PoseImportTransformType -{ - Ignore, - Difference, - Absolute -} diff --git a/Brio/Config/SceneImportConfiguration.cs b/Brio/Config/SceneImportConfiguration.cs new file mode 100644 index 00000000..41007321 --- /dev/null +++ b/Brio/Config/SceneImportConfiguration.cs @@ -0,0 +1,17 @@ +namespace Brio.Config; + +internal class SceneImportConfiguration +{ + public bool ApplyModelTransform { get; set; } = true; + + public ScenePoseTransformType PositionTransformType { get; set; } = ScenePoseTransformType.Difference; + public ScenePoseTransformType RotationTransformType { get; set; } = ScenePoseTransformType.Absolute; + public ScenePoseTransformType ScaleTransformType { get; set; } = ScenePoseTransformType.Absolute; +} + +internal enum ScenePoseTransformType +{ + Ignore, + Difference, + Absolute +} diff --git a/Brio/Entities/Actor/ActorEntity.cs b/Brio/Entities/Actor/ActorEntity.cs index e58368a0..7e25ff4f 100644 --- a/Brio/Entities/Actor/ActorEntity.cs +++ b/Brio/Entities/Actor/ActorEntity.cs @@ -16,21 +16,21 @@ internal class ActorEntity(IGameObject gameObject, IServiceProvider provider) : private readonly ConfigurationService _configService = provider.GetRequiredService(); - string name = ""; + public string RawName = ""; public override string FriendlyName { get { - if(string.IsNullOrEmpty(name)) + if(string.IsNullOrEmpty(RawName)) { return _configService.Configuration.Interface.CensorActorNames ? GameObject.GetCensoredName() : GameObject.GetFriendlyName(); } - return GameObject.GetAsCustomName(name); + return GameObject.GetAsCustomName(RawName); } set { - name = value; + RawName = value; } } public override FontAwesomeIcon Icon => GameObject.GetFriendlyIcon(); diff --git a/Brio/Files/ActorFile.cs b/Brio/Files/ActorFile.cs index b07ac5cd..45d16836 100644 --- a/Brio/Files/ActorFile.cs +++ b/Brio/Files/ActorFile.cs @@ -8,7 +8,7 @@ namespace Brio.Files; [Serializable] internal class ActorFile { - public String FriendlyName { get; set; } = ""; + public string FriendlyName { get; set; } = ""; public required AnamnesisCharaFile AnamnesisCharaFile { get; set; } public required PoseFile PoseFile { get; set; } @@ -23,7 +23,7 @@ public static implicit operator ActorFile(ActorEntity actorEntity) var actorFile = new ActorFile { - FriendlyName = actorEntity.FriendlyName, + FriendlyName = actorEntity.RawName, AnamnesisCharaFile = appearanceCapability.CurrentAppearance, PoseFile = posingCapability.GeneratePoseFile() }; diff --git a/Brio/Files/SceneFile.cs b/Brio/Files/SceneFile.cs index 89f5e4af..27351eb2 100644 --- a/Brio/Files/SceneFile.cs +++ b/Brio/Files/SceneFile.cs @@ -11,7 +11,7 @@ internal class SceneFileInfo : JsonDocumentBaseFileInfo public override string Name => "Scene File"; public override IDalamudTextureWrap Icon => ResourceProvider.Instance.GetResourceImage("Images.FileIcon_Unknown.png"); - public override string Extension => ".scene"; + public override string Extension => ".brioscn"; } diff --git a/Brio/Game/Posing/PoseImporter.cs b/Brio/Game/Posing/PoseImporter.cs index 27cf003b..db85388b 100644 --- a/Brio/Game/Posing/PoseImporter.cs +++ b/Brio/Game/Posing/PoseImporter.cs @@ -69,7 +69,4 @@ internal class PoseImporterOptions(BoneFilter filter, TransformComponents transf public BoneFilter BoneFilter { get; set; } = filter; public TransformComponents TransformComponents { get; set; } = transformComponents; public bool ApplyModelTransform { get; set; } = applyModelTransform; - public PoseImportTransformType PositionTransformType { get; set; } = PoseImportTransformType.Difference; - public PoseImportTransformType RotationTransformType { get; set; } = PoseImportTransformType.Difference; - public PoseImportTransformType ScaleTransformType { get; set; } = PoseImportTransformType.Difference; } diff --git a/Brio/Game/Posing/PosingService.cs b/Brio/Game/Posing/PosingService.cs index 741f22c7..ff34f284 100644 --- a/Brio/Game/Posing/PosingService.cs +++ b/Brio/Game/Posing/PosingService.cs @@ -1,5 +1,7 @@ -using Brio.Core; +using Brio.Config; +using Brio.Core; using ImGuizmoNET; +using System.IO.Pipes; namespace Brio.Game.Posing; @@ -16,6 +18,7 @@ internal class PosingService public BoneFilter OverlayFilter { get; } public PoseImporterOptions DefaultImporterOptions { get; } + public PoseImporterOptions SceneImporterOptions { get; } public PoseImporterOptions ExpressionOptions { get; } public PoseImporterOptions ExpressionOptions2 { get; } @@ -26,6 +29,8 @@ public PosingService() DefaultImporterOptions = new PoseImporterOptions(new BoneFilter(this), TransformComponents.Rotation, false); DefaultImporterOptions.BoneFilter.DisableCategory("weapon"); + SceneImporterOptions = new PoseImporterOptions(new BoneFilter(this), TransformComponents.All, false); + ExpressionOptions = new PoseImporterOptions(new BoneFilter(this), TransformComponents.All, false); ExpressionOptions.BoneFilter.DisableAll(); ExpressionOptions.BoneFilter.EnableCategory("head"); diff --git a/Brio/Game/Scene/SceneService.cs b/Brio/Game/Scene/SceneService.cs index 7e5cd62b..7808ba66 100644 --- a/Brio/Game/Scene/SceneService.cs +++ b/Brio/Game/Scene/SceneService.cs @@ -1,4 +1,3 @@ -using System; using Brio.Capabilities.Actor; using Brio.Capabilities.Posing; using Brio.Config; @@ -7,20 +6,22 @@ using Brio.Entities.Core; using Brio.Files; using Brio.Game.Actor.Appearance; +using Brio.Game.Actor.Extensions; +using Brio.Game.Core; using Brio.Game.Posing; using Dalamud.Plugin.Services; +using System.Threading.Tasks; namespace Brio.Game.Scene; -internal class SceneService(EntityManager entityManager, PosingService posingService, IFramework framework) +internal class SceneService(EntityManager _entityManager, PosingService _posingService, IFramework _framework) { - internal static SceneFile BuildSceneFile(EntityManager entityManager) + internal static SceneFile GenerateSceneFile(EntityManager entityManager) { SceneFile sceneFile = new(); - - var entity = entityManager.GetEntity("actorContainer") - ?? throw new NullReferenceException("Error: Entity ActorContainerEntity may not be null!"); - + + var entity = entityManager.GetEntity("actorContainer")!; + foreach(var child in entity.Children) { if(child is ActorEntity actorEntity) @@ -28,50 +29,56 @@ internal static SceneFile BuildSceneFile(EntityManager entityManager) sceneFile.AddActor(actorEntity); } } - + return sceneFile; } - internal void BuildScene(SceneFile sceneFile) + internal unsafe void LoadScene(SceneFile sceneFile) { - ActorContainerEntity actorContainerEntity = entityManager.GetEntity("actorContainer") - ?? throw new NullReferenceException("Error: Entity ActorContainerEntity may not be null!"); - + ActorContainerEntity actorContainerEntity = _entityManager.GetEntity("actorContainer")!; + var actorCapability = actorContainerEntity.GetCapability(); if(ConfigurationService.Instance.Configuration.SceneDestoryActorsBeforeImport) { actorCapability.DestroyAll(); } - + foreach(ActorFile actorFile in sceneFile.Actors) { - EntityId actorId = actorCapability.CreateCharacter(false, true, forceSpawnActorWithoutCompanion: true); - - framework.RunOnTick(() => - { - ApplyDataToActor(actorId, actorFile); - }, delayTicks: 4); // Waiting 4 frames to give the Actor time to be attached - + var (actorId, actor) = actorCapability.CreateCharacter(false, false, forceSpawnActorWithoutCompanion: true); + + _framework.RunUntilSatisfied( + () => actor.Native()->IsReadyToDraw(), + (__) => + { + _ = ApplyDataToActor(actorId, actorFile); + }, + 100, + dontStartFor: 2 + ); } } - private void ApplyDataToActor(EntityId actorId, ActorFile actorFile) + + private async Task ApplyDataToActor(EntityId actorId, ActorFile actorFile) { - var attachedActor = entityManager.GetEntity(actorId) ?? throw new NullReferenceException("Error: Failed to import Actor"); + var attachedActor = _entityManager.GetEntity(actorId)!; var posingCapability = attachedActor.GetCapability(); var appearanceCapability = attachedActor.GetCapability(); + var actionTimeline = attachedActor.GetCapability(); attachedActor.FriendlyName = actorFile.FriendlyName; - - var poseOptions = posingService.DefaultImporterOptions; - poseOptions.ApplyModelTransform = ConfigurationService.Instance.Configuration.Import.ApplyModelTransform; - poseOptions.PositionTransformType = ConfigurationService.Instance.Configuration.Import.PositionTransformType; - poseOptions.RotationTransformType = ConfigurationService.Instance.Configuration.Import.RotationTransformType; - poseOptions.ScaleTransformType = ConfigurationService.Instance.Configuration.Import.ScaleTransformType; - - posingCapability.ImportPose(actorFile.PoseFile, poseOptions); - - _ = appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Default); + actionTimeline.SetOverallSpeedOverride(0); + + await _framework.RunOnTick(async () => + { + await appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Default); + + await _framework.RunOnTick(() => + { + posingCapability.ImportPose(actorFile.PoseFile, null, asScene: true); + }, delayTicks: 10); // I dont like having to-do this but I dont think I have another way without rework + }); } } diff --git a/Brio/UI/Controls/Editors/PosingEditorCommon.cs b/Brio/UI/Controls/Editors/PosingEditorCommon.cs index 00ada7b8..471a04d2 100644 --- a/Brio/UI/Controls/Editors/PosingEditorCommon.cs +++ b/Brio/UI/Controls/Editors/PosingEditorCommon.cs @@ -15,6 +15,8 @@ internal static class PosingEditorCommon { public static void DrawSelectionName(PosingCapability posing) { + ImGui.Text(posing.Selected.DisplayName); + ImGui.SetWindowFontScale(0.75f); ImGui.TextDisabled(posing.Selected.Subtitle); ImGui.SetWindowFontScale(1.0f); diff --git a/Brio/UI/Controls/Stateless/FileUIHelpers.cs b/Brio/UI/Controls/Stateless/FileUIHelpers.cs index eca84e60..3f00c3d9 100644 --- a/Brio/UI/Controls/Stateless/FileUIHelpers.cs +++ b/Brio/UI/Controls/Stateless/FileUIHelpers.cs @@ -209,16 +209,17 @@ public static void ShowImportMCDFModal(ActorAppearanceCapability capability) } }, 1, ConfigurationService.Instance.Configuration.LastMCDFPath, true); } + public static void ShowExportSceneModal(EntityManager entityManager) { - UIManager.Instance.FileDialogManager.SaveFileDialog("Export Scene File###export_scene_window", "Scene File (*.scene){.scene}", "scene", "{.scene}", + UIManager.Instance.FileDialogManager.SaveFileDialog("Export Scene File###export_scene_window", "Brio Scene File (*.brioscn){.brioscn}", "brioscn", "{.brioscn}", (success, path) => { if(success) { Brio.Log.Info("Exporting scene..."); - if(!path.EndsWith(".scene")) - path += ".scene"; + if(!path.EndsWith(".brioscn")) + path += ".brioscn"; var directory = Path.GetDirectoryName(path); if(directory is not null) @@ -227,7 +228,7 @@ public static void ShowExportSceneModal(EntityManager entityManager) ConfigurationService.Instance.Save(); } - SceneFile sceneFile = SceneService.BuildSceneFile(entityManager); + SceneFile sceneFile = SceneService.GenerateSceneFile(entityManager); ResourceProvider.Instance.SaveFileDocument(path, sceneFile); Brio.Log.Info("Finished exporting scene"); } @@ -237,15 +238,15 @@ public static void ShowExportSceneModal(EntityManager entityManager) public static void ShowImportSceneModal(SceneService sceneService) { List types = [typeof(SceneFile)]; - TypeFilter filter = new TypeFilter("Scenes", [.. types]); + TypeFilter filter = new("Scenes", [.. types]); LibraryManager.GetWithFilePicker(filter, r => { - Brio.Log.Info("Importing scene..."); + Brio.Log.Verbose("Importing scene..."); if(r is SceneFile importedFile) { - sceneService.BuildScene(importedFile); - Brio.Log.Info("Finished imported scene"); + sceneService.LoadScene(importedFile); + Brio.Log.Verbose("Finished imported scene!"); } else { diff --git a/Brio/UI/Windows/SettingsWindow.cs b/Brio/UI/Windows/SettingsWindow.cs index 3e85f9bf..1cffd0ef 100644 --- a/Brio/UI/Windows/SettingsWindow.cs +++ b/Brio/UI/Windows/SettingsWindow.cs @@ -89,7 +89,7 @@ public override void Draw() DrawIPCTab(); DrawPosingTab(); DrawLibraryTab(); - DrawImportTab(); + DrawSceneTab(); DrawKeysTab(); DrawAdvancedTab(); } @@ -192,9 +192,9 @@ private void DrawDisplaySettings() } } - private void DrawImportTab() + private void DrawSceneTab() { - using(var tab = ImRaii.TabItem("Import")) + using(var tab = ImRaii.TabItem("Scene")) { if(tab.Success) { @@ -275,7 +275,7 @@ private void DrawThirdPartyIPC() private void DrawImportScene() { - if(ImGui.CollapsingHeader("Scene", ImGuiTreeNodeFlags.DefaultOpen)) + if(ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) { bool destroyActorsBeforeImport = _configurationService.Configuration.SceneDestoryActorsBeforeImport; if(ImGui.Checkbox("Destroy Actors before Scene import", ref destroyActorsBeforeImport)) @@ -285,11 +285,11 @@ private void DrawImportScene() } } - if(ImGui.CollapsingHeader("Pose", ImGuiTreeNodeFlags.DefaultOpen)) + if(ImGui.CollapsingHeader("Import", ImGuiTreeNodeFlags.DefaultOpen)) { bool applyModelTransform = _configurationService.Configuration.Import.ApplyModelTransform; - if(ImGui.Checkbox("Apply Model Transform", ref applyModelTransform)) + if(ImGui.Checkbox("Apply Model Transform on Import", ref applyModelTransform)) { _configurationService.Configuration.Import.ApplyModelTransform = applyModelTransform; _configurationService.ApplyChange(); @@ -301,7 +301,7 @@ private void DrawImportScene() { if(combo.Success) { - foreach(var poseImportTransformType in Enum.GetValues()) + foreach(var poseImportTransformType in Enum.GetValues()) { if(ImGui.Selectable($"{poseImportTransformType}", poseImportTransformType == positionTransformType)) { @@ -312,14 +312,13 @@ private void DrawImportScene() } } - var rotationTransformType = _configurationService.Configuration.Import.RotationTransformType; ImGui.SetNextItemWidth(200); using(var combo = ImRaii.Combo("Rotation", rotationTransformType.ToString())) { if(combo.Success) { - foreach(var poseImportTransformType in Enum.GetValues()) + foreach(var poseImportTransformType in Enum.GetValues()) { if(ImGui.Selectable($"{poseImportTransformType}", poseImportTransformType == rotationTransformType)) { @@ -336,7 +335,7 @@ private void DrawImportScene() { if(combo.Success) { - foreach(var poseImportTransformType in Enum.GetValues()) + foreach(var poseImportTransformType in Enum.GetValues()) { if(ImGui.Selectable($"{poseImportTransformType}", poseImportTransformType == scaleTransformType)) { From 5ce605dbeec07f3867289fc937e43babc7a99555 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 07:10:19 -0600 Subject: [PATCH 04/13] Add Actor visibility button to right click popup --- .../Actor/ActorAppearanceCapability.cs | 14 ++++++++++++++ Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs | 10 +++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Brio/Capabilities/Actor/ActorAppearanceCapability.cs b/Brio/Capabilities/Actor/ActorAppearanceCapability.cs index 658bdc25..6bfd87d7 100644 --- a/Brio/Capabilities/Actor/ActorAppearanceCapability.cs +++ b/Brio/Capabilities/Actor/ActorAppearanceCapability.cs @@ -166,6 +166,20 @@ public Task ToggelHide() return SetAppearance(appearance, AppearanceImportOptions.ExtendedAppearance); } + public Task Hide() + { + var appearance = _actorAppearanceService.GetActorAppearance(Character); + appearance.ExtendedAppearance.Transparency = 1f; + return SetAppearance(appearance, AppearanceImportOptions.ExtendedAppearance); + } + + public Task Show() + { + var appearance = _actorAppearanceService.GetActorAppearance(Character); + appearance.ExtendedAppearance.Transparency = 0f; + return SetAppearance(appearance, AppearanceImportOptions.ExtendedAppearance); + } + public Task ApplyEmperors() { var appearance = _actorAppearanceService.GetActorAppearance(Character); diff --git a/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs b/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs index 39e50afa..ff48f5ef 100644 --- a/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs +++ b/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs @@ -1,5 +1,6 @@ using Brio.Capabilities.Actor; using Brio.Game.Actor.Appearance; +using Brio.Game.Actor.Extensions; using Brio.UI.Controls.Editors; using Brio.UI.Controls.Stateless; using Brio.UI.Widgets.Core; @@ -13,7 +14,7 @@ internal class ActorAppearanceWidget(ActorAppearanceCapability capability) : Wid { public override string HeaderName => "Appearance"; - public override WidgetFlags Flags => WidgetFlags.DefaultOpen | WidgetFlags.DrawBody | WidgetFlags.DrawQuickIcons | WidgetFlags.HasAdvanced | WidgetFlags.CanHide; + public override WidgetFlags Flags => WidgetFlags.DefaultOpen | WidgetFlags.DrawBody | WidgetFlags.DrawQuickIcons | WidgetFlags.DrawPopup | WidgetFlags.HasAdvanced | WidgetFlags.CanHide; public override void DrawBody() { @@ -68,6 +69,13 @@ private void DrawLoadAppearance() } } + public override void DrawPopup() + { + var toggele = Capability.IsHidden ? "Show" : "Hide"; + if(ImGui.MenuItem($"{toggele} {Capability.Actor.FriendlyName}###Appearance_popup_toggle")) + Capability.ToggelHide(); + } + public override void DrawQuickIcons() { if(ImBrio.FontIconButton("redrawwidget_redraw", FontAwesomeIcon.PaintBrush, "Redraw")) From e05ff2a7771cf15132a9b93be7c45eb1b7c26303 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 08:28:42 -0600 Subject: [PATCH 05/13] Add IClientState to `DebugWidget` --- Brio/Capabilities/Debug/DebugCapability.cs | 5 +++-- Brio/UI/Widgets/Debug/DebugWidget.cs | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Brio/Capabilities/Debug/DebugCapability.cs b/Brio/Capabilities/Debug/DebugCapability.cs index 03af20dd..cf342cca 100644 --- a/Brio/Capabilities/Debug/DebugCapability.cs +++ b/Brio/Capabilities/Debug/DebugCapability.cs @@ -2,6 +2,7 @@ using Brio.Entities.Core; using Brio.Game.GPose; using Brio.UI.Widgets.Debug; +using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.Event; @@ -16,10 +17,10 @@ internal unsafe class DebugCapability : Capability { private readonly GPoseService _gPoseService; - public DebugCapability(Entity parent, GPoseService gPoseService) : base(parent) + public DebugCapability(IClientState clientState, Entity parent, GPoseService gPoseService) : base(parent) { _gPoseService = gPoseService; - Widget = new DebugWidget(this); + Widget = new DebugWidget(this, clientState); } public void EnterGPose() diff --git a/Brio/UI/Widgets/Debug/DebugWidget.cs b/Brio/UI/Widgets/Debug/DebugWidget.cs index 3d690883..66df75f5 100644 --- a/Brio/UI/Widgets/Debug/DebugWidget.cs +++ b/Brio/UI/Widgets/Debug/DebugWidget.cs @@ -1,11 +1,12 @@ using Brio.Capabilities.Debug; using Brio.UI.Widgets.Core; using Dalamud.Interface.Utility.Raii; +using Dalamud.Plugin.Services; using ImGuiNET; namespace Brio.UI.Widgets.Debug; -internal class DebugWidget(DebugCapability capability) : Widget(capability) +internal class DebugWidget(DebugCapability capability, IClientState _clientState) : Widget(capability) { public override string HeaderName => "Debug"; @@ -71,6 +72,12 @@ private void DrawAddresses() private void DrawMisc() { var io = ImGui.GetIO(); + + ImGui.Text($"MapId - {_clientState.MapId}"); + ImGui.Text($"TerritoryType - {_clientState.TerritoryType}"); + ImGui.Text($"CurrentWorld - {_clientState.LocalPlayer.CurrentWorld.Value.InternalName}"); + ImGui.Text($"HomeWorld - {_clientState.LocalPlayer.HomeWorld.Value.InternalName}"); + ImGui.Text(io.Framerate.ToString("F2") + " FPS"); } } From 5240768fa093b4bef846d37f47c072f3267f9852 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 08:29:55 -0600 Subject: [PATCH 06/13] Rename and move `XATCameraPathFile` -> `XATCameraFile` --- .../XATCameraPathFile.cs => Files/XATCameraFile.cs} | 12 +++++++++--- Brio/Game/Cutscene/CutsceneManager.cs | 4 ++-- Brio/UI/Controls/Editors/ActionTimelineEditor.cs | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) rename Brio/{Game/Cutscene/Files/XATCameraPathFile.cs => Files/XATCameraFile.cs} (71%) diff --git a/Brio/Game/Cutscene/Files/XATCameraPathFile.cs b/Brio/Files/XATCameraFile.cs similarity index 71% rename from Brio/Game/Cutscene/Files/XATCameraPathFile.cs rename to Brio/Files/XATCameraFile.cs index 7075df53..653270c5 100644 --- a/Brio/Game/Cutscene/Files/XATCameraPathFile.cs +++ b/Brio/Files/XATCameraFile.cs @@ -5,15 +5,21 @@ using System.Numerics; using System.Text; -namespace Brio.Game.Cutscene.Files; +namespace Brio.Files; +[Serializable] public record CameraKeyframe(int Frame, Vector3 Position, Quaternion Rotation, float FoV); -public class XATCameraPathFile +[Serializable] +public class XATCameraFile { public List CameraFrames { get; private set; } - public XATCameraPathFile(BinaryReader reader) +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + public XATCameraFile() { } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + + public XATCameraFile(BinaryReader reader) { // Header string fileMagic = Encoding.ASCII.GetString(reader.ReadBytes(4)); diff --git a/Brio/Game/Cutscene/CutsceneManager.cs b/Brio/Game/Cutscene/CutsceneManager.cs index 04097d6d..36952a0a 100644 --- a/Brio/Game/Cutscene/CutsceneManager.cs +++ b/Brio/Game/Cutscene/CutsceneManager.cs @@ -1,8 +1,8 @@ using Brio.Capabilities.Actor; using Brio.Entities; +using Brio.Files; using Brio.Game.Actor.Extensions; using Brio.Game.Camera; -using Brio.Game.Cutscene.Files; using Brio.Game.GPose; using Brio.Input; using Brio.UI; @@ -49,7 +49,7 @@ internal class CutsceneManager : IDisposable public CutsceneCameraSettings CameraSettings { get; } = new(); public VirtualCamera VirtualCamera { get; } - public XATCameraPathFile? CameraPath { get; set; } + public XATCameraFile? CameraPath { get; set; } public CutsceneManager(GPoseService gPoseService, VirtualCamera virtualCamera, EntityManager entityManager, ITargetManager targetManager, IFramework framework) { diff --git a/Brio/UI/Controls/Editors/ActionTimelineEditor.cs b/Brio/UI/Controls/Editors/ActionTimelineEditor.cs index 97f9a8f0..2d885a6c 100644 --- a/Brio/UI/Controls/Editors/ActionTimelineEditor.cs +++ b/Brio/UI/Controls/Editors/ActionTimelineEditor.cs @@ -2,10 +2,10 @@ using Brio.Config; using Brio.Entities; using Brio.Entities.Core; +using Brio.Files; using Brio.Game.Actor.Extensions; using Brio.Game.Camera; using Brio.Game.Cutscene; -using Brio.Game.Cutscene.Files; using Brio.Game.GPose; using Brio.Game.Posing; using Brio.Resources; @@ -510,7 +510,7 @@ private void DrawCutscene() _configService.Configuration.LastXATPath = folderPath; _configService.Save(); - _cutsceneManager.CameraPath = new XATCameraPathFile(new BinaryReader(File.OpenRead(_cameraPath))); + _cutsceneManager.CameraPath = new XATCameraFile(new BinaryReader(File.OpenRead(_cameraPath))); } } else From d2f8d2d55011d67f2e82afa6ebaef4e74be5bc41 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 08:31:34 -0600 Subject: [PATCH 07/13] SceneService now loads and saves companions --- .../Capabilities/Actor/CompanionCapability.cs | 23 ++++++-- Brio/Files/ActorFile.cs | 55 ++++++++++++++++--- Brio/Files/GameCameraFile.cs | 14 +++++ Brio/Files/SceneFile.cs | 23 ++++++-- Brio/Game/Scene/SceneService.cs | 42 +++++++++++--- 5 files changed, 132 insertions(+), 25 deletions(-) create mode 100644 Brio/Files/GameCameraFile.cs diff --git a/Brio/Capabilities/Actor/CompanionCapability.cs b/Brio/Capabilities/Actor/CompanionCapability.cs index 36458c0b..02342b6b 100644 --- a/Brio/Capabilities/Actor/CompanionCapability.cs +++ b/Brio/Capabilities/Actor/CompanionCapability.cs @@ -1,4 +1,5 @@ -using Brio.Entities.Actor; +using Brio.Entities; +using Brio.Entities.Actor; using Brio.Game.Actor; using Brio.Game.Actor.Extensions; using Brio.Game.Types; @@ -14,13 +15,14 @@ internal unsafe class CompanionCapability : ActorCharacterCapability public ModeType Mode { get; } private readonly ActorSpawnService _actorSpawnService; + private readonly EntityManager _entityManager; - - public CompanionCapability(ActorEntity parent, ModeType mode, ActorSpawnService actorSpawnService) : base(parent) + public CompanionCapability(ActorEntity parent, ModeType mode, ActorSpawnService actorSpawnService, EntityManager entityManager) : base(parent) { - Mode = mode; _actorSpawnService = actorSpawnService; + _entityManager = entityManager; + Mode = mode; Widget = new CompanionWidget(this); } @@ -34,6 +36,19 @@ public void SetCompanion(CompanionContainer container) _actorSpawnService.CreateCompanion(Character, container); } + public unsafe Entities.Core.Entity? GetCompanionAsEntity() + { + if(Character.HasSpawnedCompanion()) + { + if(_entityManager.TryGetEntity(&Character.Native()->CompanionObject->Character.GameObject, out Entities.Core.Entity? actor)) + { + return actor; + } + } + + return null; + } + public static CompanionCapability? CreateIfEligible(IServiceProvider provider, ActorEntity entity) { if(entity.GameObject is ICharacter character && character.HasCompanionSlot()) diff --git a/Brio/Files/ActorFile.cs b/Brio/Files/ActorFile.cs index 45d16836..5327b004 100644 --- a/Brio/Files/ActorFile.cs +++ b/Brio/Files/ActorFile.cs @@ -1,33 +1,72 @@ -using System; using Brio.Capabilities.Actor; using Brio.Capabilities.Posing; using Brio.Entities.Actor; +using Brio.Game.Actor.Extensions; +using Brio.Game.Types; +using System; +using System.Linq; namespace Brio.Files; [Serializable] internal class ActorFile { - public string FriendlyName { get; set; } = ""; + public string Name { get; set; } = ""; public required AnamnesisCharaFile AnamnesisCharaFile { get; set; } public required PoseFile PoseFile { get; set; } + public bool HasChild { get; set; } + public ChildActor? Child { get; set; } + + public bool HeadSlotShown { get; set; } + public bool MainHandSlotShown { get; set; } + public bool OffHandSlotShown { get; set; } + + public bool ActorFrozen { get; set; } + public bool HasBaseAnimation { get; set; } + public int BaseAnimation { get; set; } + public static implicit operator ActorFile(ActorEntity actorEntity) { - var appearanceCapability = actorEntity.GetCapability(); var posingCapability = actorEntity.GetCapability(); - - posingCapability.GeneratePoseFile(); - + var actorFile = new ActorFile { - FriendlyName = actorEntity.RawName, + Name = actorEntity.RawName, AnamnesisCharaFile = appearanceCapability.CurrentAppearance, PoseFile = posingCapability.GeneratePoseFile() }; - return actorFile; + CompanionContainer? companionContainer; + + if(posingCapability.Character.HasSpawnedCompanion()) + { + var companionCapability = actorEntity.GetCapability(); + + companionContainer = companionCapability.Character.GetCompanionInfo(); + + actorFile.HasChild = true; + actorFile.Child = new ChildActor() { Companion = companionContainer.Value }; + + + var companionEntity = companionCapability.GetCompanionAsEntity(); + + if(companionEntity is not null && companionEntity.TryGetCapability(out var companionPosingCapability)) + { + actorFile.Child.PoseFile = companionPosingCapability.GeneratePoseFile(); + } + } + + return actorFile; } } + +[Serializable] +internal class ChildActor +{ + public required CompanionContainer Companion { get; set; } + + public PoseFile? PoseFile { get; set; } +} diff --git a/Brio/Files/GameCameraFile.cs b/Brio/Files/GameCameraFile.cs new file mode 100644 index 00000000..045120c5 --- /dev/null +++ b/Brio/Files/GameCameraFile.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Brio.Files; + +[Serializable] +internal class GameCameraFile +{ + +} + diff --git a/Brio/Files/SceneFile.cs b/Brio/Files/SceneFile.cs index 27351eb2..5f9537bb 100644 --- a/Brio/Files/SceneFile.cs +++ b/Brio/Files/SceneFile.cs @@ -16,12 +16,23 @@ internal class SceneFileInfo : JsonDocumentBaseFileInfo } [Serializable] -internal class SceneFile +internal class SceneFile : JsonDocumentBase { + public string FileType { get; set; } = "Brio Scene"; + public List Actors { get; set; } = []; - - public void AddActor(ActorFile actorFile) - { - Actors.Add(actorFile); - } + + public GameCameraFile? GameCamera { get; set; } + + public XATCameraFile? XATCamera { get; set; } + + public SceneMetaData? MetaData { get; set; } +} + +[Serializable] +internal class SceneMetaData +{ + public uint Map { get; set; } + public ushort Territory { get; set; } + public string World { get; set; } } diff --git a/Brio/Game/Scene/SceneService.cs b/Brio/Game/Scene/SceneService.cs index 7808ba66..dc73acc8 100644 --- a/Brio/Game/Scene/SceneService.cs +++ b/Brio/Game/Scene/SceneService.cs @@ -14,7 +14,7 @@ namespace Brio.Game.Scene; -internal class SceneService(EntityManager _entityManager, PosingService _posingService, IFramework _framework) +internal class SceneService(EntityManager _entityManager, PosingService _posingService, IClientState _clientState, IFramework _framework) { internal static SceneFile GenerateSceneFile(EntityManager entityManager) { @@ -26,7 +26,7 @@ internal static SceneFile GenerateSceneFile(EntityManager entityManager) { if(child is ActorEntity actorEntity) { - sceneFile.AddActor(actorEntity); + sceneFile.Actors.Add(actorEntity); } } @@ -46,7 +46,7 @@ internal unsafe void LoadScene(SceneFile sceneFile) foreach(ActorFile actorFile in sceneFile.Actors) { - var (actorId, actor) = actorCapability.CreateCharacter(false, false, forceSpawnActorWithoutCompanion: true); + var (actorId, actor) = actorCapability.CreateCharacter(actorFile.HasChild, false, forceSpawnActorWithoutCompanion: !actorFile.HasChild); _framework.RunUntilSatisfied( () => actor.Native()->IsReadyToDraw(), @@ -67,7 +67,7 @@ private async Task ApplyDataToActor(EntityId actorId, ActorFile actorFile) var appearanceCapability = attachedActor.GetCapability(); var actionTimeline = attachedActor.GetCapability(); - attachedActor.FriendlyName = actorFile.FriendlyName; + attachedActor.FriendlyName = actorFile.Name; actionTimeline.SetOverallSpeedOverride(0); @@ -75,10 +75,38 @@ await _framework.RunOnTick(async () => { await appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Default); - await _framework.RunOnTick(() => + await _framework.RunOnTick(async () => { - posingCapability.ImportPose(actorFile.PoseFile, null, asScene: true); - }, delayTicks: 10); // I dont like having to-do this but I dont think I have another way without rework + bool mountPose = false; + if(actorFile.Child is not null && actorFile.Child.Companion.Kind == Types.CompanionKind.Mount) + mountPose = true; + + if(mountPose == false) + posingCapability.ImportPose(actorFile.PoseFile, null, asScene: true); + + if(attachedActor.HasCapability() == true && actorFile.HasChild && actorFile.Child is not null) + { + var companionCapability = attachedActor.GetCapability(); + + companionCapability.SetCompanion(actorFile.Child.Companion); + + await _framework.RunOnTick(() => + { + if(actorFile.Child.PoseFile is not null) + { + var companionEntity = companionCapability.GetCompanionAsEntity(); + + if(companionEntity is not null && companionEntity.TryGetCapability(out var posingCapability)) + { + posingCapability.ImportPose(actorFile.Child.PoseFile, null, asScene: true, freezeOnLoad: true); + } + } + + if(mountPose == true) + posingCapability.ImportPose(actorFile.PoseFile, null, asScene: true); + }); + } + }, delayTicks: 10); // I dont like having to-do set delayTicks to this but I dont think I have another way without rework }); } } From a5601865dd91e6fc0d076c6eba0ccfc1f3d1db9f Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 08:33:10 -0600 Subject: [PATCH 08/13] Change `PoseFile` to better support for other tools --- Brio/Capabilities/Posing/ModelPosingCapability.cs | 5 +++++ Brio/Files/PoseFile.cs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Brio/Capabilities/Posing/ModelPosingCapability.cs b/Brio/Capabilities/Posing/ModelPosingCapability.cs index 1cacaa87..633415ac 100644 --- a/Brio/Capabilities/Posing/ModelPosingCapability.cs +++ b/Brio/Capabilities/Posing/ModelPosingCapability.cs @@ -110,5 +110,10 @@ public void ExportModelPose(PoseFile poseFile) } poseFile.ModelAbsoluteValues = Transform; + + // For better support for other pose tools + poseFile.Position = Transform.Position; + poseFile.Rotation = Transform.Rotation; + poseFile.Scale = Transform.Scale; } } diff --git a/Brio/Files/PoseFile.cs b/Brio/Files/PoseFile.cs index 7a95cc03..ad526738 100644 --- a/Brio/Files/PoseFile.cs +++ b/Brio/Files/PoseFile.cs @@ -65,6 +65,8 @@ protected override void Apply(PoseFile file, ActorEntity actor, bool asExpressio [Serializable] internal class PoseFile : JsonDocumentBase { + public string TypeName { get; set; } = "Brio Pose"; + public Bone ModelDifference { get; set; } = Transform.Identity; public Bone ModelAbsoluteValues { get; set; } = Transform.Identity; @@ -72,6 +74,10 @@ internal class PoseFile : JsonDocumentBase public Dictionary MainHand { get; set; } = []; public Dictionary OffHand { get; set; } = []; + public Vector3 Position { get; set; } // legacy & for better support for other pose tools + public Quaternion Rotation { get; set; } // legacy & for better support for other pose tools + public Vector3 Scale { get; set; } // legacy & for better support for other pose tools + public class Bone { public Vector3 Position { get; set; } From 6a488ad227a196520df19cf96f16474fd5a501bd Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 10:09:21 -0600 Subject: [PATCH 09/13] Add `Open Penumbra` button to AppearanceWidget --- Brio/UI/Controls/Editors/AppearanceEditorCommon.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Brio/UI/Controls/Editors/AppearanceEditorCommon.cs b/Brio/UI/Controls/Editors/AppearanceEditorCommon.cs index 90e242ae..21956903 100644 --- a/Brio/UI/Controls/Editors/AppearanceEditorCommon.cs +++ b/Brio/UI/Controls/Editors/AppearanceEditorCommon.cs @@ -1,4 +1,5 @@ using Brio.Capabilities.Actor; +using Brio.Entities; using Brio.Game.Actor.Appearance; using Brio.UI.Controls.Selectors; using Brio.UI.Controls.Stateless; @@ -13,7 +14,7 @@ namespace Brio.UI.Controls.Editors; internal static class AppearanceEditorCommon { private const string _collectionLabel = "Collection"; - private static float _lableWidth { get; } = ImGui.CalcTextSize($"{_collectionLabel} XXXXXXXXX").X; + private static float _lableWidth { get; } = ImGui.CalcTextSize($"{_collectionLabel} XXXXXXXXXX").X; private static readonly NpcSelector _globalNpcSelector = new("global_npc_selector"); @@ -22,6 +23,15 @@ public static void DrawPenumbraCollectionSwitcher(ActorAppearanceCapability capa if(!capability.HasPenumbraIntegration) return; + if(ImBrio.FontIconButton(FontAwesomeIcon.EarthOceania)) + { + capability.PenumbraService.OpenPenumbra(); + } + + if(ImGui.IsItemHovered()) + ImGui.SetTooltip("Open Penumbra"); + ImGui.SameLine(); + var currentCollection = capability.CurrentCollection; ImGui.SetNextItemWidth(_lableWidth); From a7ee3976a2f2bd6a37f6961cc2ae922969e96025 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 10:09:37 -0600 Subject: [PATCH 10/13] Add `ToggelFontIconButton` --- Brio/UI/Controls/Stateless/ImBrio.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Brio/UI/Controls/Stateless/ImBrio.cs b/Brio/UI/Controls/Stateless/ImBrio.cs index 539150ee..fa73e7dd 100644 --- a/Brio/UI/Controls/Stateless/ImBrio.cs +++ b/Brio/UI/Controls/Stateless/ImBrio.cs @@ -6,6 +6,7 @@ using Dalamud.Interface.Utility.Raii; using ImGuiNET; using System.Numerics; +using static Dalamud.Interface.Utility.Raii.ImRaii; namespace Brio.UI.Controls.Stateless; internal static partial class ImBrio @@ -184,6 +185,31 @@ public static bool ToggelButton(string lable, Vector2 size, bool isToggled, uint return clicked; } + public static bool ToggelFontIconButton(string id, FontAwesomeIcon icon, Vector2 size, bool isToggled, uint toggledColor = UIConstants.GizmoRed, string hoverText = "") + { + var clicked = false; + + if(isToggled) + ImGui.PushStyleColor(ImGuiCol.Button, toggledColor); + + using(ImRaii.PushFont(UiBuilder.IconFont)) + { + if(ImGui.Button($"{icon.ToIconString()}###{id}")) + clicked = true; + } + + if(isToggled) + ImGui.PopStyleColor(); + + if(string.IsNullOrEmpty(hoverText) == false) + { + if(ImGui.IsItemHovered()) + ImGui.SetTooltip(hoverText); + } + + return clicked; + } + public static bool IsItemConfirmed() => ImGui.IsItemDeactivated() && (ImGui.IsKeyPressed(ImGuiKey.Enter) || ImGui.IsKeyPressed(ImGuiKey.KeypadEnter)); public static bool BorderedGameIcon(string id, uint iconId, string fallback, string? description = null, ImGuiButtonFlags flags = ImGuiButtonFlags.MouseButtonLeft, Vector2? size = null) From d0ea32000593d12f98795069edf2a2c43a749310 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 10:10:13 -0600 Subject: [PATCH 11/13] Add `freezeActor` button to `PosingWidget` --- Brio/UI/Widgets/Posing/PosingWidget.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Brio/UI/Widgets/Posing/PosingWidget.cs b/Brio/UI/Widgets/Posing/PosingWidget.cs index 857a8afa..499d36e5 100644 --- a/Brio/UI/Widgets/Posing/PosingWidget.cs +++ b/Brio/UI/Widgets/Posing/PosingWidget.cs @@ -1,4 +1,5 @@ -using Brio.Capabilities.Posing; +using Brio.Capabilities.Actor; +using Brio.Capabilities.Posing; using Brio.Game.Posing; using Brio.UI.Controls.Editors; using Brio.UI.Controls.Stateless; @@ -38,10 +39,23 @@ private void DrawButtons() { Capability.OverlayOpen = !overlayOpen; } + + ImGui.SameLine(); + + if(Capability.Actor.TryGetCapability(out var capability)) + { + if(ImBrio.ToggelFontIconButton("freezeActor", FontAwesomeIcon.Snowflake, new Vector2(110, 0), capability.SpeedMultiplier == 0, hoverText: capability.SpeedMultiplierOverride == 0 ? "Un-Freeze Character" : "Freeze Character")) + { + if(capability.SpeedMultiplierOverride == 0) + capability.ResetOverallSpeedOverride(); + else + capability.SetOverallSpeedOverride(0f); + } + } ImGui.SameLine(); - if(ImBrio.FontIconButton("import", FontAwesomeIcon.FileImport, "Import Pose")) + if(ImBrio.FontIconButton("import", FontAwesomeIcon.Download, "Import Pose")) { ImGui.OpenPopup("DrawImportPoseMenuPopup"); } From e1f16dafb963dd161800545d01f06991df26992f Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Fri, 3 Jan 2025 10:10:30 -0600 Subject: [PATCH 12/13] Change icon for importing --- Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs b/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs index ff48f5ef..3e6f9f48 100644 --- a/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs +++ b/Brio/UI/Widgets/Actor/ActorAppearanceWidget.cs @@ -32,7 +32,7 @@ private void DrawLoadAppearance() ImGui.SameLine(); - if(ImBrio.FontIconButton("import_charafile", FontAwesomeIcon.FileImport, "Import Character")) + if(ImBrio.FontIconButton("import_charafile", FontAwesomeIcon.Download, "Import Character")) FileUIHelpers.ShowImportCharacterModal(Capability, AppearanceImportOptions.Default); ImGui.SameLine(); From 23739307073c3ab5834599401bea2086944fbdd3 Mon Sep 17 00:00:00 2001 From: Kenneth M Date: Sat, 4 Jan 2025 14:04:33 -0600 Subject: [PATCH 13/13] Something something something. not done yet --- Brio/Files/EnvironmentFile.cs | 13 +++++++ Brio/Files/SceneFile.cs | 14 ++++++-- Brio/UI/Controls/Stateless/FileUIHelpers.cs | 37 ++++++++++++++++++++ Brio/UI/Windows/MainWindow.cs | 38 +++++++++++---------- 4 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 Brio/Files/EnvironmentFile.cs diff --git a/Brio/Files/EnvironmentFile.cs b/Brio/Files/EnvironmentFile.cs new file mode 100644 index 00000000..ad0637e8 --- /dev/null +++ b/Brio/Files/EnvironmentFile.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Brio.Files; + +[Serializable] +internal class EnvironmentFile +{ + +} diff --git a/Brio/Files/SceneFile.cs b/Brio/Files/SceneFile.cs index 5f9537bb..ac785fed 100644 --- a/Brio/Files/SceneFile.cs +++ b/Brio/Files/SceneFile.cs @@ -9,10 +9,18 @@ namespace Brio.Files; internal class SceneFileInfo : JsonDocumentBaseFileInfo { public override string Name => "Scene File"; - + public override IDalamudTextureWrap Icon => ResourceProvider.Instance.GetResourceImage("Images.FileIcon_Unknown.png"); public override string Extension => ".brioscn"; - + +} +internal class ProjectFileInfo : JsonDocumentBaseFileInfo +{ + public override string Name => "Brio Project"; + + public override IDalamudTextureWrap Icon => ResourceProvider.Instance.GetResourceImage("Images.FileIcon_Unknown.png"); + public override string Extension => ".briosln"; + } [Serializable] @@ -34,5 +42,5 @@ internal class SceneMetaData { public uint Map { get; set; } public ushort Territory { get; set; } - public string World { get; set; } + public string? World { get; set; } } diff --git a/Brio/UI/Controls/Stateless/FileUIHelpers.cs b/Brio/UI/Controls/Stateless/FileUIHelpers.cs index 1252b363..a79dcbc9 100644 --- a/Brio/UI/Controls/Stateless/FileUIHelpers.cs +++ b/Brio/UI/Controls/Stateless/FileUIHelpers.cs @@ -20,11 +20,48 @@ using Brio.Entities; using Brio.Game.Scene; using Brio.Resources; +using Brio.Capabilities.Core; +using System.Drawing; namespace Brio.UI.Controls.Stateless; internal class FileUIHelpers { + public static void DrawProjectPopup(SceneService sceneService, EntityManager entityManager) + { + using var popup = ImRaii.Popup("DrawProjectPopup"); + if(popup.Success) + { + using(ImRaii.PushColor(ImGuiCol.Button, UIConstants.Transparent)) + { + if(ImGui.Button("Save Project")) + { + + } + if(ImGui.Button("Load Project")) + { + + } + + if(ImGui.Button("View Auto-Saves")) + { + + } + + ImGui.Separator(); + + if(ImGui.Button("Export Scene")) + { + ShowExportSceneModal(entityManager); + } + if(ImGui.Button("Import Scene")) + { + ShowImportSceneModal(sceneService); + } + } + } + } + static bool freezeOnLoad = false; public static void DrawImportPoseMenuPopup(PosingCapability capability, bool showImportOptions = true) { diff --git a/Brio/UI/Windows/MainWindow.cs b/Brio/UI/Windows/MainWindow.cs index b3f2a3ec..4fe32991 100644 --- a/Brio/UI/Windows/MainWindow.cs +++ b/Brio/UI/Windows/MainWindow.cs @@ -1,5 +1,6 @@ using Brio.Config; using Brio.Entities; +using Brio.Game.Scene; using Brio.Input; using Brio.UI.Controls.Stateless; using Brio.UI.Entitites; @@ -9,7 +10,6 @@ using ImGuiNET; using System; using System.Numerics; -using Brio.Game.Scene; namespace Brio.UI.Windows; @@ -23,8 +23,6 @@ internal class MainWindow : Window, IDisposable private readonly EntityManager _entityManager; private readonly EntityHierarchyView _entitySelector; private readonly SceneService _sceneService; - - private const int NumberOfButtons = 4; public MainWindow( ConfigurationService configService, @@ -90,35 +88,36 @@ private void OnPromptWindowToggle() _configurationService.ApplyChange(); } + private const int Line1NumberOfButtons = 2; + private const int Line2NumberOfButtons = 0; private void DrawHeaderButtons() { float buttonWidths = 25; - float finalWidth = ImBrio.GetRemainingWidth() - ((buttonWidths * NumberOfButtons) + (ImGui.GetStyle().ItemSpacing.X * NumberOfButtons) + ImGui.GetStyle().WindowBorderSize); + float line1FinalWidth = ImBrio.GetRemainingWidth() - ((buttonWidths * Line1NumberOfButtons) + (ImGui.GetStyle().ItemSpacing.X * Line1NumberOfButtons) + ImGui.GetStyle().WindowBorderSize); + float line2FinalWidth = ImBrio.GetRemainingWidth() - ((buttonWidths * Line2NumberOfButtons) + (ImGui.GetStyle().ItemSpacing.X * Line2NumberOfButtons) + ImGui.GetStyle().WindowBorderSize); + + float line1Width = (line1FinalWidth / 2) - 3; + + if(ImBrio.Button(" Project", FontAwesomeIcon.FolderOpen, new Vector2(line1Width, 0))) + { + ImGui.OpenPopup("DrawProjectPopup"); + } + + FileUIHelpers.DrawProjectPopup(_sceneService, _entityManager); - if(ImBrio.Button("Library", FontAwesomeIcon.Book, new Vector2(finalWidth, 0))) + ImGui.SameLine(); + if(ImBrio.Button("Library", FontAwesomeIcon.Book, new Vector2(line1Width, 0))) _libraryWindow.Toggle(); if(ImGui.IsItemHovered()) ImGui.SetTooltip("Open the Library"); - - ImGui.SameLine(); - if(ImBrio.FontIconButton("buttonImportScene", FontAwesomeIcon.FileImport, "Import Scene")) - { - FileUIHelpers.ShowImportSceneModal(_sceneService); - } - - ImGui.SameLine(); - if(ImBrio.FontIconButton("buttonExportScene", FontAwesomeIcon.FileExport, "Export Scene")) - { - FileUIHelpers.ShowExportSceneModal(_entityManager); - } ImGui.SameLine(); if(ImBrio.FontIconButton(FontAwesomeIcon.InfoCircle, new(buttonWidths, 0))) _infoWindow.Toggle(); if(ImGui.IsItemHovered()) - ImGui.SetTooltip("Information"); + ImGui.SetTooltip("Information & Changelog"); ImGui.SameLine(); if(ImBrio.FontIconButton(FontAwesomeIcon.Cog, new(buttonWidths, 0))) @@ -126,6 +125,9 @@ private void DrawHeaderButtons() if(ImGui.IsItemHovered()) ImGui.SetTooltip("Settings"); + + // + // Line 2 } public void Dispose()