From 3ad2f0116e3bd06b9132118e832db9e26667dae3 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 18:35:23 +0900 Subject: [PATCH 1/7] feat: add params overload for ModifyProperties --- API-Editor/ComponentInformation.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/API-Editor/ComponentInformation.cs b/API-Editor/ComponentInformation.cs index 68ac95fce..94baa753f 100644 --- a/API-Editor/ComponentInformation.cs +++ b/API-Editor/ComponentInformation.cs @@ -194,5 +194,9 @@ internal ComponentMutationsCollector() [PublicAPI] public abstract void ModifyProperties([NotNull] Component component, [NotNull] IEnumerable properties); + + [PublicAPI] + public void ModifyProperties([NotNull] Component component, [NotNull] string[] properties) => + ModifyProperties(component, (IEnumerable) properties); } } From 6f568de2f4c5655039a96a0f248c08809dc10ffe Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 18:44:57 +0900 Subject: [PATCH 2/7] chore: collect non-AnimatorController modifications on the AvatarDescriptor on the ComponentInfos --- Editor/APIInternal/ComponentInfos.VRCSDK.cs | 78 +++++++++++++++++++++ Editor/AnimatorParsers/AnimatorParser.cs | 78 --------------------- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/Editor/APIInternal/ComponentInfos.VRCSDK.cs b/Editor/APIInternal/ComponentInfos.VRCSDK.cs index 48f2ce72e..ea7c84088 100644 --- a/Editor/APIInternal/ComponentInfos.VRCSDK.cs +++ b/Editor/APIInternal/ComponentInfos.VRCSDK.cs @@ -1,6 +1,7 @@ #if AAO_VRCSDK3_AVATARS using System; using System.Collections.Generic; +using System.Linq; using Anatawa12.AvatarOptimizer.API; using UnityEngine; using VRC.SDK3; @@ -38,6 +39,37 @@ protected override void CollectDependency(T component, collector.MarkEntrypoint(); collector.AddDependency(component.GetComponent()).EvenIfDependantDisabled(); } + + protected override void CollectMutations(T component, ComponentMutationsCollector collector) + { + base.CollectMutations(component, collector); + switch (component.lipSync) + { + case VRC_AvatarDescriptor.LipSyncStyle.Default: + // TODO + break; + case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone: + collector.TransformRotation(component.lipSyncJawBone); + break; + case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape when component.VisemeSkinnedMesh != null: + { + collector.ModifyProperties(component.VisemeSkinnedMesh, + new[] { $"blendShape.{component.MouthOpenBlendShapeName}" }); + break; + } + case VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape when component.VisemeSkinnedMesh != null: + { + collector.ModifyProperties(component.VisemeSkinnedMesh, + component.VisemeBlendShapes.Select(blendShape => $"blendShape.{blendShape}")); + break; + } + case VRC_AvatarDescriptor.LipSyncStyle.VisemeParameterOnly: + // NOP + break; + default: + throw new ArgumentOutOfRangeException(); + } + } } [ComponentInformation(typeof(VRCAvatarDescriptor))] @@ -77,6 +109,52 @@ void AddCollider(VRCAvatarDescriptor.ColliderConfig collider) } } } + + protected override void CollectMutations(VRCAvatarDescriptor component, ComponentMutationsCollector collector) + { + base.CollectMutations(component, collector); + + if (component.enableEyeLook) + { + var leftEye = component.customEyeLookSettings.leftEye; + var rightEye = component.customEyeLookSettings.rightEye; + + if (leftEye) collector.TransformRotation(leftEye); + if (rightEye) collector.TransformRotation(rightEye); + + switch (component.customEyeLookSettings.eyelidType) + { + case VRCAvatarDescriptor.EyelidType.None: + break; + case VRCAvatarDescriptor.EyelidType.Bones: + { + foreach (var eyelids in new[] + { + component.customEyeLookSettings.lowerLeftEyelid, + component.customEyeLookSettings.upperLeftEyelid, + component.customEyeLookSettings.lowerRightEyelid, + component.customEyeLookSettings.upperRightEyelid, + }) + collector.TransformRotation(eyelids); + } + break; + case VRCAvatarDescriptor.EyelidType.Blendshapes + when component.customEyeLookSettings.eyelidsSkinnedMesh != null: + { + var skinnedMeshRenderer = component.customEyeLookSettings.eyelidsSkinnedMesh; + var mesh = skinnedMeshRenderer.sharedMesh; + + collector.ModifyProperties(skinnedMeshRenderer, + from index in component.customEyeLookSettings.eyelidsBlendshapes + where 0 <= index && index < mesh.blendShapeCount + select $"blendShape.{mesh.GetBlendShapeName(index)}"); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } } [ComponentInformation(typeof(VRC.SDKBase.VRCStation))] diff --git a/Editor/AnimatorParsers/AnimatorParser.cs b/Editor/AnimatorParsers/AnimatorParser.cs index 4e8c184d5..abef9721d 100644 --- a/Editor/AnimatorParsers/AnimatorParser.cs +++ b/Editor/AnimatorParsers/AnimatorParser.cs @@ -242,84 +242,6 @@ void MergeLayer(VRCAvatarDescriptor.AnimLayerType type, bool alwaysApplied, floa // TPose and IKPose should only affect to Humanoid so skip here~ - switch (descriptor.lipSync) - { - // AvatarDescriptorから収集 - case VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape when descriptor.VisemeSkinnedMesh != null: - { - var skinnedMeshRenderer = descriptor.VisemeSkinnedMesh; - var updater = modificationsContainer.ModifyObject(skinnedMeshRenderer); - foreach (var blendShape in descriptor.VisemeBlendShapes) - updater.AddModificationAsNewLayer($"blendShape.{blendShape}", AnimationFloatProperty.Variable(descriptor)); - break; - } - case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape when descriptor.VisemeSkinnedMesh != null: - { - var skinnedMeshRenderer = descriptor.VisemeSkinnedMesh; - var shape = descriptor.MouthOpenBlendShapeName; - - modificationsContainer.ModifyObject(skinnedMeshRenderer) - .AddModificationAsNewLayer($"blendShape.{shape}", AnimationFloatProperty.Variable(descriptor)); - break; - } - } - - if (descriptor.enableEyeLook) - { - var leftEye = descriptor.customEyeLookSettings.leftEye; - var rightEye = descriptor.customEyeLookSettings.rightEye; - if (leftEye) - { - var updater = modificationsContainer.ModifyObject(leftEye); - foreach (var prop in TransformRotationAnimationKeys) - updater.AddModificationAsNewLayer(prop, AnimationFloatProperty.Variable(descriptor)); - } - - - if (rightEye) - { - var updater = modificationsContainer.ModifyObject(rightEye); - foreach (var prop in TransformRotationAnimationKeys) - updater.AddModificationAsNewLayer(prop, AnimationFloatProperty.Variable(descriptor)); - } - - switch (descriptor.customEyeLookSettings.eyelidType) - { - case VRCAvatarDescriptor.EyelidType.None: - break; - case VRCAvatarDescriptor.EyelidType.Bones: - { - foreach (var eyelids in new[] - { - descriptor.customEyeLookSettings.lowerLeftEyelid, - descriptor.customEyeLookSettings.upperLeftEyelid, - descriptor.customEyeLookSettings.lowerRightEyelid, - descriptor.customEyeLookSettings.upperRightEyelid, - }) - { - var updater = modificationsContainer.ModifyObject(eyelids); - foreach (var prop in TransformRotationAnimationKeys) - updater.AddModificationAsNewLayer(prop, AnimationFloatProperty.Variable(descriptor)); - } - } - break; - case VRCAvatarDescriptor.EyelidType.Blendshapes - when descriptor.customEyeLookSettings.eyelidsSkinnedMesh != null: - { - var skinnedMeshRenderer = descriptor.customEyeLookSettings.eyelidsSkinnedMesh; - var mesh = skinnedMeshRenderer.sharedMesh; - - var updater = modificationsContainer.ModifyObject(skinnedMeshRenderer); - - foreach (var blendShape in from index in descriptor.customEyeLookSettings.eyelidsBlendshapes - where 0 <= index && index < mesh.blendShapeCount - select mesh.GetBlendShapeName(index)) - updater.AddModificationAsNewLayer($"blendShape.{blendShape}", AnimationFloatProperty.Variable(descriptor)); - } - break; - } - } - var bodySkinnedMesh = descriptor.transform.Find("Body")?.GetComponent(); if (mmdWorldCompatibility && bodySkinnedMesh) From ab83dc2c7185138590bb0715e87155ecbc41d43f Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 20:56:54 +0900 Subject: [PATCH 3/7] refactor: apply special mapping in ComponentInformation --- API-Editor/ComponentInformation.cs | 39 +++++++- API-Editor/Internal/InternalParts.cs | 1 + Editor/APIInternal/ComponentInfos.VRCSDK.cs | 95 ++++++++++++++++++++ Editor/ObjectMapping/ObjectMappingContext.cs | 77 +++++++--------- Localization/en.po | 3 + Localization/ja.po | 3 + 6 files changed, 174 insertions(+), 44 deletions(-) diff --git a/API-Editor/ComponentInformation.cs b/API-Editor/ComponentInformation.cs index 94baa753f..376989afd 100644 --- a/API-Editor/ComponentInformation.cs +++ b/API-Editor/ComponentInformation.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using JetBrains.Annotations; using UnityEngine; +using Object = UnityEngine.Object; namespace Anatawa12.AvatarOptimizer.API { @@ -47,6 +48,9 @@ internal sealed override void CollectMutationsInternal(Component component, ComponentMutationsCollector collector) => CollectMutations((TComponent)component, collector); + internal override void ApplySpecialMappingInternal(Component component, MappingSource collector) => + ApplySpecialMapping((TComponent)component, collector); + /// /// Collects runtime mutations by . /// You have to call . @@ -68,7 +72,17 @@ internal sealed override void CollectMutationsInternal(Component component, protected virtual void CollectMutations(TComponent component, ComponentMutationsCollector collector) { } - + + /// + /// Applies some property mapping to your component. + /// For object replacements, AAO processes automatically so you don't have to implement that in this method. + /// + /// The component. + /// The mapping source + internal virtual void ApplySpecialMapping(TComponent component, MappingSource mappingSource) + { + } + // Important note for future AAO developer // 1. You MUST NOT add abstract method onto this class // 2. When you added some virtual methods onto this class, implementer MAY NOT implement that method @@ -199,4 +213,27 @@ internal ComponentMutationsCollector() public void ModifyProperties([NotNull] Component component, [NotNull] string[] properties) => ModifyProperties(component, (IEnumerable) properties); } + + internal abstract class MappingSource + { + internal MappingSource() + { + } + + public abstract MappedComponentInfo GetMappedComponent(T component) where T : Component; + public abstract MappedComponentInfo GetMappedGameObject(GameObject component); + } + + internal abstract class MappedComponentInfo where T : Object + { + public abstract T MappedComponent { get; } + + /// + /// Returns false if the property is removed. + /// + /// + /// + /// + public abstract bool TryMapFloatProperty(string property, out (Object component, string property) found); + } } diff --git a/API-Editor/Internal/InternalParts.cs b/API-Editor/Internal/InternalParts.cs index 8037d1505..85abcd15a 100644 --- a/API-Editor/Internal/InternalParts.cs +++ b/API-Editor/Internal/InternalParts.cs @@ -27,5 +27,6 @@ internal ComponentInformation() internal abstract void CollectDependencyInternal(Component component, ComponentDependencyCollector collector); internal abstract void CollectMutationsInternal(Component component, ComponentMutationsCollector collector); + internal abstract void ApplySpecialMappingInternal(Component component, MappingSource mappingSource); } } \ No newline at end of file diff --git a/Editor/APIInternal/ComponentInfos.VRCSDK.cs b/Editor/APIInternal/ComponentInfos.VRCSDK.cs index ea7c84088..ed6a0749a 100644 --- a/Editor/APIInternal/ComponentInfos.VRCSDK.cs +++ b/Editor/APIInternal/ComponentInfos.VRCSDK.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using Anatawa12.AvatarOptimizer.API; +using Anatawa12.AvatarOptimizer.ErrorReporting; +using JetBrains.Annotations; using UnityEngine; using VRC.SDK3; using VRC.Core; @@ -70,6 +72,62 @@ protected override void CollectMutations(T component, ComponentMutationsCollecto throw new ArgumentOutOfRangeException(); } } + + internal override void ApplySpecialMapping(T component, MappingSource mappingSource) + { + base.ApplySpecialMapping(component, mappingSource); + + switch (component.lipSync) + { + case VRC_AvatarDescriptor.LipSyncStyle.Default: + // TODO + break; + case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone: + break; + case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape when component.VisemeSkinnedMesh != null: + { + var info = mappingSource.GetMappedComponent(component.VisemeSkinnedMesh); + if (info.TryMapFloatProperty($"blendShape.{component.MouthOpenBlendShapeName}", out var mapped)) + { + component.VisemeSkinnedMesh = mapped.Item1 as SkinnedMeshRenderer; + component.MouthOpenBlendShapeName = ParseBlendShapeProperty(mapped.Item2); + } + else + { + component.VisemeSkinnedMesh = null; + } + break; + } + case VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape when component.VisemeSkinnedMesh != null: + { + var info = mappingSource.GetMappedComponent(component.VisemeSkinnedMesh); + component.VisemeSkinnedMesh = info.MappedComponent; + var removed = false; + foreach (ref var shapeName in component.VisemeBlendShapes.AsSpan()) + { + if (info.TryMapFloatProperty($"blendShape.{shapeName}", out var mapped) + && mapped.component == info.MappedComponent) + shapeName = ParseBlendShapeProperty(mapped.property); + else + removed = true; + } + if (removed) + BuildReport.LogFatal("ApplyObjectMapping:VRCAvatarDescriptor:viseme BlendShape Removed"); + break; + } + case VRC_AvatarDescriptor.LipSyncStyle.VisemeParameterOnly: + // NOP + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + [NotNull] + private static string ParseBlendShapeProperty(string prop) => + prop.StartsWith("blendShape.", StringComparison.Ordinal) + ? prop.Substring("blendShape.".Length) + : throw new Exception("invalid blendShape property"); } [ComponentInformation(typeof(VRCAvatarDescriptor))] @@ -155,6 +213,43 @@ from index in component.customEyeLookSettings.eyelidsBlendshapes } } } + + internal override void ApplySpecialMapping(VRCAvatarDescriptor component, MappingSource mappingSource) + { + base.ApplySpecialMapping(component, mappingSource); + + if (component.enableEyeLook) + { + switch (component.customEyeLookSettings.eyelidType) + { + case VRCAvatarDescriptor.EyelidType.None: + break; + case VRCAvatarDescriptor.EyelidType.Bones: + break; + case VRCAvatarDescriptor.EyelidType.Blendshapes + when component.customEyeLookSettings.eyelidsSkinnedMesh != null: + { + var info = mappingSource.GetMappedComponent(component.customEyeLookSettings.eyelidsSkinnedMesh); + component.customEyeLookSettings.eyelidsSkinnedMesh = info.MappedComponent; + var removed = false; + foreach (ref var eyelidsBlendshape in component.customEyeLookSettings.eyelidsBlendshapes.AsSpan()) + { + if (info.TryMapFloatProperty(VProp.BlendShapeIndex(eyelidsBlendshape), out var mapped) + && mapped.component == info.MappedComponent) + eyelidsBlendshape = VProp.ParseBlendShapeIndex(mapped.property); + else + removed = true; + } + + if (removed) + BuildReport.LogFatal("ApplyObjectMapping:VRCAvatarDescriptor:eyelids BlendShape Removed"); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } } [ComponentInformation(typeof(VRC.SDKBase.VRCStation))] diff --git a/Editor/ObjectMapping/ObjectMappingContext.cs b/Editor/ObjectMapping/ObjectMappingContext.cs index 065f90f3a..ee0bbca4b 100644 --- a/Editor/ObjectMapping/ObjectMappingContext.cs +++ b/Editor/ObjectMapping/ObjectMappingContext.cs @@ -1,16 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using Anatawa12.AvatarOptimizer.API; +using Anatawa12.AvatarOptimizer.APIInternal; using Anatawa12.AvatarOptimizer.ErrorReporting; using nadena.dev.ndmf; using UnityEditor; using UnityEditor.Animations; using UnityEngine; - -#if AAO_VRCSDK3_AVATARS -using VRC.SDK3.Avatars.Components; -#endif - using Object = UnityEngine.Object; namespace Anatawa12.AvatarOptimizer @@ -27,14 +24,20 @@ public void OnActivate(BuildContext context) public void OnDeactivate(BuildContext context) { var mapping = MappingBuilder.BuildObjectMapping(); + var mappingSource = new MappingSourceImpl(mapping); // replace all objects BuildReport.ReportingObjects(context.GetComponents(), component => { if (component is Transform) return; + + // apply special mapping + if (ComponentInfoRegistry.TryGetInformation(component.GetType(), out var info)) + info.ApplySpecialMappingInternal(component, mappingSource); + var serialized = new SerializedObject(component); AnimatorControllerMapper mapper = null; - SpecialMappingApplier.Apply(component.GetType(), serialized, mapping, ref mapper); + foreach (var p in serialized.ObjectReferenceProperties()) { if (mapping.MapComponentInstance(p.objectReferenceInstanceIDValue, out var mappedComponent)) @@ -58,53 +61,41 @@ public void OnDeactivate(BuildContext context) } } - internal static class SpecialMappingApplier + class MappingSourceImpl : MappingSource { - public static void Apply(Type type, SerializedObject serialized, - ObjectMapping mapping, ref AnimatorControllerMapper mapper) + private readonly ObjectMapping _mapping; + + public MappingSourceImpl(ObjectMapping mapping) { -#if AAO_VRCSDK3_AVATARS - if (type.IsAssignableFrom(typeof(VRCAvatarDescriptor))) - VRCAvatarDescriptor(serialized, mapping, ref mapper); -#endif + _mapping = mapping; } - -#if AAO_VRCSDK3_AVATARS - // customEyeLookSettings.eyelidsBlendshapes is index - private static void VRCAvatarDescriptor(SerializedObject serialized, - ObjectMapping mapping, ref AnimatorControllerMapper mapper) - { - var eyelidsEnabled = serialized.FindProperty("enableEyeLook"); - var eyelidType = serialized.FindProperty("customEyeLookSettings.eyelidType"); - var eyelidsSkinnedMesh = serialized.FindProperty("customEyeLookSettings.eyelidsSkinnedMesh"); - var eyelidsBlendshapes = serialized.FindProperty("customEyeLookSettings.eyelidsBlendshapes"); - if (eyelidsEnabled.boolValue && - eyelidType.enumValueIndex == (int)VRC.SDK3.Avatars.Components.VRCAvatarDescriptor.EyelidType.Blendshapes) - { - var info = mapping.GetComponentMapping(eyelidsSkinnedMesh.objectReferenceInstanceIDValue); - if (info == null) return; + public override MappedComponentInfo GetMappedComponent(T component) => + new ComponentInfo(_mapping.GetComponentMapping(component.GetInstanceID())); - eyelidsSkinnedMesh.objectReferenceValue = EditorUtility.InstanceIDToObject(info.MergedInto); + public override MappedComponentInfo GetMappedGameObject(GameObject component) => + new ComponentInfo(_mapping.GetComponentMapping(component.GetInstanceID())); - for (var i = 0; i < eyelidsBlendshapes.arraySize; i++) + private class ComponentInfo : MappedComponentInfo where T : Object + { + private readonly ComponentInfo _info; + + public ComponentInfo(ComponentInfo info) => _info = info; + + public override T MappedComponent => EditorUtility.InstanceIDToObject(_info.MergedInto) as T; + public override bool TryMapFloatProperty(string property, out (Object component, string property) found) + { + found = default; + if (_info.PropertyMapping.TryGetValue(property, out var mappedProp)) { - var indexProp = eyelidsBlendshapes.GetArrayElementAtIndex(i); - if (info.PropertyMapping.TryGetValue( - VProp.BlendShapeIndex(indexProp.intValue), - out var mappedProp)) - { - if (mappedProp.MappedProperty.Name == null) - { - BuildReport.LogFatal("ApplyObjectMapping:VRCAvatarDescriptor:eyelids BlendShape Removed"); - return; - } - indexProp.intValue = VProp.ParseBlendShapeIndex(mappedProp.MappedProperty.Name); - } + if (mappedProp.MappedProperty.Name == null) + return false; + found = (EditorUtility.InstanceIDToObject(mappedProp.MappedProperty.InstanceId), + mappedProp.MappedProperty.Name); } + throw new NotImplementedException(); } } -#endif } internal class AnimatorControllerMapper diff --git a/Localization/en.po b/Localization/en.po index 024272ef9..23a0b4339 100644 --- a/Localization/en.po +++ b/Localization/en.po @@ -438,6 +438,9 @@ msgstr "Prevents removing end bones whose parent is not removed." msgid "ApplyObjectMapping:VRCAvatarDescriptor:eyelids BlendShape Removed" msgstr "BlendShape(s) for eyelids are Removed / frozen." +msgid "ApplyObjectMapping:VRCAvatarDescriptor:viseme BlendShape Removed" +msgstr "BlendShape(s) for viseme are Removed / frozen." + #endregion #region MeshInfo2 diff --git a/Localization/ja.po b/Localization/ja.po index 28ef3b505..a8bb2af39 100644 --- a/Localization/ja.po +++ b/Localization/ja.po @@ -374,6 +374,9 @@ msgstr "親が削除されていないendボーンを削除しないようにし msgid "ApplyObjectMapping:VRCAvatarDescriptor:eyelids BlendShape Removed" msgstr "瞬き用のBlendShapeが削除・固定されています" +msgid "ApplyObjectMapping:VRCAvatarDescriptor:viseme BlendShape Removed" +msgstr "リップシンク用のBlendShapeが削除・固定されています" + #endregion #region MeshInfo2 From fdba176bc7306d5db6e3c7811499e2331ae173d9 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 21:01:54 +0900 Subject: [PATCH 4/7] fix: error with unmapped component(s) --- Editor/ObjectMapping/ObjectMappingContext.cs | 30 +++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Editor/ObjectMapping/ObjectMappingContext.cs b/Editor/ObjectMapping/ObjectMappingContext.cs index ee0bbca4b..cc94888f6 100644 --- a/Editor/ObjectMapping/ObjectMappingContext.cs +++ b/Editor/ObjectMapping/ObjectMappingContext.cs @@ -4,6 +4,7 @@ using Anatawa12.AvatarOptimizer.API; using Anatawa12.AvatarOptimizer.APIInternal; using Anatawa12.AvatarOptimizer.ErrorReporting; +using JetBrains.Annotations; using nadena.dev.ndmf; using UnityEditor; using UnityEditor.Animations; @@ -70,17 +71,38 @@ public MappingSourceImpl(ObjectMapping mapping) _mapping = mapping; } + private MappedComponentInfo GetMappedInternal(T component) where T : Object + { + var componentInfo = _mapping.GetComponentMapping(component.GetInstanceID()); + if (componentInfo == null) return new OriginalComponentInfo(component); + return new ComponentInfo(componentInfo); + } + public override MappedComponentInfo GetMappedComponent(T component) => - new ComponentInfo(_mapping.GetComponentMapping(component.GetInstanceID())); + GetMappedInternal(component); public override MappedComponentInfo GetMappedGameObject(GameObject component) => - new ComponentInfo(_mapping.GetComponentMapping(component.GetInstanceID())); + GetMappedInternal(component); + + private class OriginalComponentInfo : MappedComponentInfo where T : Object + { + private readonly T _component; + + public OriginalComponentInfo(T component) => _component = component; + + public override T MappedComponent => _component; + public override bool TryMapFloatProperty(string property, out (Object component, string property) found) + { + found = (_component, property); + return true; + } + } private class ComponentInfo : MappedComponentInfo where T : Object { - private readonly ComponentInfo _info; + [NotNull] private readonly ComponentInfo _info; - public ComponentInfo(ComponentInfo info) => _info = info; + public ComponentInfo([NotNull] ComponentInfo info) => _info = info; public override T MappedComponent => EditorUtility.InstanceIDToObject(_info.MergedInto) as T; public override bool TryMapFloatProperty(string property, out (Object component, string property) found) From 389e4b914a387bc3f277c564d77044264b9cdaaf Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 21:16:34 +0900 Subject: [PATCH 5/7] chore: refine API --- API-Editor/ComponentInformation.cs | 32 +++++++++++++++++--- Editor/APIInternal/ComponentInfos.VRCSDK.cs | 6 ++-- Editor/ObjectMapping/ObjectMappingContext.cs | 23 +++++++------- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/API-Editor/ComponentInformation.cs b/API-Editor/ComponentInformation.cs index 376989afd..62c055aa8 100644 --- a/API-Editor/ComponentInformation.cs +++ b/API-Editor/ComponentInformation.cs @@ -226,14 +226,36 @@ internal MappingSource() internal abstract class MappedComponentInfo where T : Object { + /// + /// The mapped component (or GameObject). + /// The component may be removed without mapped component. + /// If there are not mapped component, this will be null. + /// + /// Even if the component is removed without mapped component, + /// each animation property can be mapped to another component. + /// public abstract T MappedComponent { get; } /// - /// Returns false if the property is removed. + /// Maps animation property name to component and MappedPropertyInfo. + /// If the property is not removed, returns true and is set. + /// If the property is removed, returns false and will be default. /// - /// - /// - /// - public abstract bool TryMapFloatProperty(string property, out (Object component, string property) found); + /// The name of property will be mapped + /// The result parameter + /// Whether if the property is successfully mapped or removed + public abstract bool TryMapProperty(string property, out MappedPropertyInfo found); + } + + internal readonly struct MappedPropertyInfo + { + public Object Component { get; } + public string Property { get; } + + internal MappedPropertyInfo(Object component, string property) + { + Component = component; + Property = property; + } } } diff --git a/Editor/APIInternal/ComponentInfos.VRCSDK.cs b/Editor/APIInternal/ComponentInfos.VRCSDK.cs index ed6a0749a..a56f1494f 100644 --- a/Editor/APIInternal/ComponentInfos.VRCSDK.cs +++ b/Editor/APIInternal/ComponentInfos.VRCSDK.cs @@ -87,7 +87,7 @@ internal override void ApplySpecialMapping(T component, MappingSource mappingSou case VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape when component.VisemeSkinnedMesh != null: { var info = mappingSource.GetMappedComponent(component.VisemeSkinnedMesh); - if (info.TryMapFloatProperty($"blendShape.{component.MouthOpenBlendShapeName}", out var mapped)) + if (info.TryMapProperty($"blendShape.{component.MouthOpenBlendShapeName}", out var mapped)) { component.VisemeSkinnedMesh = mapped.Item1 as SkinnedMeshRenderer; component.MouthOpenBlendShapeName = ParseBlendShapeProperty(mapped.Item2); @@ -105,7 +105,7 @@ internal override void ApplySpecialMapping(T component, MappingSource mappingSou var removed = false; foreach (ref var shapeName in component.VisemeBlendShapes.AsSpan()) { - if (info.TryMapFloatProperty($"blendShape.{shapeName}", out var mapped) + if (info.TryMapProperty($"blendShape.{shapeName}", out var mapped) && mapped.component == info.MappedComponent) shapeName = ParseBlendShapeProperty(mapped.property); else @@ -234,7 +234,7 @@ internal override void ApplySpecialMapping(VRCAvatarDescriptor component, Mappin var removed = false; foreach (ref var eyelidsBlendshape in component.customEyeLookSettings.eyelidsBlendshapes.AsSpan()) { - if (info.TryMapFloatProperty(VProp.BlendShapeIndex(eyelidsBlendshape), out var mapped) + if (info.TryMapProperty(VProp.BlendShapeIndex(eyelidsBlendshape), out var mapped) && mapped.component == info.MappedComponent) eyelidsBlendshape = VProp.ParseBlendShapeIndex(mapped.property); else diff --git a/Editor/ObjectMapping/ObjectMappingContext.cs b/Editor/ObjectMapping/ObjectMappingContext.cs index cc94888f6..b5d0736ba 100644 --- a/Editor/ObjectMapping/ObjectMappingContext.cs +++ b/Editor/ObjectMapping/ObjectMappingContext.cs @@ -91,9 +91,9 @@ private class OriginalComponentInfo : MappedComponentInfo where T : Object public OriginalComponentInfo(T component) => _component = component; public override T MappedComponent => _component; - public override bool TryMapFloatProperty(string property, out (Object component, string property) found) + public override bool TryMapProperty(string property, out API.MappedPropertyInfo found) { - found = (_component, property); + found = new API.MappedPropertyInfo(_component, property); return true; } } @@ -105,17 +105,18 @@ private class ComponentInfo : MappedComponentInfo where T : Object public ComponentInfo([NotNull] ComponentInfo info) => _info = info; public override T MappedComponent => EditorUtility.InstanceIDToObject(_info.MergedInto) as T; - public override bool TryMapFloatProperty(string property, out (Object component, string property) found) + public override bool TryMapProperty(string property, out API.MappedPropertyInfo found) { found = default; - if (_info.PropertyMapping.TryGetValue(property, out var mappedProp)) - { - if (mappedProp.MappedProperty.Name == null) - return false; - found = (EditorUtility.InstanceIDToObject(mappedProp.MappedProperty.InstanceId), - mappedProp.MappedProperty.Name); - } - throw new NotImplementedException(); + + if (!_info.PropertyMapping.TryGetValue(property, out var mappedProp)) return false; + if (mappedProp.MappedProperty.Name == null) return false; + + found = new API.MappedPropertyInfo( + EditorUtility.InstanceIDToObject(mappedProp.MappedProperty.InstanceId), + mappedProp.MappedProperty.Name); + return true; + } } } From ea17c83a9dabd4874ae054ab917425f35e4cb9f7 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 21:18:17 +0900 Subject: [PATCH 6/7] chore: make Public --- API-Editor/ComponentInformation.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/API-Editor/ComponentInformation.cs b/API-Editor/ComponentInformation.cs index 62c055aa8..3b1f2ad99 100644 --- a/API-Editor/ComponentInformation.cs +++ b/API-Editor/ComponentInformation.cs @@ -79,7 +79,8 @@ protected virtual void CollectMutations(TComponent component, ComponentMutations /// /// The component. /// The mapping source - internal virtual void ApplySpecialMapping(TComponent component, MappingSource mappingSource) + [PublicAPI] + protected virtual void ApplySpecialMapping(TComponent component, MappingSource mappingSource) { } @@ -214,18 +215,25 @@ public void ModifyProperties([NotNull] Component component, [NotNull] string[] p ModifyProperties(component, (IEnumerable) properties); } - internal abstract class MappingSource + public abstract class MappingSource { internal MappingSource() { } + [PublicAPI] public abstract MappedComponentInfo GetMappedComponent(T component) where T : Component; + + [PublicAPI] public abstract MappedComponentInfo GetMappedGameObject(GameObject component); } - internal abstract class MappedComponentInfo where T : Object + public abstract class MappedComponentInfo where T : Object { + internal MappedComponentInfo() + { + } + /// /// The mapped component (or GameObject). /// The component may be removed without mapped component. @@ -234,6 +242,7 @@ internal abstract class MappedComponentInfo where T : Object /// Even if the component is removed without mapped component, /// each animation property can be mapped to another component. /// + [PublicAPI] public abstract T MappedComponent { get; } /// @@ -244,12 +253,16 @@ internal abstract class MappedComponentInfo where T : Object /// The name of property will be mapped /// The result parameter /// Whether if the property is successfully mapped or removed + [PublicAPI] public abstract bool TryMapProperty(string property, out MappedPropertyInfo found); } - internal readonly struct MappedPropertyInfo + public readonly struct MappedPropertyInfo { + [PublicAPI] public Object Component { get; } + + [PublicAPI] public string Property { get; } internal MappedPropertyInfo(Object component, string property) From abe369b0bd32346ecdf4f17c3d55e3cfdd4d086d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 31 Oct 2023 21:19:29 +0900 Subject: [PATCH 7/7] fix: CE --- Editor/APIInternal/ComponentInfos.VRCSDK.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Editor/APIInternal/ComponentInfos.VRCSDK.cs b/Editor/APIInternal/ComponentInfos.VRCSDK.cs index a56f1494f..85a1c5f27 100644 --- a/Editor/APIInternal/ComponentInfos.VRCSDK.cs +++ b/Editor/APIInternal/ComponentInfos.VRCSDK.cs @@ -73,7 +73,7 @@ protected override void CollectMutations(T component, ComponentMutationsCollecto } } - internal override void ApplySpecialMapping(T component, MappingSource mappingSource) + protected override void ApplySpecialMapping(T component, MappingSource mappingSource) { base.ApplySpecialMapping(component, mappingSource); @@ -89,8 +89,8 @@ internal override void ApplySpecialMapping(T component, MappingSource mappingSou var info = mappingSource.GetMappedComponent(component.VisemeSkinnedMesh); if (info.TryMapProperty($"blendShape.{component.MouthOpenBlendShapeName}", out var mapped)) { - component.VisemeSkinnedMesh = mapped.Item1 as SkinnedMeshRenderer; - component.MouthOpenBlendShapeName = ParseBlendShapeProperty(mapped.Item2); + component.VisemeSkinnedMesh = mapped.Component as SkinnedMeshRenderer; + component.MouthOpenBlendShapeName = ParseBlendShapeProperty(mapped.Property); } else { @@ -106,8 +106,8 @@ internal override void ApplySpecialMapping(T component, MappingSource mappingSou foreach (ref var shapeName in component.VisemeBlendShapes.AsSpan()) { if (info.TryMapProperty($"blendShape.{shapeName}", out var mapped) - && mapped.component == info.MappedComponent) - shapeName = ParseBlendShapeProperty(mapped.property); + && mapped.Component == info.MappedComponent) + shapeName = ParseBlendShapeProperty(mapped.Property); else removed = true; } @@ -214,7 +214,7 @@ from index in component.customEyeLookSettings.eyelidsBlendshapes } } - internal override void ApplySpecialMapping(VRCAvatarDescriptor component, MappingSource mappingSource) + protected override void ApplySpecialMapping(VRCAvatarDescriptor component, MappingSource mappingSource) { base.ApplySpecialMapping(component, mappingSource); @@ -235,8 +235,8 @@ internal override void ApplySpecialMapping(VRCAvatarDescriptor component, Mappin foreach (ref var eyelidsBlendshape in component.customEyeLookSettings.eyelidsBlendshapes.AsSpan()) { if (info.TryMapProperty(VProp.BlendShapeIndex(eyelidsBlendshape), out var mapped) - && mapped.component == info.MappedComponent) - eyelidsBlendshape = VProp.ParseBlendShapeIndex(mapped.property); + && mapped.Component == info.MappedComponent) + eyelidsBlendshape = VProp.ParseBlendShapeIndex(mapped.Property); else removed = true; }