From 9f9c27abda00e34d637badbb7ab6d900f643e044 Mon Sep 17 00:00:00 2001 From: tom20051012 Date: Fri, 11 Sep 2026 14:47:50 +0800 Subject: [PATCH 1/2] [ME] Share import transactions and decompose persistence flows --- .../MaterialEditor.Base.projitems | 6 +- .../MaterialCubemapImportTransaction.cs | 105 +++ .../Operations/MaterialTextureImportCommit.cs | 43 ++ .../MaterialTextureImportTransaction.cs | 44 ++ .../Operations/MaterialTextureSnapshot.cs | 34 + ...ialEditor.SceneController.Edits.Cubemap.cs | 224 +----- ...tor.SceneController.Edits.TextureShader.cs | 72 +- ...Editor.SceneController.Persistence.Copy.cs | 192 +++++ ....SceneController.Persistence.Properties.cs | 151 ++++ ...or.SceneController.Persistence.Textures.cs | 74 ++ .../Core.MaterialEditor.SceneController.cs | 441 ++--------- .../Core.MaterialEditor.Studio.projitems | 3 + ...ialEditor.CharaController.Edits.Cubemap.cs | 212 +----- ...tor.CharaController.Edits.TextureShader.cs | 72 +- ....CharaController.Persistence.Properties.cs | 171 +++++ ...r.CharaController.Persistence.Selection.cs | 126 ++++ ...erialEditor.CharaController.Persistence.cs | 709 ++---------------- ...itor.CharaController.Restore.Properties.cs | 185 +++++ ...Editor.CharaController.Restore.Textures.cs | 61 ++ .../Core.MaterialEditor.CharaController.cs | 55 +- .../Core.MaterialEditor.projitems | 7 + .../Persistence/MaterialEditLoadContext.cs | 59 ++ .../MaterialEditRecordSerialization.cs | 13 + .../Persistence/MaterialTextureDataStore.cs | 22 + 24 files changed, 1513 insertions(+), 1568 deletions(-) create mode 100644 src/MaterialEditor.Base/Operations/MaterialCubemapImportTransaction.cs create mode 100644 src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs create mode 100644 src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs create mode 100644 src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs create mode 100644 src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Copy.cs create mode 100644 src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Properties.cs create mode 100644 src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Textures.cs create mode 100644 src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Properties.cs create mode 100644 src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs create mode 100644 src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs create mode 100644 src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs create mode 100644 src/MaterialEditor.Core/Persistence/MaterialEditLoadContext.cs create mode 100644 src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs create mode 100644 src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs diff --git a/src/MaterialEditor.Base/MaterialEditor.Base.projitems b/src/MaterialEditor.Base/MaterialEditor.Base.projitems index 4f70f97ef..28c153d16 100644 --- a/src/MaterialEditor.Base/MaterialEditor.Base.projitems +++ b/src/MaterialEditor.Base/MaterialEditor.Base.projitems @@ -9,6 +9,11 @@ MaterialEditor.API + + + + + @@ -81,7 +86,6 @@ - diff --git a/src/MaterialEditor.Base/Operations/MaterialCubemapImportTransaction.cs b/src/MaterialEditor.Base/Operations/MaterialCubemapImportTransaction.cs new file mode 100644 index 000000000..3a3627ae8 --- /dev/null +++ b/src/MaterialEditor.Base/Operations/MaterialCubemapImportTransaction.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace MaterialEditorAPI +{ + /// Controller-owned byte storage and leases; no serialization types cross this boundary. + internal sealed class MaterialCubemapImportStorage + { + internal Func Count; + internal Func StoreData; + internal Action StoreLease; + internal Action RemoveCreated; + internal Action PurgeLeases; + } + + internal sealed class MaterialCubemapRecordAccess + { + internal Func GetId; + internal Action SetId; + internal Func Original; + } + + /// One transaction for Chara and Studio; candidates stay detached until application succeeds. + internal static class MaterialCubemapImportTransaction + { + private static void Cleanup(Action action) + { + try { action(); } + catch (Exception ex) { MaterialEditorPluginBase.Logger?.LogWarning("Cubemap cleanup: " + ex.Message); } + } + + internal static MaterialEditResult Execute(byte[] data, MaterialEditorCubemapContentKey key, + GameObject root, string materialName, string property, bool logNormalization, + MaterialCubemapImportStorage storage, IList records, T existing, + Func create, MaterialCubemapRecordAccess access, Func apply) where T : class + { + if (data == null || root == null) + return new MaterialEditResult(MaterialEditStatus.Failed, "Validate"); + + MaterialEditorCubemapLease lease = null; + MaterialTextureSnapshot runtime = null; + T candidate = null; + var created = false; + var committed = false; + var texId = 0; + var stage = "Acquire"; + var previousId = existing == null ? null : access.GetId(existing); + var previousState = existing == null ? null : access.Original(existing).CaptureCheckpoint(); + try + { + string warning; + string error; + var acquired = key == null + ? MaterialEditorCubemapCache.TryAcquire(data, out lease, out warning, out error) + : MaterialEditorCubemapCache.TryAcquire(data, key, out lease, out warning, out error); + if (!acquired) return new MaterialEditResult(MaterialEditStatus.Failed, stage, error); + if (logNormalization && !string.IsNullOrEmpty(warning)) + MaterialEditorPluginBase.Logger?.LogWarning(warning); + stage = "Store"; + var count = storage.Count(); + texId = storage.StoreData(data); + created = storage.Count() > count; + storage.StoreLease(texId, lease); + lease = null; + + stage = "Snapshot"; + runtime = new MaterialTextureSnapshot(root, materialName, property); + candidate = create(texId); + // Checkpoints are copy-on-replace state; Synchronize builds new collections. + if (existing != null) access.Original(candidate).RestoreCheckpoint(previousState, false); + stage = "Apply"; + if (!apply(candidate)) + return new MaterialEditResult(MaterialEditStatus.Failed, stage, "Previous override preserved."); + stage = "Commit"; + if (existing == null) records.Add(candidate); + else + { + access.SetId(existing, texId); + access.Original(existing).RestoreCheckpoint(access.Original(candidate).CaptureCheckpoint(), false); + } + committed = true; + return new MaterialEditResult(MaterialEditStatus.Succeeded, stage); + } + catch (Exception ex) { return new MaterialEditResult(MaterialEditStatus.Failed, stage, ex.Message); } + finally + { + if (!committed) + { + runtime?.Restore(); + if (existing != null) + { + access.SetId(existing, previousId); + access.Original(existing).RestoreCheckpoint(previousState, true); + } + else if (candidate != null) records.Remove(candidate); + if (candidate != null) access.Original(candidate).Clear(); + if (created) Cleanup(() => storage.RemoveCreated(texId)); + } + if (lease != null) Cleanup(lease.Dispose); + Cleanup(storage.PurgeLeases); + } + } + } +} diff --git a/src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs b/src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs new file mode 100644 index 000000000..5e7df5f19 --- /dev/null +++ b/src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace MaterialEditorAPI +{ + /// Shared persistence/animation commit. DTO identity is retained for existing animation bindings. + internal static class MaterialTextureImportCommit + { + internal static void Commit(IList records, IDictionary animations, + T existing, T candidate, Func getId, Action setId, + Func getAnimation, Action setAnimation) where T : class + { + if (existing == null) + { + records.Add(candidate); + return; + } + var previousId = getId(existing); + var previousAnimation = getAnimation(existing); + TController previousController; + TController candidateController; + var hadPrevious = animations.TryGetValue(existing, out previousController); + var hasCandidate = animations.TryGetValue(candidate, out candidateController); + try + { + setId(existing, getId(candidate)); + setAnimation(existing, getAnimation(candidate)); + animations.Remove(candidate); + if (hasCandidate) animations[existing] = candidateController; + else animations.Remove(existing); + } + catch + { + setId(existing, previousId); + setAnimation(existing, previousAnimation); + animations.Remove(candidate); + if (hadPrevious) animations[existing] = previousController; + else animations.Remove(existing); + throw; + } + } + } +} diff --git a/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs b/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs new file mode 100644 index 000000000..f40f7b076 --- /dev/null +++ b/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs @@ -0,0 +1,44 @@ +using System; +using UnityEngine; + +namespace MaterialEditorAPI +{ + /// Texture2D prepare/apply/commit boundary; animation-specific rollback stays with its controller. + internal static class MaterialTextureImportTransaction + { + internal static MaterialEditResult Execute(GameObject root, string materialName, string property, + Func prepare, Func apply, Action commit, Action discard) where T : class + { + T candidate = null; + MaterialTextureSnapshot snapshot = null; + var committed = false; + var stage = "Snapshot"; + try + { + snapshot = new MaterialTextureSnapshot(root, materialName, property); + stage = "Prepare texture/animation"; + candidate = prepare(); + stage = "Apply"; + if (!apply(candidate)) + return new MaterialEditResult(MaterialEditStatus.Failed, stage); + stage = "Commit"; + commit(candidate); + committed = true; + return new MaterialEditResult(MaterialEditStatus.Succeeded, stage); + } + catch (Exception ex) + { + return new MaterialEditResult(MaterialEditStatus.Failed, stage, ex.Message); + } + finally + { + if (!committed) + { + snapshot?.Restore(); + try { discard(candidate); } + catch (Exception ex) { MaterialEditorPluginBase.Logger?.LogWarning("Texture cleanup: " + ex.Message); } + } + } + } + } +} diff --git a/src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs b/src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs new file mode 100644 index 000000000..a8843a093 --- /dev/null +++ b/src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace MaterialEditorAPI +{ + /// Rollback values for every runtime material touched by SetTexture, including projectors. + internal sealed class MaterialTextureSnapshot + { + private readonly Dictionary _values = new Dictionary(); + private readonly string _property; + + internal MaterialTextureSnapshot(GameObject root, string name, string property) + { + _property = "_" + property; + foreach (var material in MaterialAPI.GetObjectMaterials(root, name)) + if (material != null && material.HasProperty(_property)) + _values[material] = material.GetTexture(_property); + } + + internal void Restore() + { + foreach (var entry in _values) + { + try + { + if (entry.Key != null && entry.Key.HasProperty(_property)) + entry.Key.SetTexture(_property, entry.Value); + } + catch (Exception ex) { MaterialEditorPluginBase.Logger?.LogWarning("Texture rollback: " + ex.Message); } + } + } + } +} diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.Cubemap.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.Cubemap.cs index 74740c3a4..6ed28e29e 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.Cubemap.cs +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.Cubemap.cs @@ -77,190 +77,41 @@ private bool SetMaterialCubemap( MaterialEditorCubemapContentKey contentKey, bool logNormalizationWarning) { - if (data == null) - return false; - - MaterialEditorCubemapLease lease = null; - var storedLease = false; - var textureEntryCreated = false; - var texID = 0; - MaterialCubemapProperty cubemapProperty = null; - var propertyAdded = false; - int? previousTexID = null; - MaterialCubemapOriginalState.Checkpoint previousOriginalState = null; - Dictionary previousAppliedValues = null; - string materialName = null; - GameObject gameObject = null; - try - { - string warning; - string error; - var acquired = contentKey == null - ? MaterialEditorCubemapCache.TryAcquire( - data, - out lease, - out warning, - out error) - : MaterialEditorCubemapCache.TryAcquire( - data, - contentKey, - out lease, - out warning, - out error); - if (!acquired) - { - MaterialEditorPlugin.Logger.LogMessage(error); - return false; - } - if (logNormalizationWarning && !string.IsNullOrEmpty(warning)) - MaterialEditorPlugin.Logger.LogWarning(warning); - - var textureCountBefore = TextureDictionary.Count; - texID = SetAndGetTextureID(data); - textureEntryCreated = TextureDictionary.Count > textureCountBefore; - CubemapLeases.Store(texID, lease); - lease = null; - storedLease = true; - - materialName = material.NameFormatted(); - gameObject = GetObjectByID(id); - previousAppliedValues = - MaterialCubemapOriginalSnapshot.SynchronizeByMaterialReference( - gameObject, - materialName, - propertyName, - null); - cubemapProperty = MaterialCubemapPropertyList.FirstOrDefault(x => - x.ID == id - && x.Property == propertyName - && x.MaterialName == materialName); - if (cubemapProperty == null) + if (material == null) return false; + var go = GetObjectByID(id); + var existing = MaterialCubemapPropertyList.FirstOrDefault(x => x.ID == id && x.Property == propertyName && x.MaterialName == material.NameFormatted()); + var result = MaterialCubemapImportTransaction.Execute(data, contentKey, + go, material.NameFormatted(), propertyName, logNormalizationWarning, + new MaterialCubemapImportStorage { - cubemapProperty = new MaterialCubemapProperty( - id, - materialName, - propertyName, - texID); - MaterialCubemapPropertyList.Add(cubemapProperty); - propertyAdded = true; - } - else - { - previousTexID = cubemapProperty.TexID; - previousOriginalState = - cubemapProperty.CubemapOriginalState.CaptureCheckpoint(); - cubemapProperty.TexID = texID; - } - - if (SetCubemapWithProperty(gameObject, cubemapProperty)) - return true; - - RollbackMaterialCubemapSet( - gameObject, - materialName, - propertyName, - cubemapProperty, - propertyAdded, - previousTexID, - previousOriginalState, - previousAppliedValues, - texID, - textureEntryCreated); - textureEntryCreated = false; - MaterialEditorPluginBase.Logger.LogWarning( - "Could not apply Cubemap " + materialName + "/" + propertyName - + "; the previous override was preserved."); - return false; - } - catch (Exception exception) - { - RollbackMaterialCubemapSet( - gameObject, - materialName, - propertyName, - cubemapProperty, - propertyAdded, - previousTexID, - previousOriginalState, - previousAppliedValues, - texID, - textureEntryCreated); - textureEntryCreated = false; - MaterialEditorPluginBase.Logger.LogWarning( - "Could not apply Cubemap; the previous override was preserved. " - + exception.Message); - return false; - } - finally - { - if (lease != null) - lease.Dispose(); - if (storedLease) - PurgeUnusedCubemapLeases(); - } + Count = () => TextureDictionary.Count, + StoreData = SetAndGetTextureID, + StoreLease = CubemapLeases.Store, + RemoveCreated = RemoveFailedCubemapData, + PurgeLeases = PurgeUnusedCubemapLeases + }, MaterialCubemapPropertyList, existing, + texId => new MaterialCubemapProperty(id, material.NameFormatted(), propertyName, texId), + CubemapRecordAccess, candidate => SetCubemapWithProperty(go, candidate)); + if (!result.Succeeded) + MaterialEditorPluginBase.Logger?.LogWarning("Cubemap import: " + result.Stage + ": " + result.Diagnostic); + return result.Succeeded; } - private void RollbackMaterialCubemapSet( - GameObject gameObject, - string materialName, - string propertyName, - MaterialCubemapProperty cubemapProperty, - bool propertyAdded, - int? previousTexID, - MaterialCubemapOriginalState.Checkpoint previousOriginalState, - Dictionary previousAppliedValues, - int texID, - bool textureEntryCreated) - { - try - { - MaterialCubemapOriginalSnapshot.RestoreByMaterialReference( - gameObject, - materialName, - propertyName, - previousAppliedValues); - } - catch (Exception exception) + private static readonly MaterialCubemapRecordAccess CubemapRecordAccess = + new MaterialCubemapRecordAccess { - MaterialEditorPluginBase.Logger.LogWarning( - "Could not fully restore the previous Cubemap material value. " - + exception.Message); - } + GetId = x => x.TexID, + SetId = (x, id) => x.TexID = id, + Original = x => x.CubemapOriginalState + }; - if (cubemapProperty != null) - { - if (propertyAdded) - { - MaterialCubemapPropertyList.Remove(cubemapProperty); - cubemapProperty.ClearCubemapOriginalSnapshot(); - } - else - { - cubemapProperty.TexID = previousTexID; - cubemapProperty.CubemapOriginalState.RestoreCheckpoint( - previousOriginalState, - true); - } - } - - if (textureEntryCreated) - { - CubemapLeases.Release(texID); - TextureContainer container; - if (TextureDictionary.TryGetValue(texID, out container)) - { - try - { - if (container != null) - container.Dispose(); - } - finally - { - TextureDictionary.Remove(texID); - } - } - } - PurgeUnusedCubemapLeases(); + private void RemoveFailedCubemapData(int texId) + { + CubemapLeases.Release(texId); + TextureContainer container; + if (!TextureDictionary.TryGetValue(texId, out container)) return; + try { container?.Dispose(); } + finally { TextureDictionary.Remove(texId); } } private bool SetCubemapWithProperty( @@ -294,10 +145,7 @@ public Cubemap GetMaterialCubemap( Material material, string propertyName) { - var cubemapProperty = MaterialCubemapPropertyList.FirstOrDefault(x => - x.ID == id - && x.MaterialName == material.NameFormatted() - && x.Property == propertyName); + var cubemapProperty = MaterialCubemapPropertyList.FirstOrDefault(x => x.ID == id && x.Property == propertyName && x.MaterialName == material.NameFormatted()); if (cubemapProperty == null || !cubemapProperty.TexID.HasValue) return null; @@ -313,10 +161,7 @@ public bool GetMaterialCubemapOriginal( Material material, string propertyName) { - return MaterialCubemapPropertyList.FirstOrDefault(x => - x.ID == id - && x.MaterialName == material.NameFormatted() - && x.Property == propertyName)?.TexID == null; + return MaterialCubemapPropertyList.FirstOrDefault(x => x.ID == id && x.Property == propertyName && x.MaterialName == material.NameFormatted())?.TexID == null; } public void RemoveMaterialCubemap( @@ -325,10 +170,7 @@ public void RemoveMaterialCubemap( string propertyName) { MaterialEditRequestQueue.CancelTarget(GetObjectByID(id), material == null ? null : material.NameFormatted(), propertyName); - var cubemapProperty = MaterialCubemapPropertyList.FirstOrDefault(x => - x.ID == id - && x.MaterialName == material.NameFormatted() - && x.Property == propertyName); + var cubemapProperty = MaterialCubemapPropertyList.FirstOrDefault(x => x.ID == id && x.Property == propertyName && x.MaterialName == material.NameFormatted()); if (cubemapProperty == null) return; diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs index 14895a575..10abda035 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs @@ -133,14 +133,12 @@ private bool TrySetMaterialTexture(int id, Material material, string propertyNam var materialName = material.NameFormatted(); var existingProperty = MaterialTexturePropertyList.FirstOrDefault(x => x.ID == id && x.Property == propertyName && x.MaterialName == materialName); - MaterialTextureProperty candidateProperty = null; - var committed = false; - - try - { + var result = MaterialTextureImportTransaction.Execute( + go, materialName, propertyName, () => + { var texID = SetAndGetTextureID(data); var animationDefinition = MEAnimationUtil.LoadAnimationDefFromBytes(texID, data, SetAndGetTextureID); - candidateProperty = new MaterialTextureProperty( + return new MaterialTextureProperty( id, materialName, propertyName, @@ -150,59 +148,23 @@ private bool TrySetMaterialTexture(int id, Material material, string propertyNam existingProperty == null ? null : existingProperty.Scale, existingProperty == null ? null : existingProperty.ScaleOriginal, animationDefinition); - - if (!SetTextureWithProperty(go, candidateProperty)) - return false; - - CommitTextureImport(existingProperty, candidateProperty); - committed = true; - return true; - } - finally - { - if (!committed) + }, + candidate => SetTextureWithProperty(go, candidate), + candidate => CommitTextureImport(existingProperty, candidate), + candidate => { - if (candidateProperty != null) - AnimationControllerMap.Remove(candidateProperty); + if (candidate != null) AnimationControllerMap.Remove(candidate); PurgeUnusedTextures(); - } - } + }); + if (!result.Succeeded) + MaterialEditorPluginBase.Logger?.LogWarning("Texture import: " + result.Stage + ": " + result.Diagnostic); + return result.Succeeded; } - private void CommitTextureImport(MaterialTextureProperty existingProperty, MaterialTextureProperty candidateProperty) - { - if (existingProperty == null) - { - MaterialTexturePropertyList.Add(candidateProperty); - return; - } - - var previousTexID = existingProperty.TexID; - var previousAnimationDefinition = existingProperty.TexAnimationDef; - var hadPreviousController = AnimationControllerMap.TryGetValue(existingProperty, out var previousController); - var hasCandidateController = AnimationControllerMap.TryGetValue(candidateProperty, out var candidateController); - try - { - existingProperty.TexID = candidateProperty.TexID; - existingProperty.TexAnimationDef = candidateProperty.TexAnimationDef; - AnimationControllerMap.Remove(candidateProperty); - if (hasCandidateController) - AnimationControllerMap[existingProperty] = candidateController; - else - AnimationControllerMap.Remove(existingProperty); - } - catch - { - existingProperty.TexID = previousTexID; - existingProperty.TexAnimationDef = previousAnimationDefinition; - AnimationControllerMap.Remove(candidateProperty); - if (hadPreviousController) - AnimationControllerMap[existingProperty] = previousController; - else - AnimationControllerMap.Remove(existingProperty); - throw; - } - } + private void CommitTextureImport(MaterialTextureProperty existingProperty, MaterialTextureProperty candidateProperty) => + MaterialTextureImportCommit.Commit(MaterialTexturePropertyList, AnimationControllerMap, + existingProperty, candidateProperty, x => x.TexID, (x, id) => x.TexID = id, + x => x.TexAnimationDef, (x, animation) => x.TexAnimationDef = animation); /// /// Get the saved material property value or null if none is saved diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Copy.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Copy.cs new file mode 100644 index 000000000..78e4726f0 --- /dev/null +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Copy.cs @@ -0,0 +1,192 @@ +using System.Collections.Generic; +using MaterialEditorAPI; +using UnityEngine; +using static MaterialEditorAPI.MaterialAPI; + +namespace KK_Plugins.MaterialEditor +{ + public partial class SceneController + { + // Accumulate for the entire copy event so new records cannot become sources + // for a later destination in the same event. + private sealed class SceneCopyBatch + { + internal readonly List rendererPropertyListNew = new List(); + internal readonly List projectorPropertyListNew = new List(); + internal readonly List materialNamePropertyListNew = new List(); + internal readonly List materialFloatPropertyListNew = new List(); + internal readonly List materialKeywordPropertyListNew = new List(); + internal readonly List materialColorPropertyListNew = new List(); + internal readonly List materialVectorPropertyListNew = new List(); + internal readonly List materialTexturePropertyListNew = new List(); + internal readonly List materialCubemapPropertyListNew = new List(); + internal readonly List materialShaderListNew = new List(); + internal readonly List materialCopyListNew = new List(); + } + + private sealed class SceneCopyContext + { + internal int SourceId; + internal int DestinationId; + internal GameObject Root; + internal GameObject SourceRoot; + internal SceneCopyBatch Output; + } + + private void CopyMaterialCopyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialCopyList), MaterialCopyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + { + CopyMaterial(context.Root, loadedProperty.MaterialName, loadedProperty.MaterialCopyName); + context.Output.materialCopyListNew.Add(new MaterialCopy(context.DestinationId, loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); + } + }); + } + + private void CopyMaterialNamePropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialNamePropertyList), MaterialNamePropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + { + MaterialAPI.SetName(context.Root, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value); + context.Output.materialNamePropertyListNew.Add(new MaterialNameProperty(context.DestinationId, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); + } + }); + } + + private void CopyMaterialShaderList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialShaderList), MaterialShaderList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + { + bool setShader = SetShader(context.Root, loadedProperty.MaterialName, loadedProperty.ShaderName); + bool setRenderQueue = SetRenderQueue(context.Root, loadedProperty.MaterialName, loadedProperty.RenderQueue); + if (setShader || setRenderQueue) + context.Output.materialShaderListNew.Add(new MaterialShader(context.DestinationId, loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); + } + }); + } + + private void CopyRendererPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(RendererPropertyList), RendererPropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + if (MaterialAPI.SetRendererProperty(context.Root, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value)) + context.Output.rendererPropertyListNew.Add(new RendererProperty(context.DestinationId, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void CopyProjectorPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(ProjectorPropertyList), ProjectorPropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + if (MaterialAPI.SetProjectorProperty(context.Root, loadedProperty.ProjectorName, loadedProperty.Property, float.Parse(loadedProperty.Value))) + context.Output.projectorPropertyListNew.Add(new ProjectorProperty(context.DestinationId, loadedProperty.ProjectorName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void CopyMaterialFloatPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialFloatPropertyList), MaterialFloatPropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + if (SetFloat(context.Root, loadedProperty.MaterialName, loadedProperty.Property, float.Parse(loadedProperty.Value))) + context.Output.materialFloatPropertyListNew.Add(new MaterialFloatProperty(context.DestinationId, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void CopyMaterialKeywordPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + if (SetKeyword(context.Root, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) + context.Output.materialKeywordPropertyListNew.Add(new MaterialKeywordProperty(context.DestinationId, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void CopyMaterialColorPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialColorPropertyList), MaterialColorPropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + if (SetColor(context.Root, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) + context.Output.materialColorPropertyListNew.Add(new MaterialColorProperty(context.DestinationId, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void CopyMaterialVectorPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialVectorPropertyList), MaterialVectorPropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + if (SetVector(context.Root, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) + context.Output.materialVectorPropertyListNew.Add(new MaterialVectorProperty(context.DestinationId, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void CopyMaterialTexturePropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialTexturePropertyList), MaterialTexturePropertyList, loadedProperty => + { + if (loadedProperty.ID == context.SourceId) + { + MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(context.DestinationId, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.TexID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); + + bool setTex = false; + if (loadedProperty.TexID != null) + setTex = SetTextureWithProperty(context.Root, newTextureProperty); + + bool setOffset = SetTextureOffset(context.Root, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Offset); + bool setScale = SetTextureScale(context.Root, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Scale); + + if (setTex || setOffset || setScale) context.Output.materialTexturePropertyListNew.Add(newTextureProperty); + } + }); + } + + private void CopyMaterialCubemapPropertyList(SceneCopyContext context) + { + MaterialEditLoadContext.ApplyRecords(nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList, loadedProperty => + { + if (loadedProperty.ID != context.SourceId) + return; + + var newCubemapProperty = new MaterialCubemapProperty( + context.DestinationId, + loadedProperty.MaterialName, + loadedProperty.Property, + loadedProperty.TexID); + newCubemapProperty.InheritCubemapOriginalSnapshot( + loadedProperty, + context.SourceRoot); + if (newCubemapProperty.TexID.HasValue + && SetCubemapWithProperty( + context.Root, + newCubemapProperty)) + context.Output.materialCubemapPropertyListNew.Add(newCubemapProperty); + }); + } + + private void CommitCopiedProperties(SceneCopyBatch batch) + { + RendererPropertyList.AddRange(batch.rendererPropertyListNew); + ProjectorPropertyList.AddRange(batch.projectorPropertyListNew); + MaterialNamePropertyList.AddRange(batch.materialNamePropertyListNew); + MaterialFloatPropertyList.AddRange(batch.materialFloatPropertyListNew); + MaterialKeywordPropertyList.AddRange(batch.materialKeywordPropertyListNew); + MaterialColorPropertyList.AddRange(batch.materialColorPropertyListNew); + MaterialVectorPropertyList.AddRange(batch.materialVectorPropertyListNew); + MaterialTexturePropertyList.AddRange(batch.materialTexturePropertyListNew); + MaterialCubemapPropertyList.AddRange(batch.materialCubemapPropertyListNew); + MaterialShaderList.AddRange(batch.materialShaderListNew); + MaterialCopyList.AddRange(batch.materialCopyListNew); + } + } +} diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Properties.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Properties.cs new file mode 100644 index 000000000..a6ff57aff --- /dev/null +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Properties.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using KKAPI.Utilities; +using System.Linq; +using KKAPI.Studio.SaveLoad; +using MaterialEditorAPI; +using Studio; +using UnityEngine; +using static MaterialEditorAPI.MaterialAPI; + +namespace KK_Plugins.MaterialEditor +{ + public partial class SceneController + { + private sealed class SceneLoadContext + { + internal MaterialEditLoadContext Data; + internal SceneOperationKind Operation; + internal ReadOnlyDictionary Items; + } + + private void LoadSceneMaterialCopyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialCopyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + { + CopyMaterial(go, loadedProperty.MaterialName, loadedProperty.MaterialCopyName); + if (MaterialCopyList.Any(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.MaterialCopyName == loadedProperty.MaterialCopyName)) + return; + MaterialCopyList.Add(new MaterialCopy(objID, loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); + } + }); + } + + private void LoadSceneMaterialNamePropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialNamePropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + if (MaterialAPI.SetName(go, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)) + MaterialNamePropertyList.Add(new MaterialNameProperty(objID, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); + else + MaterialEditorPlugin.Logger.LogMessage($"Could not rename material ({loadedProperty.MaterialName}) of renderer ({loadedProperty.Renderer}) to ({loadedProperty.Value}) on load!"); + }); + } + + private void LoadSceneMaterialShaderList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialShaderList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + { + bool setShader = SetShader(go, loadedProperty.MaterialName, loadedProperty.ShaderName); + bool setRenderQueue = SetRenderQueue(go, loadedProperty.MaterialName, loadedProperty.RenderQueue); + if (setShader || setRenderQueue) + MaterialShaderList.Add(new MaterialShader(objID, loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); + } + }); + } + + private void LoadSceneRendererPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(RendererPropertyList), loadedProperty => + { +#if KK + if (loadedProperty.Property == RendererProperties.UpdateWhenOffscreen) return; +#endif + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + if (MaterialAPI.SetRendererProperty(go, loadedProperty.RendererName, loadedProperty.Property, int.Parse(loadedProperty.Value))) + RendererPropertyList.Add(new RendererProperty(objID, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadSceneProjectorPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(ProjectorPropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + if (MaterialAPI.SetProjectorProperty(go, loadedProperty.ProjectorName, loadedProperty.Property, float.Parse(loadedProperty.Value))) + ProjectorPropertyList.Add(new ProjectorProperty(objID, loadedProperty.ProjectorName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadSceneMaterialFloatPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialFloatPropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + if (SetFloat(go, loadedProperty.MaterialName, loadedProperty.Property, float.Parse(loadedProperty.Value))) + MaterialFloatPropertyList.Add(new MaterialFloatProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadSceneMaterialKeywordPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialKeywordPropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + if (SetKeyword(go, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) + MaterialKeywordPropertyList.Add(new MaterialKeywordProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadSceneMaterialColorPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialColorPropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + { + if (IsVectorProperty(go, loadedProperty.MaterialName, loadedProperty.Property)) + { + var value = new Vector4(loadedProperty.Value.r, loadedProperty.Value.g, loadedProperty.Value.b, loadedProperty.Value.a); + var valueOriginal = new Vector4(loadedProperty.ValueOriginal.r, loadedProperty.ValueOriginal.g, loadedProperty.ValueOriginal.b, loadedProperty.ValueOriginal.a); + if (value != valueOriginal + && SetVector(go, loadedProperty.MaterialName, loadedProperty.Property, value) + && !MaterialVectorPropertyList.Any(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.Property == loadedProperty.Property)) + MaterialVectorPropertyList.Add(new MaterialVectorProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, value, valueOriginal)); + } + else if (SetColor(go, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) + MaterialColorPropertyList.Add(new MaterialColorProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + } + }); + } + + private void LoadSceneMaterialVectorPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialVectorPropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + { + // Native Vector data takes precedence when both representations exist. + MaterialColorPropertyList.RemoveAll(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.Property == loadedProperty.Property); + MaterialVectorPropertyList.RemoveAll(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.Property == loadedProperty.Property); + if (SetVector(go, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value) + && loadedProperty.Value != loadedProperty.ValueOriginal) + MaterialVectorPropertyList.Add(new MaterialVectorProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + } + }); + } + + } +} diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Textures.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Textures.cs new file mode 100644 index 000000000..8f586f1f5 --- /dev/null +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Persistence.Textures.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using KKAPI.Utilities; +using System.Linq; +using KKAPI.Studio.SaveLoad; +using MaterialEditorAPI; +using Studio; +using UnityEngine; +using static MaterialEditorAPI.MaterialAPI; + +namespace KK_Plugins.MaterialEditor +{ + public partial class SceneController + { + private void LoadSceneMaterialTexturePropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialTexturePropertyList), loadedProperty => + { + GameObject go = ExtractGameObject(context.Items, loadedProperty.ID, out var objID); + if (go != null) + { + int? texID = null; + if (context.Operation == SceneOperationKind.Import) + { + if (loadedProperty.TexID != null) + texID = context.Data.RemapTexture(loadedProperty.TexID); + MEAnimationUtil.RemapTexID(loadedProperty.TexAnimationDef, context.Data.TextureIds); + } + else + texID = loadedProperty.TexID; + + MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, texID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); + + bool setTex = false; + if (newTextureProperty.TexID != null) + setTex = SetTextureWithProperty(go, newTextureProperty); + + bool setOffset = SetTextureOffset(go, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Offset); + bool setScale = SetTextureScale(go, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Scale); + + if (setTex || setOffset || setScale) + MaterialTexturePropertyList.Add(newTextureProperty); + } + }); + } + + private void LoadSceneMaterialCubemapPropertyList(SceneLoadContext context) + { + context.Data.Read(nameof(MaterialCubemapPropertyList), loadedProperty => + { + GameObject go = ExtractGameObject( + context.Items, + loadedProperty.ID, + out var objID); + if (go == null) + return; + + int? texID = loadedProperty.TexID; + if (context.Operation == SceneOperationKind.Import + && loadedProperty.TexID.HasValue) + texID = context.Data.RemapTexture(loadedProperty.TexID); + + var newCubemapProperty = new MaterialCubemapProperty( + objID, + loadedProperty.MaterialName, + loadedProperty.Property, + texID); + if (newCubemapProperty.TexID.HasValue + && SetCubemapWithProperty(go, newCubemapProperty)) + MaterialCubemapPropertyList.Add(newCubemapProperty); + }); + } + + } +} diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs index ce71331fd..80f1ff7de 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs @@ -53,8 +53,6 @@ public partial class SceneController : SceneCustomFunctionController private readonly MaterialEditRequestQueue _textureImports = new MaterialEditRequestQueue(); - private Dictionary AAAAAA; - private Dictionary BBBBBB; static SceneController() { @@ -78,62 +76,28 @@ protected override void OnSceneSave() else data.data.Add(TexDicSaveKey, null); - if (RendererPropertyList.Count > 0) - data.data.Add(nameof(RendererPropertyList), MessagePackSerializer.Serialize(RendererPropertyList)); - else - data.data.Add(nameof(RendererPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(RendererPropertyList), RendererPropertyList); - if (ProjectorPropertyList.Count > 0) - data.data.Add(nameof(ProjectorPropertyList), MessagePackSerializer.Serialize(ProjectorPropertyList)); - else - data.data.Add(nameof(ProjectorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(ProjectorPropertyList), ProjectorPropertyList); - if (MaterialNamePropertyList.Count > 0) - data.data.Add(nameof(MaterialNamePropertyList), MessagePackSerializer.Serialize(MaterialNamePropertyList)); - else - data.data.Add(nameof(MaterialNamePropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialNamePropertyList), MaterialNamePropertyList); - if (MaterialFloatPropertyList.Count > 0) - data.data.Add(nameof(MaterialFloatPropertyList), MessagePackSerializer.Serialize(MaterialFloatPropertyList)); - else - data.data.Add(nameof(MaterialFloatPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialFloatPropertyList), MaterialFloatPropertyList); - if (MaterialKeywordPropertyList.Count > 0) - data.data.Add(nameof(MaterialKeywordPropertyList), MessagePackSerializer.Serialize(MaterialKeywordPropertyList)); - else - data.data.Add(nameof(MaterialKeywordPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList); - if (MaterialColorPropertyList.Count > 0) - data.data.Add(nameof(MaterialColorPropertyList), MessagePackSerializer.Serialize(MaterialColorPropertyList)); - else - data.data.Add(nameof(MaterialColorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialColorPropertyList), MaterialColorPropertyList); - if (MaterialVectorPropertyList.Count > 0) - data.data.Add(nameof(MaterialVectorPropertyList), MessagePackSerializer.Serialize(MaterialVectorPropertyList)); - else - data.data.Add(nameof(MaterialVectorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialVectorPropertyList), MaterialVectorPropertyList); - if (MaterialTexturePropertyList.Count > 0) - data.data.Add(nameof(MaterialTexturePropertyList), MessagePackSerializer.Serialize(MaterialTexturePropertyList)); - else - data.data.Add(nameof(MaterialTexturePropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialTexturePropertyList), MaterialTexturePropertyList); - if (MaterialCubemapPropertyList.Count > 0) - data.data.Add(nameof(MaterialCubemapPropertyList), MessagePackSerializer.Serialize(MaterialCubemapPropertyList)); - else - data.data.Add(nameof(MaterialCubemapPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList); - if (MaterialShaderList.Count > 0) - data.data.Add(nameof(MaterialShaderList), MessagePackSerializer.Serialize(MaterialShaderList)); - else - data.data.Add(nameof(MaterialShaderList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialShaderList), MaterialShaderList); - if (MaterialCopyList.Count > 0) - data.data.Add(nameof(MaterialCopyList), MessagePackSerializer.Serialize(MaterialCopyList)); - else - data.data.Add(nameof(MaterialCopyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialCopyList), MaterialCopyList); - AAAAAA = data.data; SetExtendedData(data); } @@ -273,8 +237,7 @@ protected override void OnSceneLoad(SceneOperationKind operation, ReadOnlyDictio var importDictionaryTemp = TextureSaveHandler.Instance.Load>(data, TexDicSaveKey, false); try { - foreach (var kvp in importDictionaryTemp) - importDictionary[kvp.Key] = SetAndGetTextureID(kvp.Value.Data); + importDictionary = MaterialEditLoadContext.ImportTextures(importDictionaryTemp, x => x.Data, SetAndGetTextureID); } finally { @@ -282,219 +245,34 @@ protected override void OnSceneLoad(SceneOperationKind operation, ReadOnlyDictio } } - if (data.data.TryGetValue(nameof(MaterialCopyList), out var materialCopyData) && materialCopyData != null) + var context = new SceneLoadContext { - var properties = MessagePackSerializer.Deserialize>((byte[])materialCopyData); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - { - CopyMaterial(go, loadedProperty.MaterialName, loadedProperty.MaterialCopyName); - if (MaterialCopyList.Any(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.MaterialCopyName == loadedProperty.MaterialCopyName)) - continue; - MaterialCopyList.Add(new MaterialCopy(objID, loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); - } - } - } + Data = new MaterialEditLoadContext(data, importDictionary), + Operation = operation, + Items = loadedItems + }; + LoadSceneMaterialCopyList(context); - BBBBBB = data.data; - if (data.data.TryGetValue(nameof(MaterialNamePropertyList), out var materialNameProperties) && materialNameProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialNameProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - if (MaterialAPI.SetName(go, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)) - MaterialNamePropertyList.Add(new MaterialNameProperty(objID, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); - else - MaterialEditorPlugin.Logger.LogMessage($"Could not rename material ({loadedProperty.MaterialName}) of renderer ({loadedProperty.Renderer}) to ({loadedProperty.Value}) on load!"); - } - } + LoadSceneMaterialNamePropertyList(context); - if (data.data.TryGetValue(nameof(MaterialShaderList), out var shaderProperties) && shaderProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])shaderProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - { - bool setShader = SetShader(go, loadedProperty.MaterialName, loadedProperty.ShaderName); - bool setRenderQueue = SetRenderQueue(go, loadedProperty.MaterialName, loadedProperty.RenderQueue); - if (setShader || setRenderQueue) - MaterialShaderList.Add(new MaterialShader(objID, loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); - } - } - } + LoadSceneMaterialShaderList(context); - if (data.data.TryGetValue(nameof(RendererPropertyList), out var rendererProperties) && rendererProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])rendererProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; -#if KK - if (loadedProperty.Property == RendererProperties.UpdateWhenOffscreen) continue; -#endif - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - if (MaterialAPI.SetRendererProperty(go, loadedProperty.RendererName, loadedProperty.Property, int.Parse(loadedProperty.Value))) - RendererPropertyList.Add(new RendererProperty(objID, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadSceneRendererPropertyList(context); - if (data.data.TryGetValue(nameof(ProjectorPropertyList), out var projectorProperties) && projectorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])projectorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - if (MaterialAPI.SetProjectorProperty(go, loadedProperty.ProjectorName, loadedProperty.Property, float.Parse(loadedProperty.Value))) - ProjectorPropertyList.Add(new ProjectorProperty(objID, loadedProperty.ProjectorName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadSceneProjectorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialFloatPropertyList), out var materialFloatProperties) && materialFloatProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialFloatProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - if (SetFloat(go, loadedProperty.MaterialName, loadedProperty.Property, float.Parse(loadedProperty.Value))) - MaterialFloatPropertyList.Add(new MaterialFloatProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadSceneMaterialFloatPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialKeywordPropertyList), out var materialKeywordProperties) && materialKeywordProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialKeywordProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - if (SetKeyword(go, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) - MaterialKeywordPropertyList.Add(new MaterialKeywordProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadSceneMaterialKeywordPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialColorPropertyList), out var materialColorProperties) && materialColorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialColorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - { - if (IsVectorProperty(go, loadedProperty.MaterialName, loadedProperty.Property)) - { - var value = new Vector4(loadedProperty.Value.r, loadedProperty.Value.g, loadedProperty.Value.b, loadedProperty.Value.a); - var valueOriginal = new Vector4(loadedProperty.ValueOriginal.r, loadedProperty.ValueOriginal.g, loadedProperty.ValueOriginal.b, loadedProperty.ValueOriginal.a); - if (value != valueOriginal - && SetVector(go, loadedProperty.MaterialName, loadedProperty.Property, value) - && !MaterialVectorPropertyList.Any(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.Property == loadedProperty.Property)) - MaterialVectorPropertyList.Add(new MaterialVectorProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, value, valueOriginal)); - } - else if (SetColor(go, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) - MaterialColorPropertyList.Add(new MaterialColorProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } - } + LoadSceneMaterialColorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialVectorPropertyList), out var materialVectorProperties) && materialVectorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialVectorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - { - // Native Vector data takes precedence when both representations exist. - MaterialColorPropertyList.RemoveAll(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.Property == loadedProperty.Property); - MaterialVectorPropertyList.RemoveAll(x => x.ID == objID && x.MaterialName == loadedProperty.MaterialName && x.Property == loadedProperty.Property); - if (SetVector(go, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value) - && loadedProperty.Value != loadedProperty.ValueOriginal) - MaterialVectorPropertyList.Add(new MaterialVectorProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } - } - - if (data.data.TryGetValue(nameof(MaterialTexturePropertyList), out var materialTextureProperties) && materialTextureProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialTextureProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject(loadedItems, loadedProperty.ID, out var objID); - if (go != null) - { - int? texID = null; - if (operation == SceneOperationKind.Import) - { - if (loadedProperty.TexID != null) - texID = importDictionary[(int)loadedProperty.TexID]; - MEAnimationUtil.RemapTexID(loadedProperty.TexAnimationDef, importDictionary); - } - else - texID = loadedProperty.TexID; + LoadSceneMaterialVectorPropertyList(context); - MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(objID, loadedProperty.MaterialName, loadedProperty.Property, texID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); + LoadSceneMaterialTexturePropertyList(context); - bool setTex = false; - if (newTextureProperty.TexID != null) - setTex = SetTextureWithProperty(go, newTextureProperty); - - bool setOffset = SetTextureOffset(go, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Offset); - bool setScale = SetTextureScale(go, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Scale); - - if (setTex || setOffset || setScale) - MaterialTexturePropertyList.Add(newTextureProperty); - } - } - } - - if (data.data.TryGetValue(nameof(MaterialCubemapPropertyList), out var materialCubemapProperties) - && materialCubemapProperties != null) - { - var properties = MessagePackSerializer.Deserialize>( - (byte[])materialCubemapProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - GameObject go = ExtractGameObject( - loadedItems, - loadedProperty.ID, - out var objID); - if (go == null) - continue; - - int? texID = loadedProperty.TexID; - if (operation == SceneOperationKind.Import - && loadedProperty.TexID.HasValue) - texID = importDictionary[loadedProperty.TexID.Value]; - - var newCubemapProperty = new MaterialCubemapProperty( - objID, - loadedProperty.MaterialName, - loadedProperty.Property, - texID); - if (newCubemapProperty.TexID.HasValue - && SetCubemapWithProperty(go, newCubemapProperty)) - MaterialCubemapPropertyList.Add(newCubemapProperty); - } - } + LoadSceneMaterialCubemapPropertyList(context); if (data.version < 1) { @@ -524,141 +302,41 @@ private void FixDuplicatesInKeywordList() /// protected override void OnObjectsCopied(ReadOnlyDictionary copiedItems) { - List rendererPropertyListNew = new List(); - List projectorPropertyListNew = new List(); - List materialNamePropertyListNew = new List(); - List materialFloatPropertyListNew = new List(); - List materialKeywordPropertyListNew = new List(); - List materialColorPropertyListNew = new List(); - List materialVectorPropertyListNew = new List(); - List materialTexturePropertyListNew = new List(); - List materialCubemapPropertyListNew = new List(); - List materialShaderListNew = new List(); - List materialCopyListNew = new List(); + var batch = new SceneCopyBatch(); foreach (var copiedItem in copiedItems) { if (copiedItem.Value is OCIItem ociItem) { - var sourceGameObject = GetObjectByID(copiedItem.Key); - for (var i = 0; i < MaterialCopyList.Count; i++) + var context = new SceneCopyContext { - var loadedProperty = MaterialCopyList[i]; - if (loadedProperty.ID == copiedItem.Key) - { - CopyMaterial(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.MaterialCopyName); - materialCopyListNew.Add(new MaterialCopy(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); - } - } + SourceId = copiedItem.Key, + DestinationId = copiedItem.Value.GetSceneId(), + Root = ociItem.objectItem, + SourceRoot = GetObjectByID(copiedItem.Key), + Output = batch + }; + CopyMaterialCopyList(context); - for (var i = 0; i < MaterialNamePropertyList.Count; i++) - { - var loadedProperty = MaterialNamePropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - { - MaterialAPI.SetName(ociItem.objectItem, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value); - materialNamePropertyListNew.Add(new MaterialNameProperty(copiedItem.Value.GetSceneId(), loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); - } - } + CopyMaterialNamePropertyList(context); - for (var i = 0; i < MaterialShaderList.Count; i++) - { - var loadedProperty = MaterialShaderList[i]; - if (loadedProperty.ID == copiedItem.Key) - { - bool setShader = SetShader(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.ShaderName); - bool setRenderQueue = SetRenderQueue(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.RenderQueue); - if (setShader || setRenderQueue) - materialShaderListNew.Add(new MaterialShader(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); - } - } - - for (var i = 0; i < RendererPropertyList.Count; i++) - { - var loadedProperty = RendererPropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - if (MaterialAPI.SetRendererProperty(ociItem.objectItem, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value)) - rendererPropertyListNew.Add(new RendererProperty(copiedItem.Value.GetSceneId(), loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - - for (var i = 0; i < ProjectorPropertyList.Count; i++) - { - var loadedProperty = ProjectorPropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - if (MaterialAPI.SetProjectorProperty(ociItem.objectItem, loadedProperty.ProjectorName, loadedProperty.Property, float.Parse(loadedProperty.Value))) - projectorPropertyListNew.Add(new ProjectorProperty(copiedItem.Value.GetSceneId(), loadedProperty.ProjectorName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - - for (var i = 0; i < MaterialFloatPropertyList.Count; i++) - { - var loadedProperty = MaterialFloatPropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - if (SetFloat(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.Property, float.Parse(loadedProperty.Value))) - materialFloatPropertyListNew.Add(new MaterialFloatProperty(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } + CopyMaterialShaderList(context); - for (var i = 0; i < MaterialKeywordPropertyList.Count; i++) - { - var loadedProperty = MaterialKeywordPropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - if (SetKeyword(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) - materialKeywordPropertyListNew.Add(new MaterialKeywordProperty(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } + CopyRendererPropertyList(context); - for (var i = 0; i < MaterialColorPropertyList.Count; i++) - { - var loadedProperty = MaterialColorPropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - if (SetColor(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) - materialColorPropertyListNew.Add(new MaterialColorProperty(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } + CopyProjectorPropertyList(context); - for (var i = 0; i < MaterialVectorPropertyList.Count; i++) - { - var loadedProperty = MaterialVectorPropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - if (SetVector(ociItem.objectItem, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value)) - materialVectorPropertyListNew.Add(new MaterialVectorProperty(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } + CopyMaterialFloatPropertyList(context); - for (var i = 0; i < MaterialTexturePropertyList.Count; i++) - { - var loadedProperty = MaterialTexturePropertyList[i]; - if (loadedProperty.ID == copiedItem.Key) - { - MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(copiedItem.Value.GetSceneId(), loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.TexID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); + CopyMaterialKeywordPropertyList(context); - bool setTex = false; - if (loadedProperty.TexID != null) - setTex = SetTextureWithProperty(ociItem.objectItem, newTextureProperty); + CopyMaterialColorPropertyList(context); - bool setOffset = SetTextureOffset(ociItem.objectItem, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Offset); - bool setScale = SetTextureScale(ociItem.objectItem, newTextureProperty.MaterialName, newTextureProperty.Property, newTextureProperty.Scale); + CopyMaterialVectorPropertyList(context); - if (setTex || setOffset || setScale) materialTexturePropertyListNew.Add(newTextureProperty); - } - } + CopyMaterialTexturePropertyList(context); - for (var i = 0; i < MaterialCubemapPropertyList.Count; i++) - { - var loadedProperty = MaterialCubemapPropertyList[i]; - if (loadedProperty.ID != copiedItem.Key) - continue; - - var newCubemapProperty = new MaterialCubemapProperty( - copiedItem.Value.GetSceneId(), - loadedProperty.MaterialName, - loadedProperty.Property, - loadedProperty.TexID); - newCubemapProperty.InheritCubemapOriginalSnapshot( - loadedProperty, - sourceGameObject); - if (newCubemapProperty.TexID.HasValue - && SetCubemapWithProperty( - ociItem.objectItem, - newCubemapProperty)) - materialCubemapPropertyListNew.Add(newCubemapProperty); - } + CopyMaterialCubemapPropertyList(context); } if (copiedItem.Value is OCIChar ociChar) { @@ -672,17 +350,7 @@ protected override void OnObjectsCopied(ReadOnlyDictionary } } - RendererPropertyList.AddRange(rendererPropertyListNew); - ProjectorPropertyList.AddRange(projectorPropertyListNew); - MaterialNamePropertyList.AddRange(materialNamePropertyListNew); - MaterialFloatPropertyList.AddRange(materialFloatPropertyListNew); - MaterialKeywordPropertyList.AddRange(materialKeywordPropertyListNew); - MaterialColorPropertyList.AddRange(materialColorPropertyListNew); - MaterialVectorPropertyList.AddRange(materialVectorPropertyListNew); - MaterialTexturePropertyList.AddRange(materialTexturePropertyListNew); - MaterialCubemapPropertyList.AddRange(materialCubemapPropertyListNew); - MaterialShaderList.AddRange(materialShaderListNew); - MaterialCopyList.AddRange(materialCopyListNew); + CommitCopiedProperties(batch); } private void Update() @@ -981,18 +649,7 @@ internal void HandleMaterialNameChange(int id, Renderer renderer, Material mater /// internal static int SetAndGetTextureID(byte[] textureBytes) { - int highestID = 0; - foreach (var tex in TextureDictionary) - if (tex.Value.Data.SequenceEqualFast(textureBytes)) - return tex.Key; - else if (tex.Key > highestID) - highestID = tex.Key; - - highestID++; - TextureDictionary.Add( - highestID, - TextureSaveHandler.CreateTextureContainer(textureBytes)); - return highestID; + return MaterialTextureDataStore.GetOrAdd(TextureDictionary, textureBytes); } diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.Studio.projitems b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.Studio.projitems index e0b8dc5d6..c6af0795c 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.Studio.projitems +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.Studio.projitems @@ -9,6 +9,9 @@ Core.MaterialEditor.Studio + + + diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.Cubemap.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.Cubemap.cs index d17a14f05..808986b84 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.Cubemap.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.Cubemap.cs @@ -101,195 +101,43 @@ private bool SetMaterialCubemap( GameObject go, bool logNormalizationWarning) { - if (data == null) - return false; - - MaterialEditorCubemapLease lease = null; - var storedLease = false; - var textureEntryCreated = false; - var texID = 0; - MaterialCubemapProperty cubemapProperty = null; - var propertyAdded = false; - int? previousTexID = null; - MaterialCubemapOriginalState.Checkpoint previousOriginalState = null; - Dictionary previousAppliedValues = null; - string materialName = null; - try - { - string warning; - string error; - var acquired = contentKey == null - ? MaterialEditorCubemapCache.TryAcquire( - data, - out lease, - out warning, - out error) - : MaterialEditorCubemapCache.TryAcquire( - data, - contentKey, - out lease, - out warning, - out error); - if (!acquired) - { - MaterialEditorPlugin.Logger.LogMessage(error); - return false; - } - if (logNormalizationWarning && !string.IsNullOrEmpty(warning)) - MaterialEditorPlugin.Logger.LogWarning(warning); - - var textureCountBefore = TextureDictionary.Count; - texID = SetAndGetTextureID(data); - textureEntryCreated = TextureDictionary.Count > textureCountBefore; - CubemapLeases.Store(texID, lease); - // Store consumes ownership both when it adds the lease and when - // an existing texture ID makes the new reference redundant. - lease = null; - storedLease = true; - - materialName = material.NameFormatted(); - previousAppliedValues = - MaterialCubemapOriginalSnapshot.SynchronizeByMaterialReference( - go, - materialName, - propertyName, - null); - cubemapProperty = FindMaterialCubemapProperty( - slot, - objectType, - material, - propertyName); - if (cubemapProperty == null) + if (material == null) return false; + var existing = FindMaterialCubemapProperty(slot, objectType, material, propertyName); + var result = MaterialCubemapImportTransaction.Execute(data, contentKey, + go, material.NameFormatted(), propertyName, logNormalizationWarning, + new MaterialCubemapImportStorage { - cubemapProperty = new MaterialCubemapProperty( - objectType, - GetCoordinateIndex(objectType), - slot, - materialName, - propertyName, - texID); - MaterialCubemapPropertyList.Add(cubemapProperty); - propertyAdded = true; - } - else - { - previousTexID = cubemapProperty.TexID; - previousOriginalState = - cubemapProperty.CubemapOriginalState.CaptureCheckpoint(); - cubemapProperty.TexID = texID; - } - - if (SetCubemapWithProperty(go, cubemapProperty)) - return true; - - RollbackMaterialCubemapSet( - go, - materialName, - propertyName, - cubemapProperty, - propertyAdded, - previousTexID, - previousOriginalState, - previousAppliedValues, - texID, - textureEntryCreated); - textureEntryCreated = false; - MaterialEditorPluginBase.Logger.LogWarning( - "Could not apply Cubemap " + materialName + "/" + propertyName - + "; the previous override was preserved."); - return false; - } - catch (Exception exception) - { - RollbackMaterialCubemapSet( - go, - materialName, - propertyName, - cubemapProperty, - propertyAdded, - previousTexID, - previousOriginalState, - previousAppliedValues, - texID, - textureEntryCreated); - textureEntryCreated = false; - MaterialEditorPluginBase.Logger.LogWarning( - "Could not apply Cubemap; the previous override was preserved. " - + exception.Message); - return false; - } - finally - { - if (lease != null) - lease.Dispose(); - if (storedLease) - PurgeUnusedCubemapLeases(); - } + Count = () => TextureDictionary.Count, + StoreData = SetAndGetTextureID, + StoreLease = CubemapLeases.Store, + RemoveCreated = RemoveFailedCubemapData, + PurgeLeases = PurgeUnusedCubemapLeases + }, MaterialCubemapPropertyList, existing, + texId => new MaterialCubemapProperty(objectType, GetCoordinateIndex(objectType), slot, material.NameFormatted(), propertyName, texId), + CubemapRecordAccess, candidate => SetCubemapWithProperty(go, candidate)); + if (!result.Succeeded) + MaterialEditorPluginBase.Logger?.LogWarning("Cubemap import: " + result.Stage + ": " + result.Diagnostic); + return result.Succeeded; } - private void RollbackMaterialCubemapSet( - GameObject gameObject, - string materialName, - string propertyName, - MaterialCubemapProperty cubemapProperty, - bool propertyAdded, - int? previousTexID, - MaterialCubemapOriginalState.Checkpoint previousOriginalState, - Dictionary previousAppliedValues, - int texID, - bool textureEntryCreated) - { - try - { - MaterialCubemapOriginalSnapshot.RestoreByMaterialReference( - gameObject, - materialName, - propertyName, - previousAppliedValues); - } - catch (Exception exception) + private static readonly MaterialCubemapRecordAccess CubemapRecordAccess = + new MaterialCubemapRecordAccess { - MaterialEditorPluginBase.Logger.LogWarning( - "Could not fully restore the previous Cubemap material value. " - + exception.Message); - } + GetId = x => x.TexID, + SetId = (x, id) => x.TexID = id, + Original = x => x.CubemapOriginalState + }; - if (cubemapProperty != null) - { - if (propertyAdded) - { - MaterialCubemapPropertyList.Remove(cubemapProperty); - cubemapProperty.ClearCubemapOriginalSnapshot(); - } - else - { - cubemapProperty.TexID = previousTexID; - cubemapProperty.CubemapOriginalState.RestoreCheckpoint( - previousOriginalState, - true); - } - } - - if (textureEntryCreated) - { - CubemapLeases.Release(texID); - TextureContainer container; - if (TextureDictionary.TryGetValue(texID, out container)) - { - try - { - if (container != null) - container.Dispose(); - } - finally - { - TextureDictionary.Remove(texID); - } - } - } - PurgeUnusedCubemapLeases(); + private void RemoveFailedCubemapData(int texId) + { + CubemapLeases.Release(texId); + TextureContainer container; + if (!TextureDictionary.TryGetValue(texId, out container)) return; + try { container?.Dispose(); } + finally { TextureDictionary.Remove(texId); } } + /// /// Get the persisted native Cubemap value, or null when no override exists. /// diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs index bc3ee925e..fe9f846e6 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs @@ -114,14 +114,12 @@ private bool TrySetMaterialTexture(int slot, ObjectType objectType, Material mat var coordinateIndex = GetCoordinateIndex(objectType); var materialName = material.NameFormatted(); var existingProperty = MaterialTexturePropertyList.FirstOrDefault(x => x.ObjectType == objectType && x.CoordinateIndex == coordinateIndex && x.Slot == slot && x.Property == propertyName && x.MaterialName == materialName); - MaterialTextureProperty candidateProperty = null; - var committed = false; - - try - { + var result = MaterialTextureImportTransaction.Execute( + go, materialName, propertyName, () => + { var texID = SetAndGetTextureID(data); var animationDefinition = MEAnimationUtil.LoadAnimationDefFromBytes(texID, data, SetAndGetTextureID); - candidateProperty = new MaterialTextureProperty( + return new MaterialTextureProperty( objectType, coordinateIndex, slot, @@ -133,59 +131,23 @@ private bool TrySetMaterialTexture(int slot, ObjectType objectType, Material mat existingProperty == null ? null : existingProperty.Scale, existingProperty == null ? null : existingProperty.ScaleOriginal, animationDefinition); - - if (!SetTextureWithProperty(go, candidateProperty)) - return false; - - CommitTextureImport(existingProperty, candidateProperty); - committed = true; - return true; - } - finally - { - if (!committed) + }, + candidate => SetTextureWithProperty(go, candidate), + candidate => CommitTextureImport(existingProperty, candidate), + candidate => { - if (candidateProperty != null) - AnimationControllerMap.Remove(candidateProperty); + if (candidate != null) AnimationControllerMap.Remove(candidate); PurgeUnusedTextures(); - } - } + }); + if (!result.Succeeded) + MaterialEditorPluginBase.Logger?.LogWarning("Texture import: " + result.Stage + ": " + result.Diagnostic); + return result.Succeeded; } - private void CommitTextureImport(MaterialTextureProperty existingProperty, MaterialTextureProperty candidateProperty) - { - if (existingProperty == null) - { - MaterialTexturePropertyList.Add(candidateProperty); - return; - } - - var previousTexID = existingProperty.TexID; - var previousAnimationDefinition = existingProperty.TexAnimationDef; - var hadPreviousController = AnimationControllerMap.TryGetValue(existingProperty, out var previousController); - var hasCandidateController = AnimationControllerMap.TryGetValue(candidateProperty, out var candidateController); - try - { - existingProperty.TexID = candidateProperty.TexID; - existingProperty.TexAnimationDef = candidateProperty.TexAnimationDef; - AnimationControllerMap.Remove(candidateProperty); - if (hasCandidateController) - AnimationControllerMap[existingProperty] = candidateController; - else - AnimationControllerMap.Remove(existingProperty); - } - catch - { - existingProperty.TexID = previousTexID; - existingProperty.TexAnimationDef = previousAnimationDefinition; - AnimationControllerMap.Remove(candidateProperty); - if (hadPreviousController) - AnimationControllerMap[existingProperty] = previousController; - else - AnimationControllerMap.Remove(existingProperty); - throw; - } - } + private void CommitTextureImport(MaterialTextureProperty existingProperty, MaterialTextureProperty candidateProperty) => + MaterialTextureImportCommit.Commit(MaterialTexturePropertyList, AnimationControllerMap, + existingProperty, candidateProperty, x => x.TexID, (x, id) => x.TexID = id, + x => x.TexAnimationDef, (x, animation) => x.TexAnimationDef = animation); /// /// Get the saved material property value or null if none is saved diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Properties.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Properties.cs new file mode 100644 index 000000000..4f3b8988f --- /dev/null +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Properties.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using System.Linq; +using MaterialEditorAPI; +using UnityEngine; + +namespace KK_Plugins.MaterialEditor +{ + public partial class MaterialEditorCharaController + { + private sealed class CharacterLoadContext + { + internal MaterialEditLoadContext Data; + internal List ObjectTypes; + internal int? DestinationCoordinate; + internal MaterialEditorCharaController DuplicateSource; + internal int Coordinate(ObjectType type, int saved) => + DestinationCoordinate ?? (type == ObjectType.Character ? 0 : saved); + } + + private void LoadMaterialShaderList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialShaderList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialShaderList.Add(new MaterialShader(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); + }); + } + + private void LoadRendererPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(RendererPropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + RendererPropertyList.Add(new RendererProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadProjectorPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(ProjectorPropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + ProjectorPropertyList.Add(new ProjectorProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.ProjectorName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadMaterialNamePropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialNamePropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialNamePropertyList.Add(new MaterialNameProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); + }); + } + + private void LoadMaterialFloatPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialFloatPropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialFloatPropertyList.Add(new MaterialFloatProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadMaterialKeywordPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialKeywordPropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialKeywordPropertyList.Add(new MaterialKeywordProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadMaterialColorPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialColorPropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialColorPropertyList.Add(new MaterialColorProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadMaterialVectorPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialVectorPropertyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialVectorPropertyList.Add(new MaterialVectorProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); + }); + } + + private void LoadMaterialTexturePropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialTexturePropertyList), loadedProperty => + { + if (context.ObjectTypes.Contains(loadedProperty.ObjectType) && !loadedProperty.NullCheck()) + { + int? texID = null; + if (loadedProperty.TexID != null && context.Data.TextureIds.TryGetValue((int)loadedProperty.TexID, out var importTextID)) + texID = importTextID; + MEAnimationUtil.RemapTexID(loadedProperty.TexAnimationDef, context.Data.TextureIds); + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, texID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); + MaterialTexturePropertyList.Add(newTextureProperty); + } + }); + } + + private void LoadMaterialCubemapPropertyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialCubemapPropertyList), loadedProperty => + { + if (!context.ObjectTypes.Contains(loadedProperty.ObjectType) || loadedProperty.NullCheck()) + return; + + int importTexID; + if (!context.Data.TextureIds.TryGetValue(loadedProperty.TexID.Value, out importTexID)) + return; + var coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + var newCubemapProperty = new MaterialCubemapProperty( + loadedProperty.ObjectType, + coordinateIndex, + loadedProperty.Slot, + loadedProperty.MaterialName, + loadedProperty.Property, + importTexID); + + if (context.DuplicateSource != null) + { + var sourceProperty = context.DuplicateSource.MaterialCubemapPropertyList.FirstOrDefault(x => + x.ObjectType == loadedProperty.ObjectType + && x.CoordinateIndex == coordinateIndex + && x.Slot == loadedProperty.Slot + && x.MaterialName == loadedProperty.MaterialName + && x.Property == loadedProperty.Property); + GameObject sourceGameObject = null; + if (sourceProperty != null + && (sourceProperty.ObjectType == ObjectType.Character + || sourceProperty.ObjectType == ObjectType.Hair + || sourceProperty.CoordinateIndex == context.DuplicateSource.CurrentCoordinateIndex)) + sourceGameObject = context.DuplicateSource.FindGameObject( + sourceProperty.ObjectType, + sourceProperty.Slot); + newCubemapProperty.InheritCubemapOriginalSnapshot( + sourceProperty, + sourceGameObject); + } + MaterialCubemapPropertyList.Add(newCubemapProperty); + }); + } + + private void LoadMaterialCopyList(CharacterLoadContext context) + { + context.Data.Read(nameof(MaterialCopyList), loadedProperty => + { + int coordinateIndex = context.Coordinate(loadedProperty.ObjectType, loadedProperty.CoordinateIndex); + if (context.ObjectTypes.Contains(loadedProperty.ObjectType)) + MaterialCopyList.Add(new MaterialCopy(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); + }); + } + + } +} diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs new file mode 100644 index 000000000..753d89048 --- /dev/null +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using KKAPI.Maker; + +namespace KK_Plugins.MaterialEditor +{ + public partial class MaterialEditorCharaController + { + private List PrepareCharacterLoad() + { + List objectTypesToLoad = new List(); + + var loadFlags = MakerAPI.GetCharacterLoadFlags(); + if (loadFlags == null) + { + RendererPropertyList.Clear(); + ProjectorPropertyList.Clear(); + MaterialNamePropertyList.Clear(); + MaterialFloatPropertyList.Clear(); + MaterialKeywordPropertyList.Clear(); + MaterialColorPropertyList.Clear(); + MaterialVectorPropertyList.Clear(); + MaterialTexturePropertyList.Clear(); + MaterialCubemapPropertyList.Clear(); + MaterialShaderList.Clear(); + MaterialCopyList.Clear(); + AnimationControllerMap.Clear(); + + objectTypesToLoad.Add(ObjectType.Accessory); + objectTypesToLoad.Add(ObjectType.Character); + objectTypesToLoad.Add(ObjectType.Clothing); + objectTypesToLoad.Add(ObjectType.Hair); + } + else + { + bool changed = false; + + if (loadFlags.Face || loadFlags.Body) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Character, true); + + objectTypesToLoad.Add(ObjectType.Character); + + changed = true; + } + if (loadFlags.Clothes) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Clothing, true); + objectTypesToLoad.Add(ObjectType.Clothing); + + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Accessory, true); + objectTypesToLoad.Add(ObjectType.Accessory); + + changed = true; + } + if (loadFlags.Hair) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Hair, true); + objectTypesToLoad.Add(ObjectType.Hair); + + changed = true; + } + + if (changed) + { + PurgeUnusedAnimation(); + } + } + + return objectTypesToLoad; + } + + private List PrepareCoordinateLoad() + { + List objectTypesToLoad = new List(); + + var loadFlags = MakerAPI.GetCoordinateLoadFlags(); + if (loadFlags == null) + { + RemovePropertiesForLoad((type, coordinate) => (type == ObjectType.Clothing || type == ObjectType.Accessory) && coordinate == CurrentCoordinateIndex, false); + + objectTypesToLoad.Add(ObjectType.Accessory); + objectTypesToLoad.Add(ObjectType.Clothing); + + PurgeUnusedAnimation(); + } + else + { + if (loadFlags.Clothes) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Clothing && coordinate == CurrentCoordinateIndex, false); + objectTypesToLoad.Add(ObjectType.Clothing); + } + if (loadFlags.Accessories) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Accessory && coordinate == CurrentCoordinateIndex, false); + objectTypesToLoad.Add(ObjectType.Accessory); + } + + if (loadFlags.Clothes || loadFlags.Accessories) + { + PurgeUnusedAnimation(); + } + } + + return objectTypesToLoad; + } + + private void RemovePropertiesForLoad(Func remove, bool includeProjectors) + { + RendererPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialNamePropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialFloatPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialKeywordPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialColorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialVectorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialTexturePropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialCubemapPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialShaderList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialCopyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + // Coordinate loading historically excludes projector records. + if (includeProjectors) + ProjectorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + } + } +} diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs index ccc017471..50d32b5c3 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs @@ -73,60 +73,27 @@ protected override void OnCoordinateBeingSaved(ChaFileCoordinate coordinate) else data.data.Add(TexDicSaveKey, null); - if (coordinateRendererPropertyList.Count > 0) - data.data.Add(nameof(RendererPropertyList), MessagePackSerializer.Serialize(coordinateRendererPropertyList)); - else - data.data.Add(nameof(RendererPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(RendererPropertyList), coordinateRendererPropertyList); - if (coordinateProjectorPropertyList.Count > 0) - data.data.Add(nameof(ProjectorPropertyList), MessagePackSerializer.Serialize(coordinateProjectorPropertyList)); - else - data.data.Add(nameof(ProjectorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(ProjectorPropertyList), coordinateProjectorPropertyList); - if (coordinateMaterialNamePropertyList.Count > 0) - data.data.Add(nameof(MaterialNamePropertyList), MessagePackSerializer.Serialize(coordinateMaterialNamePropertyList)); - else - data.data.Add(nameof(MaterialNamePropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialNamePropertyList), coordinateMaterialNamePropertyList); - if (coordinateMaterialFloatPropertyList.Count > 0) - data.data.Add(nameof(MaterialFloatPropertyList), MessagePackSerializer.Serialize(coordinateMaterialFloatPropertyList)); - else - data.data.Add(nameof(MaterialFloatPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialFloatPropertyList), coordinateMaterialFloatPropertyList); - if (coordinateMaterialKeywordPropertyList.Count > 0) - data.data.Add(nameof(MaterialKeywordPropertyList), MessagePackSerializer.Serialize(coordinateMaterialKeywordPropertyList)); - else - data.data.Add(nameof(MaterialKeywordPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialKeywordPropertyList), coordinateMaterialKeywordPropertyList); - if (coordinateMaterialColorPropertyList.Count > 0) - data.data.Add(nameof(MaterialColorPropertyList), MessagePackSerializer.Serialize(coordinateMaterialColorPropertyList)); - else - data.data.Add(nameof(MaterialColorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialColorPropertyList), coordinateMaterialColorPropertyList); - if (coordinateMaterialVectorPropertyList.Count > 0) - data.data.Add(nameof(MaterialVectorPropertyList), MessagePackSerializer.Serialize(coordinateMaterialVectorPropertyList)); - else - data.data.Add(nameof(MaterialVectorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialVectorPropertyList), coordinateMaterialVectorPropertyList); - if (coordinateMaterialTexturePropertyList.Count > 0) - data.data.Add(nameof(MaterialTexturePropertyList), MessagePackSerializer.Serialize(coordinateMaterialTexturePropertyList)); - else - data.data.Add(nameof(MaterialTexturePropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialTexturePropertyList), coordinateMaterialTexturePropertyList); - if (coordinateMaterialCubemapPropertyList.Count > 0) - data.data.Add(nameof(MaterialCubemapPropertyList), MessagePackSerializer.Serialize(coordinateMaterialCubemapPropertyList)); - else - data.data.Add(nameof(MaterialCubemapPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialCubemapPropertyList), coordinateMaterialCubemapPropertyList); - if (coordinateMaterialShaderList.Count > 0) - data.data.Add(nameof(MaterialShaderList), MessagePackSerializer.Serialize(coordinateMaterialShaderList)); - else - data.data.Add(nameof(MaterialShaderList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialShaderList), coordinateMaterialShaderList); - if (coordinateMaterialCopyList.Count > 0) - data.data.Add(nameof(MaterialCopyList), MessagePackSerializer.Serialize(coordinateMaterialCopyList)); - else - data.data.Add(nameof(MaterialCopyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialCopyList), coordinateMaterialCopyList); SetCoordinateExtendedData(coordinate, data); } @@ -157,104 +124,7 @@ private void LoadCharacterExtSaveData() { RemoveMaterialCopies(ChaControl.gameObject); - List objectTypesToLoad = new List(); - - var loadFlags = MakerAPI.GetCharacterLoadFlags(); - if (loadFlags == null) - { - RendererPropertyList.Clear(); - ProjectorPropertyList.Clear(); - MaterialNamePropertyList.Clear(); - MaterialFloatPropertyList.Clear(); - MaterialKeywordPropertyList.Clear(); - MaterialColorPropertyList.Clear(); - MaterialVectorPropertyList.Clear(); - MaterialTexturePropertyList.Clear(); - MaterialCubemapPropertyList.Clear(); - MaterialShaderList.Clear(); - MaterialCopyList.Clear(); - AnimationControllerMap.Clear(); - - objectTypesToLoad.Add(ObjectType.Accessory); - objectTypesToLoad.Add(ObjectType.Character); - objectTypesToLoad.Add(ObjectType.Clothing); - objectTypesToLoad.Add(ObjectType.Hair); - } - else - { - bool changed = false; - - if (loadFlags.Face || loadFlags.Body) - { - RendererPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - ProjectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialNamePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialFloatPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialKeywordPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialColorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialVectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialTexturePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialCubemapPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialShaderList.RemoveAll(x => x.ObjectType == ObjectType.Character); - MaterialCopyList.RemoveAll(x => x.ObjectType == ObjectType.Character); - - objectTypesToLoad.Add(ObjectType.Character); - - changed = true; - } - if (loadFlags.Clothes) - { - RendererPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - ProjectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialNamePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialFloatPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialKeywordPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialColorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialVectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialTexturePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialCubemapPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialShaderList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - MaterialCopyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing); - objectTypesToLoad.Add(ObjectType.Clothing); - - RendererPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - ProjectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialNamePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialFloatPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialKeywordPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialColorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialVectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialTexturePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialCubemapPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialShaderList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - MaterialCopyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory); - objectTypesToLoad.Add(ObjectType.Accessory); - - changed = true; - } - if (loadFlags.Hair) - { - RendererPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - ProjectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialNamePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialFloatPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialKeywordPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialColorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialVectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialTexturePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialCubemapPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialShaderList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - MaterialCopyList.RemoveAll(x => x.ObjectType == ObjectType.Hair); - objectTypesToLoad.Add(ObjectType.Hair); - - changed = true; - } - - if (changed) - { - PurgeUnusedAnimation(); - } - } + var objectTypesToLoad = PrepareCharacterLoad(); //Don't destroy the textures in H mode because they will still be needed if (KoikatuAPI.GetCurrentGameMode() != GameMode.MainGame) @@ -279,8 +149,7 @@ private void LoadCharacterExtSaveData() #endif ; duplicateSourceController = MaterialEditorPlugin.GetCharaController(chaCtrl); - foreach (var kvp in duplicateSourceController.TextureDictionary) - importDictionary[kvp.Key] = SetAndGetTextureID(kvp.Value.Data); + importDictionary = MaterialEditLoadContext.ImportTextures(duplicateSourceController.TextureDictionary, x => x.Data, SetAndGetTextureID); DuplicatingFrom = null; } else @@ -289,8 +158,7 @@ private void LoadCharacterExtSaveData() var importDictionaryTemp = TextureSaveHandler.Instance.Load>(data, TexDicSaveKey, true); try { - foreach (var kvp in importDictionaryTemp) - importDictionary[kvp.Key] = SetAndGetTextureID(kvp.Value.Data); + importDictionary = MaterialEditLoadContext.ImportTextures(importDictionaryTemp, x => x.Data, SetAndGetTextureID); } finally { @@ -298,251 +166,39 @@ private void LoadCharacterExtSaveData() } } - //Debug for dumping all textures - //int counter = 1; - //foreach (var tex in TextureDictionary.Values) - //{ - // string filename = Path.Combine(MaterialEditorPlugin.ExportPath, $"_Export_{ChaControl.GetCharacterName()}_{counter}.png"); - // MaterialEditorPlugin.SaveTex(tex.Texture, filename); - // MaterialEditorPlugin.Logger.LogInfo($"Exported {filename}"); - // counter++; - //} - - if (data.data.TryGetValue(nameof(MaterialShaderList), out var shaderProperties) && shaderProperties != null) + var context = new CharacterLoadContext { - var properties = MessagePackSerializer.Deserialize>((byte[])shaderProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialShaderList.Add(new MaterialShader(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); - } - } + Data = new MaterialEditLoadContext(data, importDictionary), + ObjectTypes = objectTypesToLoad, + DuplicateSource = duplicateSourceController + }; + LoadMaterialShaderList(context); - if (data.data.TryGetValue(nameof(RendererPropertyList), out var rendererProperties) && rendererProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])rendererProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - RendererPropertyList.Add(new RendererProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadRendererPropertyList(context); - if (data.data.TryGetValue(nameof(ProjectorPropertyList), out var projectorProperties) && projectorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])projectorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - ProjectorPropertyList.Add(new ProjectorProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.ProjectorName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadProjectorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialNamePropertyList), out var materialNameProperties) && materialNameProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialNameProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialNamePropertyList.Add(new MaterialNameProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); - } - } + LoadMaterialNamePropertyList(context); - if (data.data.TryGetValue(nameof(MaterialFloatPropertyList), out var materialFloatProperties) && materialFloatProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialFloatProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialFloatPropertyList.Add(new MaterialFloatProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialFloatPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialKeywordPropertyList), out var materialKeywordProperties) && materialKeywordProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialKeywordProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialKeywordPropertyList.Add(new MaterialKeywordProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialKeywordPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialColorPropertyList), out var materialColorProperties) && materialColorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialColorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialColorPropertyList.Add(new MaterialColorProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialColorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialVectorPropertyList), out var materialVectorProperties) && materialVectorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialVectorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialVectorPropertyList.Add(new MaterialVectorProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialVectorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialTexturePropertyList), out var materialTextureProperties) && materialTextureProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialTextureProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType) && !loadedProperty.NullCheck()) - { - int? texID = null; - if (loadedProperty.TexID != null && importDictionary.TryGetValue((int)loadedProperty.TexID, out var importTextID)) - texID = importTextID; - MEAnimationUtil.RemapTexID(loadedProperty.TexAnimationDef, importDictionary); - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, texID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); - MaterialTexturePropertyList.Add(newTextureProperty); - } - } - } + LoadMaterialTexturePropertyList(context); - if (data.data.TryGetValue(nameof(MaterialCubemapPropertyList), out var materialCubemapProperties) && materialCubemapProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialCubemapProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (!objectTypesToLoad.Contains(loadedProperty.ObjectType) || loadedProperty.NullCheck()) - continue; - - int importTexID; - if (!importDictionary.TryGetValue(loadedProperty.TexID.Value, out importTexID)) - continue; - var coordinateIndex = loadedProperty.ObjectType == ObjectType.Character - ? 0 - : loadedProperty.CoordinateIndex; - var newCubemapProperty = new MaterialCubemapProperty( - loadedProperty.ObjectType, - coordinateIndex, - loadedProperty.Slot, - loadedProperty.MaterialName, - loadedProperty.Property, - importTexID); - - if (duplicateSourceController != null) - { - var sourceProperty = duplicateSourceController.MaterialCubemapPropertyList.FirstOrDefault(x => - x.ObjectType == loadedProperty.ObjectType - && x.CoordinateIndex == coordinateIndex - && x.Slot == loadedProperty.Slot - && x.MaterialName == loadedProperty.MaterialName - && x.Property == loadedProperty.Property); - GameObject sourceGameObject = null; - if (sourceProperty != null - && (sourceProperty.ObjectType == ObjectType.Character - || sourceProperty.ObjectType == ObjectType.Hair - || sourceProperty.CoordinateIndex == duplicateSourceController.CurrentCoordinateIndex)) - sourceGameObject = duplicateSourceController.FindGameObject( - sourceProperty.ObjectType, - sourceProperty.Slot); - newCubemapProperty.InheritCubemapOriginalSnapshot( - sourceProperty, - sourceGameObject); - } - MaterialCubemapPropertyList.Add(newCubemapProperty); - } - } + LoadMaterialCubemapPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialCopyList), out var materialCopyData) && materialCopyData != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialCopyData); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - int coordinateIndex = loadedProperty.ObjectType == ObjectType.Character ? 0 : loadedProperty.CoordinateIndex; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialCopyList.Add(new MaterialCopy(loadedProperty.ObjectType, coordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); - } - } + LoadMaterialCopyList(context); } } private void LoadCoordinateExtSaveData(ChaFileCoordinate coordinate) { - List objectTypesToLoad = new List(); - - var loadFlags = MakerAPI.GetCoordinateLoadFlags(); - if (loadFlags == null) - { - RendererPropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialNamePropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialFloatPropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialKeywordPropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialColorPropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialVectorPropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialTexturePropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialCubemapPropertyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialShaderList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialCopyList.RemoveAll(x => (x.ObjectType == ObjectType.Clothing || x.ObjectType == ObjectType.Accessory) && x.CoordinateIndex == CurrentCoordinateIndex); - - objectTypesToLoad.Add(ObjectType.Accessory); - objectTypesToLoad.Add(ObjectType.Clothing); - - PurgeUnusedAnimation(); - } - else - { - if (loadFlags.Clothes) - { - RendererPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialNamePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialFloatPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialKeywordPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialColorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialVectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialTexturePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialCubemapPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialShaderList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialCopyList.RemoveAll(x => x.ObjectType == ObjectType.Clothing && x.CoordinateIndex == CurrentCoordinateIndex); - objectTypesToLoad.Add(ObjectType.Clothing); - } - if (loadFlags.Accessories) - { - RendererPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialNamePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialFloatPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialKeywordPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialColorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialVectorPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialTexturePropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialCubemapPropertyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialShaderList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - MaterialCopyList.RemoveAll(x => x.ObjectType == ObjectType.Accessory && x.CoordinateIndex == CurrentCoordinateIndex); - objectTypesToLoad.Add(ObjectType.Accessory); - } - - if (loadFlags.Clothes || loadFlags.Accessories) - { - PurgeUnusedAnimation(); - } - } + var objectTypesToLoad = PrepareCoordinateLoad(); var data = GetCoordinateExtendedData(coordinate); if (data?.data != null) @@ -550,130 +206,33 @@ private void LoadCoordinateExtSaveData(ChaFileCoordinate coordinate) var importDictionary = new Dictionary(); if (data.data.TryGetValue(TexDicSaveKey, out var texDic) && texDic != null) - foreach (var x in MessagePackSerializer.Deserialize>((byte[])texDic)) - importDictionary[x.Key] = SetAndGetTextureID(x.Value); + importDictionary = MaterialEditLoadContext.ImportTextures(MessagePackSerializer.Deserialize>((byte[])texDic), x => x, SetAndGetTextureID); - if (data.data.TryGetValue(nameof(MaterialShaderList), out var materialShaders) && materialShaders != null) + var context = new CharacterLoadContext { - var properties = MessagePackSerializer.Deserialize>((byte[])materialShaders); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialShaderList.Add(new MaterialShader(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.ShaderName, loadedProperty.ShaderNameOriginal, loadedProperty.RenderQueue, loadedProperty.RenderQueueOriginal)); - } - } + Data = new MaterialEditLoadContext(data, importDictionary), + ObjectTypes = objectTypesToLoad, + DestinationCoordinate = CurrentCoordinateIndex + }; + LoadMaterialShaderList(context); - if (data.data.TryGetValue(nameof(RendererPropertyList), out var rendererProperties) && rendererProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])rendererProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - RendererPropertyList.Add(new RendererProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.RendererName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadRendererPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialNamePropertyList), out var materialNameProperties) && materialNameProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialNameProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialNamePropertyList.Add(new MaterialNameProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.Renderer, loadedProperty.MaterialName, loadedProperty.Value)); - } - } + LoadMaterialNamePropertyList(context); - if (data.data.TryGetValue(nameof(MaterialFloatPropertyList), out var materialFloatProperties) && materialFloatProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialFloatProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialFloatPropertyList.Add(new MaterialFloatProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialFloatPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialKeywordPropertyList), out var materialKeywordProperties) && materialKeywordProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialKeywordProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialKeywordPropertyList.Add(new MaterialKeywordProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialKeywordPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialColorPropertyList), out var materialColorProperties) && materialColorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialColorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialColorPropertyList.Add(new MaterialColorProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialColorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialVectorPropertyList), out var materialVectorProperties) && materialVectorProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialVectorProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialVectorPropertyList.Add(new MaterialVectorProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, loadedProperty.Value, loadedProperty.ValueOriginal)); - } - } + LoadMaterialVectorPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialTexturePropertyList), out var materialTextureProperties) && materialTextureProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialTextureProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - { - int? texID = null; - if (loadedProperty.TexID != null) - texID = importDictionary[(int)loadedProperty.TexID]; - MEAnimationUtil.RemapTexID(loadedProperty.TexAnimationDef, importDictionary); - MaterialTextureProperty newTextureProperty = new MaterialTextureProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, texID, loadedProperty.Offset, loadedProperty.OffsetOriginal, loadedProperty.Scale, loadedProperty.ScaleOriginal, loadedProperty.TexAnimationDef); - MaterialTexturePropertyList.Add(newTextureProperty); - } - } - } + LoadMaterialTexturePropertyList(context); - if (data.data.TryGetValue(nameof(MaterialCubemapPropertyList), out var materialCubemapProperties) && materialCubemapProperties != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialCubemapProperties); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType) && !loadedProperty.NullCheck()) - { - int? texID = null; - if (loadedProperty.TexID != null) - texID = importDictionary[loadedProperty.TexID.Value]; - MaterialCubemapPropertyList.Add(new MaterialCubemapProperty(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.Property, texID)); - } - } - } + LoadMaterialCubemapPropertyList(context); - if (data.data.TryGetValue(nameof(MaterialCopyList), out var materialCopyData) && materialCopyData != null) - { - var properties = MessagePackSerializer.Deserialize>((byte[])materialCopyData); - for (var i = 0; i < properties.Count; i++) - { - var loadedProperty = properties[i]; - if (objectTypesToLoad.Contains(loadedProperty.ObjectType)) - MaterialCopyList.Add(new MaterialCopy(loadedProperty.ObjectType, CurrentCoordinateIndex, loadedProperty.Slot, loadedProperty.MaterialName, loadedProperty.MaterialCopyName)); - } - } + LoadMaterialCopyList(context); } } @@ -704,161 +263,26 @@ public IEnumerator LoadData(bool clothes, bool accessories, bool hair, bool body CorrectFace(); #endif - //Instantiate all material copies before applying any edits to ensure edits are applied to copies - for (var i = 0; i < MaterialCopyList.Count; i++) - { - var property = MaterialCopyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; + var scope = new CharacterRestoreScope(clothes, accessories, hair, body); - CopyMaterial(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.MaterialCopyName); - } + //Instantiate all material copies before applying any edits to ensure edits are applied to copies + RestoreMaterialCopyList(scope); // Rename materials before applying edits, but after copying materials, to ensure no missing material mishaps occur // Do not move this anywhere else - for (var i = 0; i < MaterialNamePropertyList.Count; i++) - { - var property = MaterialNamePropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - MaterialAPI.SetName(FindGameObject(property.ObjectType, property.Slot), property.Renderer, property.MaterialName, property.Value); - } + RestoreMaterialNamePropertyList(scope); - for (var i = 0; i < MaterialShaderList.Count; i++) - { - var property = MaterialShaderList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - -#if KK || EC || KKS - if (property.ObjectType == ObjectType.Character && MaterialEditorPlugin.EyeMaterials.Contains(property.MaterialName)) - { - SetShader(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.ShaderName, true); - } - else -#endif - { - SetShader(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.ShaderName); - } - SetRenderQueue(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.RenderQueue); - } + RestoreMaterialShaderList(scope); MigrateLegacyMaterialVectorProperties(clothes, accessories, hair, body); - for (var i = 0; i < RendererPropertyList.Count; i++) - { - var property = RendererPropertyList[i]; -#if KK - if (property.Property == RendererProperties.UpdateWhenOffscreen) continue; -#endif - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - MaterialAPI.SetRendererProperty(FindGameObject(property.ObjectType, property.Slot), property.RendererName, property.Property, property.Value); - } - for (var i = 0; i < MaterialFloatPropertyList.Count; i++) - { - var property = MaterialFloatPropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - SetFloat(go, property.MaterialName, property.Property, float.Parse(property.Value)); - } - for (var i = 0; i < MaterialKeywordPropertyList.Count; i++) - { - var property = MaterialKeywordPropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - SetKeyword(go, property.MaterialName, property.Property, property.Value); - } - for (var i = 0; i < MaterialColorPropertyList.Count; i++) - { - var property = MaterialColorPropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - SetColor(go, property.MaterialName, property.Property, property.Value); - } - for (var i = 0; i < MaterialVectorPropertyList.Count; i++) - { - var property = MaterialVectorPropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - SetVector(go, property.MaterialName, property.Property, property.Value); - } - for (var i = 0; i < MaterialTexturePropertyList.Count; i++) - { - var property = MaterialTexturePropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - - SetTextureWithProperty(go, property); - SetTextureOffset(go, property.MaterialName, property.Property, property.Offset); - SetTextureScale(go, property.MaterialName, property.Property, property.Scale); - } - for (var i = 0; i < MaterialCubemapPropertyList.Count; i++) - { - var property = MaterialCubemapPropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - - SetCubemapWithProperty(go, property); - } - for (var i = 0; i < ProjectorPropertyList.Count; i++) - { - var property = ProjectorPropertyList[i]; - if (property.ObjectType == ObjectType.Clothing && !clothes) continue; - if (property.ObjectType == ObjectType.Accessory && !accessories) continue; - if (property.ObjectType == ObjectType.Hair && !hair) continue; - if ((property.ObjectType == ObjectType.Clothing || property.ObjectType == ObjectType.Accessory) && property.CoordinateIndex != CurrentCoordinateIndex) continue; - if (property.ObjectType == ObjectType.Character && !body) continue; - - MaterialAPI.SetProjectorProperty(FindGameObject(property.ObjectType, property.Slot), property.ProjectorName, property.Property, float.Parse(property.Value)); - } + RestoreRendererPropertyList(scope); + RestoreMaterialFloatPropertyList(scope); + RestoreMaterialKeywordPropertyList(scope); + RestoreMaterialColorPropertyList(scope); + RestoreMaterialVectorPropertyList(scope); + RestoreMaterialTexturePropertyList(scope); + RestoreMaterialCubemapPropertyList(scope); + RestoreProjectorPropertyList(scope); #if KK || EC || KKS @@ -1008,18 +432,7 @@ private void RemoveRimAccessory(int slot) /// private int SetAndGetTextureID(byte[] textureBytes) { - int highestID = 0; - foreach (var tex in TextureDictionary) - if (tex.Value.Data.SequenceEqualFast(textureBytes)) - return tex.Key; - else if (tex.Key > highestID) - highestID = tex.Key; - - highestID++; - TextureDictionary.Add( - highestID, - TextureSaveHandler.CreateTextureContainer(textureBytes)); - return highestID; + return MaterialTextureDataStore.GetOrAdd(TextureDictionary, textureBytes); } } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs new file mode 100644 index 000000000..262484d59 --- /dev/null +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs @@ -0,0 +1,185 @@ +using ExtensibleSaveFormat; +using KKAPI; +using KKAPI.Chara; +using KKAPI.Maker; +using MaterialEditorAPI; +using MessagePack; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using UniRx; +using UnityEngine; +using static MaterialEditorAPI.MaterialAPI; +using static MaterialEditorAPI.MaterialEditorPluginBase; +#if !EC +using KKAPI.Studio; +#endif +#if AI || HS2 +using AIChara; +#endif +#if PH +using ChaFileCoordinate = Character.CustomParameter; +using ChaControl = Human; +#endif +namespace KK_Plugins.MaterialEditor +{ + public partial class MaterialEditorCharaController + { + private struct CharacterRestoreScope + { + internal readonly bool Body; + private readonly bool _clothes; + private readonly bool _accessories; + private readonly bool _hair; + + internal CharacterRestoreScope(bool clothes, bool accessories, bool hair, bool body) + { + _clothes = clothes; + _accessories = accessories; + _hair = hair; + Body = body; + } + + internal bool Includes(ObjectType type, int coordinate, int currentCoordinate) + { + if (type == ObjectType.Clothing && !_clothes) return false; + if (type == ObjectType.Accessory && !_accessories) return false; + if (type == ObjectType.Hair && !_hair) return false; + return (type != ObjectType.Clothing && type != ObjectType.Accessory) + || coordinate == currentCoordinate; + } + } + + private void RestoreMaterialCopyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialCopyList.Count; i++) + { + var property = MaterialCopyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + CopyMaterial(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.MaterialCopyName); + } + } + + private void RestoreMaterialNamePropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialNamePropertyList.Count; i++) + { + var property = MaterialNamePropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + MaterialAPI.SetName(FindGameObject(property.ObjectType, property.Slot), property.Renderer, property.MaterialName, property.Value); + } + } + + private void RestoreMaterialShaderList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialShaderList.Count; i++) + { + var property = MaterialShaderList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + +#if KK || EC || KKS + if (property.ObjectType == ObjectType.Character && MaterialEditorPlugin.EyeMaterials.Contains(property.MaterialName)) + { + SetShader(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.ShaderName, true); + } + else +#endif + { + SetShader(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.ShaderName); + } + SetRenderQueue(FindGameObject(property.ObjectType, property.Slot), property.MaterialName, property.RenderQueue); + } + } + + private void RestoreRendererPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < RendererPropertyList.Count; i++) + { + var property = RendererPropertyList[i]; +#if KK + if (property.Property == RendererProperties.UpdateWhenOffscreen) continue; +#endif + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + MaterialAPI.SetRendererProperty(FindGameObject(property.ObjectType, property.Slot), property.RendererName, property.Property, property.Value); + } + } + + private void RestoreMaterialFloatPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialFloatPropertyList.Count; i++) + { + var property = MaterialFloatPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + SetFloat(go, property.MaterialName, property.Property, float.Parse(property.Value)); + } + } + + private void RestoreMaterialKeywordPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialKeywordPropertyList.Count; i++) + { + var property = MaterialKeywordPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + SetKeyword(go, property.MaterialName, property.Property, property.Value); + } + } + + private void RestoreMaterialColorPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialColorPropertyList.Count; i++) + { + var property = MaterialColorPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + SetColor(go, property.MaterialName, property.Property, property.Value); + } + } + + private void RestoreMaterialVectorPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialVectorPropertyList.Count; i++) + { + var property = MaterialVectorPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + SetVector(go, property.MaterialName, property.Property, property.Value); + } + } + + private void RestoreProjectorPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < ProjectorPropertyList.Count; i++) + { + var property = ProjectorPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + + MaterialAPI.SetProjectorProperty(FindGameObject(property.ObjectType, property.Slot), property.ProjectorName, property.Property, float.Parse(property.Value)); + } + } + } +} diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs new file mode 100644 index 000000000..030f4ee44 --- /dev/null +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs @@ -0,0 +1,61 @@ +using ExtensibleSaveFormat; +using KKAPI; +using KKAPI.Chara; +using KKAPI.Maker; +using MaterialEditorAPI; +using MessagePack; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using UniRx; +using UnityEngine; +using static MaterialEditorAPI.MaterialAPI; +using static MaterialEditorAPI.MaterialEditorPluginBase; +#if !EC +using KKAPI.Studio; +#endif +#if AI || HS2 +using AIChara; +#endif +#if PH +using ChaFileCoordinate = Character.CustomParameter; +using ChaControl = Human; +#endif +namespace KK_Plugins.MaterialEditor +{ + public partial class MaterialEditorCharaController + { + private void RestoreMaterialTexturePropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialTexturePropertyList.Count; i++) + { + var property = MaterialTexturePropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + + SetTextureWithProperty(go, property); + SetTextureOffset(go, property.MaterialName, property.Property, property.Offset); + SetTextureScale(go, property.MaterialName, property.Property, property.Scale); + } + } + + private void RestoreMaterialCubemapPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialCubemapPropertyList.Count; i++) + { + var property = MaterialCubemapPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + + SetCubemapWithProperty(go, property); + } + } + } +} diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs index e741535d4..e64fa0822 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs @@ -100,60 +100,27 @@ protected override void OnCardBeingSaved(GameMode currentGameMode) else data.data.Add(TexDicSaveKey, null); - if (RendererPropertyList.Count > 0) - data.data.Add(nameof(RendererPropertyList), MessagePackSerializer.Serialize(RendererPropertyList)); - else - data.data.Add(nameof(RendererPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(RendererPropertyList), RendererPropertyList); - if (ProjectorPropertyList.Count > 0) - data.data.Add(nameof(ProjectorPropertyList), MessagePackSerializer.Serialize(ProjectorPropertyList)); - else - data.data.Add(nameof(ProjectorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(ProjectorPropertyList), ProjectorPropertyList); - if (MaterialNamePropertyList.Count > 0) - data.data.Add(nameof(MaterialNamePropertyList), MessagePackSerializer.Serialize(MaterialNamePropertyList)); - else - data.data.Add(nameof(MaterialNamePropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialNamePropertyList), MaterialNamePropertyList); - if (MaterialFloatPropertyList.Count > 0) - data.data.Add(nameof(MaterialFloatPropertyList), MessagePackSerializer.Serialize(MaterialFloatPropertyList)); - else - data.data.Add(nameof(MaterialFloatPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialFloatPropertyList), MaterialFloatPropertyList); - if (MaterialKeywordPropertyList.Count > 0) - data.data.Add(nameof(MaterialKeywordPropertyList), MessagePackSerializer.Serialize(MaterialKeywordPropertyList)); - else - data.data.Add(nameof(MaterialKeywordPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList); - if (MaterialColorPropertyList.Count > 0) - data.data.Add(nameof(MaterialColorPropertyList), MessagePackSerializer.Serialize(MaterialColorPropertyList)); - else - data.data.Add(nameof(MaterialColorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialColorPropertyList), MaterialColorPropertyList); - if (MaterialVectorPropertyList.Count > 0) - data.data.Add(nameof(MaterialVectorPropertyList), MessagePackSerializer.Serialize(MaterialVectorPropertyList)); - else - data.data.Add(nameof(MaterialVectorPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialVectorPropertyList), MaterialVectorPropertyList); - if (MaterialTexturePropertyList.Count > 0) - data.data.Add(nameof(MaterialTexturePropertyList), MessagePackSerializer.Serialize(MaterialTexturePropertyList)); - else - data.data.Add(nameof(MaterialTexturePropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialTexturePropertyList), MaterialTexturePropertyList); - if (MaterialCubemapPropertyList.Count > 0) - data.data.Add(nameof(MaterialCubemapPropertyList), MessagePackSerializer.Serialize(MaterialCubemapPropertyList)); - else - data.data.Add(nameof(MaterialCubemapPropertyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList); - if (MaterialShaderList.Count > 0) - data.data.Add(nameof(MaterialShaderList), MessagePackSerializer.Serialize(MaterialShaderList)); - else - data.data.Add(nameof(MaterialShaderList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialShaderList), MaterialShaderList); - if (MaterialCopyList.Count > 0) - data.data.Add(nameof(MaterialCopyList), MessagePackSerializer.Serialize(MaterialCopyList)); - else - data.data.Add(nameof(MaterialCopyList), null); + MaterialEditRecordSerialization.Write(data, nameof(MaterialCopyList), MaterialCopyList); SetExtendedData(data); } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.projitems b/src/MaterialEditor.Core/Core.MaterialEditor.projitems index 69efee5b3..ae01a638f 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.projitems +++ b/src/MaterialEditor.Core/Core.MaterialEditor.projitems @@ -9,6 +9,13 @@ Core.MaterialEditor + + + + + + + diff --git a/src/MaterialEditor.Core/Persistence/MaterialEditLoadContext.cs b/src/MaterialEditor.Core/Persistence/MaterialEditLoadContext.cs new file mode 100644 index 000000000..dd105c040 --- /dev/null +++ b/src/MaterialEditor.Core/Persistence/MaterialEditLoadContext.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using ExtensibleSaveFormat; +using MessagePack; + +namespace MaterialEditorAPI +{ + /// One game persistence scope. Owns source, ID mapping and per-record diagnostics. + internal sealed class MaterialEditLoadContext + { + internal readonly PluginData Source; + internal readonly Dictionary TextureIds; + + internal MaterialEditLoadContext(PluginData source, Dictionary textureIds) + { + Source = source; + TextureIds = textureIds; + } + + internal static Dictionary ImportTextures(IEnumerable> source, + Func readBytes, Func store) + { + var ids = new Dictionary(); + ApplyRecords("TextureDictionary", source, entry => ids[entry.Key] = store(readBytes(entry.Value))); + return ids; + } + + internal int? RemapTexture(int? id) + { + if (!id.HasValue) return null; + int mapped; + if (TextureIds.TryGetValue(id.Value, out mapped)) return mapped; + throw new InvalidOperationException("Missing imported texture ID " + id.Value); + } + + internal void Read(string family, Action accept) + { + object bytes; + if (Source?.data == null || !Source.data.TryGetValue(family, out bytes) || bytes == null) return; + List records; + try { records = MessagePackSerializer.Deserialize>((byte[])bytes); } + catch (Exception ex) { Report(family, ex); return; } + ApplyRecords(family, records, accept); + } + + internal static void ApplyRecords(string family, IEnumerable records, Action accept) + { + if (records == null) return; + foreach (var record in records) + { + try { accept(record); } + catch (Exception ex) { Report(family, ex); } + } + } + + private static void Report(string family, Exception error) => + MaterialEditorPluginBase.Logger?.LogWarning("Skipped invalid " + family + " record: " + error.Message); + } +} diff --git a/src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs b/src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs new file mode 100644 index 000000000..fd444a668 --- /dev/null +++ b/src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using ExtensibleSaveFormat; +using MessagePack; + +namespace KK_Plugins.MaterialEditor +{ + /// Preserves explicit legacy keys, concrete List serialization and present-but-null empty families. + internal static class MaterialEditRecordSerialization + { + internal static void Write(PluginData data, string key, List records) => + data.data.Add(key, records.Count > 0 ? MessagePackSerializer.Serialize(records) : null); + } +} diff --git a/src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs b/src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs new file mode 100644 index 000000000..412a2a95c --- /dev/null +++ b/src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace KK_Plugins.MaterialEditor +{ + /// Shared byte deduplication. Preserves first equal payload and max-existing-ID + 1 allocation. + internal static class MaterialTextureDataStore + { + internal static int GetOrAdd(Dictionary textures, byte[] data) + { + int highestId = 0; + foreach (var texture in textures) + if (texture.Value.Data.SequenceEqualFast(data)) + return texture.Key; + else if (texture.Key > highestId) + highestId = texture.Key; + + highestId++; + textures.Add(highestId, TextureSaveHandler.CreateTextureContainer(data)); + return highestId; + } + } +} From ab2220cf6958c3d16aa0b7489cf46e76437d3b7c Mon Sep 17 00:00:00 2001 From: tom20051012 Date: Sat, 12 Sep 2026 19:36:27 +0800 Subject: [PATCH 2/2] Simplify persistence helpers and keep texture transactions together --- .../MaterialEditor.Base.projitems | 2 - .../Operations/MaterialTextureImportCommit.cs | 43 ------ .../MaterialTextureImportTransaction.cs | 64 ++++++++ .../Operations/MaterialTextureSnapshot.cs | 34 ----- ...tor.SceneController.Edits.TextureShader.cs | 2 +- .../Core.MaterialEditor.SceneController.cs | 24 +-- ...tor.CharaController.Edits.TextureShader.cs | 2 +- ...r.CharaController.Persistence.Selection.cs | 126 ---------------- ...erialEditor.CharaController.Persistence.cs | 140 ++++++++++++++++-- ...itor.CharaController.Restore.Properties.cs | 30 ++++ ...Editor.CharaController.Restore.Textures.cs | 61 -------- .../Core.MaterialEditor.CharaController.cs | 22 +-- .../Core.MaterialEditor.TextureSaveHandler.cs | 15 ++ .../Core.MaterialEditor.projitems | 4 - .../MaterialEditRecordSerialization.cs | 13 -- .../Persistence/MaterialTextureDataStore.cs | 22 --- 16 files changed, 262 insertions(+), 342 deletions(-) delete mode 100644 src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs delete mode 100644 src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs delete mode 100644 src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs delete mode 100644 src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs delete mode 100644 src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs delete mode 100644 src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs diff --git a/src/MaterialEditor.Base/MaterialEditor.Base.projitems b/src/MaterialEditor.Base/MaterialEditor.Base.projitems index 28c153d16..1c573eddf 100644 --- a/src/MaterialEditor.Base/MaterialEditor.Base.projitems +++ b/src/MaterialEditor.Base/MaterialEditor.Base.projitems @@ -11,8 +11,6 @@ - - diff --git a/src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs b/src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs deleted file mode 100644 index 5e7df5f19..000000000 --- a/src/MaterialEditor.Base/Operations/MaterialTextureImportCommit.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MaterialEditorAPI -{ - /// Shared persistence/animation commit. DTO identity is retained for existing animation bindings. - internal static class MaterialTextureImportCommit - { - internal static void Commit(IList records, IDictionary animations, - T existing, T candidate, Func getId, Action setId, - Func getAnimation, Action setAnimation) where T : class - { - if (existing == null) - { - records.Add(candidate); - return; - } - var previousId = getId(existing); - var previousAnimation = getAnimation(existing); - TController previousController; - TController candidateController; - var hadPrevious = animations.TryGetValue(existing, out previousController); - var hasCandidate = animations.TryGetValue(candidate, out candidateController); - try - { - setId(existing, getId(candidate)); - setAnimation(existing, getAnimation(candidate)); - animations.Remove(candidate); - if (hasCandidate) animations[existing] = candidateController; - else animations.Remove(existing); - } - catch - { - setId(existing, previousId); - setAnimation(existing, previousAnimation); - animations.Remove(candidate); - if (hadPrevious) animations[existing] = previousController; - else animations.Remove(existing); - throw; - } - } - } -} diff --git a/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs b/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs index f40f7b076..200838d41 100644 --- a/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs +++ b/src/MaterialEditor.Base/Operations/MaterialTextureImportTransaction.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEngine; namespace MaterialEditorAPI @@ -40,5 +41,68 @@ internal static MaterialEditResult Execute(GameObject root, string materialNa } } } + + // Keep the existing DTO identity: animation bindings may still reference it. + internal static void Commit(IList records, IDictionary animations, + T existing, T candidate, Func getId, Action setId, + Func getAnimation, Action setAnimation) where T : class + { + if (existing == null) + { + records.Add(candidate); + return; + } + var previousId = getId(existing); + var previousAnimation = getAnimation(existing); + TController previousController; + TController candidateController; + var hadPrevious = animations.TryGetValue(existing, out previousController); + var hasCandidate = animations.TryGetValue(candidate, out candidateController); + try + { + setId(existing, getId(candidate)); + setAnimation(existing, getAnimation(candidate)); + animations.Remove(candidate); + if (hasCandidate) animations[existing] = candidateController; + else animations.Remove(existing); + } + catch + { + setId(existing, previousId); + setAnimation(existing, previousAnimation); + animations.Remove(candidate); + if (hadPrevious) animations[existing] = previousController; + else animations.Remove(existing); + throw; + } + } + } + + /// Rollback values for every runtime material touched by SetTexture, including projectors. + internal sealed class MaterialTextureSnapshot + { + private readonly Dictionary _values = new Dictionary(); + private readonly string _property; + + internal MaterialTextureSnapshot(GameObject root, string name, string property) + { + _property = "_" + property; + foreach (var material in MaterialAPI.GetObjectMaterials(root, name)) + if (material != null && material.HasProperty(_property)) + _values[material] = material.GetTexture(_property); + } + + internal void Restore() + { + foreach (var entry in _values) + { + try + { + if (entry.Key != null && entry.Key.HasProperty(_property)) + entry.Key.SetTexture(_property, entry.Value); + } + catch (Exception ex) { MaterialEditorPluginBase.Logger?.LogWarning("Texture rollback: " + ex.Message); } + } + } } } diff --git a/src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs b/src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs deleted file mode 100644 index a8843a093..000000000 --- a/src/MaterialEditor.Base/Operations/MaterialTextureSnapshot.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace MaterialEditorAPI -{ - /// Rollback values for every runtime material touched by SetTexture, including projectors. - internal sealed class MaterialTextureSnapshot - { - private readonly Dictionary _values = new Dictionary(); - private readonly string _property; - - internal MaterialTextureSnapshot(GameObject root, string name, string property) - { - _property = "_" + property; - foreach (var material in MaterialAPI.GetObjectMaterials(root, name)) - if (material != null && material.HasProperty(_property)) - _values[material] = material.GetTexture(_property); - } - - internal void Restore() - { - foreach (var entry in _values) - { - try - { - if (entry.Key != null && entry.Key.HasProperty(_property)) - entry.Key.SetTexture(_property, entry.Value); - } - catch (Exception ex) { MaterialEditorPluginBase.Logger?.LogWarning("Texture rollback: " + ex.Message); } - } - } - } -} diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs index 10abda035..d467e3c04 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.Edits.TextureShader.cs @@ -162,7 +162,7 @@ private bool TrySetMaterialTexture(int id, Material material, string propertyNam } private void CommitTextureImport(MaterialTextureProperty existingProperty, MaterialTextureProperty candidateProperty) => - MaterialTextureImportCommit.Commit(MaterialTexturePropertyList, AnimationControllerMap, + MaterialTextureImportTransaction.Commit(MaterialTexturePropertyList, AnimationControllerMap, existingProperty, candidateProperty, x => x.TexID, (x, id) => x.TexID = id, x => x.TexAnimationDef, (x, animation) => x.TexAnimationDef = animation); diff --git a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs index 80f1ff7de..d9ec81f8b 100644 --- a/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs +++ b/src/MaterialEditor.Core.Studio/Core.MaterialEditor.SceneController.cs @@ -76,27 +76,27 @@ protected override void OnSceneSave() else data.data.Add(TexDicSaveKey, null); - MaterialEditRecordSerialization.Write(data, nameof(RendererPropertyList), RendererPropertyList); + data.data.Add(nameof(RendererPropertyList), RendererPropertyList.Count > 0 ? MessagePackSerializer.Serialize(RendererPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(ProjectorPropertyList), ProjectorPropertyList); + data.data.Add(nameof(ProjectorPropertyList), ProjectorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(ProjectorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialNamePropertyList), MaterialNamePropertyList); + data.data.Add(nameof(MaterialNamePropertyList), MaterialNamePropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialNamePropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialFloatPropertyList), MaterialFloatPropertyList); + data.data.Add(nameof(MaterialFloatPropertyList), MaterialFloatPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialFloatPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList); + data.data.Add(nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialKeywordPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialColorPropertyList), MaterialColorPropertyList); + data.data.Add(nameof(MaterialColorPropertyList), MaterialColorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialColorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialVectorPropertyList), MaterialVectorPropertyList); + data.data.Add(nameof(MaterialVectorPropertyList), MaterialVectorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialVectorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialTexturePropertyList), MaterialTexturePropertyList); + data.data.Add(nameof(MaterialTexturePropertyList), MaterialTexturePropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialTexturePropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList); + data.data.Add(nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialCubemapPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialShaderList), MaterialShaderList); + data.data.Add(nameof(MaterialShaderList), MaterialShaderList.Count > 0 ? MessagePackSerializer.Serialize(MaterialShaderList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialCopyList), MaterialCopyList); + data.data.Add(nameof(MaterialCopyList), MaterialCopyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialCopyList) : null); SetExtendedData(data); @@ -649,7 +649,7 @@ internal void HandleMaterialNameChange(int id, Renderer renderer, Material mater /// internal static int SetAndGetTextureID(byte[] textureBytes) { - return MaterialTextureDataStore.GetOrAdd(TextureDictionary, textureBytes); + return TextureSaveHandler.GetOrAddTexture(TextureDictionary, textureBytes); } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs index fe9f846e6..e208e04c6 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Edits.TextureShader.cs @@ -145,7 +145,7 @@ private bool TrySetMaterialTexture(int slot, ObjectType objectType, Material mat } private void CommitTextureImport(MaterialTextureProperty existingProperty, MaterialTextureProperty candidateProperty) => - MaterialTextureImportCommit.Commit(MaterialTexturePropertyList, AnimationControllerMap, + MaterialTextureImportTransaction.Commit(MaterialTexturePropertyList, AnimationControllerMap, existingProperty, candidateProperty, x => x.TexID, (x, id) => x.TexID = id, x => x.TexAnimationDef, (x, animation) => x.TexAnimationDef = animation); diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs deleted file mode 100644 index 753d89048..000000000 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.Selection.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using KKAPI.Maker; - -namespace KK_Plugins.MaterialEditor -{ - public partial class MaterialEditorCharaController - { - private List PrepareCharacterLoad() - { - List objectTypesToLoad = new List(); - - var loadFlags = MakerAPI.GetCharacterLoadFlags(); - if (loadFlags == null) - { - RendererPropertyList.Clear(); - ProjectorPropertyList.Clear(); - MaterialNamePropertyList.Clear(); - MaterialFloatPropertyList.Clear(); - MaterialKeywordPropertyList.Clear(); - MaterialColorPropertyList.Clear(); - MaterialVectorPropertyList.Clear(); - MaterialTexturePropertyList.Clear(); - MaterialCubemapPropertyList.Clear(); - MaterialShaderList.Clear(); - MaterialCopyList.Clear(); - AnimationControllerMap.Clear(); - - objectTypesToLoad.Add(ObjectType.Accessory); - objectTypesToLoad.Add(ObjectType.Character); - objectTypesToLoad.Add(ObjectType.Clothing); - objectTypesToLoad.Add(ObjectType.Hair); - } - else - { - bool changed = false; - - if (loadFlags.Face || loadFlags.Body) - { - RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Character, true); - - objectTypesToLoad.Add(ObjectType.Character); - - changed = true; - } - if (loadFlags.Clothes) - { - RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Clothing, true); - objectTypesToLoad.Add(ObjectType.Clothing); - - RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Accessory, true); - objectTypesToLoad.Add(ObjectType.Accessory); - - changed = true; - } - if (loadFlags.Hair) - { - RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Hair, true); - objectTypesToLoad.Add(ObjectType.Hair); - - changed = true; - } - - if (changed) - { - PurgeUnusedAnimation(); - } - } - - return objectTypesToLoad; - } - - private List PrepareCoordinateLoad() - { - List objectTypesToLoad = new List(); - - var loadFlags = MakerAPI.GetCoordinateLoadFlags(); - if (loadFlags == null) - { - RemovePropertiesForLoad((type, coordinate) => (type == ObjectType.Clothing || type == ObjectType.Accessory) && coordinate == CurrentCoordinateIndex, false); - - objectTypesToLoad.Add(ObjectType.Accessory); - objectTypesToLoad.Add(ObjectType.Clothing); - - PurgeUnusedAnimation(); - } - else - { - if (loadFlags.Clothes) - { - RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Clothing && coordinate == CurrentCoordinateIndex, false); - objectTypesToLoad.Add(ObjectType.Clothing); - } - if (loadFlags.Accessories) - { - RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Accessory && coordinate == CurrentCoordinateIndex, false); - objectTypesToLoad.Add(ObjectType.Accessory); - } - - if (loadFlags.Clothes || loadFlags.Accessories) - { - PurgeUnusedAnimation(); - } - } - - return objectTypesToLoad; - } - - private void RemovePropertiesForLoad(Func remove, bool includeProjectors) - { - RendererPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialNamePropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialFloatPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialKeywordPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialColorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialVectorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialTexturePropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialCubemapPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialShaderList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - MaterialCopyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - // Coordinate loading historically excludes projector records. - if (includeProjectors) - ProjectorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); - } - } -} diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs index 50d32b5c3..ad15ec082 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Persistence.cs @@ -73,27 +73,27 @@ protected override void OnCoordinateBeingSaved(ChaFileCoordinate coordinate) else data.data.Add(TexDicSaveKey, null); - MaterialEditRecordSerialization.Write(data, nameof(RendererPropertyList), coordinateRendererPropertyList); + data.data.Add(nameof(RendererPropertyList), coordinateRendererPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateRendererPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(ProjectorPropertyList), coordinateProjectorPropertyList); + data.data.Add(nameof(ProjectorPropertyList), coordinateProjectorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateProjectorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialNamePropertyList), coordinateMaterialNamePropertyList); + data.data.Add(nameof(MaterialNamePropertyList), coordinateMaterialNamePropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialNamePropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialFloatPropertyList), coordinateMaterialFloatPropertyList); + data.data.Add(nameof(MaterialFloatPropertyList), coordinateMaterialFloatPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialFloatPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialKeywordPropertyList), coordinateMaterialKeywordPropertyList); + data.data.Add(nameof(MaterialKeywordPropertyList), coordinateMaterialKeywordPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialKeywordPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialColorPropertyList), coordinateMaterialColorPropertyList); + data.data.Add(nameof(MaterialColorPropertyList), coordinateMaterialColorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialColorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialVectorPropertyList), coordinateMaterialVectorPropertyList); + data.data.Add(nameof(MaterialVectorPropertyList), coordinateMaterialVectorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialVectorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialTexturePropertyList), coordinateMaterialTexturePropertyList); + data.data.Add(nameof(MaterialTexturePropertyList), coordinateMaterialTexturePropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialTexturePropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialCubemapPropertyList), coordinateMaterialCubemapPropertyList); + data.data.Add(nameof(MaterialCubemapPropertyList), coordinateMaterialCubemapPropertyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialCubemapPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialShaderList), coordinateMaterialShaderList); + data.data.Add(nameof(MaterialShaderList), coordinateMaterialShaderList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialShaderList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialCopyList), coordinateMaterialCopyList); + data.data.Add(nameof(MaterialCopyList), coordinateMaterialCopyList.Count > 0 ? MessagePackSerializer.Serialize(coordinateMaterialCopyList) : null); SetCoordinateExtendedData(coordinate, data); } @@ -432,8 +432,124 @@ private void RemoveRimAccessory(int slot) /// private int SetAndGetTextureID(byte[] textureBytes) { - return MaterialTextureDataStore.GetOrAdd(TextureDictionary, textureBytes); + return TextureSaveHandler.GetOrAddTexture(TextureDictionary, textureBytes); } + private List PrepareCharacterLoad() + { + List objectTypesToLoad = new List(); + + var loadFlags = MakerAPI.GetCharacterLoadFlags(); + if (loadFlags == null) + { + RendererPropertyList.Clear(); + ProjectorPropertyList.Clear(); + MaterialNamePropertyList.Clear(); + MaterialFloatPropertyList.Clear(); + MaterialKeywordPropertyList.Clear(); + MaterialColorPropertyList.Clear(); + MaterialVectorPropertyList.Clear(); + MaterialTexturePropertyList.Clear(); + MaterialCubemapPropertyList.Clear(); + MaterialShaderList.Clear(); + MaterialCopyList.Clear(); + AnimationControllerMap.Clear(); + + objectTypesToLoad.Add(ObjectType.Accessory); + objectTypesToLoad.Add(ObjectType.Character); + objectTypesToLoad.Add(ObjectType.Clothing); + objectTypesToLoad.Add(ObjectType.Hair); + } + else + { + bool changed = false; + + if (loadFlags.Face || loadFlags.Body) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Character, true); + + objectTypesToLoad.Add(ObjectType.Character); + + changed = true; + } + if (loadFlags.Clothes) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Clothing, true); + objectTypesToLoad.Add(ObjectType.Clothing); + + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Accessory, true); + objectTypesToLoad.Add(ObjectType.Accessory); + + changed = true; + } + if (loadFlags.Hair) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Hair, true); + objectTypesToLoad.Add(ObjectType.Hair); + + changed = true; + } + + if (changed) + { + PurgeUnusedAnimation(); + } + } + + return objectTypesToLoad; + } + + private List PrepareCoordinateLoad() + { + List objectTypesToLoad = new List(); + + var loadFlags = MakerAPI.GetCoordinateLoadFlags(); + if (loadFlags == null) + { + RemovePropertiesForLoad((type, coordinate) => (type == ObjectType.Clothing || type == ObjectType.Accessory) && coordinate == CurrentCoordinateIndex, false); + + objectTypesToLoad.Add(ObjectType.Accessory); + objectTypesToLoad.Add(ObjectType.Clothing); + + PurgeUnusedAnimation(); + } + else + { + if (loadFlags.Clothes) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Clothing && coordinate == CurrentCoordinateIndex, false); + objectTypesToLoad.Add(ObjectType.Clothing); + } + if (loadFlags.Accessories) + { + RemovePropertiesForLoad((type, coordinate) => type == ObjectType.Accessory && coordinate == CurrentCoordinateIndex, false); + objectTypesToLoad.Add(ObjectType.Accessory); + } + + if (loadFlags.Clothes || loadFlags.Accessories) + { + PurgeUnusedAnimation(); + } + } + + return objectTypesToLoad; + } + + private void RemovePropertiesForLoad(Func remove, bool includeProjectors) + { + RendererPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialNamePropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialFloatPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialKeywordPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialColorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialVectorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialTexturePropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialCubemapPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialShaderList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + MaterialCopyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + // Coordinate loading historically excludes projector records. + if (includeProjectors) + ProjectorPropertyList.RemoveAll(x => remove(x.ObjectType, x.CoordinateIndex)); + } } } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs index 262484d59..328d2a11e 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Properties.cs @@ -181,5 +181,35 @@ private void RestoreProjectorPropertyList(CharacterRestoreScope scope) MaterialAPI.SetProjectorProperty(FindGameObject(property.ObjectType, property.Slot), property.ProjectorName, property.Property, float.Parse(property.Value)); } } + + private void RestoreMaterialTexturePropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialTexturePropertyList.Count; i++) + { + var property = MaterialTexturePropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + + SetTextureWithProperty(go, property); + SetTextureOffset(go, property.MaterialName, property.Property, property.Offset); + SetTextureScale(go, property.MaterialName, property.Property, property.Scale); + } + } + + private void RestoreMaterialCubemapPropertyList(CharacterRestoreScope scope) + { + for (var i = 0; i < MaterialCubemapPropertyList.Count; i++) + { + var property = MaterialCubemapPropertyList[i]; + if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; + if (property.ObjectType == ObjectType.Character && !scope.Body) continue; + var go = FindGameObject(property.ObjectType, property.Slot); + if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; + + SetCubemapWithProperty(go, property); + } + } } } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs deleted file mode 100644 index 030f4ee44..000000000 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.Restore.Textures.cs +++ /dev/null @@ -1,61 +0,0 @@ -using ExtensibleSaveFormat; -using KKAPI; -using KKAPI.Chara; -using KKAPI.Maker; -using MaterialEditorAPI; -using MessagePack; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using UniRx; -using UnityEngine; -using static MaterialEditorAPI.MaterialAPI; -using static MaterialEditorAPI.MaterialEditorPluginBase; -#if !EC -using KKAPI.Studio; -#endif -#if AI || HS2 -using AIChara; -#endif -#if PH -using ChaFileCoordinate = Character.CustomParameter; -using ChaControl = Human; -#endif -namespace KK_Plugins.MaterialEditor -{ - public partial class MaterialEditorCharaController - { - private void RestoreMaterialTexturePropertyList(CharacterRestoreScope scope) - { - for (var i = 0; i < MaterialTexturePropertyList.Count; i++) - { - var property = MaterialTexturePropertyList[i]; - if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; - if (property.ObjectType == ObjectType.Character && !scope.Body) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - - SetTextureWithProperty(go, property); - SetTextureOffset(go, property.MaterialName, property.Property, property.Offset); - SetTextureScale(go, property.MaterialName, property.Property, property.Scale); - } - } - - private void RestoreMaterialCubemapPropertyList(CharacterRestoreScope scope) - { - for (var i = 0; i < MaterialCubemapPropertyList.Count; i++) - { - var property = MaterialCubemapPropertyList[i]; - if (!scope.Includes(property.ObjectType, property.CoordinateIndex, CurrentCoordinateIndex)) continue; - if (property.ObjectType == ObjectType.Character && !scope.Body) continue; - var go = FindGameObject(property.ObjectType, property.Slot); - if (Instance.CheckBlacklist(property.MaterialName, property.Property)) continue; - - SetCubemapWithProperty(go, property); - } - } - } -} diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs index e64fa0822..d86173c74 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.CharaController.cs @@ -100,27 +100,27 @@ protected override void OnCardBeingSaved(GameMode currentGameMode) else data.data.Add(TexDicSaveKey, null); - MaterialEditRecordSerialization.Write(data, nameof(RendererPropertyList), RendererPropertyList); + data.data.Add(nameof(RendererPropertyList), RendererPropertyList.Count > 0 ? MessagePackSerializer.Serialize(RendererPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(ProjectorPropertyList), ProjectorPropertyList); + data.data.Add(nameof(ProjectorPropertyList), ProjectorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(ProjectorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialNamePropertyList), MaterialNamePropertyList); + data.data.Add(nameof(MaterialNamePropertyList), MaterialNamePropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialNamePropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialFloatPropertyList), MaterialFloatPropertyList); + data.data.Add(nameof(MaterialFloatPropertyList), MaterialFloatPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialFloatPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList); + data.data.Add(nameof(MaterialKeywordPropertyList), MaterialKeywordPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialKeywordPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialColorPropertyList), MaterialColorPropertyList); + data.data.Add(nameof(MaterialColorPropertyList), MaterialColorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialColorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialVectorPropertyList), MaterialVectorPropertyList); + data.data.Add(nameof(MaterialVectorPropertyList), MaterialVectorPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialVectorPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialTexturePropertyList), MaterialTexturePropertyList); + data.data.Add(nameof(MaterialTexturePropertyList), MaterialTexturePropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialTexturePropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList); + data.data.Add(nameof(MaterialCubemapPropertyList), MaterialCubemapPropertyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialCubemapPropertyList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialShaderList), MaterialShaderList); + data.data.Add(nameof(MaterialShaderList), MaterialShaderList.Count > 0 ? MessagePackSerializer.Serialize(MaterialShaderList) : null); - MaterialEditRecordSerialization.Write(data, nameof(MaterialCopyList), MaterialCopyList); + data.data.Add(nameof(MaterialCopyList), MaterialCopyList.Count > 0 ? MessagePackSerializer.Serialize(MaterialCopyList) : null); SetExtendedData(data); } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.TextureSaveHandler.cs b/src/MaterialEditor.Core/Core.MaterialEditor.TextureSaveHandler.cs index 4219b0f7a..5447c8cf9 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.TextureSaveHandler.cs +++ b/src/MaterialEditor.Core/Core.MaterialEditor.TextureSaveHandler.cs @@ -317,5 +317,20 @@ private static bool HasBytes(byte[] data, int offset, params byte[] expected) return false; return true; } + + // Preserve the first equal payload and allocate above the highest existing ID. + internal static int GetOrAddTexture(Dictionary textures, byte[] data) + { + int highestId = 0; + foreach (var texture in textures) + if (texture.Value.Data.SequenceEqualFast(data)) + return texture.Key; + else if (texture.Key > highestId) + highestId = texture.Key; + + highestId++; + textures.Add(highestId, CreateTextureContainer(data)); + return highestId; + } } } diff --git a/src/MaterialEditor.Core/Core.MaterialEditor.projitems b/src/MaterialEditor.Core/Core.MaterialEditor.projitems index ae01a638f..8a5b0f47b 100644 --- a/src/MaterialEditor.Core/Core.MaterialEditor.projitems +++ b/src/MaterialEditor.Core/Core.MaterialEditor.projitems @@ -9,12 +9,8 @@ Core.MaterialEditor - - - - diff --git a/src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs b/src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs deleted file mode 100644 index fd444a668..000000000 --- a/src/MaterialEditor.Core/Persistence/MaterialEditRecordSerialization.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using ExtensibleSaveFormat; -using MessagePack; - -namespace KK_Plugins.MaterialEditor -{ - /// Preserves explicit legacy keys, concrete List serialization and present-but-null empty families. - internal static class MaterialEditRecordSerialization - { - internal static void Write(PluginData data, string key, List records) => - data.data.Add(key, records.Count > 0 ? MessagePackSerializer.Serialize(records) : null); - } -} diff --git a/src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs b/src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs deleted file mode 100644 index 412a2a95c..000000000 --- a/src/MaterialEditor.Core/Persistence/MaterialTextureDataStore.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace KK_Plugins.MaterialEditor -{ - /// Shared byte deduplication. Preserves first equal payload and max-existing-ID + 1 allocation. - internal static class MaterialTextureDataStore - { - internal static int GetOrAdd(Dictionary textures, byte[] data) - { - int highestId = 0; - foreach (var texture in textures) - if (texture.Value.Data.SequenceEqualFast(data)) - return texture.Key; - else if (texture.Key > highestId) - highestId = texture.Key; - - highestId++; - textures.Add(highestId, TextureSaveHandler.CreateTextureContainer(data)); - return highestId; - } - } -}