diff --git a/Packages/com.chocopoi.vrc.dressingtools/Editor/Integrations/VRChat/VRCMergeAnimLayerWearableModule.cs b/Packages/com.chocopoi.vrc.dressingtools/Editor/Integrations/VRChat/VRCMergeAnimLayerWearableModule.cs
new file mode 100644
index 00000000..6178dd34
--- /dev/null
+++ b/Packages/com.chocopoi.vrc.dressingtools/Editor/Integrations/VRChat/VRCMergeAnimLayerWearableModule.cs
@@ -0,0 +1,136 @@
+/*
+ * File: VRCMergeAnimLayerWearableModule.cs
+ * Project: DressingTools
+ * Created Date: Tuesday, 29th Aug 2023, 02:53:11 pm
+ * Author: chocopoi (poi@chocopoi.com)
+ * -----
+ * Copyright (c) 2023 chocopoi
+ *
+ * This file is part of DressingTools.
+ *
+ * DressingTools is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * DressingTools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with DressingTools. If not, see .
+ */
+
+#if VRC_SDK_VRCSDK3
+using System.Diagnostics.CodeAnalysis;
+using Chocopoi.DressingTools.Lib;
+using Chocopoi.DressingTools.Lib.Extensibility.Providers;
+using Chocopoi.DressingTools.Lib.Serialization;
+using Chocopoi.DressingTools.Lib.Wearable.Modules;
+using Newtonsoft.Json.Linq;
+using UnityEditor;
+using VRC.SDK3.Avatars.Components;
+
+namespace Chocopoi.DressingTools.Integration.VRChat.Modules
+{
+ internal class VRCMergeAnimLayerWearableModuleConfig : IModuleConfig
+ {
+ public enum AnimLayer
+ {
+ Base = 0,
+ Additive = 1,
+ Gesture = 2,
+ Action = 3,
+ FX = 4,
+ Sitting = 5,
+ TPose = 6,
+ IKPose = 7
+ }
+
+ public enum PathMode
+ {
+ Relative = 0,
+ Absolute = 1
+ }
+
+ public static readonly SerializationVersion CurrentConfigVersion = new SerializationVersion(1, 0, 0);
+
+ public SerializationVersion version;
+ public AnimLayer animLayer;
+ public PathMode pathMode;
+ public bool removeAnimatorAfterApply;
+ public bool matchLayerWriteDefaults;
+
+ public VRCMergeAnimLayerWearableModuleConfig()
+ {
+ version = CurrentConfigVersion;
+ animLayer = AnimLayer.FX;
+ pathMode = PathMode.Relative;
+ removeAnimatorAfterApply = true;
+ matchLayerWriteDefaults = true;
+ }
+
+ private static VRCAvatarDescriptor.AnimLayerType? ToAnimLayerType(AnimLayer type)
+ {
+ switch (type)
+ {
+ case AnimLayer.Base:
+ return VRCAvatarDescriptor.AnimLayerType.Base;
+ case AnimLayer.Additive:
+ return VRCAvatarDescriptor.AnimLayerType.Additive;
+ case AnimLayer.Gesture:
+ return VRCAvatarDescriptor.AnimLayerType.Gesture;
+ case AnimLayer.Action:
+ return VRCAvatarDescriptor.AnimLayerType.Action;
+ case AnimLayer.FX:
+ return VRCAvatarDescriptor.AnimLayerType.FX;
+ case AnimLayer.Sitting:
+ return VRCAvatarDescriptor.AnimLayerType.Sitting;
+ case AnimLayer.TPose:
+ return VRCAvatarDescriptor.AnimLayerType.TPose;
+ case AnimLayer.IKPose:
+ return VRCAvatarDescriptor.AnimLayerType.IKPose;
+ }
+ return null;
+ }
+ }
+
+ [InitializeOnLoad]
+ internal class VRCMergeAnimLayerWearableModuleProvider : WearableModuleProviderBase
+ {
+ public const string MODULE_IDENTIFIER = "com.chocopoi.dressingtools.integration.vrchat.wearable.merge-anim-layer";
+
+ [ExcludeFromCodeCoverage] public override string ModuleIdentifier => MODULE_IDENTIFIER;
+ [ExcludeFromCodeCoverage] public override string FriendlyName => "VRChat: Merge Animation Layer";
+ [ExcludeFromCodeCoverage] public override int CallOrder => 6;
+ [ExcludeFromCodeCoverage] public override bool AllowMultiple => false;
+
+ static VRCMergeAnimLayerWearableModuleProvider()
+ {
+ WearableModuleProviderLocator.Instance.Register(new VRCMergeAnimLayerWearableModuleProvider());
+ }
+
+ public override IModuleConfig DeserializeModuleConfig(JObject jObject)
+ {
+ // TODO: do schema check
+
+ var version = jObject["version"].ToObject();
+ if (version.Major > VRCMergeAnimLayerWearableModuleConfig.CurrentConfigVersion.Major)
+ {
+ throw new System.Exception("Incompatible VRCMergeAnimLayerWearableModuleConfig version: " + version.Major + " > " + VRCMergeAnimLayerWearableModuleConfig.CurrentConfigVersion.Major);
+ }
+
+ return jObject.ToObject();
+ }
+
+ public override IModuleConfig NewModuleConfig() => new VRCMergeAnimLayerWearableModuleConfig();
+
+ public override bool OnApplyWearable(ApplyCabinetContext cabCtx, ApplyWearableContext wearCtx, WearableModule module)
+ {
+ var malm = (VRCMergeAnimLayerWearableModuleConfig)module.config;
+
+ if (malm == null)
+ {
+ // no merge anim layer module
+ return true;
+ }
+
+ return true;
+ }
+ }
+}
+#endif
diff --git a/Packages/com.chocopoi.vrc.dressingtools/Editor/Integrations/VRChat/VRCMergeAnimLayerWearableModule.cs.meta b/Packages/com.chocopoi.vrc.dressingtools/Editor/Integrations/VRChat/VRCMergeAnimLayerWearableModule.cs.meta
new file mode 100644
index 00000000..3ad4e62e
--- /dev/null
+++ b/Packages/com.chocopoi.vrc.dressingtools/Editor/Integrations/VRChat/VRCMergeAnimLayerWearableModule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bdbcebd3d680646479d17ad8d8a842fb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: