/// <summary> /// nesessaryShapeKeysで指定されたシェイプキーから法線・接線を削除し、それ以外のシェイプキーは削除します。 /// </summary> /// <param name="mesh"></param> /// <param name="nesessaryShapeKeys"></param> /// <returns></returns> internal static void CleanUpShapeKeys(Mesh mesh, IEnumerable <string> nesessaryShapeKeys) { var shapeKeys = SkinnedMeshUtility.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents: false); mesh.ClearBlendShapes(); foreach (var name in nesessaryShapeKeys) { var shapeKey = shapeKeys.FirstOrDefault(key => key.Name == name); if (shapeKey == null) { continue; } mesh.AddBlendShapeFrame( shapeKey.Name, BlendShapeReplacer.MaxBlendShapeFrameWeight, shapeKey.Positions.ToArray(), shapeKey.Normals.ToArray(), shapeKey.Tangents.ToArray() ); } }
protected override bool DrawWizardGUI() { this.isValid = true; if (VRChatUtility.SDKVersion == null) { EditorGUILayout.HelpBox(_("VRChat SDK2 or SDK3 has not been imported."), MessageType.Error); this.isValid = false; return(true); } if (!this.prefabOrInstance.GetComponent <Animator>().isHuman) { EditorGUILayout.HelpBox(_("This is not humanoid."), MessageType.Error); this.isValid = false; return(true); } if (Application.unityVersion != VRChatUtility.SDKSupportedUnityVersion) { EditorGUILayout.HelpBox(string.Format( _("Unity {0} is running. If you are using a different version than {1}, VRChat SDK might not work correctly. Recommended using Unity downloaded from {2} ."), Application.unityVersion, VRChatUtility.SDKSupportedUnityVersion, VRChatUtility.DownloadURL ), MessageType.Warning); } if (this.expressions == null) { this.shapeKeyNames = this.prefabOrInstance.GetComponentsInChildren <SkinnedMeshRenderer>() .Select(renderer => renderer.sharedMesh) .Where(mesh => mesh != null) .SelectMany(mesh => SkinnedMeshUtility.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents: false)) .Select(shapeKey => shapeKey.Name) .Distinct(); (this.animations, this.expressions) = VRChatUtility.DetectVRChatExpressions(this.prefabOrInstance, this.shapeKeyNames); this.animationNames = new[] { "-" }.Concat(this.animations.Select(animation => animation.name)).ToArray(); this.maybeBlinkShapeKeyNames = this.expressions .Where(expression => VRChatToVRMWizard.PresetFieldPairs.ContainsKey(expression.Key) && VRChatToVRMWizard.PresetFieldPairs[expression.Key] == nameof(VRChatExpressionBinding.ShapeKeyNames)) .SelectMany(expression => expression.Value.ShapeKeyNames) .Concat(VRChatUtility.DetectBlinkShapeKeyNames(this.shapeKeyNames)) .Distinct() .Take(VRChatToVRMWizard.UnityEditorMaxMultiSelectCount) .ToArray(); this.expressionPresetFlagPairs = VRChatToVRMWizard.PresetFieldPairs .ToDictionary( presetFieldPair => presetFieldPair.Key, presetFieldPair => this.expressions.ContainsKey(presetFieldPair.Key) ? (VRChatToVRMWizard.PresetFieldPairs[presetFieldPair.Key] == nameof(VRChatExpressionBinding.AnimationClip) ? 1 + this.animations.ToList().IndexOf(this.expressions[presetFieldPair.Key].AnimationClip) : VRChatToVRMWizard.ToFlags(this.maybeBlinkShapeKeyNames, this.expressions[presetFieldPair.Key].ShapeKeyNames)) : 0 ); this.metaEditor = Editor.CreateEditor(this.meta); } EditorGUILayout.LabelField("Expressions", EditorStyles.boldLabel); foreach (var(preset, field) in VRChatToVRMWizard.PresetFieldPairs) { this.expressionPresetFlagPairs[preset] = VRChatToVRMWizard.PresetFieldPairs[preset] == nameof(VRChatExpressionBinding.AnimationClip) ? EditorGUILayout.Popup( preset.ToString(), this.expressionPresetFlagPairs[preset], this.animationNames ) : EditorGUILayout.MaskField( preset.ToString(), this.expressionPresetFlagPairs[preset], this.maybeBlinkShapeKeyNames ); } this.metaEditor.OnInspectorGUI(); return(true); }
/// <summary> /// リップシンクの設定を行います。 /// </summary> /// <remarks> /// <see cref="BlendShapePreset.A"/>、<see cref="BlendShapePreset.I"/>、<see cref="BlendShapePreset.O"/>が /// 単一のフレームを持つシェイプキーが存在しない場合、設定を行いません。 /// 生成するシェイプキー名と同じシェイプキーが存在する場合、それを利用します。 /// </remarks> /// <param name="avatar"></param> /// <param name="clips"></param> /// <param name="useShapeKeyNormalsAndTangents"></param> private static void SetLipSync( GameObject avatar, IEnumerable <VRMBlendShapeClip> clips, bool useShapeKeyNormalsAndTangents ) { Transform transform = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath); var renderer = transform.GetComponent <SkinnedMeshRenderer>(); Mesh mesh = renderer.sharedMesh; foreach (var preset in new[] { BlendShapePreset.A, BlendShapePreset.I, BlendShapePreset.O }) { if (!clips.FirstOrDefault(c => c.Preset == preset)) { return; } } IEnumerable <BlendShape> shapeKeys = SkinnedMeshUtility.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents); foreach (var(newName, values) in BlendShapeReplacer.VisemeShapeKeyNamesAndValues) { if (mesh.GetBlendShapeIndex(newName) != -1) { continue; } Vector3[] deltaVertices = null; foreach (Vector3[] vertices in values.SelectMany(presetAndWeight => BlendShapeReplacer.SubtractNeutralShapeKeyValues(clips.First(clip => clip.Preset == presetAndWeight.Key).ShapeKeyValues, clips) .Select(shapeKeyNameAndWeight => shapeKeys .First(shapeKey => shapeKey.Name == shapeKeyNameAndWeight.Key) .Positions .Select(vertix => vertix * (shapeKeyNameAndWeight.Value / VRMUtility.MaxBlendShapeBindingWeight * presetAndWeight.Value)) .ToArray() ) )) { if (deltaVertices == null) { deltaVertices = vertices; continue; } for (var i = 0; i < deltaVertices.Length; i++) { deltaVertices[i] += vertices[i]; } } mesh.AddBlendShapeFrame( newName, BlendShapeReplacer.MaxBlendShapeFrameWeight, deltaVertices, null, null ); } EditorUtility.SetDirty(mesh); var avatarDescriptor #if VRC_SDK_VRCSDK3 = avatar.GetComponent <VRC_AvatarDescriptor>(); avatarDescriptor.lipSync = VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape;