/// <summary> /// 【SDK3】<see cref="BlendShapePreset.Blink"/>を変換し、視線追従を有効化します。 /// </summary> /// <param name="avatar"></param> /// <param name="clips"></param> /// <param name="useShapeKeyNormalsAndTangents"/> private static void EnableEyeLook( GameObject avatar, IEnumerable <VRMBlendShapeClip> clips, bool useShapeKeyNormalsAndTangents ) { VRMBlendShapeClip clip = clips.FirstOrDefault(c => c.Preset == BlendShapePreset.Blink); var lookAtBoneApplyer = avatar.GetComponent <VRMLookAtBoneApplyer>(); if (!clip && !lookAtBoneApplyer) { return; } var renderer = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath).GetComponent <SkinnedMeshRenderer>(); var mesh = renderer.sharedMesh; if (clip) { mesh.AddBlendShapeFrame( BlendShapeReplacer.BlinkShapeKeyName, 1, BlendShapeReplacer.GenerateShapeKey( clip.ShapeKeyValues, BlendShapeReplacer.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents) ), null, null ); EditorUtility.SetDirty(mesh); } #if VRC_SDK_VRCSDK3 var descriptor = avatar.GetComponent <VRCAvatarDescriptor>(); descriptor.enableEyeLook = true; var settings = new VRCAvatarDescriptor.CustomEyeLookSettings(); if (clip) { settings.eyelidType = VRCAvatarDescriptor.EyelidType.Blendshapes; settings.eyelidsSkinnedMesh = renderer; settings.eyelidsBlendshapes = new[] { mesh.blendShapeCount - 1, -1, -1 }; } if (lookAtBoneApplyer) { settings.eyeMovement = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeMovements() { excitement = 0.5f, confidence = 0, }; settings.leftEye = lookAtBoneApplyer.LeftEye.Transform; settings.rightEye = lookAtBoneApplyer.RightEye.Transform; settings.eyesLookingUp = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations() { left = Quaternion.Euler(x: -lookAtBoneApplyer.VerticalUp.CurveYRangeDegree, 0, 0), right = Quaternion.Euler(x: -lookAtBoneApplyer.VerticalUp.CurveYRangeDegree, 0, 0), }; settings.eyesLookingDown = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations() { left = Quaternion.Euler(x: lookAtBoneApplyer.VerticalUp.CurveYRangeDegree, 0, 0), right = Quaternion.Euler(x: lookAtBoneApplyer.VerticalUp.CurveYRangeDegree, 0, 0), }; settings.eyesLookingLeft = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations() { left = Quaternion.Euler(0, y: -lookAtBoneApplyer.HorizontalOuter.CurveYRangeDegree, 0), right = Quaternion.Euler(0, y: -lookAtBoneApplyer.HorizontalInner.CurveYRangeDegree, 0), }; settings.eyesLookingRight = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations() { left = Quaternion.Euler(0, y: lookAtBoneApplyer.HorizontalInner.CurveYRangeDegree, 0), right = Quaternion.Euler(0, y: lookAtBoneApplyer.HorizontalOuter.CurveYRangeDegree, 0), }; } descriptor.customEyeLookSettings = settings; #endif }
/// <summary> /// Animatorコンポーネントを使用せずに、<see cref="BlendShapePreset.Neutral"/>、および<see cref="BlendShapePreset.Blink"/>を変換します。 /// </summary> /// <remarks> /// <see cref="BlendShapePreset.Blink"/>が関連付けられたメッシュが見つからない、またはそのメッシュに /// <see cref="BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin"/>がそろっていれば何もしません。 /// それらのキーが存在せず、<see cref="BlendShapePreset.Blink_L"/>、<see cref="BlendShapePreset.Blink_R"/>がいずれも設定されていればそれを優先します。 /// </remarks> /// <param name="avatar"></param> /// <param name="clips"></param> /// <param name="useShapeKeyNormalsAndTangents"></param> private static void SetBlinkWithoutAnimator( GameObject avatar, IEnumerable <VRMBlendShapeClip> clips, bool useShapeKeyNormalsAndTangents ) { var renderer = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath).GetComponent <SkinnedMeshRenderer>(); Mesh mesh = renderer.sharedMesh; if (BlendShapeReplacer.GetBlendShapeNames(mesh: mesh) .Take(BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.Count()) .SequenceEqual(BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin)) { return; } IEnumerable <BlendShape> shapeKeys = BlendShapeReplacer.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents); mesh.ClearBlendShapes(); var dummyShapeKeyNames = new List <string>(); if (clips.FirstOrDefault(c => c.Preset == BlendShapePreset.Blink_L) && clips.FirstOrDefault(c => c.Preset == BlendShapePreset.Blink_R)) { mesh.AddBlendShapeFrame( BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.ElementAt(0), BlendShapeReplacer.MaxBlendShapeFrameWeight, BlendShapeReplacer.GenerateShapeKey( namesAndWeights: clips.First(c => c.Preset == BlendShapePreset.Blink_L).ShapeKeyValues, shapeKeys: shapeKeys ), null, null ); mesh.AddBlendShapeFrame( BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.ElementAt(1), BlendShapeReplacer.MaxBlendShapeFrameWeight, BlendShapeReplacer.GenerateShapeKey( namesAndWeights: clips.First(c => c.Preset == BlendShapePreset.Blink_R).ShapeKeyValues, shapeKeys: shapeKeys ), null, null ); } else { mesh.AddBlendShapeFrame( BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.ElementAt(0), BlendShapeReplacer.MaxBlendShapeFrameWeight, BlendShapeReplacer.GenerateShapeKey( namesAndWeights: clips.First(c => c.Preset == BlendShapePreset.Blink).ShapeKeyValues, shapeKeys: shapeKeys ), null, null ); dummyShapeKeyNames.Add(BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.ElementAt(1)); } dummyShapeKeyNames.AddRange(BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.Skip(2)); foreach (string name in dummyShapeKeyNames) { BlendShapeReplacer.AddDummyShapeKey(mesh: mesh, name: name); } foreach (BlendShape shapeKey in shapeKeys) { if (BlendShapeReplacer.OrderedBlinkGeneratedByCatsBlenderPlugin.Contains(shapeKey.Name)) { continue; } mesh.AddBlendShapeFrame( shapeKey.Name, BlendShapeReplacer.MaxBlendShapeFrameWeight, shapeKey.Positions.ToArray(), shapeKey.Normals.ToArray(), shapeKey.Tangents.ToArray() ); } EditorUtility.SetDirty(mesh); }