diff --git a/Brio/Capabilities/Actor/ActorAppearanceCapability.cs b/Brio/Capabilities/Actor/ActorAppearanceCapability.cs index cb3d8cde..a55dd109 100644 --- a/Brio/Capabilities/Actor/ActorAppearanceCapability.cs +++ b/Brio/Capabilities/Actor/ActorAppearanceCapability.cs @@ -4,6 +4,7 @@ using Brio.Game.Actor; using Brio.Game.Actor.Appearance; using Brio.Game.Actor.Extensions; +using Brio.Game.Actor.Interop; using Brio.Game.GPose; using Brio.Game.Types; using Brio.IPC; @@ -206,9 +207,18 @@ public void ImportAppearance(string file, AppearanceImportOptions options) _ = SetAppearance(doc, options); } - public void ExportAppearance(string file) + public unsafe void ExportAppearance(string file) { - AnamnesisCharaFile appearance = CurrentAppearance; + var currentAppearance = CurrentAppearance; + BrioHuman.ShaderParams* shaders = Character.GetShaderParams(); + + ActorAppearanceExtended actor = new() + { + Appearance = currentAppearance, + ShaderParams = *shaders + }; + + AnamnesisCharaFile appearance = actor; ResourceProvider.Instance.SaveFileDocument(file, appearance); } diff --git a/Brio/Files/ActorFile.cs b/Brio/Files/ActorFile.cs index 33626f99..b403f189 100644 --- a/Brio/Files/ActorFile.cs +++ b/Brio/Files/ActorFile.cs @@ -2,6 +2,7 @@ using Brio.Capabilities.Posing; using Brio.Core; using Brio.Entities.Actor; +using Brio.Game.Actor.Appearance; using Brio.Game.Actor.Extensions; using Brio.Game.Types; using MessagePack; @@ -32,7 +33,7 @@ public class ActorFile public bool IsProp { get; set; } - public static implicit operator ActorFile(ActorEntity actorEntity) + public static unsafe implicit operator ActorFile(ActorEntity actorEntity) { var appearanceCapability = actorEntity.GetCapability(); var posingCapability = actorEntity.GetCapability(); @@ -41,7 +42,7 @@ public static implicit operator ActorFile(ActorEntity actorEntity) var actorFile = new ActorFile { Name = actorEntity.RawName, - AnamnesisCharaFile = appearanceCapability.CurrentAppearance, + AnamnesisCharaFile = new ActorAppearanceExtended { Appearance = appearanceCapability.CurrentAppearance, ShaderParams = *appearanceCapability.Character.GetShaderParams() }, PoseFile = posingCapability.GeneratePoseFile(), IsProp = actorEntity.IsProp, PropData = new PropData diff --git a/Brio/Files/AnamnesisCharaFile.cs b/Brio/Files/AnamnesisCharaFile.cs index 650f3f1d..6e1fd98f 100644 --- a/Brio/Files/AnamnesisCharaFile.cs +++ b/Brio/Files/AnamnesisCharaFile.cs @@ -2,6 +2,7 @@ using Brio.Entities; using Brio.Entities.Actor; using Brio.Game.Actor.Appearance; +using Brio.Game.Actor.Interop; using Brio.Library.Tags; using Brio.Resources; using Dalamud.Interface.Textures.TextureWraps; @@ -164,8 +165,31 @@ public static implicit operator ActorAppearance(AnamnesisCharaFile chara) return appearance; } - public static implicit operator AnamnesisCharaFile(ActorAppearance appearance) + public static implicit operator BrioHuman.ShaderParams(AnamnesisCharaFile chara) { + // More Extended Appearance (Shaders) + var shaders = new BrioHuman.ShaderParams + { + SkinColor = chara.SkinColor ?? Vector3.One, + SkinGloss = chara.SkinGloss ?? Vector3.One, + LeftEyeColor = chara.LeftEyeColor ?? Vector3.One, + RightEyeColor = chara.RightEyeColor ?? Vector3.One, + HairColor = chara.HairColor ?? Vector3.One, + HairGloss = chara.HairGloss ?? Vector3.One, + HairHighlight = chara.HairHighlight ?? Vector3.One, + MouthColor = chara.MouthColor ?? Vector4.One, + MuscleTone = chara.MuscleTone, + FeatureColor = chara.LimbalRingColor ?? Vector3.One, + }; + + return shaders; + } + + public static implicit operator AnamnesisCharaFile(ActorAppearanceExtended appearanceExt) + { + var appearance = appearanceExt.Appearance; + var shaders = appearanceExt.ShaderParams; + var charaFile = new AnamnesisCharaFile { // Model @@ -220,7 +244,18 @@ public static implicit operator AnamnesisCharaFile(ActorAppearance appearance) // Extended Appearance Transparency = appearance.ExtendedAppearance.Transparency, - HeightMultiplier = appearance.ExtendedAppearance.HeightMultiplier + HeightMultiplier = appearance.ExtendedAppearance.HeightMultiplier, + + SkinColor = shaders.SkinColor, + SkinGloss = shaders.SkinGloss, + LeftEyeColor = shaders.LeftEyeColor, + RightEyeColor = shaders.RightEyeColor, + HairColor = shaders.HairColor, + HairGloss = shaders.HairGloss, + HairHighlight = shaders.HairHighlight, + MouthColor = shaders.MouthColor, + MuscleTone = shaders.MuscleTone, + LimbalRingColor = shaders.FeatureColor, }; return charaFile; diff --git a/Brio/Game/Actor/Appearance/ActorAppearance.cs b/Brio/Game/Actor/Appearance/ActorAppearance.cs index 6c5eaa16..ec94c084 100644 --- a/Brio/Game/Actor/Appearance/ActorAppearance.cs +++ b/Brio/Game/Actor/Appearance/ActorAppearance.cs @@ -1,4 +1,5 @@ using Brio.Game.Actor.Extensions; +using Brio.Game.Actor.Interop; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Lumina.Excel.Sheets; using DalamudCharacter = Dalamud.Game.ClientState.Objects.Types.ICharacter; @@ -332,3 +333,9 @@ private static (WeaponModelId, WeaponModelId, ActorEquipment) FromNpcEquip(NpcEq return (mainHand, offHand, equipment); } } + +public struct ActorAppearanceExtended +{ + public ActorAppearance Appearance; + public BrioHuman.ShaderParams ShaderParams; +} diff --git a/Brio/Game/Scene/SceneService.cs b/Brio/Game/Scene/SceneService.cs index 3240a441..e69c9eef 100644 --- a/Brio/Game/Scene/SceneService.cs +++ b/Brio/Game/Scene/SceneService.cs @@ -145,12 +145,14 @@ await _framework.RunOnTick(async () => if(actorFile.PropData is not null) modelCapability.Transform += actorFile.PropData.PropTransformDifference; - await appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Weapon); - - await _framework.RunOnTick(() => + await _framework.RunOnTick(async () => { - appearanceCapability.AttachWeapon(); - }, delayTicks: 10); + await appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Weapon); + await _framework.RunOnTick(() => + { + appearanceCapability.AttachWeapon(); + }, delayTicks: 10); + }, delayTicks: 10); }, delayTicks: 2); } @@ -167,7 +169,7 @@ private async Task ApplyDataToActor(EntityId actorId, ActorFile actorFile) await _framework.RunOnTick(async () => { - await appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.Default); + await appearanceCapability.SetAppearance(actorFile.AnamnesisCharaFile, AppearanceImportOptions.All); await _framework.RunOnTick(async () => { diff --git a/Brio/UI/Controls/Stateless/FileUIHelpers.cs b/Brio/UI/Controls/Stateless/FileUIHelpers.cs index 9239fc7c..6676b3e6 100644 --- a/Brio/UI/Controls/Stateless/FileUIHelpers.cs +++ b/Brio/UI/Controls/Stateless/FileUIHelpers.cs @@ -5,6 +5,7 @@ using Brio.Entities; using Brio.Files; using Brio.Game.Actor.Appearance; +using Brio.Game.Actor.Interop; using Brio.Game.Core; using Brio.Game.Posing; using Brio.Game.Scene; @@ -273,6 +274,20 @@ public static void ShowExportPoseModal(PosingCapability capability) }, ConfigurationService.Instance.Configuration.LastExportPath, true); } + public static void ImportShadersFromFile(ref ModelShaderOverride modelShaderOverride, BrioHuman.ShaderParams shaderParams) + { + modelShaderOverride.SkinColor = shaderParams.SkinColor; + modelShaderOverride.SkinGloss = shaderParams.SkinGloss; + modelShaderOverride.MuscleTone = shaderParams.MuscleTone; + modelShaderOverride.MouthColor = shaderParams.MouthColor; + modelShaderOverride.HairColor = shaderParams.HairColor; + modelShaderOverride.HairGloss = shaderParams.HairGloss; + modelShaderOverride.HairHighlight = shaderParams.HairHighlight; + modelShaderOverride.LeftEyeColor = shaderParams.LeftEyeColor; + modelShaderOverride.RightEyeColor = shaderParams.RightEyeColor; + modelShaderOverride.FeatureColor = shaderParams.FeatureColor; + } + public static void ShowImportCharacterModal(ActorAppearanceCapability capability, AppearanceImportOptions options) { List types = [typeof(ActorAppearanceUnion), typeof(AnamnesisCharaFile)]; @@ -292,6 +307,11 @@ public static void ShowImportCharacterModal(ActorAppearanceCapability capability } else if(r is AnamnesisCharaFile appearanceFile) { + if (options.HasFlag(AppearanceImportOptions.Shaders)) + { + BrioHuman.ShaderParams shaderParams = appearanceFile; + ImportShadersFromFile(ref capability._modelShaderOverride, shaderParams); + } _ = capability.SetAppearance(appearanceFile, options); } else if(r is MareCharacterDataFile mareFile) @@ -311,6 +331,11 @@ public static void ShowImportCharacterModal(ActorAppearanceCapability capability } else if(r is AnamnesisCharaFile appearanceFile) { + if(options.HasFlag(AppearanceImportOptions.Shaders)) + { + BrioHuman.ShaderParams shaderParams = appearanceFile; + ImportShadersFromFile(ref capability._modelShaderOverride, shaderParams); + } _ = capability.SetAppearance(appearanceFile, options); } else if(r is MareCharacterDataFile mareFile) diff --git a/Brio/UI/Windows/Specialized/ActorAppearanceWindow.cs b/Brio/UI/Windows/Specialized/ActorAppearanceWindow.cs index 531987e1..967c0141 100644 --- a/Brio/UI/Windows/Specialized/ActorAppearanceWindow.cs +++ b/Brio/UI/Windows/Specialized/ActorAppearanceWindow.cs @@ -240,6 +240,15 @@ private void DrawImportOptions() else _importOptions &= ~AppearanceImportOptions.ExtendedAppearance; } + + bool shaders = _importOptions.HasFlag(AppearanceImportOptions.Shaders); + if(ImGui.Checkbox("Shaders", ref shaders)) + { + if(shaders) + _importOptions |= AppearanceImportOptions.Shaders; + else + _importOptions &= ~AppearanceImportOptions.Shaders; + } } } }