Esempio n. 1
0
        /// <summary>
        /// <see cref="VRC_AvatarDescriptor.CustomStandingAnims"/>、および<see cref="VRC_AvatarDescriptor.CustomSittingAnims"/>を作成します。
        /// </summary>
        /// <param name="avatar"></param>
        /// <exception cref="FileNotFoundException">VRChat SDKに含まれるカスタムアニメーション設定用のテンプレートファイルが見つからなかった場合。</exception>
        /// <returns></returns>
        internal static void AddCustomAnims(GameObject avatar)
        {
#if VRC_SDK_VRCSDK2
            var avatarDescriptor = avatar.GetOrAddComponent <VRC_AvatarDescriptor>();
            var templatePath     = AssetDatabase.GUIDToAssetPath(VRChatUtility.CustomAnimsTemplateGUID);
            if (string.IsNullOrEmpty(templatePath))
            {
                templatePath = VRChatUtility.CustomAnimsTemplatePath;
            }
            var template = AssetDatabase.LoadAssetAtPath <AnimatorOverrideController>(templatePath);
            if (!template)
            {
                new FileNotFoundException("VRChat SDKに含まれるカスタムアニメーション設定用のテンプレートファイルが見つかりません。", fileName: templatePath);
            }
            if (!avatarDescriptor.CustomStandingAnims)
            {
                avatarDescriptor.CustomStandingAnims = Duplicator.DuplicateAssetToFolder <AnimatorOverrideController>(
                    source: template,
                    prefabInstance: avatar,
                    fileName: "CustomStandingAnims.overrideController"
                    );
            }

            if (!avatarDescriptor.CustomSittingAnims)
            {
                avatarDescriptor.CustomSittingAnims = Duplicator.DuplicateAssetToFolder <AnimatorOverrideController>(
                    source: template,
                    prefabInstance: avatar,
                    fileName: "CustomSittingAnims.overrideController"
                    );
            }
#endif
        }
        /// <summary>
        /// 表情の設定を行うアニメーションクリップを作成します。
        /// </summary>
        /// <param name="avatar"></param>
        /// <param name="vrmBlendShape"></param>
        /// <param name="clips"></param>
        /// <returns></returns>
        private static AnimationClip CreateFeeling(
            GameObject avatar,
            VRMBlendShapeClip clip,
            ref IEnumerable <VRMBlendShapeClip> clips
            )
        {
            var fileName = clip.Preset + ".anim";

#if VRC_SDK_VRCSDK2
            var anim = Duplicator.DuplicateAssetToFolder <AnimationClip>(
                source: UnityPath.FromUnityPath(Converter.RootFolderPath).Child("animations")
                .Child(BlendShapeReplacer.MappingBlendShapeToVRChatAnim[clip.Preset] + ".anim")
                .LoadAsset <AnimationClip>(),
                prefabInstance: avatar,
                fileName
                );

            Transform transform = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath);
            if (transform.GetComponent <Animator>())
            {
                var curve = new AnimationCurve();
                foreach (var(seconds, value) in BlendShapeReplacer.NeutralAndBlinkStopperWeights)
                {
                    curve.AddKey(new Keyframe(seconds, value));
                }
                anim.SetCurve(
                    VRChatUtility.AutoBlinkMeshPath,
                    typeof(Behaviour),
                    "m_Enabled",
                    curve
                    );

                clips = BlendShapeReplacer.DuplicateShapeKeyToUnique(avatar, clip, clips);
            }
#else
            var anim = new AnimationClip();
            AssetDatabase.CreateAsset(
                anim,
                Duplicator.DetermineAssetPath(prefabInstance: avatar, typeof(AnimationClip), fileName)
                );
            clips = BlendShapeReplacer.DuplicateShapeKeyToUnique(avatar, clip, clips);
#endif

            SetBlendShapeCurves(
                avatar,
                animationClip: anim,
                clip: clip,
                keys: new Dictionary <float, float> {
                { 0, 1 },
                { anim.length, 1 },
            }
                );

            return(anim);
        }
        /// <summary>
        /// アニメーションクリップに、指定されたマテリアルを追加します。
        /// </summary>
        /// <param name="avatar"></param>
        /// <param name="animationClip"></param>
        /// <param name="name"></param>
        /// <param name="binding"></param>
        /// <param name="secondsList"></param>
        private static void SetBlendShapeCurve(
            AnimationClip animationClip,
            GameObject avatar,
            string vrmBlendShapeName,
            IEnumerable <MaterialValueBinding> bindings,
            IEnumerable <float> secondsList
            )
        {
            var materials = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath)
                            .GetComponent <SkinnedMeshRenderer>().sharedMaterials;

            var materialIndex = materials.ToList().FindIndex(m => m.name == bindings.First().MaterialName);

            var material = Duplicator.DuplicateAssetToFolder <Material>(
                source: materials[materialIndex],
                prefabInstance: avatar,
                fileName: $"{materials[materialIndex].name}-{vrmBlendShapeName}.mat"
                );

            VRMUtility.Bake(material, bindings);

            AnimationUtility.SetObjectReferenceCurve(
                animationClip,
                new EditorCurveBinding()
            {
                path         = VRChatUtility.AutoBlinkMeshPath,
                type         = typeof(SkinnedMeshRenderer),
                propertyName = $"m_Materials.Array.data[{materialIndex}]",
            },
                secondsList
                .Select(seconds => new ObjectReferenceKeyframe()
            {
                time = seconds, value = material
            }).ToArray()
                );
        }
        /// <summary>
        /// 手の形に喜怒哀楽を割り当てます。
        /// </summary>
        /// <param name="avatar"></param>
        /// <param name="clips"></param>
        /// <param name="vrmBlendShapeForFINGERPOINT"></param>
        private static void SetFeelings(
            GameObject avatar,
            IEnumerable <VRMBlendShapeClip> clips,
            VRMBlendShapeClip vrmBlendShapeForFINGERPOINT
            )
        {
#if VRC_SDK_VRCSDK2
            VRChatUtility.AddCustomAnims(avatar: avatar);

            var avatarDescriptor = avatar.GetOrAddComponent <VRC_AvatarDescriptor>();
#elif VRC_SDK_VRCSDK3
            var fxController = Duplicator.DuplicateAssetToFolder(
                source: AssetDatabase.LoadAssetAtPath <AnimatorController>(
                    AssetDatabase.GUIDToAssetPath(BlendShapeReplacer.FXTemplateGUID)
                    ),
                prefabInstance: avatar
                );

            var avatarDescriptor = avatar.GetOrAddComponent <VRCAvatarDescriptor>();
            avatarDescriptor.customizeAnimationLayers = true;
            avatarDescriptor.baseAnimationLayers      = new[] {
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.Base,
                    isDefault = true,
                },
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.Additive,
                    isDefault = true,
                },
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.Gesture,
                    isDefault = true,
                },
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.Action,
                    isDefault = true,
                },
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type = VRCAvatarDescriptor.AnimLayerType.FX,
                    animatorController = fxController,
                },
            };
            avatarDescriptor.specialAnimationLayers = new[] {
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.Sitting,
                    isDefault = true,
                },
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.TPose,
                    isDefault = true,
                },
                new VRCAvatarDescriptor.CustomAnimLayer()
                {
                    type      = VRCAvatarDescriptor.AnimLayerType.IKPose,
                    isDefault = true,
                },
            };

            var states = fxController.layers[1].stateMachine.states.Select(childState => childState.state).ToList();
#endif

            foreach (var preset in BlendShapeReplacer.MappingBlendShapeToVRChatAnim.Keys)
            {
                VRMBlendShapeClip blendShapeClip = preset == BlendShapePreset.Unknown
                    ? vrmBlendShapeForFINGERPOINT
                    : clips.FirstOrDefault(c => c.Preset == preset);
                if (!blendShapeClip)
                {
                    continue;
                }

                AnimationClip animationClip = CreateFeeling(avatar, blendShapeClip, ref clips);
                string        anim          = BlendShapeReplacer.MappingBlendShapeToVRChatAnim[preset].ToString();
#if VRC_SDK_VRCSDK2
                avatarDescriptor.CustomStandingAnims[anim] = animationClip;
                avatarDescriptor.CustomSittingAnims[anim]  = animationClip;
#elif VRC_SDK_VRCSDK3
                states.First(s => s.name.ToLower() == anim.ToLower()).motion = animationClip;
#endif
            }
        }