From 4422fe6970df75628bb8513be6a46982b18dfb72 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 4 Sep 2024 20:21:19 +0900 Subject: [PATCH 01/50] feat: initial commit for fix yaw / pitch --- .../.MergePhysBoneEditorModificationUtils.ts | 9 -- Editor/Inspector/MergePhysBoneEditor.cs | 97 ++++++++++++++----- .../MergePhysBoneEditorModificationUtils.cs | 55 +++++++++++ ...ysBoneEditorModificationUtils.generated.cs | 45 --------- Editor/Processors/MergePhysBoneProcessor.cs | 71 ++++++++++---- Localization/en-us.po | 3 + Runtime/MergePhysBone.cs | 10 +- 7 files changed, 191 insertions(+), 99 deletions(-) diff --git a/Editor/.MergePhysBoneEditorModificationUtils.ts b/Editor/.MergePhysBoneEditorModificationUtils.ts index 8174d252f..120a5a5c2 100644 --- a/Editor/.MergePhysBoneEditorModificationUtils.ts +++ b/Editor/.MergePhysBoneEditorModificationUtils.ts @@ -14,15 +14,6 @@ const config: Config = { ['Curve', 'curve'], ], }, - CurveVector3ConfigProp: { - base: "OverridePropBase", - values: [ - ['Value', 'value'], - ['CurveX', 'curveX'], - ['CurveY', 'curveY'], - ['CurveZ', "curveZ"] - ], - }, PermissionConfigProp: { base: "OverridePropBase", values: [ diff --git a/Editor/Inspector/MergePhysBoneEditor.cs b/Editor/Inspector/MergePhysBoneEditor.cs index e41b187df..30f048d06 100644 --- a/Editor/Inspector/MergePhysBoneEditor.cs +++ b/Editor/Inspector/MergePhysBoneEditor.cs @@ -307,14 +307,58 @@ protected override void Pb3DCurveProp(string label, string pbXCurveLabel, string pbYCurveLabel, string pbZCurveLabel, CurveVector3ConfigProp prop, bool forceOverride = false) { - PbPropImpl(label, prop, forceOverride, (rect, merged, labelContent) => + var (rect, overrideRect) = SplitRect(EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight), OverrideWidth); + + switch (prop.GetOverride(forceOverride)) { - var (valueRect, buttonRect) = SplitRect(rect, CurveButtonWidth); + case MergePhysBone.CurveVector3Config.CurveOverride.Copy: + { + var valueProp = prop.SourceValue!; + var xCurveProp = prop.SourceCurveX!; + var yCurveProp = prop.SourceCurveY!; + var zCurveProp = prop.SourceCurveZ!; - var valueProp = prop.GetValueProperty(merged); - var xCurveProp = prop.GetCurveXProperty(merged); - var yCurveProp = prop.GetCurveYProperty(merged); - var zCurveProp = prop.GetCurveZProperty(merged); + EditorGUI.BeginDisabledGroup(true); + DrawProperties(rect, new GUIContent(label), valueProp, xCurveProp, yCurveProp, zCurveProp); + EditorGUI.EndDisabledGroup(); + + if (valueProp.hasMultipleDifferentValues + || xCurveProp.hasMultipleDifferentValues + || yCurveProp.hasMultipleDifferentValues + || zCurveProp.hasMultipleDifferentValues) + { + EditorGUILayout.HelpBox(AAOL10N.Tr("MergePhysBone:error:differValueSingle"), MessageType.Error); + } + } + break; + case MergePhysBone.CurveVector3Config.CurveOverride.Override: + { + var valueProp = prop.OverrideValue; + var xCurveProp = prop.OverrideCurveX; + var yCurveProp = prop.OverrideCurveY; + var zCurveProp = prop.OverrideCurveZ; + + DrawProperties(rect, new GUIContent(label), valueProp, xCurveProp, yCurveProp, zCurveProp); + } + break; + case MergePhysBone.CurveVector3Config.CurveOverride.Fix: + { + EditorGUI.LabelField(rect, label, AAOL10N.Tr("MergePhysBone:message:fix-yaw-pitch")); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + + EditorGUI.BeginProperty(overrideRect, null, prop.OverrideProperty); + var selected = PopupNoIndent(overrideRect, prop.OverrideProperty.enumValueIndex, prop.OverrideProperty.enumDisplayNames); + if (selected != prop.OverrideProperty.enumValueIndex) + prop.OverrideProperty.enumValueIndex = selected; + EditorGUI.EndProperty(); + + void DrawProperties(Rect rect, GUIContent labelContent, SerializedProperty valueProp, SerializedProperty xCurveProp, SerializedProperty yCurveProp, SerializedProperty zCurveProp) + { + var (valueRect, buttonRect) = SplitRect(rect, CurveButtonWidth); void DrawCurve(string curveLabel, SerializedProperty curveProp) { @@ -337,7 +381,7 @@ void DrawCurve(string curveLabel, SerializedProperty curveProp) { // without curve: constant EditorGUI.PropertyField(valueRect, valueProp, labelContent); - + if (GUI.Button(buttonRect, "C")) { var curve = new AnimationCurve(); @@ -348,12 +392,7 @@ void DrawCurve(string curveLabel, SerializedProperty curveProp) zCurveProp.animationCurveValue = curve; } } - - return valueProp.hasMultipleDifferentValues - || xCurveProp.hasMultipleDifferentValues - || yCurveProp.hasMultipleDifferentValues - || zCurveProp.hasMultipleDifferentValues; - }); + } } private static readonly string[] CopyOverride = { "C:Copy", "O:Override" }; @@ -646,17 +685,27 @@ protected override void Pb3DCurveProp(string label, string pbXCurveLabel, string pbYCurveLabel, string pbZCurveLabel, CurveVector3ConfigProp prop, bool forceOverride = false) { - if (forceOverride || prop.IsOverride) return; - - if (prop.SourceValue!.hasMultipleDifferentValues - || prop.SourceCurveX!.hasMultipleDifferentValues - || prop.SourceCurveY!.hasMultipleDifferentValues - || prop.SourceCurveZ!.hasMultipleDifferentValues) - _differProps.Add(label); - - _usingCopyCurve |= prop.GetCurveXProperty(false).animationCurveValue.length > 0; - _usingCopyCurve |= prop.GetCurveYProperty(false).animationCurveValue.length > 0; - _usingCopyCurve |= prop.GetCurveZProperty(false).animationCurveValue.length > 0; + switch (prop.GetOverride(forceOverride)) + { + case MergePhysBone.CurveVector3Config.CurveOverride.Copy: + if (prop.SourceValue!.hasMultipleDifferentValues + || prop.SourceCurveX!.hasMultipleDifferentValues + || prop.SourceCurveY!.hasMultipleDifferentValues + || prop.SourceCurveZ!.hasMultipleDifferentValues) + _differProps.Add(label); + + _usingCopyCurve |= prop.SourceCurveX!.animationCurveValue.length > 0; + _usingCopyCurve |= prop.SourceCurveY!.animationCurveValue.length > 0; + _usingCopyCurve |= prop.SourceCurveZ!.animationCurveValue.length > 0; + break; + case MergePhysBone.CurveVector3Config.CurveOverride.Override: + break; + case MergePhysBone.CurveVector3Config.CurveOverride.Fix: + // more validation about skew scaling and rotation animation on build + break; + default: + throw new ArgumentOutOfRangeException(); + } } protected override void PbPermissionProp(string label, PermissionConfigProp prop, bool forceOverride = false) diff --git a/Editor/MergePhysBoneEditorModificationUtils.cs b/Editor/MergePhysBoneEditorModificationUtils.cs index 2d4b74c1f..4beeccf4f 100644 --- a/Editor/MergePhysBoneEditorModificationUtils.cs +++ b/Editor/MergePhysBoneEditorModificationUtils.cs @@ -377,6 +377,61 @@ internal override void UpdateSource(SerializedObject sourcePb) PhysBoneValue = sourcePb.FindProperty(PhysBoneValueName); } } + + // Very Special Case + protected partial class CurveVector3ConfigProp : PropBase + { + public readonly SerializedProperty OverrideProperty; + public readonly SerializedProperty OverrideValue; + public SerializedProperty? SourceValue { get; private set; } + public readonly string PhysBoneValueName; + public readonly SerializedProperty OverrideCurveX; + public SerializedProperty? SourceCurveX { get; private set; } + public readonly string PhysBoneCurveXName; + public readonly SerializedProperty OverrideCurveY; + public SerializedProperty? SourceCurveY { get; private set; } + public readonly string PhysBoneCurveYName; + public readonly SerializedProperty OverrideCurveZ; + public SerializedProperty? SourceCurveZ { get; private set; } + public readonly string PhysBoneCurveZName; + + public CurveVector3ConfigProp( + SerializedProperty rootProperty + , string physBoneValueName + , string physBoneCurveXName + , string physBoneCurveYName + , string physBoneCurveZName + ) : base(rootProperty) + { + OverrideProperty = rootProperty.FindPropertyRelative("override"); + OverrideValue = rootProperty.FindPropertyRelative("value"); + PhysBoneValueName = physBoneValueName; + OverrideCurveX = rootProperty.FindPropertyRelative("curveX"); + PhysBoneCurveXName = physBoneCurveXName; + OverrideCurveY = rootProperty.FindPropertyRelative("curveY"); + PhysBoneCurveYName = physBoneCurveYName; + OverrideCurveZ = rootProperty.FindPropertyRelative("curveZ"); + PhysBoneCurveZName = physBoneCurveZName; + } + + internal override void UpdateSource(SerializedObject sourcePb) + { + SourceValue = sourcePb.FindProperty(PhysBoneValueName); + SourceCurveX = sourcePb.FindProperty(PhysBoneCurveXName); + SourceCurveY = sourcePb.FindProperty(PhysBoneCurveYName); + SourceCurveZ = sourcePb.FindProperty(PhysBoneCurveZName); + } + + public MergePhysBone.CurveVector3Config.CurveOverride GetOverride(bool forceOverride) => + forceOverride + ? MergePhysBone.CurveVector3Config.CurveOverride.Override + : (MergePhysBone.CurveVector3Config.CurveOverride)OverrideProperty.enumValueIndex; + + public SerializedProperty GetValueProperty(bool @override) => @override ? OverrideValue : SourceValue!; + public SerializedProperty GetCurveXProperty(bool @override) => @override ? OverrideCurveX : SourceCurveX!; + public SerializedProperty GetCurveYProperty(bool @override) => @override ? OverrideCurveY : SourceCurveY!; + public SerializedProperty GetCurveZProperty(bool @override) => @override ? OverrideCurveZ : SourceCurveZ!; + } } } diff --git a/Editor/MergePhysBoneEditorModificationUtils.generated.cs b/Editor/MergePhysBoneEditorModificationUtils.generated.cs index 082318e2c..227565b65 100644 --- a/Editor/MergePhysBoneEditorModificationUtils.generated.cs +++ b/Editor/MergePhysBoneEditorModificationUtils.generated.cs @@ -40,51 +40,6 @@ internal override void UpdateSource(SerializedObject sourcePb) public SerializedProperty GetValueProperty(bool @override) => @override ? OverrideValue : SourceValue!; public SerializedProperty GetCurveProperty(bool @override) => @override ? OverrideCurve : SourceCurve!; } - protected partial class CurveVector3ConfigProp : OverridePropBase - { - public readonly SerializedProperty OverrideValue; - public SerializedProperty? SourceValue { get; private set; } - public readonly string PhysBoneValueName; - public readonly SerializedProperty OverrideCurveX; - public SerializedProperty? SourceCurveX { get; private set; } - public readonly string PhysBoneCurveXName; - public readonly SerializedProperty OverrideCurveY; - public SerializedProperty? SourceCurveY { get; private set; } - public readonly string PhysBoneCurveYName; - public readonly SerializedProperty OverrideCurveZ; - public SerializedProperty? SourceCurveZ { get; private set; } - public readonly string PhysBoneCurveZName; - - public CurveVector3ConfigProp( - SerializedProperty rootProperty - , string physBoneValueName - , string physBoneCurveXName - , string physBoneCurveYName - , string physBoneCurveZName - ) : base(rootProperty) - { - OverrideValue = rootProperty.FindPropertyRelative("value"); - PhysBoneValueName = physBoneValueName; - OverrideCurveX = rootProperty.FindPropertyRelative("curveX"); - PhysBoneCurveXName = physBoneCurveXName; - OverrideCurveY = rootProperty.FindPropertyRelative("curveY"); - PhysBoneCurveYName = physBoneCurveYName; - OverrideCurveZ = rootProperty.FindPropertyRelative("curveZ"); - PhysBoneCurveZName = physBoneCurveZName; - } - - internal override void UpdateSource(SerializedObject sourcePb) - { - SourceValue = sourcePb.FindProperty(PhysBoneValueName); - SourceCurveX = sourcePb.FindProperty(PhysBoneCurveXName); - SourceCurveY = sourcePb.FindProperty(PhysBoneCurveYName); - SourceCurveZ = sourcePb.FindProperty(PhysBoneCurveZName); - } - public SerializedProperty GetValueProperty(bool @override) => @override ? OverrideValue : SourceValue!; - public SerializedProperty GetCurveXProperty(bool @override) => @override ? OverrideCurveX : SourceCurveX!; - public SerializedProperty GetCurveYProperty(bool @override) => @override ? OverrideCurveY : SourceCurveY!; - public SerializedProperty GetCurveZProperty(bool @override) => @override ? OverrideCurveZ : SourceCurveZ!; - } protected partial class PermissionConfigProp : OverridePropBase { public readonly SerializedProperty OverrideValue; diff --git a/Editor/Processors/MergePhysBoneProcessor.cs b/Editor/Processors/MergePhysBoneProcessor.cs index 8257cd4ad..8783e7baa 100644 --- a/Editor/Processors/MergePhysBoneProcessor.cs +++ b/Editor/Processors/MergePhysBoneProcessor.cs @@ -75,6 +75,11 @@ internal static void DoMerge(MergePhysBone merge, BuildContext? context) } } + // yaw / pitch fix + if (merge.limitRotationConfig.@override == MergePhysBone.CurveVector3Config.CurveOverride.Fix) + foreach (var physBone in sourceComponents) + FixYawPitch(physBone, context); + // clear endpoint position if (merge.endpointPositionConfig.@override == MergePhysBone.EndPointPositionConfig.Override.Clear) foreach (var physBone in sourceComponents) @@ -143,7 +148,21 @@ internal static void DoMerge(MergePhysBone merge, BuildContext? context) } } } - + + // To preserve bone reference, we keep original bone and create new GameObject for it. + // and later Trace and Object remove unused objects will merge original bones + public static void FixYawPitch(VRCPhysBoneBase physBone, BuildContext? context) + { + // Already fixed; nothing to do! + if (physBone.limitRotation.Equals(Vector3.zero)) return; + + + physBone.InitTransforms(true); + var maxChainLength = physBone.BoneChainLength(); + + throw new NotImplementedException(); + } + private static readonly string[] TransformRotationAndPositionAnimationKeys = { "m_LocalRotation.x", "m_LocalRotation.y", "m_LocalRotation.z", "m_LocalRotation.w", @@ -236,26 +255,38 @@ protected override void PbCurveProp(string label, CurveConfigProp prop, bool for protected override void Pb3DCurveProp(string label, string pbXCurveLabel, string pbYCurveLabel, string pbZCurveLabel, CurveVector3ConfigProp prop, bool forceOverride = false) { - var @override = forceOverride || prop.IsOverride; - _mergedPhysBone.FindProperty(prop.PhysBoneValueName).vector3Value = - prop.GetValueProperty(@override).vector3Value; - if (@override) - { - _mergedPhysBone.FindProperty(prop.PhysBoneCurveXName).animationCurveValue = - prop.GetCurveXProperty(@override).animationCurveValue; - _mergedPhysBone.FindProperty(prop.PhysBoneCurveYName).animationCurveValue = - prop.GetCurveYProperty(@override).animationCurveValue; - _mergedPhysBone.FindProperty(prop.PhysBoneCurveZName).animationCurveValue = - prop.GetCurveZProperty(@override).animationCurveValue; - } - else + switch (prop.GetOverride(forceOverride)) { - _mergedPhysBone.FindProperty(prop.PhysBoneCurveXName).animationCurveValue = - FixCurve(prop.GetCurveXProperty(@override).animationCurveValue); - _mergedPhysBone.FindProperty(prop.PhysBoneCurveYName).animationCurveValue = - FixCurve(prop.GetCurveYProperty(@override).animationCurveValue); - _mergedPhysBone.FindProperty(prop.PhysBoneCurveZName).animationCurveValue = - FixCurve(prop.GetCurveZProperty(@override).animationCurveValue); + case MergePhysBone.CurveVector3Config.CurveOverride.Copy: + _mergedPhysBone.FindProperty(prop.PhysBoneValueName).vector3Value = + prop.SourceValue!.vector3Value; + _mergedPhysBone.FindProperty(prop.PhysBoneCurveXName).animationCurveValue = + FixCurve(prop.SourceCurveX!.animationCurveValue); + _mergedPhysBone.FindProperty(prop.PhysBoneCurveYName).animationCurveValue = + FixCurve(prop.SourceCurveY!.animationCurveValue); + _mergedPhysBone.FindProperty(prop.PhysBoneCurveZName).animationCurveValue = + FixCurve(prop.SourceCurveZ!.animationCurveValue); + break; + case MergePhysBone.CurveVector3Config.CurveOverride.Override: + _mergedPhysBone.FindProperty(prop.PhysBoneValueName).vector3Value = + prop.OverrideValue.vector3Value; + _mergedPhysBone.FindProperty(prop.PhysBoneCurveXName).animationCurveValue = + prop.OverrideCurveX.animationCurveValue; + _mergedPhysBone.FindProperty(prop.PhysBoneCurveYName).animationCurveValue = + prop.OverrideCurveY.animationCurveValue; + _mergedPhysBone.FindProperty(prop.PhysBoneCurveZName).animationCurveValue = + prop.OverrideCurveZ.animationCurveValue; + break; + case MergePhysBone.CurveVector3Config.CurveOverride.Fix: + // Fixing rotation is proceeded before. + // We just reset the value and curve. + _mergedPhysBone.FindProperty(prop.PhysBoneValueName).vector3Value = Vector3.zero; + _mergedPhysBone.FindProperty(prop.PhysBoneCurveXName).animationCurveValue = new AnimationCurve(); + _mergedPhysBone.FindProperty(prop.PhysBoneCurveYName).animationCurveValue = new AnimationCurve(); + _mergedPhysBone.FindProperty(prop.PhysBoneCurveZName).animationCurveValue = new AnimationCurve(); + break; + default: + throw new ArgumentOutOfRangeException(); } } diff --git a/Localization/en-us.po b/Localization/en-us.po index c31395798..de18e6f48 100644 --- a/Localization/en-us.po +++ b/Localization/en-us.po @@ -153,6 +153,9 @@ msgid "MergePhysBone:error:unsupportedPbVersion" msgstr "The PhysBone Version is not supported (yet) by Avatar Optimizer.\n" "Please tell author on twitter (@anatawa12_vrc) or GitHub (anatawa12/AvatarOptimizer)!" +msgid "MergePhysBone:message:fix-yaw-pitch" +msgstr "Fix to 0, 0, 0 by rotating bones" + msgid "MergePhysBone:error:differValues" msgstr "The values is differ between two or more sources. You have to set same value OR override this property: {0}" diff --git a/Runtime/MergePhysBone.cs b/Runtime/MergePhysBone.cs index baf0a9879..c30dceb0b 100644 --- a/Runtime/MergePhysBone.cs +++ b/Runtime/MergePhysBone.cs @@ -150,11 +150,19 @@ public struct CurveNoLimitConfig [Serializable] public struct CurveVector3Config { - public bool @override; + public CurveOverride @override; public Vector3 value; public AnimationCurve curveX; public AnimationCurve curveY; public AnimationCurve curveZ; + + public enum CurveOverride + { + Copy, + Override, + // Change bone angle to match the curve + Fix, + } } [Serializable] From c10db680fb3dd21bd2351886bda90603825d6efc Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 24 Oct 2024 22:10:34 +0900 Subject: [PATCH 02/50] feat: implement fixing roll with physBone --- Editor/Processors/MergePhysBoneProcessor.cs | 238 +++++++++++++++++- ...m.anatawa12.avatar-optimizer.editor.asmdef | 1 + 2 files changed, 231 insertions(+), 8 deletions(-) diff --git a/Editor/Processors/MergePhysBoneProcessor.cs b/Editor/Processors/MergePhysBoneProcessor.cs index 8783e7baa..c458d4dcb 100644 --- a/Editor/Processors/MergePhysBoneProcessor.cs +++ b/Editor/Processors/MergePhysBoneProcessor.cs @@ -5,6 +5,7 @@ using System.Linq; using Anatawa12.AvatarOptimizer.AnimatorParsersV2; using nadena.dev.ndmf; +using Unity.Mathematics; using UnityEditor; using UnityEngine; using VRC.Dynamics; @@ -75,11 +76,6 @@ internal static void DoMerge(MergePhysBone merge, BuildContext? context) } } - // yaw / pitch fix - if (merge.limitRotationConfig.@override == MergePhysBone.CurveVector3Config.CurveOverride.Fix) - foreach (var physBone in sourceComponents) - FixYawPitch(physBone, context); - // clear endpoint position if (merge.endpointPositionConfig.@override == MergePhysBone.EndPointPositionConfig.Override.Clear) foreach (var physBone in sourceComponents) @@ -127,6 +123,70 @@ internal static void DoMerge(MergePhysBone merge, BuildContext? context) default: throw new ArgumentOutOfRangeException(); } + // == Limits == + + // yaw / pitch fix + if (merge.limitRotationConfig.@override == MergePhysBone.CurveVector3Config.CurveOverride.Fix) + { + var newIgnores = new List(); + // fix rotations + foreach (var physBone in sourceComponents) + FixYawPitch(physBone, root, context, newIgnores); + + // fix configurations + merged.ignoreTransforms = merged.ignoreTransforms.Concat(newIgnores).ToList(); + + var sourceComponent = sourceComponents[0]; + var chainLength = sourceComponent.BoneChainLength(); + var yaws = new float[chainLength]; + float fixedRollOfLastBone = 0; + var pitches = new float[chainLength]; + + for (var i = 0; i < chainLength; i++) + { + var rotationSpecified = sourceComponent.CalcLimitRotation((float)i / (chainLength - 1)); + var rotation = ConvertRotation(rotationSpecified); + pitches[i] = rotation.x; + fixedRollOfLastBone = rotation.y; + yaws[i] = rotation.z; + } + + var maxPitch = pitches.Select(Mathf.Abs).Max(); + var maxYaw = yaws.Select(Mathf.Abs).Max(); + + merged.limitRotation = new Vector3(maxPitch, 0, maxYaw); + + if (maxPitch != 0 || maxYaw != 0) + { + // avoid NaN + if (maxPitch == 0) maxPitch = 1; + if (maxYaw == 0) maxYaw = 1; + + var pitchCurve = new AnimationCurve(); + var yawCurve = new AnimationCurve(); + + pitchCurve.AddKey(0, pitches[0] / maxPitch); + yawCurve.AddKey(0, yaws[0] / maxYaw); + + for (var i = 0; i < chainLength; i++) + { + var time = (float)(i + 1) / chainLength; + pitchCurve.AddKey(time, pitches[i] / maxPitch); + yawCurve.AddKey(time, yaws[i] / maxYaw); + } + + merged.limitRotationXCurve = pitchCurve; + merged.limitRotationZCurve = yawCurve; + } + + if (merged.endpointPosition != Vector3.zero) + { + // TODO: this Endpoint Fix might not enough + // Rotation fix will conflict with this fix + merged.endpointPosition = Quaternion.Euler(0, -fixedRollOfLastBone, 0) * merged.endpointPosition; + } + } + // == Options == merged.isAnimated = merge.isAnimatedConfig.value || sourceComponents.Any(x => x.isAnimated); @@ -151,16 +211,178 @@ internal static void DoMerge(MergePhysBone merge, BuildContext? context) // To preserve bone reference, we keep original bone and create new GameObject for it. // and later Trace and Object remove unused objects will merge original bones - public static void FixYawPitch(VRCPhysBoneBase physBone, BuildContext? context) + public static void FixYawPitch( + VRCPhysBoneBase physBone, + Transform root, + BuildContext? context, + List newIgnores) { // Already fixed; nothing to do! if (physBone.limitRotation.Equals(Vector3.zero)) return; - physBone.InitTransforms(true); var maxChainLength = physBone.BoneChainLength(); - throw new NotImplementedException(); + var ignoreTransforms = new HashSet(physBone.ignoreTransforms); + + RotateRecursive(physBone, physBone.GetTarget(), root, maxChainLength, 0, ignoreTransforms, newIgnores); + } + + /* + RotateRecursive will transform + Parent <= Parent + `- Root <= Transform + +- Bone1 + | +- Bone2 + | +- Bone3 + `- Bone4 + +- Bone5 + into + Parent + `- Root (AAO Merge Proxy) + +- Root + +- Bone1 (AAO Merge Proxy) + | +- Bone1 + | `- Bone2 (AAO Merge Proxy) + | +- Bone2 + | `- Bone3 (AAO Merge Proxy) + | +- Bone3 + `- Bone4 (AAO Merge Proxy) + +- Bone4 + `- Bone5 (AAO Merge Proxy) + +- Bone5 + + One pass of this method will transform into + + Parent + `- Root (AAO Merge Proxy) <= New Parent + `- Root + +- Bone1 <= New Transform + | +- Bone2 + | +- Bone3 + `- Bone4 <= New Transform + +- Bone5 + and calls RotateRecursive with new set of bones to complete + + */ + + private static void RotateRecursive(VRCPhysBoneBase physBone, + Transform transform, + Transform parent, + int totalDepth, + int depth, + HashSet ignoreTransforms, + List newIgnores) + { + Vector3 targetLocation; + + var activeChildren = Enumerable.Range(0, transform.childCount) + .Select(transform.GetChild) + .Where(child => !ignoreTransforms.Contains(child)) + .ToArray(); + + switch (activeChildren.Length) + { + case 0: + // end bone + if (physBone.endpointPosition != Vector3.zero) + targetLocation = physBone.endpointPosition; + else + targetLocation = Vector3.up; + break; + case 1: + targetLocation = activeChildren[0].localPosition; + break; + default: + switch (physBone.multiChildType) + { + case VRCPhysBoneBase.MultiChildType.Ignore: + targetLocation = Vector3.up; + break; + case VRCPhysBoneBase.MultiChildType.First: + targetLocation = activeChildren[0].localPosition; + break; + case VRCPhysBoneBase.MultiChildType.Average: + targetLocation = + activeChildren.Aggregate(Vector3.zero, (current, child) => current + child.localPosition) / + activeChildren.Length; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + break; + } + + var specifiedRotation = physBone.CalcLimitRotation((float)depth / totalDepth); + var rotation = ConvertRotation(specifiedRotation).y; + + // if the bone is at (0, -x, 0), we have infinite rotation for `FromToRotation` and + // `Quaternion.FromToRotation`'s choice is not happy for logic below. + // We need special handling for this case. + var dot = Vector3.Dot(Vector3.up, math.normalizesafe(targetLocation)); + var critical = dot <= -1; + + //Debug.Log($"is critical: {critical}, dot: {dot}, transform: {transform.name}"); + var thisRotation = !critical ? rotation : -rotation; + + // create new (actual) bone + var newBone = new GameObject($"{transform.name} (AAO Merge Proxy)"); + + // new bone should be at exactly same transform as the original bone + newBone.transform.parent = transform; + newBone.transform.localPosition = Vector3.zero; + newBone.transform.localRotation = Quaternion.identity; + newBone.transform.localScale = Vector3.one; + + // move to parent + newBone.transform.SetParent(parent, true); + + // rotate newBone to fix roll + newBone.transform.Rotate(Vector3.up, thisRotation, Space.Self); + + // move old bone to child of newBone + transform.SetParent(newBone.transform, true); + + newIgnores.Add(transform); + + //var rotationQuaternion = Quaternion.Euler(0, -thisRotation, 0); + + foreach (var child in activeChildren) + { + //child.localPosition = rotationQuaternion * child.localPosition; + //child.localRotation = rotationQuaternion * child.localRotation; + + if (ignoreTransforms.Contains(child)) continue; + RotateRecursive(physBone, child, newBone.transform, totalDepth, depth + 1, ignoreTransforms, newIgnores); + } + } + + private static Vector3 ConvertRotation( + Vector3 limitRotation + ) + { + // XYZ is the order used in VRCPhysBone + var quat = quaternion.EulerXYZ(limitRotation * Mathf.Deg2Rad); + return QuaternionToEulerXZY(quat) * Mathf.Rad2Deg; + } + + private static Vector3 QuaternionToEulerXZY(Quaternion q) + { + // Quaternion to Euler + // https://qiita.com/aa_debdeb/items/abe90a9bd0b4809813da + // YZX Order in the article. (XZY in Unity) + // We use different perspective to represent same order of Euler order between Unity and the article. + var sz = 2 * q.x * q.y + 2 * q.z * q.w; + var unlocked = Mathf.Abs(sz) < 0.99999f; + Debug.Log("unlocked: " + unlocked); + return new Vector3( + unlocked ? Mathf.Atan2(-(2 * q.y * q.z - 2 * q.x * q.w), 2 * q.w * q.w + 2 * q.y * q.y - 1) : 0, + unlocked + ? Mathf.Atan2(-(2 * q.x * q.z - 2 * q.y * q.w), 2 * q.w * q.w + 2 * q.x * q.x - 1) + : Mathf.Atan2(2 * q.x * q.z + 2 * q.y * q.w, 2 * q.w * q.w + 2 * q.z * q.z - 1), + Mathf.Asin(sz) + ); } private static readonly string[] TransformRotationAndPositionAnimationKeys = diff --git a/Editor/com.anatawa12.avatar-optimizer.editor.asmdef b/Editor/com.anatawa12.avatar-optimizer.editor.asmdef index 72816d14f..b27602886 100644 --- a/Editor/com.anatawa12.avatar-optimizer.editor.asmdef +++ b/Editor/com.anatawa12.avatar-optimizer.editor.asmdef @@ -14,6 +14,7 @@ "com.anatawa12.avatar-optimizer.internal.meshinfo2", "com.anatawa12.avatar-optimizer.internal.utils", "Unity.Burst", + "Unity.Mathematics", "nadena.dev.ndmf", "nadena.dev.ndmf.runtime", "nadena.dev.ndmf.reactive-query.core", From 8212b287179e488101a687faee424778c9b70215 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 09:20:22 +0900 Subject: [PATCH 03/50] refactor: move ScaledEvenly to Utils --- Editor/Processors/MergeBoneProcessor.cs | 9 +-------- .../TraceAndOptimize/FindUnusedObjectsProcessor.cs | 2 +- Internal/Utils/Utils.cs | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Editor/Processors/MergeBoneProcessor.cs b/Editor/Processors/MergeBoneProcessor.cs index f7fbad319..158ca7925 100644 --- a/Editor/Processors/MergeBoneProcessor.cs +++ b/Editor/Processors/MergeBoneProcessor.cs @@ -33,7 +33,7 @@ public static void Validate(MergeBone mergeBone, GameObject root) if (AnyNotMergedBone(mergeBone.transform)) { // if the bone has non-merged bones, uneven scaling is not supported. - if (!ScaledEvenly(mergeBone.transform.localScale)) + if (!Utils.ScaledEvenly(mergeBone.transform.localScale)) BuildLog.LogWarning("MergeBone:validation:unevenScaling"); } @@ -48,13 +48,6 @@ bool AnyNotMergedBone(Transform bone) } } - public static bool ScaledEvenly(Vector3 localScale) - { - bool CheckScale(float scale) => 0.995 < scale && scale < 1.005; - return CheckScale(localScale.x / localScale.y) && CheckScale(localScale.x / localScale.z) && - CheckScale(localScale.y / localScale.z); - } - protected override void Execute(BuildContext context) { // merge from -> merge into diff --git a/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs b/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs index 6d648dc6b..02d5440ce 100644 --- a/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs +++ b/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs @@ -375,7 +375,7 @@ private void MergeBone(GCComponentInfoHolder componentInfos) // if this is not identity transform, animating children is not good return NotMerged(); - if (!MergeBoneProcessor.ScaledEvenly(localScale)) + if (!Utils.ScaledEvenly(localScale)) // non even scaling is not possible to reproduce in children return NotMerged(); } diff --git a/Internal/Utils/Utils.cs b/Internal/Utils/Utils.cs index e0efea931..e8cc113a8 100644 --- a/Internal/Utils/Utils.cs +++ b/Internal/Utils/Utils.cs @@ -379,5 +379,19 @@ bool ComputeSafeToUseFastImplementation() // Exception-safe swap public static void Swap(ref T a, ref T b) => (a, b) = (b, a); + + /// + /// Returns whether the given local scale is scaled evenly. + /// + /// If the scale is skewed, this returns false. + /// + /// the local scale to check + /// whether the given local scale is scaled evenly + public static bool ScaledEvenly(Vector3 localScale) + { + bool CheckScale(float scale) => 0.995 < scale && scale < 1.005; + return CheckScale(localScale.x / localScale.y) && CheckScale(localScale.x / localScale.z) && + CheckScale(localScale.y / localScale.z); + } } } From 80c0656ed207f4f6a39d0f7e021941b45479b821 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 11:25:36 +0900 Subject: [PATCH 04/50] feat(utils): MaxBy --- Internal/Utils/Utils.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Internal/Utils/Utils.cs b/Internal/Utils/Utils.cs index e8cc113a8..62d62dc88 100644 --- a/Internal/Utils/Utils.cs +++ b/Internal/Utils/Utils.cs @@ -393,5 +393,27 @@ public static bool ScaledEvenly(Vector3 localScale) return CheckScale(localScale.x / localScale.y) && CheckScale(localScale.x / localScale.z) && CheckScale(localScale.y / localScale.z); } + + public static TSource MaxBy(this IEnumerable source, + Func selector) + where TComparable : IComparable + { + using var enumerator = source.GetEnumerator(); + if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence is empty"); + var max = enumerator.Current; + var maxComparable = selector(max); + while (enumerator.MoveNext()) + { + var current = enumerator.Current; + var currentComparable = selector(current); + if (currentComparable.CompareTo(maxComparable) > 0) + { + max = current; + maxComparable = currentComparable; + } + } + + return max; + } } } From e4489cb55fdefcc0bc617fb9abdf0d779a95d4ce Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 11:26:43 +0900 Subject: [PATCH 05/50] feat(merge-pb): build-time validation for LimitRotationFix --- Editor/Inspector/MergePhysBoneEditor.cs | 73 ++++++++++++++++++++- Editor/Processors/MergePhysBoneProcessor.cs | 4 +- Localization/en-us.po | 20 ++++++ 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/Editor/Inspector/MergePhysBoneEditor.cs b/Editor/Inspector/MergePhysBoneEditor.cs index 30f048d06..a4969c76e 100644 --- a/Editor/Inspector/MergePhysBoneEditor.cs +++ b/Editor/Inspector/MergePhysBoneEditor.cs @@ -607,6 +607,9 @@ protected override void BeginPbConfig() { if (SourcePhysBones.Count() <= 1) BuildLog.LogError("MergePhysBone:error:oneSource"); + + foreach (var vrcPhysBoneBase in SourcePhysBones) + vrcPhysBoneBase.InitTransforms(true); } protected override bool BeginSection(string name, string docTag) => true; @@ -619,8 +622,6 @@ protected override void EndPbConfig() { if (_usingCopyCurve) { - foreach (var vrcPhysBoneBase in SourcePhysBones) - vrcPhysBoneBase.InitTransforms(true); var maxLength = SourcePhysBones.Max(x => x.BoneChainLength()); if (SourcePhysBones.Any(x => x.BoneChainLength() != maxLength)) BuildLog.LogWarning("MergePhysBone:warning:differChainLength", @@ -701,7 +702,73 @@ protected override void Pb3DCurveProp(string label, case MergePhysBone.CurveVector3Config.CurveOverride.Override: break; case MergePhysBone.CurveVector3Config.CurveOverride.Fix: - // more validation about skew scaling and rotation animation on build + if (SourcePhysBones.Any()) + { + // skew scaling is disallowed + var scaledUnevenly = SourcePhysBones + .SelectMany(x => x.GetAffectedTransforms()) + .Where(x => !Utils.ScaledEvenly(x.localScale)) + .ToList(); + + if (scaledUnevenly.Count != 0) + BuildLog.LogError("MergePhysBone:error:LimitRotationFix:SkewScaling", scaledUnevenly); + + // error if there is different limit / rotation + var longestPhysBone = SourcePhysBones.MaxBy(x => x.BoneChainLength()); + + var fixedRotations = Enumerable.Range(0, longestPhysBone.BoneChainLength()) + .Select(index => + { + var time = (float)index / longestPhysBone.BoneChainLength() - 1; + + var rotation = longestPhysBone.CalcLimitRotation(time); + + return Processors.MergePhysBoneProcessor.ConvertRotation(rotation) + with + { + y = 0 + }; + }) + .ToList(); + + var differRotation = SourcePhysBones + .Any(physBone => + { + return Enumerable.Range(0, physBone.BoneChainLength()).Any(index => + { + var time = (float)index / physBone.BoneChainLength() - 1; + var rotation = longestPhysBone.CalcLimitRotation(time); + var fixedRot = Processors.MergePhysBoneProcessor.ConvertRotation(rotation)with + { + y = 0 + }; + + return fixedRot != fixedRotations[index]; + }); + }); + + if (differRotation) + { + BuildLog.LogError("MergePhysBone:error:LimitRotationFix:DifferRotation"); + } + + // endpoint position must be zero + switch ((MergePhysBone.EndPointPositionConfig.Override)EndpointPosition.OverrideProperty.enumValueIndex) + { + case MergePhysBone.EndPointPositionConfig.Override.Clear: + // no problem; endpoint position is zero + break; + case MergePhysBone.EndPointPositionConfig.Override.Copy: + if (EndpointPosition.PhysBoneValue!.vector3Value != Vector3.zero) + BuildLog.LogError("MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition"); + break; + case MergePhysBone.EndPointPositionConfig.Override.Override: + if (EndpointPosition.ValueProperty.vector3Value != Vector3.zero) + BuildLog.LogError("MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition"); + break; + } + } + break; default: throw new ArgumentOutOfRangeException(); diff --git a/Editor/Processors/MergePhysBoneProcessor.cs b/Editor/Processors/MergePhysBoneProcessor.cs index c458d4dcb..6390eb4be 100644 --- a/Editor/Processors/MergePhysBoneProcessor.cs +++ b/Editor/Processors/MergePhysBoneProcessor.cs @@ -358,9 +358,7 @@ private static void RotateRecursive(VRCPhysBoneBase physBone, } } - private static Vector3 ConvertRotation( - Vector3 limitRotation - ) + public static Vector3 ConvertRotation(Vector3 limitRotation) { // XYZ is the order used in VRCPhysBone var quat = quaternion.EulerXYZ(limitRotation * Mathf.Deg2Rad); diff --git a/Localization/en-us.po b/Localization/en-us.po index de18e6f48..a3573e5e0 100644 --- a/Localization/en-us.po +++ b/Localization/en-us.po @@ -165,6 +165,26 @@ msgstr "The value is differ between two or more sources. You have to set same va msgid "MergePhysBone:warning:differChainLength" msgstr "The chain length is differ between two or more sources. Shorter chain will be thicker than original." +msgid "MergePhysBone:error:LimitRotationFix:SkewScaling" +msgstr "" +"Skew scaling is not supported with Limit Rotation mode Fix.\n" +"Please change the Limit Rotation mode to other than Fix, or fix the skew scaling." + +msgid "MergePhysBone:error:LimitRotationFix:DifferRotation" +msgstr "" +"Limit Rotation of source phys bones differs in unfixable way.\n" +"Please fix Limit Rotation of source phys bones to same value, or change to other mode." + +msgid "MergePhysBone:error:LimitRotationFix:DifferRotation:description" +msgstr "" +"Limit Rotation Fix can fix Roll axis of different rotation but we cannot fix difference in other axis.\n" +"Roll axis in this context is local X axis, and might be differs from Roll config on PhysBone inspector if there is Yaw rotation." + +msgid "MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition" +msgstr "" +"Endpoint Position is not zero while Limit Rotation mode Fix.\n" +"Please set Endpoint Position to zero, or change Limit Rotation mode to other than Fix." + msgid "MergePhysBone:dialog:versionInfo:title" msgstr "Version Info" From b5326291823ea271cd7341e85d0da3261ef21270 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 13:15:10 +0900 Subject: [PATCH 06/50] chore: update MergePhysBone messages --- Localization/en-us.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Localization/en-us.po b/Localization/en-us.po index a3573e5e0..34cf23272 100644 --- a/Localization/en-us.po +++ b/Localization/en-us.po @@ -154,7 +154,7 @@ msgstr "The PhysBone Version is not supported (yet) by Avatar Optimizer.\n" "Please tell author on twitter (@anatawa12_vrc) or GitHub (anatawa12/AvatarOptimizer)!" msgid "MergePhysBone:message:fix-yaw-pitch" -msgstr "Fix to 0, 0, 0 by rotating bones" +msgstr "Fix Roll with rotating bones" msgid "MergePhysBone:error:differValues" msgstr "The values is differ between two or more sources. You have to set same value OR override this property: {0}" @@ -183,7 +183,7 @@ msgstr "" msgid "MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition" msgstr "" "Endpoint Position is not zero while Limit Rotation mode Fix.\n" -"Please set Endpoint Position to zero, or change Limit Rotation mode to other than Fix." +"Please set Endpoint Position to zero, set Endpoint Position mode to Clear, or change Limit Rotation mode to other than Fix." msgid "MergePhysBone:dialog:versionInfo:title" msgstr "Version Info" From 51f0392563b19f94447cdd1cb931ab6b6151182d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 14:02:57 +0900 Subject: [PATCH 07/50] chore(merge-pb): show errors on inspector --- Editor/Inspector/MergePhysBoneEditor.cs | 129 ++++++++++++++---------- 1 file changed, 76 insertions(+), 53 deletions(-) diff --git a/Editor/Inspector/MergePhysBoneEditor.cs b/Editor/Inspector/MergePhysBoneEditor.cs index a4969c76e..b06e89eae 100644 --- a/Editor/Inspector/MergePhysBoneEditor.cs +++ b/Editor/Inspector/MergePhysBoneEditor.cs @@ -344,6 +344,29 @@ protected override void Pb3DCurveProp(string label, case MergePhysBone.CurveVector3Config.CurveOverride.Fix: { EditorGUI.LabelField(rect, label, AAOL10N.Tr("MergePhysBone:message:fix-yaw-pitch")); + + if (SourcePhysBones.Any()) + { + foreach (var physBone in SourcePhysBones) + physBone.InitTransforms(force: false); + + // skew scaling is disallowed + if (MergePhysBoneValidator.SkewBones(SourcePhysBones) is { Count: > 0 }) + EditorGUILayout.HelpBox(AAOL10N.Tr("MergePhysBone:error:LimitRotationFix:SkewScaling"), MessageType.Error); + + // error if there is different limit / rotation + if (MergePhysBoneValidator.HasDifferentYawPitch(SourcePhysBones)) + EditorGUILayout.HelpBox(AAOL10N.Tr("MergePhysBone:error:LimitRotationFix:DifferRotation"), MessageType.Error); + + // endpoint position must be zero + switch ((MergePhysBone.EndPointPositionConfig.Override)EndpointPosition.OverrideProperty.enumValueIndex) + { + case MergePhysBone.EndPointPositionConfig.Override.Copy when EndpointPosition.PhysBoneValue!.vector3Value != Vector3.zero: + case MergePhysBone.EndPointPositionConfig.Override.Override when EndpointPosition.ValueProperty.vector3Value != Vector3.zero: + EditorGUILayout.HelpBox(AAOL10N.Tr("MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition"), MessageType.Error); + break; + } + } } break; default: @@ -705,66 +728,19 @@ protected override void Pb3DCurveProp(string label, if (SourcePhysBones.Any()) { // skew scaling is disallowed - var scaledUnevenly = SourcePhysBones - .SelectMany(x => x.GetAffectedTransforms()) - .Where(x => !Utils.ScaledEvenly(x.localScale)) - .ToList(); - - if (scaledUnevenly.Count != 0) - BuildLog.LogError("MergePhysBone:error:LimitRotationFix:SkewScaling", scaledUnevenly); + if (SkewBones(SourcePhysBones) is { Count: > 0 } skewBones) + BuildLog.LogError("MergePhysBone:error:LimitRotationFix:SkewScaling", skewBones); // error if there is different limit / rotation - var longestPhysBone = SourcePhysBones.MaxBy(x => x.BoneChainLength()); - - var fixedRotations = Enumerable.Range(0, longestPhysBone.BoneChainLength()) - .Select(index => - { - var time = (float)index / longestPhysBone.BoneChainLength() - 1; - - var rotation = longestPhysBone.CalcLimitRotation(time); - - return Processors.MergePhysBoneProcessor.ConvertRotation(rotation) - with - { - y = 0 - }; - }) - .ToList(); - - var differRotation = SourcePhysBones - .Any(physBone => - { - return Enumerable.Range(0, physBone.BoneChainLength()).Any(index => - { - var time = (float)index / physBone.BoneChainLength() - 1; - var rotation = longestPhysBone.CalcLimitRotation(time); - var fixedRot = Processors.MergePhysBoneProcessor.ConvertRotation(rotation)with - { - y = 0 - }; - - return fixedRot != fixedRotations[index]; - }); - }); - - if (differRotation) - { + if (HasDifferentYawPitch(SourcePhysBones)) BuildLog.LogError("MergePhysBone:error:LimitRotationFix:DifferRotation"); - } // endpoint position must be zero switch ((MergePhysBone.EndPointPositionConfig.Override)EndpointPosition.OverrideProperty.enumValueIndex) { - case MergePhysBone.EndPointPositionConfig.Override.Clear: - // no problem; endpoint position is zero - break; - case MergePhysBone.EndPointPositionConfig.Override.Copy: - if (EndpointPosition.PhysBoneValue!.vector3Value != Vector3.zero) - BuildLog.LogError("MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition"); - break; - case MergePhysBone.EndPointPositionConfig.Override.Override: - if (EndpointPosition.ValueProperty.vector3Value != Vector3.zero) - BuildLog.LogError("MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition"); + case MergePhysBone.EndPointPositionConfig.Override.Copy when EndpointPosition.PhysBoneValue!.vector3Value != Vector3.zero: + case MergePhysBone.EndPointPositionConfig.Override.Override when EndpointPosition.ValueProperty.vector3Value != Vector3.zero: + BuildLog.LogError("MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition"); break; } } @@ -775,6 +751,53 @@ protected override void Pb3DCurveProp(string label, } } + public static List SkewBones(IEnumerable sourcePhysBones) + { + // skew scaling is disallowed + return sourcePhysBones + .SelectMany(x => x.GetAffectedTransforms()) + .Where(x => !Utils.ScaledEvenly(x.localScale)) + .ToList(); + } + + public static bool HasDifferentYawPitch(IEnumerable sourcePhysBones) + { + var longestPhysBone = sourcePhysBones.MaxBy(x => x.BoneChainLength()); + + var fixedRotations = Enumerable.Range(0, longestPhysBone.BoneChainLength()) + .Select(index => + { + var time = (float)index / longestPhysBone.BoneChainLength() - 1; + + var rotation = longestPhysBone.CalcLimitRotation(time); + + return Processors.MergePhysBoneProcessor.ConvertRotation(rotation) + with + { + y = 0 + }; + }) + .ToList(); + + var differRotation = sourcePhysBones + .Any(physBone => + { + return Enumerable.Range(0, physBone.BoneChainLength()).Any(index => + { + var time = (float)index / physBone.BoneChainLength() - 1; + var rotation = longestPhysBone.CalcLimitRotation(time); + var fixedRot = Processors.MergePhysBoneProcessor.ConvertRotation(rotation)with + { + y = 0 + }; + + return fixedRot != fixedRotations[index]; + }); + }); + + return differRotation; + } + protected override void PbPermissionProp(string label, PermissionConfigProp prop, bool forceOverride = false) { if (forceOverride || prop.IsOverride) return; From 86cc57ea8b425511e7924e2f3559390d6538c71d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 14:49:03 +0900 Subject: [PATCH 08/50] docs(merge-pb): document Fix mode --- .docs/content/docs/reference/merge-physbone/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.docs/content/docs/reference/merge-physbone/index.md b/.docs/content/docs/reference/merge-physbone/index.md index 5fcd8f1d4..15ee5033e 100644 --- a/.docs/content/docs/reference/merge-physbone/index.md +++ b/.docs/content/docs/reference/merge-physbone/index.md @@ -41,3 +41,6 @@ For each property, you may select `Copy` to copy value from source property For colliders, you can select `Merge` to merge colliders array from source physbones. For Endpoint Position, you can select `Clear` to apply [Clear Endpoint Position](../clear-endpoint-position) + +For Limit Rotation, you can select `Fix` to fix different roll axis on the model. +If your model has different roll bones and fixes with Limit Rotation, you may use this option. From b536989485208198c0c7a95d1cb50a78ee98def0 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 15:09:16 +0900 Subject: [PATCH 09/50] docs(changelog): Fix mode for PhysBone Limits in Merge PhysBone --- CHANGELOG-PRERELEASE.md | 5 +++++ CHANGELOG.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 8995566ad..bc1e3e39d 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -9,6 +9,11 @@ The format is based on [Keep a Changelog]. ## [Unreleased] ### Added - Right-click menu option to create a new GameObject with a specified component [`#1290`](https://github.com/anatawa12/AvatarOptimizer/pull/1290) +- Fix mode for PhysBone Limits in Merge PhysBone `#665` + - In addition to existing `Copy` and `Override`, we added `Fix` mode. + - This mode will try to correct roll axis by rotating bone. + - This feature allows you to configure the mode for PhysBone Limits in Merge PhysBone. + - This is useful if all configuration is same but roll axis is different. ### Changed - More Preference Improvement `#1288` diff --git a/CHANGELOG.md b/CHANGELOG.md index a6552b3b9..c7d625b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,11 @@ The format is based on [Keep a Changelog]. - When you changed shader for an material, properties for previously used shaders might be remain - This may increase your avatar size by unexpectedly including unused textures - Right-click menu option to create a new GameObject with a specified component [`#1290`](https://github.com/anatawa12/AvatarOptimizer/pull/1290) +- Fix mode for PhysBone Limits in Merge PhysBone `#665` + - In addition to existing `Copy` and `Override`, we added `Fix` mode. + - This mode will try to correct roll axis by rotating bone. + - This feature allows you to configure the mode for PhysBone Limits in Merge PhysBone. + - This is useful if all configuration is same but roll axis is different. ### Changed - Skip Enablement Mismatched Renderers is now disabled by default `#1169` From fcfaf85b8e5e3da5161608f7b2bc50dcfb9b402b Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 25 Oct 2024 23:18:06 +0900 Subject: [PATCH 10/50] chore: rename freezeBlendShape to optimizeBlendShape --- .docs/content/docs/reference/merge-skinned-mesh/index.md | 2 +- .docs/content/docs/reference/trace-and-optimize/index.md | 5 +++-- Editor/Inspector/TraceAndOptimizeEditor.cs | 2 +- Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs | 2 +- Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs | 4 ++-- Localization/en-us.po | 4 ++-- Runtime/TraceAndOptimize.cs | 5 +++-- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.md b/.docs/content/docs/reference/merge-skinned-mesh/index.md index 73e282ce4..40349c153 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.md @@ -35,7 +35,7 @@ That's why it's not good to merge face meshes. In addition, because of same reasons, you should freeze & remove unchanging BlendShapes for body / cloth meshes. You can freeze & remove BlendShape using [Freeze BlendShape](../freeze-blendshape) component. Add this component to both/either merge source SkinnedMeshRenderer and/or merged SkinnedMeshRenderer to freeze & remove BlendShapes. -Also, you can use `Automatically Freeze BlendShape` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. +Also, you can use `Freeze BlendShapes` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. {{< hint info >}} diff --git a/.docs/content/docs/reference/trace-and-optimize/index.md b/.docs/content/docs/reference/trace-and-optimize/index.md index b914b695f..bb8e8207b 100644 --- a/.docs/content/docs/reference/trace-and-optimize/index.md +++ b/.docs/content/docs/reference/trace-and-optimize/index.md @@ -24,8 +24,9 @@ We will fix it as much as we can. {{< /hint >}} Currently the following optimizations are applied automatically. -- `Automatically Freeze BlendShape`\ - Automatically freezes BlendShapes which are always the same value or unused in animation, etc. +- `Optimize BlendShape`\ + Previously known as `Freeze BlendShapes` but renamed to add more functionality.\ + Optimizes BlendShapes and remove if BlendShapes are not used or unnecessary. - `Remove unused Objects`\ By scanning animation etc., automatically removes unused Objects (e.g. GameObjects, Components).\ In addition, this will automatically toggle PhysBone Components if they are only used by toggled objects. diff --git a/Editor/Inspector/TraceAndOptimizeEditor.cs b/Editor/Inspector/TraceAndOptimizeEditor.cs index 64965c34a..05e87edb7 100644 --- a/Editor/Inspector/TraceAndOptimizeEditor.cs +++ b/Editor/Inspector/TraceAndOptimizeEditor.cs @@ -22,7 +22,7 @@ internal class TraceAndOptimizeEditor : AvatarGlobalComponentEditorBase private void OnEnable() { - _freezeBlendShape = serializedObject.FindProperty(nameof(TraceAndOptimize.freezeBlendShape)); + _freezeBlendShape = serializedObject.FindProperty(nameof(TraceAndOptimize.optimizeBlendShape)); _removeUnusedObjects = serializedObject.FindProperty(nameof(TraceAndOptimize.removeUnusedObjects)); _preserveEndBone = serializedObject.FindProperty(nameof(TraceAndOptimize.preserveEndBone)); _removeZeroSizedPolygons = serializedObject.FindProperty(nameof(TraceAndOptimize.removeZeroSizedPolygons)); diff --git a/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs index d7e6d2420..545952e07 100644 --- a/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs +++ b/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs @@ -18,7 +18,7 @@ internal class AutoFreezeBlendShape : TraceAndOptimizePass protected override void Execute(BuildContext context, TraceAndOptimizeState state) { - if (!state.FreezeBlendShape) return; + if (!state.OptimizeBlendShape) return; if (!state.SkipFreezingNonAnimatedBlendShape) FreezeNonAnimatedBlendShapes(context, state); diff --git a/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs b/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs index 7cab2d891..4a76bdbc3 100644 --- a/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs +++ b/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs @@ -7,7 +7,7 @@ namespace Anatawa12.AvatarOptimizer.Processors.TraceAndOptimizes public class TraceAndOptimizeState { public bool Enabled; - public bool FreezeBlendShape; + public bool OptimizeBlendShape; public bool RemoveUnusedObjects; public bool RemoveZeroSizedPolygon; public bool OptimizePhysBone; @@ -43,7 +43,7 @@ public class TraceAndOptimizeState internal void Initialize(TraceAndOptimize config) { - FreezeBlendShape = config.freezeBlendShape; + OptimizeBlendShape = config.optimizeBlendShape; RemoveUnusedObjects = config.removeUnusedObjects; RemoveZeroSizedPolygon = config.removeZeroSizedPolygons; OptimizePhysBone = config.optimizePhysBone; diff --git a/Localization/en-us.po b/Localization/en-us.po index e00de17bc..10badf356 100644 --- a/Localization/en-us.po +++ b/Localization/en-us.po @@ -700,8 +700,8 @@ msgstr "Migrating to Trace and Optimize is finished!" msgid "TraceAndOptimize:description" msgstr "When you added this component to your avatar, AvatarOptimizer will trace your avatar and optimize automatically." -msgid "TraceAndOptimize:prop:freezeBlendShape" -msgstr "Automatically Freeze BlendShape" +msgid "TraceAndOptimize:prop:optimizeBlendShape" +msgstr "Optimize BlendShape" msgid "TraceAndOptimize:prop:removeUnusedObjects" msgstr "Remove unused Objects Automatically" diff --git a/Runtime/TraceAndOptimize.cs b/Runtime/TraceAndOptimize.cs index 2049f640f..5373b84a5 100644 --- a/Runtime/TraceAndOptimize.cs +++ b/Runtime/TraceAndOptimize.cs @@ -23,10 +23,11 @@ internal TraceAndOptimize() } [NotKeyable] - [AAOLocalized("TraceAndOptimize:prop:freezeBlendShape")] + [AAOLocalized("TraceAndOptimize:prop:optimizeBlendShape")] [ToggleLeft] [SerializeField] - internal bool freezeBlendShape = true; + [FormerlySerializedAs("freezeBlendShape")] // renamed in 1.8.0 + internal bool optimizeBlendShape = true; [NotKeyable] [AAOLocalized("TraceAndOptimize:prop:removeUnusedObjects")] [ToggleLeft] From fae42d2447083fee46b7fdf3f9b29c8435e84949 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 26 Oct 2024 00:21:18 +0900 Subject: [PATCH 11/50] feat: automatically merge similarly animated meshes --- Editor/OptimizerPlugin.cs | 1 + .../TraceAndOptimize/AutoMergeBlendShape.cs | 175 ++++++++++++++++++ .../AutoMergeBlendShape.cs.meta | 3 + .../TraceAndOptimizeProcessor.cs | 2 + Runtime/TraceAndOptimize.cs | 2 + 5 files changed, 183 insertions(+) create mode 100644 Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs create mode 100644 Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs.meta diff --git a/Editor/OptimizerPlugin.cs b/Editor/OptimizerPlugin.cs index 0d44e99b0..bdb10cb43 100644 --- a/Editor/OptimizerPlugin.cs +++ b/Editor/OptimizerPlugin.cs @@ -78,6 +78,7 @@ protected override void Configure() .Then.Run(Processors.TraceAndOptimizes.FindUnusedObjects.Instance) .Then.Run(Processors.TraceAndOptimizes.ConfigureRemoveZeroSizedPolygon.Instance) .Then.Run(Processors.TraceAndOptimizes.RemoveUnusedMaterialProperties.Instance) + .Then.Run(Processors.TraceAndOptimizes.AutoMergeBlendShape.Instance) .Then.Run(Processors.MergeBoneProcessor.Instance) .Then.Run(Processors.RemoveZeroSizedPolygonProcessor.Instance) .Then.Run(Processors.TraceAndOptimizes.OptimizeTexture.Instance) diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs new file mode 100644 index 000000000..d8dd8f66e --- /dev/null +++ b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Anatawa12.AvatarOptimizer.AnimatorParsersV2; +using Anatawa12.AvatarOptimizer.Processors.SkinnedMeshes; +using nadena.dev.ndmf; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine; + +namespace Anatawa12.AvatarOptimizer.Processors.TraceAndOptimizes; + +public class AutoMergeBlendShape: TraceAndOptimizePass +{ + public override string DisplayName => "T&O: Auto Merge Blend Shape"; + + protected override void Execute(BuildContext context, TraceAndOptimizeState state) + { + if (!state.OptimizeBlendShape) return; + if (state.SkipAutoMergeBlendShape) return; + + foreach (var skinnedMeshRenderer in context.GetComponents()) + { + if (state.Exclusions.Contains(skinnedMeshRenderer.gameObject)) continue; // manual exclusion + + ErrorReport.WithContextObject(skinnedMeshRenderer, () => DoAutoMerge(context.GetMeshInfoFor(skinnedMeshRenderer), context)); + } + } + + private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) + { + var animationComponent = context.GetAnimationComponent(meshInfo2.SourceRenderer); + var groups = new Dictionary>(); + + foreach (var (name, weight) in meshInfo2.BlendShapes) + { + if (MergeKey.Create(weight, name, animationComponent) is { } key) + { + if (!groups.TryGetValue(key, out var list)) + groups.Add(key, list = new List()); + list.Add(name); + } + } + + // nothing to merge + if (groups.Values.All(x => x.Count <= 1)) return; + + // prepare merge + var buffers = meshInfo2.Vertices.Select(x => x.BlendShapeBuffer).ToArray(); + + // bulk remove to optimize removing blendshape process + var removeNames = new HashSet(); + + var i = 0; + // there is thing to merge + foreach (var (key, names) in groups) + { + // validate the blendShapes are simple enough to merge + // if not, skip + foreach (var buffer in buffers) + { + float? commonFrameWeight = null; + + foreach (var name in names) + { + if (buffer.Shapes.TryGetValue(name, out var shapeShape)) + { + if (shapeShape.FrameCount != 1) goto next_shape; + var frameWeight = shapeShape.Frames[0].Weight; + if (commonFrameWeight is { } common) + { + if (!common.Equals(frameWeight)) goto next_shape; + } + else + { + commonFrameWeight = frameWeight; + } + } + } + } + + // validation passed, merge + var newName = $"AAO_Merged_{string.Join("_", names)}_{i++}"; + + // process meshInfo2 + meshInfo2.BlendShapes.Add((newName, key.defaultWeight)); + removeNames.UnionWith(names); + + // actually merge data + foreach (var buffer in buffers) + { + BlendShapeShape? newShapeShape = null; + + foreach (var name in names) + { + if (buffer.Shapes.Remove(name, out var shapeShape)) + { + if (newShapeShape == null) + { + newShapeShape = shapeShape; + } + else + { + var vertices = new MergeBlendShapeJob + { + vertices = buffer.DeltaVertices[newShapeShape.Frames[0].BufferIndex], + toMerge = buffer.DeltaVertices[shapeShape.Frames[0].BufferIndex], + }.Schedule(buffer.VertexCount, 64); + var normals = new MergeBlendShapeJob + { + vertices = buffer.DeltaNormals[newShapeShape.Frames[0].BufferIndex], + toMerge = buffer.DeltaNormals[shapeShape.Frames[0].BufferIndex], + }.Schedule(buffer.VertexCount, 64); + var tangents = new MergeBlendShapeJob + { + vertices = buffer.DeltaTangents[newShapeShape.Frames[0].BufferIndex], + toMerge = buffer.DeltaTangents[shapeShape.Frames[0].BufferIndex], + }.Schedule(buffer.VertexCount, 64); + JobHandle.CombineDependencies(vertices, normals, tangents).Complete(); + } + } + } + + // null means there are no shapes to merge for this buffer + if (newShapeShape != null) + { + buffer.Shapes.Add(newName, newShapeShape); + } + } + + next_shape: ; + } + + // remove merged blendShapes + meshInfo2.BlendShapes.RemoveAll(x => removeNames.Contains(x.name)); + } + + readonly struct MergeKey : IEquatable + { + public readonly float defaultWeight; + public readonly EqualsHashSet animationLocations; + + public MergeKey(float defaultWeight, EqualsHashSet animationLocations) + { + this.defaultWeight = defaultWeight; + this.animationLocations = animationLocations; + } + + public static MergeKey? Create(float defaultWeight, string name, AnimationComponentInfo animationComponent) + { + var node = animationComponent.GetFloatNode($"blendShape.{name}"); + // to merge, all nodes must be AnimatorPropModNode + if (!node.ComponentNodes.All(x => x is AnimatorPropModNode)) return null; + var animationLocations = AnimationLocation.CollectAnimationLocation(node).ToEqualsHashSet(); + return new MergeKey(defaultWeight, animationLocations); + } + + public bool Equals(MergeKey other) => + defaultWeight.Equals(other.defaultWeight) && + animationLocations.Equals(other.animationLocations); + + public override bool Equals(object? obj) => obj is MergeKey other && Equals(other); + public override int GetHashCode() => HashCode.Combine(defaultWeight, animationLocations); + public static bool operator ==(MergeKey left, MergeKey right) => left.Equals(right); + public static bool operator !=(MergeKey left, MergeKey right) => !left.Equals(right); + } + + struct MergeBlendShapeJob : IJobParallelFor + { + public NativeArray vertices; + public NativeArray toMerge; + + public void Execute(int index) => vertices[index] += toMerge[index]; + } +} diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs.meta b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs.meta new file mode 100644 index 000000000..3fa02c76e --- /dev/null +++ b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0bdd3f9c9d8b404aabe0cdf475622189 +timeCreated: 1729866342 \ No newline at end of file diff --git a/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs b/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs index 4a76bdbc3..b58e60509 100644 --- a/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs +++ b/Internal/TraceAndOptimizeBase/TraceAndOptimizeProcessor.cs @@ -37,6 +37,7 @@ public class TraceAndOptimizeState public bool SkipRemoveEmptySubMesh; public bool SkipAnyStateToEntryExit; public bool SkipRemoveMaterialUnusedProperties; + public bool SkipAutoMergeBlendShape; public Dictionary> PreserveBlendShapes = new Dictionary>(); @@ -74,6 +75,7 @@ internal void Initialize(TraceAndOptimize config) SkipRemoveEmptySubMesh = config.debugOptions.skipRemoveEmptySubMesh; SkipAnyStateToEntryExit = config.debugOptions.skipAnyStateToEntryExit; SkipRemoveMaterialUnusedProperties = config.debugOptions.skipRemoveMaterialUnusedProperties; + SkipAutoMergeBlendShape = config.debugOptions.skipAutoMergeBlendShape; Enabled = true; } diff --git a/Runtime/TraceAndOptimize.cs b/Runtime/TraceAndOptimize.cs index 5373b84a5..4fb877a6a 100644 --- a/Runtime/TraceAndOptimize.cs +++ b/Runtime/TraceAndOptimize.cs @@ -141,6 +141,8 @@ internal struct DebugOptions public bool skipAnyStateToEntryExit; [ToggleLeft] public bool skipRemoveMaterialUnusedProperties; + [ToggleLeft] + public bool skipAutoMergeBlendShape; } } } From be2988a1d7c87fe6a0add35911ddea6ce77e8fe1 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 26 Oct 2024 00:38:08 +0900 Subject: [PATCH 12/50] docs(changelog): Automatically Merge Blendshape --- CHANGELOG-PRERELEASE.md | 7 +++++++ CHANGELOG.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 5f4e96c80..c19124407 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -13,9 +13,16 @@ The format is based on [Keep a Changelog]. - You now can successfully merge Meshes with BlendShape with Merge Skinned Mesh. - Actually, previous version does not have proper consideration for BlendShape. - This version introduces options to select BlendShape behavior in Merge Skinned Mesh. +- Automatically Merge Blendshape `#1300` + - This is new automatic optimization in Trace and Optimize + - This is a part of "Optimize BlendShape" optimization. + - AAO 1.8.0 introduced BlendShape support for Merge Skinned Mesh, but new default mode "Rename to avoid conflicts" would increase number of BlendShape. + - This feature is added to relax this problem by automatically merging multiple BlendShapes of one Mesh. + - With this feature, you can use rename mode without performance loss. ### Changed - More Preference Improvement `#1288` +- Renamed "Automatically Freeze BlendShape" to "Optimize BlendShape" `#1300` ### Deprecated diff --git a/CHANGELOG.md b/CHANGELOG.md index 99c161a3e..1e349c7c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,12 @@ The format is based on [Keep a Changelog]. - When you changed shader for an material, properties for previously used shaders might be remain - This may increase your avatar size by unexpectedly including unused textures - Right-click menu option to create a new GameObject with a specified component [`#1290`](https://github.com/anatawa12/AvatarOptimizer/pull/1290) +- Automatically Merge Blendshape `#1300` + - This is new automatic optimization in Trace and Optimize + - This is a part of "Optimize BlendShape" optimization. + - AAO 1.8.0 introduced BlendShape support for Merge Skinned Mesh, but new default mode "Rename to avoid conflicts" would increase number of BlendShape. + - This feature is added to relax this problem by automatically merging multiple BlendShapes of one Mesh. + - With this feature, you can use rename mode without performance loss. ### Changed - Skip Enablement Mismatched Renderers is now disabled by default `#1169` @@ -86,6 +92,7 @@ The format is based on [Keep a Changelog]. - You now can successfully merge Meshes with BlendShape with Merge Skinned Mesh. - Actually, previous version does not have proper consideration for BlendShape. - This version introduces options to select BlendShape behavior in Merge Skinned Mesh. +- Renamed "Automatically Freeze BlendShape" to "Optimize BlendShape" `#1300` ### Deprecated From d2c89d05396edcf08463f97f1d85c4393dc72641 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 26 Oct 2024 00:39:52 +0900 Subject: [PATCH 13/50] docs(trace-and-optimize): improve Optimize BlendShape description --- .docs/content/docs/reference/trace-and-optimize/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docs/content/docs/reference/trace-and-optimize/index.md b/.docs/content/docs/reference/trace-and-optimize/index.md index bb8e8207b..a5a183dbd 100644 --- a/.docs/content/docs/reference/trace-and-optimize/index.md +++ b/.docs/content/docs/reference/trace-and-optimize/index.md @@ -26,7 +26,7 @@ We will fix it as much as we can. Currently the following optimizations are applied automatically. - `Optimize BlendShape`\ Previously known as `Freeze BlendShapes` but renamed to add more functionality.\ - Optimizes BlendShapes and remove if BlendShapes are not used or unnecessary. + Optimizes BlendShapes. Remove, freeze, or merge BlendShapes to reduce the number of BlendShapes. - `Remove unused Objects`\ By scanning animation etc., automatically removes unused Objects (e.g. GameObjects, Components).\ In addition, this will automatically toggle PhysBone Components if they are only used by toggled objects. From 6c5417ee071c7a0bd1fde2a9651733a668a5359f Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 26 Oct 2024 03:12:17 +0900 Subject: [PATCH 14/50] fix(AutoMergeBlendShape): unexpected public --- Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs index d8dd8f66e..91c9dcb06 100644 --- a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs +++ b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs @@ -10,7 +10,7 @@ namespace Anatawa12.AvatarOptimizer.Processors.TraceAndOptimizes; -public class AutoMergeBlendShape: TraceAndOptimizePass +class AutoMergeBlendShape: TraceAndOptimizePass { public override string DisplayName => "T&O: Auto Merge Blend Shape"; From f050cf48be97dff9a6f23ac1e82a827b3b02b592 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 00:27:08 +0900 Subject: [PATCH 15/50] fix: Merge Skinned Mesh with Basic Mesh is broken --- .../MergeSkinnedMeshProcessor.cs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs b/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs index c247c83a9..ea26fda57 100644 --- a/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs @@ -28,7 +28,8 @@ public MergeSkinnedMeshProcessor(MergeSkinnedMesh component) : base(component) public override void Process(BuildContext context, MeshInfo2 target) { - var meshInfos = CollectMeshInfos(context); + var (disposableList, meshInfos) = CollectMeshInfos(context); + using var _ = disposableList; GenerateWarningsOrErrors(context, Component.copyEnablementAnimation, Component.blendShapeMode, target, meshInfos); @@ -51,7 +52,7 @@ public override void Process(BuildContext context, MeshInfo2 target) RemoveOldRenderers(target, meshInfos, Component.removeEmptyRendererObject); } - public MeshInfo2[] CollectMeshInfos(BuildContext context) + public (DisposableList, MeshInfo2[] meshInfos) CollectMeshInfos(BuildContext context) { List skinnedMeshRenderers; List staticMeshRenderers; @@ -95,16 +96,24 @@ bool RendererEnabled(Renderer x) Profiler.BeginSample("Collect MeshInfos"); // Owns staticRendererMeshInfos - using var staticRendererMeshInfos = staticMeshRenderers.Select(renderer => new MeshInfo2(renderer)).ToDisposableList(); - var meshInfos = skinnedMeshRenderers.Select(context.GetMeshInfoFor) - .Concat(staticRendererMeshInfos) - .ToArray(); + var staticRendererMeshInfos = staticMeshRenderers.Select(renderer => new MeshInfo2(renderer)).ToDisposableList(); + try + { + var meshInfos = skinnedMeshRenderers.Select(context.GetMeshInfoFor) + .Concat(staticRendererMeshInfos) + .ToArray(); - foreach (var meshInfo2 in meshInfos) meshInfo2.FlattenMultiPassRendering("Merge Skinned Mesh"); - foreach (var meshInfo2 in meshInfos) meshInfo2.MakeBoned(); - Profiler.EndSample(); + foreach (var meshInfo2 in meshInfos) meshInfo2.FlattenMultiPassRendering("Merge Skinned Mesh"); + foreach (var meshInfo2 in meshInfos) meshInfo2.MakeBoned(); + Profiler.EndSample(); - return meshInfos; + return (staticRendererMeshInfos, meshInfos); + } + catch + { + staticRendererMeshInfos.Dispose(); + throw; + } } public static void GenerateWarningsOrErrors(BuildContext context, From ec48cb3aba0802e0b30fc0e0a773633aaa5ccd9f Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 00:40:34 +0900 Subject: [PATCH 16/50] fix: assertion not working --- .../AnimatorControllerPropModNode.cs | 2 +- Editor/AnimatorParserV2/PropModNode.cs | 6 ++-- Editor/AssetDescription.cs | 7 ++--- .../MergeSkinnedMeshProcessor.cs | 3 +- .../MergeToonLitMaterialProcessor.cs | 2 +- .../TraceAndOptimize/AutoMergeSkinnedMesh.cs | 2 +- .../ComponentDependencyCollector.cs | 3 +- .../FindUnusedObjectsProcessor.cs | 4 +-- .../TraceAndOptimize/OptimizeTexture.cs | 8 +++--- .../AnimatorOptimizer/EntryExitToBlendTree.cs | 28 +++++++++---------- Internal/MeshInfo2/MeshInfo2.cs | 12 +++----- Internal/Utils/EqualsHashSet.cs | 2 +- Internal/Utils/Utils.CopyDataFrom.cs | 7 ++--- Internal/Utils/Utils.cs | 10 +++++++ 14 files changed, 49 insertions(+), 47 deletions(-) diff --git a/Editor/AnimatorParserV2/AnimatorControllerPropModNode.cs b/Editor/AnimatorParserV2/AnimatorControllerPropModNode.cs index 0553459fc..8e161d377 100644 --- a/Editor/AnimatorParserV2/AnimatorControllerPropModNode.cs +++ b/Editor/AnimatorParserV2/AnimatorControllerPropModNode.cs @@ -137,7 +137,7 @@ public AnimatorLayerPropModNode(ICollection { // expected to pass list or array // ReSharper disable once PossibleMultipleEnumeration - Debug.Assert(children.Any()); + Utils.Assert(children.Any()); // ReSharper disable once PossibleMultipleEnumeration _children = children; _layerApplyState = applyState; diff --git a/Editor/AnimatorParserV2/PropModNode.cs b/Editor/AnimatorParserV2/PropModNode.cs index 8c18ccb58..3fd82fdff 100644 --- a/Editor/AnimatorParserV2/PropModNode.cs +++ b/Editor/AnimatorParserV2/PropModNode.cs @@ -5,8 +5,6 @@ using UnityEditor; using UnityEditor.Animations; using UnityEngine; -using UnityEngine.Profiling; -using Debug = System.Diagnostics.Debug; using Object = UnityEngine.Object; namespace Anatawa12.AvatarOptimizer.AnimatorParsersV2 @@ -488,7 +486,7 @@ internal class ObjectAnimationCurveNode : ImmutablePropModNode private ObjectAnimationCurveNode(AnimationClip clip, ObjectReferenceKeyframe[] frames) { - Debug.Assert(frames.Length > 0); + Utils.Assert(frames.Length > 0); Clip = clip; Frames = frames; _constantInfo = new Lazy(() => ParseProperty(frames), isThreadSafe: false); @@ -530,7 +528,7 @@ public BlendTreeNode(List> children, { // expected to pass list or array // ReSharper disable once PossibleMultipleEnumeration - Debug.Assert(children.Any()); + Utils.Assert(children.Any()); // ReSharper disable once PossibleMultipleEnumeration _children = children; _blendTreeType = blendTreeType; diff --git a/Editor/AssetDescription.cs b/Editor/AssetDescription.cs index c34de678c..ec344770b 100644 --- a/Editor/AssetDescription.cs +++ b/Editor/AssetDescription.cs @@ -5,7 +5,6 @@ using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; -using Debug = System.Diagnostics.Debug; namespace Anatawa12.AvatarOptimizer { @@ -45,7 +44,7 @@ public static IEnumerable GetMeaninglessComponents() private static Object GetMonoScriptFromGuid(string guid, ulong fileid) { var idString = $"GlobalObjectId_V1-{MonoScriptIdentifierType}-{guid}-{fileid}-0"; - Debug.Assert(GlobalObjectId.TryParse(idString, out var id)); + Utils.Assert(GlobalObjectId.TryParse(idString, out var id)); return GlobalObjectId.GlobalObjectIdentifierToObjectSlow(id); } @@ -145,8 +144,8 @@ void SetScript(Object asset) if (asset != null && type != null) { var id = GlobalObjectId.GetGlobalObjectIdSlow(asset); - Debug.Assert(id.identifierType == MonoScriptIdentifierType); - Debug.Assert(id.targetPrefabId == 0); + Utils.Assert(id.identifierType == MonoScriptIdentifierType); + Utils.Assert(id.targetPrefabId == 0); guidProperty.stringValue = id.assetGUID.ToString(); fileIDProperty.longValue = (long)id.targetObjectId; classNameProperty.stringValue = type.Name; diff --git a/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs b/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs index ea26fda57..c99287356 100644 --- a/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs @@ -4,6 +4,7 @@ using Anatawa12.AvatarOptimizer.AnimatorParsersV2; using nadena.dev.ndmf; using UnityEngine; +using UnityEngine.Assertions; using UnityEngine.Profiling; using UnityEngine.Rendering; using Debug = System.Diagnostics.Debug; @@ -340,7 +341,7 @@ TexCoordStatus TexCoordStatusMax(TexCoordStatus x, TexCoordStatus y) => var targetSubMeshIndex = subMeshIndexMap[i][j]; var targetSubMesh = target.SubMeshes[targetSubMeshIndex]; var sourceSubMesh = meshInfo.SubMeshes[j]; - System.Diagnostics.Debug.Assert(targetSubMesh.Topology == sourceSubMesh.Topology); + Utils.Assert(targetSubMesh.Topology == sourceSubMesh.Topology); targetSubMesh.Vertices.AddRange(sourceSubMesh.Vertices); mappings.Add(($"m_Materials.Array.data[{j}]", $"m_Materials.Array.data[{targetSubMeshIndex}]")); diff --git a/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs b/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs index 0702234f0..874809db0 100644 --- a/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs @@ -293,7 +293,7 @@ bool compress if (compress && IsCompressedFormat(finalFormat)) EditorUtility.CompressTexture(texture, (TextureFormat)finalFormat, TextureCompressionQuality.Normal); - System.Diagnostics.Debug.Assert(texture.format == (TextureFormat)finalFormat, + Utils.Assert(texture.format == (TextureFormat)finalFormat, $"TextureFormat mismatch: expected {finalFormat} but was {texture.format}"); return texture; diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs b/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs index 48048f45d..3ccb25b98 100644 --- a/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs +++ b/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs @@ -261,7 +261,7 @@ private static void MergeAnimatingSkinnedMesh( newSkinnedMeshRenderer.enabled = initial; } - Debug.Assert(activenessAnimatingProperties.Count == 0); + Utils.Assert(activenessAnimatingProperties.Count == 0); var newMeshInfo = context.GetMeshInfoFor(newSkinnedMeshRenderer); var meshInfosArray = meshInfos.ToArray(); diff --git a/Editor/Processors/TraceAndOptimize/ComponentDependencyCollector.cs b/Editor/Processors/TraceAndOptimize/ComponentDependencyCollector.cs index 2b7d84cf0..78ce54cc7 100644 --- a/Editor/Processors/TraceAndOptimize/ComponentDependencyCollector.cs +++ b/Editor/Processors/TraceAndOptimize/ComponentDependencyCollector.cs @@ -6,7 +6,6 @@ using nadena.dev.ndmf; using UnityEditor; using UnityEngine; -using Debug = System.Diagnostics.Debug; using Object = UnityEngine.Object; namespace Anatawa12.AvatarOptimizer.Processors.TraceAndOptimizes @@ -156,7 +155,7 @@ public Collector(ComponentDependencyCollector collector, GCComponentInfoHolder c public void Init(GCComponentInfo info) { - Debug.Assert(_info == null, "Init on not finished"); + Utils.Assert(_info == null, "Init on not finished"); _info = info; } diff --git a/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs b/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs index 6d648dc6b..34d29a6f3 100644 --- a/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs +++ b/Editor/Processors/TraceAndOptimize/FindUnusedObjectsProcessor.cs @@ -121,7 +121,7 @@ private void ActivenessAnimation(GCComponentInfoHolder componentInfos) HashSet resultSet; using (var enumerator = componentInfo.DependantComponents.GetEnumerator()) { - System.Diagnostics.Debug.Assert(enumerator.MoveNext()); + Utils.Assert(enumerator.MoveNext()); resultSet = GetEntrypointActiveness(enumerator.Current, _context); // resultSet.Count == 0 => no longer meaning @@ -182,7 +182,7 @@ private void ActivenessAnimation(GCComponentInfoHolder componentInfos) } } - System.Diagnostics.Debug.Assert(commonActiveness != null); + Utils.Assert(commonActiveness != null); } } diff --git a/Editor/Processors/TraceAndOptimize/OptimizeTexture.cs b/Editor/Processors/TraceAndOptimize/OptimizeTexture.cs index cbcb56bec..56a7c15b7 100644 --- a/Editor/Processors/TraceAndOptimize/OptimizeTexture.cs +++ b/Editor/Processors/TraceAndOptimize/OptimizeTexture.cs @@ -1260,7 +1260,7 @@ private static List UVToIslandImpl(ICollection triangles) Profiler.BeginSample("Preprocess vertices"); var indexToUv = new List(); var uvToIndex = new Dictionary(); - var inputVertToUniqueIndex = new List(); + //var inputVertToUniqueIndex = new List(); var vertexToUniqueIndex = new Dictionary(); { var uniqueUv = 0; @@ -1275,13 +1275,13 @@ private static List UVToIslandImpl(ICollection triangles) indexToUv.Add(uv); } - inputVertToUniqueIndex.Add(uvVert); + //inputVertToUniqueIndex.Add(uvVert); vertexToUniqueIndex[vertex] = uvVert; } } } - System.Diagnostics.Debug.Assert(indexToUv.Count == uvToIndex.Count); - System.Diagnostics.Debug.Assert(indexToUv.Count == inputVertToUniqueIndex.Count); + Utils.Assert(indexToUv.Count == uvToIndex.Count); + // Utils.Assert(indexToUv.Count == inputVertToUniqueIndex.Count); // Came from upstream but assertion fails. actually inputVertToUniqueIndex is unused Profiler.EndSample(); // Union-Find用のデータストラクチャーを初期化 diff --git a/Internal/AnimatorOptimizer/EntryExitToBlendTree.cs b/Internal/AnimatorOptimizer/EntryExitToBlendTree.cs index d6ec5b396..5232482f9 100644 --- a/Internal/AnimatorOptimizer/EntryExitToBlendTree.cs +++ b/Internal/AnimatorOptimizer/EntryExitToBlendTree.cs @@ -533,7 +533,7 @@ void AddFrames(int before, Motion beforeMotion, int after, Motion afterMotion) // ReSharper disable once CompareOfFloatsByEqualityOperator if (rounded == after) { - Debug.Assert((int)Mathf.Round(Utils.PreviousFloat(threshold)) == before); + Utils.Assert((int)Mathf.Round(Utils.PreviousFloat(threshold)) == before); // in this case, .5 will go the motion so default is one before .5 children.Add(CreateChild(Utils.PreviousFloat(threshold), beforeMotion)); children.Add(CreateChild(threshold, afterMotion)); @@ -541,7 +541,7 @@ void AddFrames(int before, Motion beforeMotion, int after, Motion afterMotion) // ReSharper disable once CompareOfFloatsByEqualityOperator else if (rounded == before) { - Debug.Assert((int)Mathf.Round(Utils.NextFloat(threshold)) == after); + Utils.Assert((int)Mathf.Round(Utils.NextFloat(threshold)) == after); // in this case, .5 will go to default motion so default is .5 children.Add(CreateChild(threshold, beforeMotion)); children.Add(CreateChild(Utils.NextFloat(threshold), afterMotion)); @@ -711,9 +711,9 @@ public static AnimatorCondition[][] ConvertIntOrBoolConditionToFloat(AnimatorCon threshold = Utils.PreviousFloat(threshold); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(threshold) == thresholdRound - 1); + Utils.Assert(Mathf.Round(threshold) == thresholdRound - 1); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(Utils.NextFloat(threshold)) == thresholdRound); + Utils.Assert(Mathf.Round(Utils.NextFloat(threshold)) == thresholdRound); return new[] { @@ -737,9 +737,9 @@ public static AnimatorCondition[][] ConvertIntOrBoolConditionToFloat(AnimatorCon threshold = Utils.NextFloat(threshold); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(threshold) == thresholdRound + 1); + Utils.Assert(Mathf.Round(threshold) == thresholdRound + 1); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(Utils.PreviousFloat(threshold)) == thresholdRound); + Utils.Assert(Mathf.Round(Utils.PreviousFloat(threshold)) == thresholdRound); return new[] { @@ -768,13 +768,13 @@ public static AnimatorCondition[][] ConvertIntOrBoolConditionToFloat(AnimatorCon upperThreshold = Utils.NextFloat(upperThreshold); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(lowerThreshold) == thresholdRound - 1); + Utils.Assert(Mathf.Round(lowerThreshold) == thresholdRound - 1); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(Utils.NextFloat(lowerThreshold)) == thresholdRound); + Utils.Assert(Mathf.Round(Utils.NextFloat(lowerThreshold)) == thresholdRound); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(Utils.PreviousFloat(upperThreshold)) == thresholdRound); + Utils.Assert(Mathf.Round(Utils.PreviousFloat(upperThreshold)) == thresholdRound); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(upperThreshold) == thresholdRound + 1); + Utils.Assert(Mathf.Round(upperThreshold) == thresholdRound + 1); // AND condition return new[] @@ -813,13 +813,13 @@ public static AnimatorCondition[][] ConvertIntOrBoolConditionToFloat(AnimatorCon upperThreshold = Utils.PreviousFloat(upperThreshold); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(Utils.PreviousFloat(lowerThreshold)) == thresholdRound - 1); + Utils.Assert(Mathf.Round(Utils.PreviousFloat(lowerThreshold)) == thresholdRound - 1); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(lowerThreshold) == thresholdRound); + Utils.Assert(Mathf.Round(lowerThreshold) == thresholdRound); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(upperThreshold) == thresholdRound); + Utils.Assert(Mathf.Round(upperThreshold) == thresholdRound); // ReSharper disable once CompareOfFloatsByEqualityOperator - Debug.Assert(Mathf.Round(Utils.NextFloat(upperThreshold)) == thresholdRound + 1); + Utils.Assert(Mathf.Round(Utils.NextFloat(upperThreshold)) == thresholdRound + 1); // OR condition return new[] diff --git a/Internal/MeshInfo2/MeshInfo2.cs b/Internal/MeshInfo2/MeshInfo2.cs index e47aa614d..65199a9d3 100644 --- a/Internal/MeshInfo2/MeshInfo2.cs +++ b/Internal/MeshInfo2/MeshInfo2.cs @@ -143,10 +143,10 @@ public void AssertInvariantContract(string context) Profiler.BeginSample("AssertInvariantContract"); var vertices = new HashSet(Vertices); - Debug.Assert(SubMeshes.SelectMany(x => x.Vertices).All(vertices.Contains), + Utils.Assert(SubMeshes.SelectMany(x => x.Vertices).All(vertices.Contains), $"{context}: some SubMesh has invalid triangles"); var bones = new HashSet(Bones); - Debug.Assert(Vertices.SelectMany(x => x.BoneWeights).Select(x => x.bone).All(bones.Contains), + Utils.Assert(Vertices.SelectMany(x => x.BoneWeights).Select(x => x.bone).All(bones.Contains), $"{context}: some SubMesh has invalid bone weights"); Profiler.EndSample(); } @@ -677,8 +677,6 @@ static void AddSubmesh(SubMesh subMesh, Dictionary vertexIndices, L destMesh.indexFormat = IndexFormat.UInt32; } - var submeshIndex = 0; - destMesh.subMeshCount = submeshIndexBuffers.Length; for (var i = 0; i < submeshIndexBuffers.Length; i++) @@ -687,8 +685,6 @@ static void AddSubmesh(SubMesh subMesh, Dictionary vertexIndices, L destMesh.SetIndices(indices, 0, subMesh.Vertices.Count, subMesh.Topology, i, baseVertex: baseVertex); } - - Debug.Assert(submeshIndexBuffers.Length == submeshIndex); } Profiler.EndSample(); @@ -745,7 +741,7 @@ static void AddSubmesh(SubMesh subMesh, Dictionary vertexIndices, L for (var i = 0; i < BlendShapes.Count; i++) { Profiler.BeginSample("Process Shape"); - Debug.Assert(destMesh.blendShapeCount == i, "Unexpected state: BlendShape count"); + Utils.Assert(destMesh.blendShapeCount == i, "Unexpected state: BlendShape count"); var (shapeName, _) = BlendShapes[i]; Profiler.BeginSample("Collect Weights"); @@ -923,7 +919,7 @@ public List Triangles { get { - Debug.Assert(Topology == MeshTopology.Triangles); + Utils.Assert(Topology == MeshTopology.Triangles); return Vertices; } } diff --git a/Internal/Utils/EqualsHashSet.cs b/Internal/Utils/EqualsHashSet.cs index 9a1cb9a15..1c95f78b2 100644 --- a/Internal/Utils/EqualsHashSet.cs +++ b/Internal/Utils/EqualsHashSet.cs @@ -10,7 +10,7 @@ public sealed class EqualsHashSet : IEquatable> public EqualsHashSet(HashSet backedSet) { if (backedSet == null) throw new ArgumentNullException(nameof(backedSet)); - System.Diagnostics.Debug.Assert(Equals(backedSet.Comparer, EqualityComparer.Default)); + Utils.Assert(Equals(backedSet.Comparer, EqualityComparer.Default)); this.backedSet = backedSet; } diff --git a/Internal/Utils/Utils.CopyDataFrom.cs b/Internal/Utils/Utils.CopyDataFrom.cs index 98d5d1dda..e5b639198 100644 --- a/Internal/Utils/Utils.CopyDataFrom.cs +++ b/Internal/Utils/Utils.CopyDataFrom.cs @@ -1,6 +1,5 @@ using System; using UnityEditor; -using UnityEngine.Assertions; namespace Anatawa12.AvatarOptimizer { @@ -25,14 +24,14 @@ void CopyBetweenTwoRecursively(SerializedProperty src, SerializedProperty dst) while (srcIter.Next(enterChildren) && !SerializedProperty.EqualContents(srcIter, srcEnd)) { var destCheck = dstIter.Next(enterChildren) && !SerializedProperty.EqualContents(dstIter, dstEnd); - Assert.IsTrue(destCheck); + Utils.Assert(destCheck); //Debug.Log($"prop: {dstIter.propertyPath}: {dstIter.propertyType}"); switch (dstIter.propertyType) { case SerializedPropertyType.FixedBufferSize: - Assert.AreEqual(srcIter.fixedBufferSize, dstIter.fixedBufferSize); + Utils.Assert(srcIter.fixedBufferSize == dstIter.fixedBufferSize); break; case SerializedPropertyType.Generic: break; @@ -47,7 +46,7 @@ void CopyBetweenTwoRecursively(SerializedProperty src, SerializedProperty dst) { var destCheck = dstIter.NextVisible(enterChildren) && !SerializedProperty.EqualContents(dstIter, dstEnd); - Assert.IsFalse(destCheck); + Utils.Assert(!destCheck); } } diff --git a/Internal/Utils/Utils.cs b/Internal/Utils/Utils.cs index e0efea931..c3fda1624 100644 --- a/Internal/Utils/Utils.cs +++ b/Internal/Utils/Utils.cs @@ -363,6 +363,16 @@ bool ComputeSafeToUseFastImplementation() } } } + + public static void Assert(bool condition) + { + if (!condition) throw new InvalidOperationException("assertion failed"); + } + + public static void Assert(bool condition, string message) + { + if (!condition) throw new InvalidOperationException(message); + } /// /// Fast compare of array. From 9ba990764e54e4351a707f4579ee703017ac7fe1 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 00:42:19 +0900 Subject: [PATCH 17/50] docs(changelog): Merge Skinned Mesh with Basic Mesh is not working --- CHANGELOG-PRERELEASE.md | 2 ++ CHANGELOG.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 2af02cc24..9d27e11a9 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -19,6 +19,8 @@ The format is based on [Keep a Changelog]. - NRE if specified expression parameters is None `#1303` - This error only happens if you don't use Modular Avatar since Modular Avatar will assign parameters asset. - "asset is not temporary asset" error if no Modular Avatar is used `#1304` +- Merge Skinned Mesh with Basic Mesh is not working `#1307` +- Validation system in Avatar Optimizer is not working `#1307` ### Security diff --git a/CHANGELOG.md b/CHANGELOG.md index 62427614d..0628f0c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ The format is based on [Keep a Changelog]. - Renamed debug options internally `#1228` - This will lose previously configured debug options. - However, debug options are not considered as Public API as stated in documents so this is not backward incompatible changes in semver 2.0.0 section 8. -- Performance Improvements `#1234` `#1243` `#1240` `#1288` `#1304` +- Performance Improvements `#1234` `#1243` `#1240` `#1288` `#1304` `#1307` - Transform gizmo are now hidden while you're editing box of Remove Mesh in Box `#1259` - This prevents mistakenly moving the Skinned Mesh Renderer while editing the box. - Make MergePhysBone implement `INetworkID` `#1260` From 39a1d852282616fb2888dae7566cdc59b0bd098e Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 14:29:32 +0900 Subject: [PATCH 18/50] feat: Automatically Merge Meshes with BlendShapes --- .../TraceAndOptimize/AutoMergeSkinnedMesh.cs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs b/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs index 3ccb25b98..3266a3f93 100644 --- a/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs +++ b/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs @@ -47,6 +47,22 @@ public static List FilterMergeMeshes(BuildContext context, TraceAndOp } } + // if MMD World Compatibility is enabled, body mesh will be animated by MMD World + if (state.MmdWorldCompatibility) + { + var mmdBody = context.AvatarRootTransform.Find("Body"); + if (mmdBody != null) + { + var mmdBodyRenderer = mmdBody.GetComponent(); + if (mmdBodyRenderer != null) + { + componentInfos.GetInfo(mmdBodyRenderer) + .DependantEntrypoint + .Add(context.AvatarRootTransform, GCComponentInfo.DependencyType.Normal); + } + } + } + Profiler.EndSample(); Profiler.BeginSample("Collect Merging Targets"); @@ -78,8 +94,8 @@ public static List FilterMergeMeshes(BuildContext context, TraceAndOp meshInfo.Bones.Count > 0 // FlattenMultiPassRendering will increase polygon count by VRChat so it's not good for T&O && meshInfo.SubMeshes.All(x => x.SharedMaterials.Length == 1) - // Merging Meshes with BlendShapes can increase rendering cost or break the animation - && meshInfo.BlendShapes.Count == 0 + // Since 1.8.0 (targets 2022) we merge meshes with BlendShapes with RenameToAvoidConflict + // && meshInfo.BlendShapes.Count == 0 // Animating renderer is not supported by this optimization && !IsAnimatedForbidden(context.GetAnimationComponent(meshRenderer)) // any other components are not supported @@ -415,12 +431,10 @@ private static (Activeness, EqualsHashSet<(bool initial, EqualsHashSet x is not AnimatorParsersV2.AnimatorPropModNode)) return null; - if (property.StartsWith("blendShape.", StringComparison.Ordinal)) - continue; - float? defaultValue; if (node.ApplyState == AnimatorParsersV2.ApplyState.Always) { @@ -634,8 +648,8 @@ private static bool IsAnimatedForbidden(AnimationComponentInfo com // Note: when you added some other allowed properties, // You have to add default value handling in GetDefaultValue below - // blendShapes are removed so it's allowed - if (name.StartsWith("blendShapes.", StringComparison.Ordinal)) continue; + // blendShapes are renamed to avoid conflict, so it's allowed + if (name.StartsWith("blendShape.", StringComparison.Ordinal)) continue; // material properties are allowed, will be merged if animated similarly if (name.StartsWith("material.", StringComparison.Ordinal)) continue; // other float properties are forbidden @@ -684,12 +698,6 @@ private static bool IsAnimatedForbidden(AnimationComponentInfo com } } - if (property.StartsWith("blendShapes.", StringComparison.Ordinal)) - { - var blendShapeName = property.Substring("blendShapes.".Length); - return meshInfo.BlendShapes.FirstOrDefault(x => x.name == blendShapeName).weight; - } - throw new InvalidOperationException($"AAO forgot to implement handling for {property}"); } From 0e1cea06600bdfa9c2ea3f00ada93285eacd1bda Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 14:40:37 +0900 Subject: [PATCH 19/50] docs(changelog): Automatically merging meshes with BlendShapes --- CHANGELOG-PRERELEASE.md | 6 ++++++ CHANGELOG.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 9d27e11a9..dfae43084 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -8,6 +8,12 @@ The format is based on [Keep a Changelog]. ## [Unreleased] ### Added +- Automatically merging meshes with BlendShapes `#1308` + - In previous version of Avatar Optimizer, meshes with BlendShapes are not automatically merged. + - This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019. + - However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices. + - This means that increasing the number of vertices in a mesh with BlendShapes does not increase the load of BlendShape manipulation much. + - Therefore, we decided to automatically merge meshes with BlendShapes. ### Changed diff --git a/CHANGELOG.md b/CHANGELOG.md index 0628f0c3c..f21cc74c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,12 @@ The format is based on [Keep a Changelog]. - When you changed shader for an material, properties for previously used shaders might be remain - This may increase your avatar size by unexpectedly including unused textures - Right-click menu option to create a new GameObject with a specified component [`#1290`](https://github.com/anatawa12/AvatarOptimizer/pull/1290) +- Automatically merging meshes with BlendShapes `#1308` + - In previous version of Avatar Optimizer, meshes with BlendShapes are not automatically merged. + - This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019. + - However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices. + - This means that increasing the number of vertices in a mesh with BlendShapes does not increase the load of BlendShape manipulation much. + - Therefore, we decided to automatically merge meshes with BlendShapes. ### Changed - Skip Enablement Mismatched Renderers is now disabled by default `#1169` From a92eccb23894184e8c5db6a5603299684c5cc2b2 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 14:46:12 +0900 Subject: [PATCH 20/50] docs(merge-smr): remove recommendation to not merge face mesh --- .../docs/reference/merge-skinned-mesh/index.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.md b/.docs/content/docs/reference/merge-skinned-mesh/index.md index 73e282ce4..75a1d2d12 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.md @@ -27,19 +27,17 @@ If you are using [Modular Avatar], you can add [`MA Mesh Settings`] component to {{< /hint >}} -This component is good for merging your cloth meshes and body meshes but not good for face meshes because BlendShape can cause performance impact. -BlendShape is a feature became heavier in proportion to the count of vertices and BlendShapes. -Merging SkinnedMesh increases vertices and face mesh usually have many BlendShapes. -That's why it's not good to merge face meshes. - -In addition, because of same reasons, you should freeze & remove unchanging BlendShapes for body / cloth meshes. +It's better freeze & remove unchanging BlendShapes for body / cloth meshes to reduce BlendShape load. You can freeze & remove BlendShape using [Freeze BlendShape](../freeze-blendshape) component. Add this component to both/either merge source SkinnedMeshRenderer and/or merged SkinnedMeshRenderer to freeze & remove BlendShapes. Also, you can use `Automatically Freeze BlendShape` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. +In previous versions of Avatar Optimizer, Unity 2019 recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much. +However, in Unity 2022, we no longer recommends not merging face meshes because the BlendShape load has been improved. + {{< hint info >}} -[Trace And Optimize](../trace-and-optimize) will automatically do the same process, so in some cases you do not need to use this component. +[Trace And Optimize](../trace-and-optimize) will automatically do the same process, so in most cases you do not need to use this component. {{< /hint >}} From 646ee962027857272d519d55fbf229055d93bdca Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 15:07:13 +0900 Subject: [PATCH 21/50] chore: show version name on NDMF Console --- Editor/OptimizerPlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/OptimizerPlugin.cs b/Editor/OptimizerPlugin.cs index 0d44e99b0..ca79f97b9 100644 --- a/Editor/OptimizerPlugin.cs +++ b/Editor/OptimizerPlugin.cs @@ -9,7 +9,7 @@ namespace Anatawa12.AvatarOptimizer.ndmf { internal class OptimizerPlugin : Plugin { - public override string DisplayName => "AAO: Avatar Optimizer"; + public override string DisplayName => $"AAO: Avatar Optimizer ({CheckForUpdate.Checker.CurrentVersionName})"; public override string QualifiedName => "com.anatawa12.avatar-optimizer"; From aa08af1b445eddd36056e701577f2bc5215d054f Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 27 Oct 2024 15:11:18 +0900 Subject: [PATCH 22/50] docs(changelog): Show version name on NDMF Console --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 9d27e11a9..2f1dc143d 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog]. ### Added ### Changed +- Show version name on NDMF Console `#1309` ### Deprecated diff --git a/CHANGELOG.md b/CHANGELOG.md index 0628f0c3c..0c75b788c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ The format is based on [Keep a Changelog]. - Now you can rename BlendShape to avoid conflicts. - NRE if specified expression parameters is None `#1303` - This error only happens if you don't use Modular Avatar since Modular Avatar will assign parameters asset. +- Show version name on NDMF Console `#1309` ### Security From 56c1fdd6baef3f6a61c662090834c27e03997d43 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2024 06:30:52 +0000 Subject: [PATCH 23/50] chore: bump version to 1.8.0-beta.11 --- CHANGELOG-PRERELEASE.md | 20 +++++++++++++------- package.json | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 2f1dc143d..70f0373af 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -10,21 +10,26 @@ The format is based on [Keep a Changelog]. ### Added ### Changed -- Show version name on NDMF Console `#1309` ### Deprecated ### Removed ### Fixed -- NRE if specified expression parameters is None `#1303` - - This error only happens if you don't use Modular Avatar since Modular Avatar will assign parameters asset. -- "asset is not temporary asset" error if no Modular Avatar is used `#1304` -- Merge Skinned Mesh with Basic Mesh is not working `#1307` -- Validation system in Avatar Optimizer is not working `#1307` ### Security +## [1.8.0-beta.11] - 2024-10-27 +### Changed +- Show version name on NDMF Console [`#1309`](https://github.com/anatawa12/AvatarOptimizer/pull/1309) + +### Fixed +- NRE if specified expression parameters is None [`#1303`](https://github.com/anatawa12/AvatarOptimizer/pull/1303) + - This error only happens if you don't use Modular Avatar since Modular Avatar will assign parameters asset. +- "asset is not temporary asset" error if no Modular Avatar is used [`#1304`](https://github.com/anatawa12/AvatarOptimizer/pull/1304) +- Merge Skinned Mesh with Basic Mesh is not working [`#1307`](https://github.com/anatawa12/AvatarOptimizer/pull/1307) +- Validation system in Avatar Optimizer is not working [`#1307`](https://github.com/anatawa12/AvatarOptimizer/pull/1307) + ## [1.8.0-beta.10] - 2024-10-26 ### Added - Right-click menu option to create a new GameObject with a specified component [`#1290`](https://github.com/anatawa12/AvatarOptimizer/pull/1290) @@ -1690,7 +1695,8 @@ This release is mistake. - Merge Bone - Clear Endpoint Position -[Unreleased]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.8.0-beta.10...HEAD +[Unreleased]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.8.0-beta.11...HEAD +[1.8.0-beta.11]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.8.0-beta.10...v1.8.0-beta.11 [1.8.0-beta.10]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.8.0-beta.9...v1.8.0-beta.10 [1.8.0-beta.9]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.8.0-beta.8...v1.8.0-beta.9 [1.8.0-beta.8]: https://github.com/anatawa12/AvatarOptimizer/compare/v1.8.0-beta.7...v1.8.0-beta.8 diff --git a/package.json b/package.json index 5988207c4..70d035850 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.anatawa12.avatar-optimizer", - "version": "1.8.0-beta.10", + "version": "1.8.0-beta.11", "private": true, "private-beta": false, "unity": "2022.3", From f87fc949caec98c80541cc9a5c1321b78ac65845 Mon Sep 17 00:00:00 2001 From: kaikoga Date: Sun, 27 Oct 2024 18:05:28 +0900 Subject: [PATCH 24/50] chore: add version defines for VRCSDK --- Editor/NewGameObjectAndAddComponent.cs | 2 ++ Editor/ObjectMapping/ObjectMappingContext.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Editor/NewGameObjectAndAddComponent.cs b/Editor/NewGameObjectAndAddComponent.cs index df0cd567d..3beae629c 100644 --- a/Editor/NewGameObjectAndAddComponent.cs +++ b/Editor/NewGameObjectAndAddComponent.cs @@ -41,7 +41,9 @@ private static void CreateAndAddComponent(string componentName) where T : Mon [MenuItem(BASE_PATH + MERGE_PHYSBONE, true, PRIORITY)] private static bool ValidateCreateMergePhysBone() => Selection.activeGameObject != null; +#if AAO_VRCSDK3_AVATARS [MenuItem(BASE_PATH + MERGE_PHYSBONE, false, PRIORITY)] private static void CreateMergePhysBone() => CreateAndAddComponent(MERGE_PHYSBONE); +#endif } } diff --git a/Editor/ObjectMapping/ObjectMappingContext.cs b/Editor/ObjectMapping/ObjectMappingContext.cs index 30722bf56..cf01cfc84 100644 --- a/Editor/ObjectMapping/ObjectMappingContext.cs +++ b/Editor/ObjectMapping/ObjectMappingContext.cs @@ -48,7 +48,9 @@ public void OnDeactivate(BuildContext context) switch (component) { case Animator _: +#if AAO_VRCSDK3_AVATARS case VRC.SDK3.Avatars.Components.VRCAvatarDescriptor _: +#endif #if AAO_VRM0 case VRM.VRMBlendShapeProxy _: #endif From 0aefca13fac7b0dc98c8ddc1339d7cc93b84d8ed Mon Sep 17 00:00:00 2001 From: kaikoga Date: Sun, 27 Oct 2024 18:52:39 +0900 Subject: [PATCH 25/50] docs(changelog): Fix non-VRChat project support --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 70f0373af..4b9525fa4 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog]. ### Removed ### Fixed +- Fix non-VRChat project support `#1310` ### Security diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c75b788c..1802ec5c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ The format is based on [Keep a Changelog]. - NRE if specified expression parameters is None `#1303` - This error only happens if you don't use Modular Avatar since Modular Avatar will assign parameters asset. - Show version name on NDMF Console `#1309` +- Fix non-VRChat project support `#1310` ### Security From 03d4abc34d73339c2b912b4a5a48af490ae3f1f9 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Mon, 28 Oct 2024 09:39:27 +0900 Subject: [PATCH 26/50] fix: 'shader' doesn't have a float or range property 'prop' error --- API-Editor/ShaderInformation.cs | 6 +++--- Editor/Processors/ShaderMaterialInformation.cs | 6 +++--- .../TraceAndOptimize/AutoMergeSkinnedMesh.cs | 18 +++++++++--------- Internal/Utils/Utils.cs | 12 ++++++++++++ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/API-Editor/ShaderInformation.cs b/API-Editor/ShaderInformation.cs index 341aeba2d..9f889f815 100644 --- a/API-Editor/ShaderInformation.cs +++ b/API-Editor/ShaderInformation.cs @@ -202,7 +202,7 @@ internal MaterialInformationCallback() /// /// The name of the property in the material. /// Whether to consider the animation of the property. If this is true, this function will never - /// The integer value for the property in the material, which is same as , or null if the property is not set or not found. + /// The integer value for the property in the material, which is same as , or null if the property is animated. [PublicAPI] public abstract int? GetInteger(string propertyName, bool considerAnimation = true); @@ -211,7 +211,7 @@ internal MaterialInformationCallback() /// /// The name of the property in the material. /// Whether to consider the animation of the property. If this is true, this function will never - /// The integer value for the property in the material, which is same as , or null if the property is not set or not found. + /// The float value for the property in the material, which is same as , or null if the property is animated. [PublicAPI] public abstract float? GetFloat(string propertyName, bool considerAnimation = true); @@ -220,7 +220,7 @@ internal MaterialInformationCallback() /// /// The name of the property in the material. /// Whether to consider the animation of the property. If this is true, this function will never - /// The integer value for the property in the material, which is same as , or null if the property is not set or not found. + /// The integer value for the property in the material, which is same as , or null if the property is animated. [PublicAPI] public abstract Vector4? GetVector(string propertyName, bool considerAnimation = true); diff --git a/Editor/Processors/ShaderMaterialInformation.cs b/Editor/Processors/ShaderMaterialInformation.cs index 9739dfd7b..1ac58edca 100644 --- a/Editor/Processors/ShaderMaterialInformation.cs +++ b/Editor/Processors/ShaderMaterialInformation.cs @@ -124,13 +124,13 @@ public MaterialInformationCallbackImpl(Material material, ShaderInformationKind } public override int? GetInteger(string propertyName, bool considerAnimation = true) => - GetValue(propertyName, _material.GetInt, considerAnimation); + GetValue(propertyName, _material.SafeGetInteger, considerAnimation); public override float? GetFloat(string propertyName, bool considerAnimation = true) => - GetValue(propertyName, _material.GetFloat, considerAnimation); + GetValue(propertyName, _material.SafeGetFloat, considerAnimation); public override Vector4? GetVector(string propertyName, bool considerAnimation = true) => - GetValue(propertyName, _material.GetVector, considerAnimation); + GetValue(propertyName, _material.SafeGetVector, considerAnimation); public override void RegisterOtherUVUsage(UsingUVChannels uvChannel) { diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs b/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs index 3ccb25b98..563cf0b2f 100644 --- a/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs +++ b/Editor/Processors/TraceAndOptimize/AutoMergeSkinnedMesh.cs @@ -666,21 +666,21 @@ private static bool IsAnimatedForbidden(AnimationComponentInfo com return channel switch { - 'x' => material.GetVector(propertyName).x, - 'y' => material.GetVector(propertyName).y, - 'z' => material.GetVector(propertyName).z, - 'w' => material.GetVector(propertyName).w, - 'r' => material.GetColor(propertyName).r, - 'g' => material.GetColor(propertyName).g, - 'b' => material.GetColor(propertyName).b, - 'a' => material.GetColor(propertyName).a, + 'x' => material.SafeGetVector(propertyName).x, + 'y' => material.SafeGetVector(propertyName).y, + 'z' => material.SafeGetVector(propertyName).z, + 'w' => material.SafeGetVector(propertyName).w, + 'r' => material.SafeGetColor(propertyName).r, + 'g' => material.SafeGetColor(propertyName).g, + 'b' => material.SafeGetColor(propertyName).b, + 'a' => material.SafeGetColor(propertyName).a, _ => null }; } else { // float - return material.GetFloat(materialProperty); + return material.SafeGetFloat(materialProperty); } } diff --git a/Internal/Utils/Utils.cs b/Internal/Utils/Utils.cs index c3fda1624..bd54760ee 100644 --- a/Internal/Utils/Utils.cs +++ b/Internal/Utils/Utils.cs @@ -374,6 +374,18 @@ public static void Assert(bool condition, string message) if (!condition) throw new InvalidOperationException(message); } + public static float SafeGetFloat(this Material material, string propertyName) => + material.HasProperty(propertyName) ? material.GetFloat(propertyName) : 0; + + public static int SafeGetInteger(this Material material, string propertyName) => + material.HasProperty(propertyName) ? material.GetInteger(propertyName) : 0; + + public static Vector4 SafeGetVector(this Material material, string propertyName) => + material.HasProperty(propertyName) ? material.GetVector(propertyName) : Vector4.zero; + + public static Color SafeGetColor(this Material material, string propertyName) => + material.HasProperty(propertyName) ? material.GetColor(propertyName) : Color.clear; + /// /// Fast compare of array. /// From e02962dffb36aeeae2cec7b0d62ccfedb80f9d60 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Mon, 28 Oct 2024 09:41:19 +0900 Subject: [PATCH 27/50] docs(changelog): 'shader' doesn't have a float or range property 'prop' error --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 4b9525fa4..803fb3a66 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog]. ### Fixed - Fix non-VRChat project support `#1310` +- 'shader' doesn't have a float or range property 'prop' error `#1312` ### Security diff --git a/CHANGELOG.md b/CHANGELOG.md index 1802ec5c0..28e49f0fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ The format is based on [Keep a Changelog]. - This error only happens if you don't use Modular Avatar since Modular Avatar will assign parameters asset. - Show version name on NDMF Console `#1309` - Fix non-VRChat project support `#1310` +- 'shader' doesn't have a float or range property 'prop' error `#1312` ### Security From 702d85ec58f98d927bad2f29abdbb2b0d212b435 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 29 Oct 2024 09:50:30 +0900 Subject: [PATCH 28/50] fix: NativeArray leak --- Internal/MeshInfo2/MeshInfo2.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Internal/MeshInfo2/MeshInfo2.cs b/Internal/MeshInfo2/MeshInfo2.cs index 65199a9d3..a9ba9d390 100644 --- a/Internal/MeshInfo2/MeshInfo2.cs +++ b/Internal/MeshInfo2/MeshInfo2.cs @@ -1285,6 +1285,7 @@ public ApplyFrame2Array GetApplyFramesInfo(string shapeName, float weight, bool public void Dispose() { + Utils.DisposeAll(DeltaVertices.Concat(DeltaNormals).Concat(DeltaTangents)); } } From 308074a0958d7eedb7df80c86277c0321fc3cf80 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 29 Oct 2024 09:55:43 +0900 Subject: [PATCH 29/50] fix: Integer and Int confusion --- API-Editor/ShaderInformation.cs | 9 +++ .../APIInternal/ShaderInformation.Liltoon.cs | 66 +++++++++---------- .../Processors/ShaderMaterialInformation.cs | 3 + Internal/Utils/Utils.cs | 3 + 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/API-Editor/ShaderInformation.cs b/API-Editor/ShaderInformation.cs index 9f889f815..c460be816 100644 --- a/API-Editor/ShaderInformation.cs +++ b/API-Editor/ShaderInformation.cs @@ -206,6 +206,15 @@ internal MaterialInformationCallback() [PublicAPI] public abstract int? GetInteger(string propertyName, bool considerAnimation = true); + /// + /// Returns the int value for the property in the material, or null if the property is not set or not found. + /// + /// The name of the property in the material. + /// Whether to consider the animation of the property. If this is true, this function will never + /// The int value for the property in the material, which is same as , or null if the property is animated. + [PublicAPI] + public abstract int? GetInt(string propertyName, bool considerAnimation = true); + /// /// Returns the float value for the property in the material, or null if the property is not set or not found. /// diff --git a/Editor/APIInternal/ShaderInformation.Liltoon.cs b/Editor/APIInternal/ShaderInformation.Liltoon.cs index fc4a83f06..b6d4f8117 100644 --- a/Editor/APIInternal/ShaderInformation.Liltoon.cs +++ b/Editor/APIInternal/ShaderInformation.Liltoon.cs @@ -131,13 +131,13 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) UsingUVChannels.NonMesh, null); // GradationMap UV is based on color LIL_SAMPLE_2D_WithMat("_MainColorAdjustMask", "_MainTex", uvMain, uvMainMatrix); // simple LIL_SAMPLE_2D - if (matInfo.GetInteger("_UseMain2ndTex") != 0) + if (matInfo.GetInt("_UseMain2ndTex") != 0) { // caller of lilGetMain2nd will pass sampler for _MainTex as samp SamplerStateInformation samp = "_MainTex"; UsingUVChannels uv2nd; - switch (matInfo.GetInteger("_Main2ndTex_UVMode")) + switch (matInfo.GetInt("_Main2ndTex_UVMode")) { case 0: uv2nd = UsingUVChannels.UV0; @@ -172,13 +172,13 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) ); } - if (matInfo.GetInteger("_UseMain3rdTex") != 0) + if (matInfo.GetInt("_UseMain3rdTex") != 0) { // caller of lilGetMain3rd will pass sampler for _MainTex as samp var samp = "_MainTex"; UsingUVChannels uv3rd; - switch (matInfo.GetInteger("_Main2ndTex_UVMode")) + switch (matInfo.GetInt("_Main2ndTex_UVMode")) { case 0: uv3rd = UsingUVChannels.UV0; @@ -214,16 +214,16 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) } LIL_SAMPLE_2D_ST_WithMat("_AlphaMask", "_MainTex", uvMain, uvMainMatrix); - if (matInfo.GetInteger("_UseBumpMap") != 0) + if (matInfo.GetInt("_UseBumpMap") != 0) { LIL_SAMPLE_2D_ST_WithMat("_BumpMap", "_MainTex", uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_UseBump2ndMap") != 0) + if (matInfo.GetInt("_UseBump2ndMap") != 0) { var uvBump2nd = UsingUVChannels.UV0; - switch (matInfo.GetInteger("_Bump2ndMap_UVMode")) + switch (matInfo.GetInt("_Bump2ndMap_UVMode")) { case 0: uvBump2nd = UsingUVChannels.UV0; @@ -248,7 +248,7 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) // Note: _Bump2ndScaleMask is defined as NoScaleOffset but sampled with LIL_SAMPLE_2D_ST? } - if (matInfo.GetInteger("_UseAnisotropy") != 0) + if (matInfo.GetInt("_UseAnisotropy") != 0) { LIL_SAMPLE_2D_ST_WithMat("_AnisotropyTangentMap", "_MainTex", uvMain, uvMainMatrix); LIL_SAMPLE_2D_ST_WithMat("_AnisotropyScaleMask", "_MainTex", uvMain, uvMainMatrix); @@ -257,13 +257,13 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) LIL_SAMPLE_2D_ST_WithMat("_AnisotropyShiftNoiseMask", "_MainTex", uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_UseBacklight") != 0) + if (matInfo.GetInt("_UseBacklight") != 0) { var samp = "_MainTex"; LIL_SAMPLE_2D_ST_WithMat("_BacklightColorTex", samp, uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_UseShadow") != 0) + if (matInfo.GetInt("_UseShadow") != 0) { SamplerStateInformation samp = "_MainTex"; LIL_SAMPLE_2D_GRAD_WithMat("_ShadowStrengthMask", SamplerStateInformation.LinearRepeatSampler, uvMain, @@ -273,7 +273,7 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) LIL_SAMPLE_2D_GRAD_WithMat("_ShadowBlurMask", SamplerStateInformation.LinearRepeatSampler, uvMain, uvMainMatrix); // lilSampleLUT - switch (matInfo.GetInteger("_ShadowColorType")) + switch (matInfo.GetInt("_ShadowColorType")) { case 1: LIL_SAMPLE_2D_WithMat("_ShadowColorTex", SamplerStateInformation.LinearClampSampler, @@ -297,14 +297,14 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) } } - if (matInfo.GetInteger("_UseRimShade") != 0) + if (matInfo.GetInt("_UseRimShade") != 0) { var samp = "_MainTex"; LIL_SAMPLE_2D_WithMat("_RimShadeMask", samp, uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_UseReflection") != 0) + if (matInfo.GetInt("_UseReflection") != 0) { // TODO: research var samp = "_MainTex"; // or SamplerStateInformation.LinearRepeatSampler in lil_pass_foreward_reblur.hlsl @@ -315,45 +315,45 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) } // Matcap - if (matInfo.GetInteger("_UseMatCap") != 0) + if (matInfo.GetInt("_UseMatCap") != 0) { var samp = "_MainTex"; // caller of lilGetMatCap LIL_SAMPLE_2D("_MatCapTex", SamplerStateInformation.LinearRepeatSampler, UsingUVChannels.NonMesh); LIL_SAMPLE_2D_ST_WithMat("_MatCapBlendMask", samp, uvMain, uvMainMatrix); - if (matInfo.GetInteger("_MatCapCustomNormal") != 0) + if (matInfo.GetInt("_MatCapCustomNormal") != 0) { LIL_SAMPLE_2D_ST_WithMat("_MatCapBumpMap", samp, uvMain, uvMainMatrix); } } - if (matInfo.GetInteger("_UseMatCap2nd") != 0) + if (matInfo.GetInt("_UseMatCap2nd") != 0) { var samp = "_MainTex"; // caller of lilGetMatCap LIL_SAMPLE_2D("_MatCap2ndTex", SamplerStateInformation.LinearRepeatSampler, UsingUVChannels.NonMesh); LIL_SAMPLE_2D_ST_WithMat("_MatCap2ndBlendMask", samp, uvMain, uvMainMatrix); - if (matInfo.GetInteger("_MatCap2ndCustomNormal") != 0) + if (matInfo.GetInt("_MatCap2ndCustomNormal") != 0) { LIL_SAMPLE_2D_ST_WithMat("_MatCap2ndBumpMap", samp, uvMain, uvMainMatrix); } } // rim light - if (matInfo.GetInteger("_UseRim") != 0) + if (matInfo.GetInt("_UseRim") != 0) { var samp = "_MainTex"; // caller of lilGetRim LIL_SAMPLE_2D_ST_WithMat("_RimColorTex", samp, uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_UseGlitter") != 0) + if (matInfo.GetInt("_UseGlitter") != 0) { var samp = "_MainTex"; // caller of lilGetGlitter LIL_SAMPLE_2D_ST_WithMat("_GlitterColorTex", samp, uvMain, uvMainMatrix); - if (matInfo.GetInteger("_GlitterApplyShape") != 0) + if (matInfo.GetInt("_GlitterApplyShape") != 0) { // complex uv LIL_SAMPLE_2D_GRAD("_GlitterShapeTex", SamplerStateInformation.LinearClampSampler, @@ -361,11 +361,11 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) } } - if (matInfo.GetInteger("_UseEmission") != 0) + if (matInfo.GetInt("_UseEmission") != 0) { UsingUVChannels emissionUV = UsingUVChannels.UV0; - switch (matInfo.GetInteger("_EmissionMap_UVMode")) + switch (matInfo.GetInt("_EmissionMap_UVMode")) { case 1: emissionUV = UsingUVChannels.UV1; @@ -403,17 +403,17 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) LIL_GET_EMIMASK_WithMat("_EmissionBlendMask", uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_EmissionUseGrad") != 0) + if (matInfo.GetInt("_EmissionUseGrad") != 0) { LIL_SAMPLE_1D("_EmissionGradTex", SamplerStateInformation.LinearRepeatSampler, UsingUVChannels.NonMesh); } } - if (matInfo.GetInteger("_UseEmission2nd") != 0) + if (matInfo.GetInt("_UseEmission2nd") != 0) { UsingUVChannels emission2ndUV = UsingUVChannels.UV0; - switch (matInfo.GetInteger("_Emission2ndMap_UVMode")) + switch (matInfo.GetInt("_Emission2ndMap_UVMode")) { case 1: emission2ndUV = UsingUVChannels.UV1; @@ -453,28 +453,28 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) LIL_GET_EMIMASK_WithMat("_Emission2ndBlendMask", uvMain, uvMainMatrix); } - if (matInfo.GetInteger("_Emission2ndUseGrad") != 0) + if (matInfo.GetInt("_Emission2ndUseGrad") != 0) { LIL_SAMPLE_1D("_Emission2ndGradTex", SamplerStateInformation.LinearRepeatSampler, UsingUVChannels.NonMesh); } } - if (matInfo.GetInteger("_UseParallax") != 0) + if (matInfo.GetInt("_UseParallax") != 0) { matInfo.RegisterTextureUVUsage("_ParallaxMap", SamplerStateInformation.LinearRepeatSampler, UsingUVChannels.UV0, null); } - if (matInfo.GetInteger("_UseAudioLink") != 0 && matInfo.GetInteger("_AudioLink2Vertex") != 0) + if (matInfo.GetInt("_UseAudioLink") != 0 && matInfo.GetInt("_AudioLink2Vertex") != 0) { - var _AudioLinkUVMode = matInfo.GetInteger("_AudioLinkUVMode"); + var _AudioLinkUVMode = matInfo.GetInt("_AudioLinkUVMode"); if (_AudioLinkUVMode is 3 or 4 or null) { // TODO: _AudioLinkMask_ScrollRotate var sampler = "_AudioLinkMask" | SamplerStateInformation.LinearRepeatSampler; - switch (matInfo.GetInteger("_AudioLinkMask_UVMode")) + switch (matInfo.GetInt("_AudioLinkMask_UVMode")) { case 0: default: @@ -518,7 +518,7 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) ); } - if (matInfo.GetInteger("_UseOutline") != 0) + if (matInfo.GetInt("_UseOutline") != 0) { // not on material side, on editor side toggle LIL_SAMPLE_2D_WithMat("_OutlineTex", "_OutlineTex", uvMain, uvMainMatrix); @@ -527,7 +527,7 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) // _OutlineVectorTex SamplerStateInformation.LinearRepeatSampler // UVs _OutlineVectorUVMode main,1,2,3 - switch (matInfo.GetInteger("_AudioLinkMask_UVMode")) + switch (matInfo.GetInt("_AudioLinkMask_UVMode")) { case 0: LIL_SAMPLE_2D_WithMat("_OutlineVectorTex", SamplerStateInformation.LinearRepeatSampler, uvMain, @@ -566,7 +566,7 @@ public override void GetMaterialInformation(MaterialInformationCallback matInfo) "_IDMaskPrior1", "_IDMaskPrior2", "_IDMaskPrior3", "_IDMaskPrior4", "_IDMaskPrior5", "_IDMaskPrior6", "_IDMaskPrior7", "_IDMaskPrior8", "_IDMaskIsBitmap", "_IDMaskCompile" }; - if (idMaskProperties.Any(prop => matInfo.GetInteger(prop) != 0)) + if (idMaskProperties.Any(prop => matInfo.GetInt(prop) != 0)) { // with _IDMaskFrom = 0..7, uv is used for ID Mask, but it will only use integer part // (cast to int with normal rounding in hlsl) so it's not necessary to register UV usage. diff --git a/Editor/Processors/ShaderMaterialInformation.cs b/Editor/Processors/ShaderMaterialInformation.cs index 1ac58edca..6eb81c661 100644 --- a/Editor/Processors/ShaderMaterialInformation.cs +++ b/Editor/Processors/ShaderMaterialInformation.cs @@ -126,6 +126,9 @@ public MaterialInformationCallbackImpl(Material material, ShaderInformationKind public override int? GetInteger(string propertyName, bool considerAnimation = true) => GetValue(propertyName, _material.SafeGetInteger, considerAnimation); + public override int? GetInt(string propertyName, bool considerAnimation = true) => + GetValue(propertyName, _material.SafeGetInt, considerAnimation); + public override float? GetFloat(string propertyName, bool considerAnimation = true) => GetValue(propertyName, _material.SafeGetFloat, considerAnimation); diff --git a/Internal/Utils/Utils.cs b/Internal/Utils/Utils.cs index bd54760ee..0f4ecf82d 100644 --- a/Internal/Utils/Utils.cs +++ b/Internal/Utils/Utils.cs @@ -380,6 +380,9 @@ public static float SafeGetFloat(this Material material, string propertyName) => public static int SafeGetInteger(this Material material, string propertyName) => material.HasProperty(propertyName) ? material.GetInteger(propertyName) : 0; + public static int SafeGetInt(this Material material, string propertyName) => + material.HasProperty(propertyName) ? material.GetInt(propertyName) : 0; + public static Vector4 SafeGetVector(this Material material, string propertyName) => material.HasProperty(propertyName) ? material.GetVector(propertyName) : Vector4.zero; From d3071a33a7ce9f84f2d7fcedcf3d6dd40480b3ba Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 29 Oct 2024 10:28:15 +0900 Subject: [PATCH 30/50] chore: improve SafeGet check --- Internal/Utils/Utils.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Internal/Utils/Utils.cs b/Internal/Utils/Utils.cs index 0f4ecf82d..50076f0c1 100644 --- a/Internal/Utils/Utils.cs +++ b/Internal/Utils/Utils.cs @@ -375,19 +375,19 @@ public static void Assert(bool condition, string message) } public static float SafeGetFloat(this Material material, string propertyName) => - material.HasProperty(propertyName) ? material.GetFloat(propertyName) : 0; + material.HasFloat(propertyName) ? material.GetFloat(propertyName) : 0; public static int SafeGetInteger(this Material material, string propertyName) => - material.HasProperty(propertyName) ? material.GetInteger(propertyName) : 0; + material.HasInteger(propertyName) ? material.GetInteger(propertyName) : 0; public static int SafeGetInt(this Material material, string propertyName) => - material.HasProperty(propertyName) ? material.GetInt(propertyName) : 0; + material.HasInt(propertyName) ? material.GetInt(propertyName) : 0; public static Vector4 SafeGetVector(this Material material, string propertyName) => - material.HasProperty(propertyName) ? material.GetVector(propertyName) : Vector4.zero; + material.HasVector(propertyName) ? material.GetVector(propertyName) : Vector4.zero; public static Color SafeGetColor(this Material material, string propertyName) => - material.HasProperty(propertyName) ? material.GetColor(propertyName) : Color.clear; + material.HasColor(propertyName) ? material.GetColor(propertyName) : Color.clear; /// /// Fast compare of array. From f7e0d78baec86cc6304017a9fe20a9f0df40848f Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 29 Oct 2024 10:31:08 +0900 Subject: [PATCH 31/50] chore: improve animation consideration in shader material information --- Editor/Processors/ShaderMaterialInformation.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Editor/Processors/ShaderMaterialInformation.cs b/Editor/Processors/ShaderMaterialInformation.cs index 6eb81c661..fdac7efce 100644 --- a/Editor/Processors/ShaderMaterialInformation.cs +++ b/Editor/Processors/ShaderMaterialInformation.cs @@ -110,7 +110,8 @@ public MaterialInformationCallbackImpl(Material material, ShaderInformationKind public Shader Shader => _material.shader; - private T? GetValue(string propertyName, Func computer, bool considerAnimation) where T : struct + private T? GetValue(string propertyName, Func computer, bool considerAnimation, + string[]? subProperties = null) where T : struct { // animated; return null if (considerAnimation) @@ -118,6 +119,12 @@ public MaterialInformationCallbackImpl(Material material, ShaderInformationKind var animationProperty = $"material.{propertyName}"; if (_infos.Any(x => x.GetFloatNode(animationProperty).ComponentNodes.Any())) return null; + foreach (var subProperty in subProperties ?? Array.Empty()) + { + var subAnimationProperty = $"material.{propertyName}.{subProperty}"; + if (_infos.Any(x => x.GetFloatNode(subAnimationProperty).ComponentNodes.Any())) + return null; + } } return computer(propertyName); @@ -129,11 +136,13 @@ public MaterialInformationCallbackImpl(Material material, ShaderInformationKind public override int? GetInt(string propertyName, bool considerAnimation = true) => GetValue(propertyName, _material.SafeGetInt, considerAnimation); + private static readonly string[] VectorSubProperties = new[] { "r", "g", "b", "a", "x", "y", "z", "w" }; + public override float? GetFloat(string propertyName, bool considerAnimation = true) => - GetValue(propertyName, _material.SafeGetFloat, considerAnimation); + GetValue(propertyName, _material.SafeGetFloat, considerAnimation, VectorSubProperties); public override Vector4? GetVector(string propertyName, bool considerAnimation = true) => - GetValue(propertyName, _material.SafeGetVector, considerAnimation); + GetValue(propertyName, _material.SafeGetVector, considerAnimation, VectorSubProperties); public override void RegisterOtherUVUsage(UsingUVChannels uvChannel) { From fe37910e88451b0b3c661fe9761a92b91d47faef Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 29 Oct 2024 10:32:16 +0900 Subject: [PATCH 32/50] docs(changelog): Integer and Int confusion --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 803fb3a66..6ba56e8a9 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog]. ### Fixed - Fix non-VRChat project support `#1310` - 'shader' doesn't have a float or range property 'prop' error `#1312` +- Integer and Int confusion `#1313` ### Security diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e49f0fa..0690bd471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ The format is based on [Keep a Changelog]. - We may relax some restriction in the future. - Because we have to check for each condition if we use AnyState but we can check for only one (in best case) with entry/exit, this generally reduces cost for checking an parameter in a state. - Combined with Entry / Exit to 1D BlendTree optimization, which is implemented in previous release, your AnyState layer may be optimized to 1D BlendTree. -- Optimize Texture in Trace nad Optimize `#1181` `#1184` `#1193` `#1215` `#1225` `#1235` `#1268` `#1278` +- Optimize Texture in Trace nad Optimize `#1181` `#1184` `#1193` `#1215` `#1225` `#1235` `#1268` `#1278` `#1313` - Avatar Optimizer will pack texture and tries to reduce the VRAM usage. - Currently liltoon is only supported. - `Copy Enablement Animation` to Merge Skinned Mesh `#1173` From 3c57ca26eda09e9a098b6798c860fb9543a08781 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 29 Oct 2024 10:33:44 +0900 Subject: [PATCH 33/50] docs(changelog): NativeArray leak --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 803fb3a66..82ae824c1 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog]. ### Fixed - Fix non-VRChat project support `#1310` - 'shader' doesn't have a float or range property 'prop' error `#1312` +- NativeArray leak `#1314` ### Security diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e49f0fa..d852757d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ The format is based on [Keep a Changelog]. - Renamed debug options internally `#1228` - This will lose previously configured debug options. - However, debug options are not considered as Public API as stated in documents so this is not backward incompatible changes in semver 2.0.0 section 8. -- Performance Improvements `#1234` `#1243` `#1240` `#1288` `#1304` `#1307` +- Performance Improvements `#1234` `#1243` `#1240` `#1288` `#1304` `#1307` `#1314` - Transform gizmo are now hidden while you're editing box of Remove Mesh in Box `#1259` - This prevents mistakenly moving the Skinned Mesh Renderer while editing the box. - Make MergePhysBone implement `INetworkID` `#1260` From f27ec3207d91edc5eda81a4297a472b44ae87776 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:26:37 +0900 Subject: [PATCH 34/50] chore(ja/en): l10n follow 1296 --- Localization/en-us.po | 2 +- Localization/ja-jp.po | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Localization/en-us.po b/Localization/en-us.po index e00de17bc..f354d5420 100644 --- a/Localization/en-us.po +++ b/Localization/en-us.po @@ -623,7 +623,7 @@ msgid "RenameBlendShape:error:empty-name-some" msgstr "Some changed names are empty." msgid "RenameBlendShape:warning:name-conflict-some" -msgstr "Some changed names are duplicated. Those blendShapes will be merged to one." +msgstr "Some changed names are duplicated." msgid "RenameBlendShape:error:empty-name-this" msgstr "Changed name is empty." diff --git a/Localization/ja-jp.po b/Localization/ja-jp.po index 169bde441..6c0839694 100644 --- a/Localization/ja-jp.po +++ b/Localization/ja-jp.po @@ -535,19 +535,22 @@ msgid "RenameBlendShape:error:empty-name-some" msgstr "変更後の名前が空になっているものがあります。" msgid "RenameBlendShape:warning:name-conflict-some" -msgstr "変更後の名前が重複しているものがあります。重複するBlendShapeは1つに統合されます。" +msgstr "変更後の名前が重複しているものがあります。" msgid "RenameBlendShape:error:empty-name-this" msgstr "変更後の名前が空です。" msgid "RenameBlendShape:warning:name-conflict-this" -msgstr "変更後の名前が他と重複しています。重複するBlendShapeは1つに統合されます。" +msgstr "変更後の名前が他と重複しています。" # used in NDMF Console msgid "RenameBlendShape:error:empty-name" msgstr "BlendShape '{0}' の変更後の名前が空です。BlendShape名を空にすることはできません。" +msgid "RenameBlendShape:error:after-name-conflict" +msgstr "BlendShape名 '{0}' は重複しています。Rename BlendShapeコンポーネントによる名前変更で同名のBlendShapeを作成することはできません。" + # endregion # region AvatarGlobalComponent From c8c44cb31be0ea5b4e4a6f3d817b06d99f4cc6d3 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:40:11 +0900 Subject: [PATCH 35/50] chore(ja/en): update some text --- .docs/content/docs/basic-concept/index.ja.md | 2 +- .docs/content/docs/basic-concept/index.md | 2 +- .docs/content/docs/reference/merge-skinned-mesh/index.ja.md | 2 +- .docs/content/docs/reference/merge-skinned-mesh/index.md | 2 +- .docs/content/docs/reference/trace-and-optimize/index.ja.md | 5 +++-- Localization/ja-jp.po | 4 ++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.docs/content/docs/basic-concept/index.ja.md b/.docs/content/docs/basic-concept/index.ja.md index bb2fb6fa8..50fb8127a 100644 --- a/.docs/content/docs/basic-concept/index.ja.md +++ b/.docs/content/docs/basic-concept/index.ja.md @@ -31,7 +31,7 @@ Avatar Optimizerは、アバターを非破壊的に最適化するためのツ BlendShapeを統合すると、そのBlendShapeに対するアニメーションによる動作が変わってしまうため、これはバグとして扱われます。\ このバグを使用して、あるSkinned Mesh RendererのBlendShapeに対するアニメーションを、別のSkinned Mesh RendererのBlendShapeに対しても同時に作用させることができます。\ しかし、これはサポートされていない動作であり、他のコンポーネントが動作を壊すかもしれません。\ -例えば、`AAO Trace and Optimize`コンポーネントの`BlendShapeを自動的に固定・除去する`は、このバグによって`AAO Merge Skinned Mesh`コンポーネントで統合されて動くようになる可能性があるBlendShapeであっても、固定・除去します。 +例えば、`AAO Trace and Optimize`コンポーネントの`BlendShapeを最適化する`は、このバグによって`AAO Merge Skinned Mesh`コンポーネントで統合されて動くようになる可能性があるBlendShapeであっても、固定・除去します。 ## Avatar Optimizerの振る舞いは将来のバージョンでも安定していますか? {#behavior-stability} diff --git a/.docs/content/docs/basic-concept/index.md b/.docs/content/docs/basic-concept/index.md index 46baf8106..e6c0d3ce3 100644 --- a/.docs/content/docs/basic-concept/index.md +++ b/.docs/content/docs/basic-concept/index.md @@ -33,7 +33,7 @@ For example, `AAO Merge Skinned Mesh` component in 1.7.x or older merges BlendSh This behavior is treated as a bug since this makes impossible to animate them separately.\ You may use this bug to sync the BlendShape animation of an Skinned Mesh Renderer with one of another Skinned Mesh Renderer.\ However, this is not supported behavior and some other components may break the behavior.\ -For example, `Automatically Freeze BlendShape` in `AAO Trace and Optimize` component will freezes the BlendShapes which might be animated with this buggy behavior by being merged by `AAO Merge Skinned Mesh` component. +For example, `Optimize BlendShape` in `AAO Trace and Optimize` component will freezes the BlendShapes which might be animated with this buggy behavior by being merged by `AAO Merge Skinned Mesh` component. ## How is the behavior of Avatar Optimizer stable for future versions? {#behavior-stability} diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md b/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md index 99cd3cbba..ab06c4a1a 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md @@ -33,7 +33,7 @@ BlendShapeは、頂点とBlendShapeの数に比例して負荷が大きくなる 同様に、体や服のメッシュのBlendShapeは固定・除去することを推奨します。 [Freeze BlendShape](../freeze-blendshape)コンポーネントを統合対象・統合先のSkinnedMeshRendererコンポーネントのいずれか(または両方)に追加して、BlendShapeを固定・除去することが出来ます。 -[Trace and Optimize](../trace-and-optimize)コンポーネントの`BlendShapeを自動的に固定・除去する`によっても同様の効果を得ることが出来ます。 +[Trace and Optimize](../trace-and-optimize)コンポーネントの`BlendShapeを最適化する`によっても同様の効果を得ることが出来ます。 {{< hint info >}} diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.md b/.docs/content/docs/reference/merge-skinned-mesh/index.md index 40349c153..4812b5616 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.md @@ -35,7 +35,7 @@ That's why it's not good to merge face meshes. In addition, because of same reasons, you should freeze & remove unchanging BlendShapes for body / cloth meshes. You can freeze & remove BlendShape using [Freeze BlendShape](../freeze-blendshape) component. Add this component to both/either merge source SkinnedMeshRenderer and/or merged SkinnedMeshRenderer to freeze & remove BlendShapes. -Also, you can use `Freeze BlendShapes` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. +Also, you can use `Optimize BlendShapes` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. {{< hint info >}} diff --git a/.docs/content/docs/reference/trace-and-optimize/index.ja.md b/.docs/content/docs/reference/trace-and-optimize/index.ja.md index 2d38b4d95..8fb723b7d 100644 --- a/.docs/content/docs/reference/trace-and-optimize/index.ja.md +++ b/.docs/content/docs/reference/trace-and-optimize/index.ja.md @@ -23,8 +23,9 @@ Trace and Optimizeは「**見た目に絶対に影響させてはならない** {{< /hint >}} 現在、以下の機能を使った自動最適化が可能です。 -- `BlendShapeを自動的に固定・除去する`\ - アニメーションなどで使われていないか、常に同じ値になっているBlendShapeを自動的に固定・除去します。 +- `BlendShapeを最適化する`\ + 以前は`BlendShapeを自動的に固定・除去する`という名前でしたが、機能が増えたため名前が変わりました。\ + BlendShapeを自動的に固定・除去・統合することでBlendShapeの数を削減します。 - `使われていないObjectを自動的に削除する`\ アニメーションなどを走査して、使われていないObject(GameObjectやコンポーネントなど)を自動的に削除します。\ また、切り替えるものと一緒に使われていて、他の方法で使われていないPhysBooneコンポーネントを自動的に切り替えるようにします。 diff --git a/Localization/ja-jp.po b/Localization/ja-jp.po index 169bde441..9849b343e 100644 --- a/Localization/ja-jp.po +++ b/Localization/ja-jp.po @@ -608,8 +608,8 @@ msgstr "Trace and Optimizeへの移行が完了しました!" msgid "TraceAndOptimize:description" msgstr "このコンポーネントをアバターにつけると、アバターを走査して自動的にできる限りの最適化を行います。" -msgid "TraceAndOptimize:prop:freezeBlendShape" -msgstr "BlendShapeを自動的に固定・除去する" +msgid "TraceAndOptimize:prop:optimizeBlendShape" +msgstr "BlendShapeを最適化する" msgid "TraceAndOptimize:prop:removeUnusedObjects" msgstr "使われていないObjectを自動的に削除する" From 28b55e93336b7a233de8d181fbf9d1bb3799d39c Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 30 Oct 2024 09:22:32 +0900 Subject: [PATCH 36/50] docs: Apply suggestions from code review Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --- .docs/content/docs/reference/trace-and-optimize/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docs/content/docs/reference/trace-and-optimize/index.md b/.docs/content/docs/reference/trace-and-optimize/index.md index a5a183dbd..46fd06abc 100644 --- a/.docs/content/docs/reference/trace-and-optimize/index.md +++ b/.docs/content/docs/reference/trace-and-optimize/index.md @@ -26,7 +26,7 @@ We will fix it as much as we can. Currently the following optimizations are applied automatically. - `Optimize BlendShape`\ Previously known as `Freeze BlendShapes` but renamed to add more functionality.\ - Optimizes BlendShapes. Remove, freeze, or merge BlendShapes to reduce the number of BlendShapes. + By scanning animation etc., remove, freeze, or merge BlendShapes automatically to reduce the number of BlendShapes. - `Remove unused Objects`\ By scanning animation etc., automatically removes unused Objects (e.g. GameObjects, Components).\ In addition, this will automatically toggle PhysBone Components if they are only used by toggled objects. From c230c5d677a6c461afac78e6aa1860ff30f263e3 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:55:16 +0900 Subject: [PATCH 37/50] docs(ja): Apply suggestion from Code Review Co-authored-by: anatawa12 --- .docs/content/docs/reference/trace-and-optimize/index.ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docs/content/docs/reference/trace-and-optimize/index.ja.md b/.docs/content/docs/reference/trace-and-optimize/index.ja.md index 8fb723b7d..42c51047a 100644 --- a/.docs/content/docs/reference/trace-and-optimize/index.ja.md +++ b/.docs/content/docs/reference/trace-and-optimize/index.ja.md @@ -25,7 +25,7 @@ Trace and Optimizeは「**見た目に絶対に影響させてはならない** 現在、以下の機能を使った自動最適化が可能です。 - `BlendShapeを最適化する`\ 以前は`BlendShapeを自動的に固定・除去する`という名前でしたが、機能が増えたため名前が変わりました。\ - BlendShapeを自動的に固定・除去・統合することでBlendShapeの数を削減します。 + アニメーションなどを走査して、BlendShapeを自動的に固定・除去・統合することでBlendShapeの数を削減します。 - `使われていないObjectを自動的に削除する`\ アニメーションなどを走査して、使われていないObject(GameObjectやコンポーネントなど)を自動的に削除します。\ また、切り替えるものと一緒に使われていて、他の方法で使われていないPhysBooneコンポーネントを自動的に切り替えるようにします。 From ad8c688564288cbe07e9033f7e4130b29eb16529 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Wed, 30 Oct 2024 23:50:12 +0900 Subject: [PATCH 38/50] chore: en fix message --- Localization/en-us.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/en-us.po b/Localization/en-us.po index add3213ce..7397f017f 100644 --- a/Localization/en-us.po +++ b/Localization/en-us.po @@ -147,7 +147,7 @@ msgid "MergePhysBone:error:oneSource" msgstr "There is only one source PhysBone. You must specify two or more merge source PhysBones." msgid "MergePhysBone:error:multiChildType" -msgstr "Some PysBone has multi child type != Ignore" +msgstr "Some PhysBone has multi child type != Ignore" msgid "MergePhysBone:error:unsupportedPbVersion" msgstr "The PhysBone Version is not supported (yet) by Avatar Optimizer.\n" @@ -172,8 +172,8 @@ msgstr "" msgid "MergePhysBone:error:LimitRotationFix:DifferRotation" msgstr "" -"Limit Rotation of source phys bones differs in unfixable way.\n" -"Please fix Limit Rotation of source phys bones to same value, or change to other mode." +"Limit Rotation of source PhysBones differs in unfixable way.\n" +"Please fix Limit Rotation of source PhysBones to same value, or change to other mode." msgid "MergePhysBone:error:LimitRotationFix:DifferRotation:description" msgstr "" @@ -182,7 +182,7 @@ msgstr "" msgid "MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition" msgstr "" -"Endpoint Position is not zero while Limit Rotation mode Fix.\n" +"Endpoint Position is not zero while Limit Rotation mode is Fix.\n" "Please set Endpoint Position to zero, set Endpoint Position mode to Clear, or change Limit Rotation mode to other than Fix." msgid "MergePhysBone:dialog:versionInfo:title" From ab2349a44432d6565fa2adceabb64440ccd52e3e Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Wed, 30 Oct 2024 23:50:45 +0900 Subject: [PATCH 39/50] chore(l10n): [ja] update --- .../docs/reference/merge-physbone/index.ja.md | 6 +++-- Localization/ja-jp.po | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.docs/content/docs/reference/merge-physbone/index.ja.md b/.docs/content/docs/reference/merge-physbone/index.ja.md index f9835713c..32b130f8f 100644 --- a/.docs/content/docs/reference/merge-physbone/index.ja.md +++ b/.docs/content/docs/reference/merge-physbone/index.ja.md @@ -39,6 +39,8 @@ MultiChildTypeはIgnoreになります。 それぞれの項目について、統合対象の項目から値をコピーする場合は`Copy`(すべての統合対象で値が同じ場合のみ有効)、 代わりに新しい値を設定する場合は`Override`を選択してください。 -コライダーについては、`Merge`を選択して統合対象のコライダー一覧を統合することも出来ます。 +コライダーについては、`Merge`を選択して統合対象のコライダー一覧を統合することができます。 -Endpoint Positionについては、`Clear`を選択して[Clear Endpoint Position](../clear-endpoint-position)を使用することもできます。 +Endpoint Positionについては、`Clear`を選択して[Clear Endpoint Position](../clear-endpoint-position)を使用することができます。 + +角度制限については、`Fix`を選択して向きの異なるボーンに対する角度制限を1つに纏めるすることができます。 \ No newline at end of file diff --git a/Localization/ja-jp.po b/Localization/ja-jp.po index 169bde441..94f79c627 100644 --- a/Localization/ja-jp.po +++ b/Localization/ja-jp.po @@ -156,6 +156,9 @@ msgid "MergePhysBone:error:unsupportedPbVersion" msgstr "このPhysBoneバージョンは(まだ)Avatar Optimizerによって対応されていません。\n" "作者にtwitter (@anatawa12_vrc)またはGitHub (anatawa12/AvatarOptimizer)で連絡してください!" +msgid "MergePhysBone:message:fix-yaw-pitch" +msgstr "ボーンの向きを揃える" + msgid "MergePhysBone:error:differValues" msgstr "複数の統合対象の間で値に差異があります。以下の値は同じ値にするかOverrideする必要があります: {0}" @@ -165,6 +168,26 @@ msgstr "複数の統合対象の間で値に差異があります。同じ値に msgid "MergePhysBone:warning:differChainLength" msgstr "複数の統合対象の間でチェーンの長さが異なります。短いチェーンのPBは元よりも太くなる可能性があります。" +msgid "MergePhysBone:error:LimitRotationFix:SkewScaling" +msgstr "" +"Scaleが均一でないボーンの角度制限を統合することはサポートされていません。\n" +"角度制限の設定をFix以外にするか、Scaleを均一にしてください。" + +msgid "MergePhysBone:error:LimitRotationFix:DifferRotation" +msgstr "" +"角度制限を統合できる状態ではありません。\n" +"統合対象の角度制限の値を揃えるか、角度制限の設定をFix以外にしてください。" + +msgid "MergePhysBone:error:LimitRotationFix:DifferRotation:description" +msgstr "" +"角度制限の統合はロール回転の差がある場合にのみ用いることができ、他の軸で回転に差がある場合には用いることができません。\n" +"ロール回転の軸は通常ローカル座標におけるX軸ですが、ヨー回転が含まれている場合はInspectorの表示と異なることがあります。" + +msgid "MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition" +msgstr "" +"角度制限の設定がFixですが、Endpoint Positionが空ではありません。\n" +"Endpoint Positionを空にするか、Endpoint Positionの設定をClearにするか、角度制限の設定をFix以外にしてください。" + msgid "MergePhysBone:dialog:versionInfo:title" msgstr "バージョンについて" From d87a7e9baad675a4aff62ae3ddbf55c54a38db54 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 31 Oct 2024 20:44:03 +0900 Subject: [PATCH 40/50] docs: Apply suggestions from code review Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --- .docs/content/docs/reference/merge-physbone/index.ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docs/content/docs/reference/merge-physbone/index.ja.md b/.docs/content/docs/reference/merge-physbone/index.ja.md index 32b130f8f..d7318baac 100644 --- a/.docs/content/docs/reference/merge-physbone/index.ja.md +++ b/.docs/content/docs/reference/merge-physbone/index.ja.md @@ -43,4 +43,4 @@ MultiChildTypeはIgnoreになります。 Endpoint Positionについては、`Clear`を選択して[Clear Endpoint Position](../clear-endpoint-position)を使用することができます。 -角度制限については、`Fix`を選択して向きの異なるボーンに対する角度制限を1つに纏めるすることができます。 \ No newline at end of file +角度制限では、Fix`を選択することで、ボーンに対して捻るような回転(ロール)を自動で調整して角度制限を纏めて適用することができます。 \ No newline at end of file From 700941807a3dd3a9c4484cc0c945d6df00c44d92 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 31 Oct 2024 22:11:14 +0900 Subject: [PATCH 41/50] fix: AAO not enabled if all components are on inactive GameObjects --- Editor/OptimizerPlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/OptimizerPlugin.cs b/Editor/OptimizerPlugin.cs index 4d404f1b9..c4221e0e5 100644 --- a/Editor/OptimizerPlugin.cs +++ b/Editor/OptimizerPlugin.cs @@ -48,7 +48,7 @@ protected override void Configure() ctx => { ctx.GetState().Enabled = - ctx.AvatarRootObject.GetComponentInChildren(); + ctx.AvatarRootObject.GetComponentInChildren(true); // invalidate ComponentInfoRegistry cache to support newly added assets APIInternal.ComponentInfoRegistry.InvalidateCache(); }) From 855677d2173a3dafc2a5b683f4d475e160029cf2 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 31 Oct 2024 22:19:32 +0900 Subject: [PATCH 42/50] docs(changelog): Error if all components are on inactive GameObject --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 6fc8cf65a..1f4bacd26 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -26,6 +26,7 @@ The format is based on [Keep a Changelog]. - 'shader' doesn't have a float or range property 'prop' error `#1312` - Integer and Int confusion `#1313` - NativeArray leak `#1314` +- Error if all components are on inactive GameObject`#1318` ### Security diff --git a/CHANGELOG.md b/CHANGELOG.md index 116e454a2..2d27e0586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,7 @@ The format is based on [Keep a Changelog]. - Show version name on NDMF Console `#1309` - Fix non-VRChat project support `#1310` - 'shader' doesn't have a float or range property 'prop' error `#1312` +- Error if all components are on inactive GameObject `#1318` ### Security From e054414ab12f3c86174b48050259e7a70996b08d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 31 Oct 2024 23:00:23 +0900 Subject: [PATCH 43/50] docs(en): Apply suggestions from code review Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --- .docs/content/docs/reference/merge-physbone/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docs/content/docs/reference/merge-physbone/index.md b/.docs/content/docs/reference/merge-physbone/index.md index 15ee5033e..d0bc77977 100644 --- a/.docs/content/docs/reference/merge-physbone/index.md +++ b/.docs/content/docs/reference/merge-physbone/index.md @@ -43,4 +43,4 @@ For colliders, you can select `Merge` to merge colliders array from source physb For Endpoint Position, you can select `Clear` to apply [Clear Endpoint Position](../clear-endpoint-position) For Limit Rotation, you can select `Fix` to fix different roll axis on the model. -If your model has different roll bones and fixes with Limit Rotation, you may use this option. +If your model has different roll bones, you can limit their rotations together with this option. From eaef26ccc77f5a22039af1ebdab98ab99dc7feb5 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 31 Oct 2024 23:16:57 +0900 Subject: [PATCH 44/50] chore: Apply suggestions from code review Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --- .docs/content/docs/reference/merge-physbone/index.ja.md | 2 +- Localization/ja-jp.po | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.docs/content/docs/reference/merge-physbone/index.ja.md b/.docs/content/docs/reference/merge-physbone/index.ja.md index d7318baac..c9a45687a 100644 --- a/.docs/content/docs/reference/merge-physbone/index.ja.md +++ b/.docs/content/docs/reference/merge-physbone/index.ja.md @@ -43,4 +43,4 @@ MultiChildTypeはIgnoreになります。 Endpoint Positionについては、`Clear`を選択して[Clear Endpoint Position](../clear-endpoint-position)を使用することができます。 -角度制限では、Fix`を選択することで、ボーンに対して捻るような回転(ロール)を自動で調整して角度制限を纏めて適用することができます。 \ No newline at end of file +角度制限では、`Fix`を選択することで、ボーンに対して捻るような回転(ロール)を自動で調整して角度制限を纏めて適用することができます。 \ No newline at end of file diff --git a/Localization/ja-jp.po b/Localization/ja-jp.po index 7965c5143..b44cf8566 100644 --- a/Localization/ja-jp.po +++ b/Localization/ja-jp.po @@ -157,7 +157,7 @@ msgstr "このPhysBoneバージョンは(まだ)Avatar Optimizerによって対 "作者にtwitter (@anatawa12_vrc)またはGitHub (anatawa12/AvatarOptimizer)で連絡してください!" msgid "MergePhysBone:message:fix-yaw-pitch" -msgstr "ボーンの向きを揃える" +msgstr "ボーンの回転(ロール)を揃える" msgid "MergePhysBone:error:differValues" msgstr "複数の統合対象の間で値に差異があります。以下の値は同じ値にするかOverrideする必要があります: {0}" @@ -180,7 +180,7 @@ msgstr "" msgid "MergePhysBone:error:LimitRotationFix:DifferRotation:description" msgstr "" -"角度制限の統合はロール回転の差がある場合にのみ用いることができ、他の軸で回転に差がある場合には用いることができません。\n" +"角度制限の統合は、ボーンに対する捻るような回転(ロール)の差は揃えることができますが、他の軸の回転は揃えることができません。\n" "ロール回転の軸は通常ローカル座標におけるX軸ですが、ヨー回転が含まれている場合はInspectorの表示と異なることがあります。" msgid "MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition" From fd5fe3472e5d7579ef4a01000624ac9b5b5c16ba Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 1 Nov 2024 00:48:57 +0900 Subject: [PATCH 45/50] chore(ja): Apply suggestions from code review Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --- .docs/content/docs/reference/merge-physbone/index.ja.md | 3 ++- Localization/ja-jp.po | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.docs/content/docs/reference/merge-physbone/index.ja.md b/.docs/content/docs/reference/merge-physbone/index.ja.md index c9a45687a..2862abaa2 100644 --- a/.docs/content/docs/reference/merge-physbone/index.ja.md +++ b/.docs/content/docs/reference/merge-physbone/index.ja.md @@ -43,4 +43,5 @@ MultiChildTypeはIgnoreになります。 Endpoint Positionについては、`Clear`を選択して[Clear Endpoint Position](../clear-endpoint-position)を使用することができます。 -角度制限では、`Fix`を選択することで、ボーンに対して捻るような回転(ロール)を自動で調整して角度制限を纏めて適用することができます。 \ No newline at end of file +角度制限では、`Fix`を選択することで、ボーンに対する捻るような回転(Roll)の値を自動で揃えられます。 +これにより、Rollの値だけが異なっているような場合に角度制限を纏めて適用することができます。 \ No newline at end of file diff --git a/Localization/ja-jp.po b/Localization/ja-jp.po index b44cf8566..e70681b2f 100644 --- a/Localization/ja-jp.po +++ b/Localization/ja-jp.po @@ -157,7 +157,7 @@ msgstr "このPhysBoneバージョンは(まだ)Avatar Optimizerによって対 "作者にtwitter (@anatawa12_vrc)またはGitHub (anatawa12/AvatarOptimizer)で連絡してください!" msgid "MergePhysBone:message:fix-yaw-pitch" -msgstr "ボーンの回転(ロール)を揃える" +msgstr "ボーンを回転させてRollの値を揃える" msgid "MergePhysBone:error:differValues" msgstr "複数の統合対象の間で値に差異があります。以下の値は同じ値にするかOverrideする必要があります: {0}" @@ -180,8 +180,8 @@ msgstr "" msgid "MergePhysBone:error:LimitRotationFix:DifferRotation:description" msgstr "" -"角度制限の統合は、ボーンに対する捻るような回転(ロール)の差は揃えることができますが、他の軸の回転は揃えることができません。\n" -"ロール回転の軸は通常ローカル座標におけるX軸ですが、ヨー回転が含まれている場合はInspectorの表示と異なることがあります。" +"角度制限の統合は、ボーンに対する捻るような回転(Roll)の値は揃えることができますが、他の軸の回転は揃えることができません。\n" +"Roll回転の軸はローカル座標系におけるX軸になります。Yaw回転が含まれている場合はPBのInspectorにあるRollの表示とは異なることがあります。" msgid "MergePhysBone:error:LimitRotationFix:NonZeroEndpointPosition" msgstr "" From 0f43d32ea6c8eaf55862132764facb8fb4d7bb75 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 2 Nov 2024 10:35:15 +0900 Subject: [PATCH 46/50] docs(changelog): meshes with BlendShapes => which have BlendShapes --- CHANGELOG-PRERELEASE.md | 4 ++-- CHANGELOG.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index dfae43084..2fc3f28d7 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -8,8 +8,8 @@ The format is based on [Keep a Changelog]. ## [Unreleased] ### Added -- Automatically merging meshes with BlendShapes `#1308` - - In previous version of Avatar Optimizer, meshes with BlendShapes are not automatically merged. +- Automatically merging meshes which have BlendShapes `#1308` + - In previous version of Avatar Optimizer, meshes which have BlendShapes are not automatically merged. - This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019. - However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices. - This means that increasing the number of vertices in a mesh with BlendShapes does not increase the load of BlendShape manipulation much. diff --git a/CHANGELOG.md b/CHANGELOG.md index f21cc74c7..0bcf9bf91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,8 +49,8 @@ The format is based on [Keep a Changelog]. - When you changed shader for an material, properties for previously used shaders might be remain - This may increase your avatar size by unexpectedly including unused textures - Right-click menu option to create a new GameObject with a specified component [`#1290`](https://github.com/anatawa12/AvatarOptimizer/pull/1290) -- Automatically merging meshes with BlendShapes `#1308` - - In previous version of Avatar Optimizer, meshes with BlendShapes are not automatically merged. +- Automatically merging meshes which have BlendShapes `#1308` + - In previous version of Avatar Optimizer, meshes which have BlendShapes are not automatically merged. - This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019. - However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices. - This means that increasing the number of vertices in a mesh with BlendShapes does not increase the load of BlendShape manipulation much. From f328ce57ee26787b68579094d6f589f9deecbe17 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 2 Nov 2024 12:08:44 +0900 Subject: [PATCH 47/50] docs: Apply suggestions from code review Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --- .docs/content/docs/reference/merge-skinned-mesh/index.md | 4 ++-- CHANGELOG-PRERELEASE.md | 4 ++-- CHANGELOG.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.md b/.docs/content/docs/reference/merge-skinned-mesh/index.md index 75a1d2d12..3501a0aaf 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.md @@ -27,12 +27,12 @@ If you are using [Modular Avatar], you can add [`MA Mesh Settings`] component to {{< /hint >}} -It's better freeze & remove unchanging BlendShapes for body / cloth meshes to reduce BlendShape load. +It's better to freeze & remove unchanging BlendShapes for body / cloth meshes to reduce BlendShape load. You can freeze & remove BlendShape using [Freeze BlendShape](../freeze-blendshape) component. Add this component to both/either merge source SkinnedMeshRenderer and/or merged SkinnedMeshRenderer to freeze & remove BlendShapes. Also, you can use `Automatically Freeze BlendShape` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. -In previous versions of Avatar Optimizer, Unity 2019 recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much. +In previous versions of Avatar Optimizer, we recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much in Unity 2019. However, in Unity 2022, we no longer recommends not merging face meshes because the BlendShape load has been improved. {{< hint info >}} diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 2fc3f28d7..ff16e7dc1 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -12,8 +12,8 @@ The format is based on [Keep a Changelog]. - In previous version of Avatar Optimizer, meshes which have BlendShapes are not automatically merged. - This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019. - However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices. - - This means that increasing the number of vertices in a mesh with BlendShapes does not increase the load of BlendShape manipulation much. - - Therefore, we decided to automatically merge meshes with BlendShapes. + - This means that increasing the number of vertices in a mesh which has BlendShapes does not increase the load of BlendShape manipulation much. + - Therefore, we decided to automatically merge such meshes. ### Changed diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bcf9bf91..34439e9a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,8 +53,8 @@ The format is based on [Keep a Changelog]. - In previous version of Avatar Optimizer, meshes which have BlendShapes are not automatically merged. - This was because BlendShape manipulation load is proportional to the number of vertices in Unity 2019. - However, in Unity 2020 and later, BlendShape manipulation load is mostly proportional to the number of moving vertices. - - This means that increasing the number of vertices in a mesh with BlendShapes does not increase the load of BlendShape manipulation much. - - Therefore, we decided to automatically merge meshes with BlendShapes. + - This means that increasing the number of vertices in a mesh which has BlendShapes does not increase the load of BlendShape manipulation much. + - Therefore, we decided to automatically merge such meshes. ### Changed - Skip Enablement Mismatched Renderers is now disabled by default `#1169` From eefbaed037ede16fcdcb587aa20ff18b80b4d3ac Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 2 Nov 2024 12:09:37 +0900 Subject: [PATCH 48/50] docs(merge-smr): move hint up --- .../docs/reference/merge-skinned-mesh/index.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.md b/.docs/content/docs/reference/merge-skinned-mesh/index.md index 3501a0aaf..24f4ab2fc 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.md @@ -9,6 +9,12 @@ Merges one or more SkinnedMeshRenderers and MeshRenderers into one SkinnedMeshRe This component should be added to a new GameObject which has a SkinnedMeshRenderer component without Mesh specified. (Kind: [Source Edit Skinned Mesh Component](../../component-kind/edit-skinned-mesh-components#source-component)) +{{< hint info >}} + +[Trace And Optimize](../trace-and-optimize) will automatically do the same process, so in most cases you do not need to use this component. + +{{< /hint >}} + ## Benefits Merging SkinnedMeshRenderer will reduce number of deforming mesh (skinning). @@ -35,12 +41,6 @@ Also, you can use `Automatically Freeze BlendShape` of [Trace and Optimize](../t In previous versions of Avatar Optimizer, we recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much in Unity 2019. However, in Unity 2022, we no longer recommends not merging face meshes because the BlendShape load has been improved. -{{< hint info >}} - -[Trace And Optimize](../trace-and-optimize) will automatically do the same process, so in most cases you do not need to use this component. - -{{< /hint >}} - ## Settings ![component.png](component.png) From 6d1dfb9f679691f9001df9cf2e1edc8d6bad840d Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:46:57 +0900 Subject: [PATCH 49/50] docs: just adjust newline --- .docs/content/docs/reference/merge-skinned-mesh/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.md b/.docs/content/docs/reference/merge-skinned-mesh/index.md index da88ed825..b0ef4c36b 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.md @@ -33,12 +33,12 @@ If you are using [Modular Avatar], you can add [`MA Mesh Settings`] component to {{< /hint >}} -It's better to freeze & remove unchanging BlendShapes for body / cloth meshes to reduce BlendShape load. +It's better to freeze & remove unchanging BlendShapes for body / cloth meshes to reduce BlendShape load.\ You can freeze & remove BlendShape using [Freeze BlendShape](../freeze-blendshape) component. Add this component to both/either merge source SkinnedMeshRenderer and/or merged SkinnedMeshRenderer to freeze & remove BlendShapes. Also, you can use `Optimize BlendShapes` of [Trace and Optimize](../trace-and-optimize) component to get the same benefits. -In previous versions of Avatar Optimizer, we recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much in Unity 2019. +In previous versions of Avatar Optimizer, we recommended not merging face meshes due to merging BlendShape-heavy mesh will increase load on BlendShape much in Unity 2019.\ However, in Unity 2022, we no longer recommends not merging face meshes because the BlendShape load has been improved. ## Settings From 5c677759d6a299f5ae1dc65296f4986f381cc7e6 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:47:30 +0900 Subject: [PATCH 50/50] docs(ja): update mergeSMR docs --- .../reference/merge-skinned-mesh/index.ja.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md b/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md index ab06c4a1a..61fdee32a 100644 --- a/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md +++ b/.docs/content/docs/reference/merge-skinned-mesh/index.ja.md @@ -9,6 +9,12 @@ weight: 21 このコンポーネントは、メッシュを指定していないSkinnedMeshRendererコンポーネントがある新規GameObjectに追加してください。(分類: [Source Edit Skinned Mesh Component](../../component-kind/edit-skinned-mesh-components#source-component)) +{{< hint info >}} + +[Trace And Optimize](../trace-and-optimize)が自動で同様の処理を行うため、大抵の場合、このコンポーネントを使用する必要はありません。 + +{{< /hint >}} + ## 利点 {#benefits} SkinnedMeshRendererを統合することでメッシュを変形させる処理の回数が減り、負荷が軽くなります。 @@ -27,19 +33,13 @@ Anchor Override等の設定を行うには、MergeSkinnedMeshのあるGameObject {{< /hint >}} -また、このコンポーネントは、服のメッシュや体のメッシュを統合するのには適していますが、顔のメッシュを統合するのには適していません。\ -BlendShapeは、頂点とBlendShapeの数に比例して負荷が大きくなる機能です。 -顔のメッシュは一般的に多くのBlendShapeを持っているため、統合対象に含めると頂点数の増加により負荷が大きくなってしまいます。 - -同様に、体や服のメッシュのBlendShapeは固定・除去することを推奨します。 +BlendShapeによる負荷を減らすために、体や服のメッシュのBlendShapeは固定・除去することを推奨します。\ [Freeze BlendShape](../freeze-blendshape)コンポーネントを統合対象・統合先のSkinnedMeshRendererコンポーネントのいずれか(または両方)に追加して、BlendShapeを固定・除去することが出来ます。 [Trace and Optimize](../trace-and-optimize)コンポーネントの`BlendShapeを最適化する`によっても同様の効果を得ることが出来ます。 -{{< hint info >}} - -いくつかのケースでは、[Trace And Optimize](../trace-and-optimize)が自動で同様の処理を行うため、このコンポーネントを使用する必要がないかもしれません。 - -{{< /hint >}} +以前のAvatar Optimizerは顔のメッシュを他のメッシュと統合することを推奨していませんでした。 +これは、Unity 2019でBlendShapeの多いメッシュを統合するとメッシュの負荷が大幅に増加してしまうためです。\ +Unity 2022ではBlendShapeの負荷が改善されているため、その記述は取り下げられました。 ## 設定 {#settings}