/* copyright : Yasushi Emoto Created by 2024-10-22 version1.03 */ using System.Linq; using UnityEngine; using UnityEditor; using System.Collections; using System; using System.IO; using System.Text; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; public class FacialReaderFromFileWindow : EditorWindow { private float oneFrameTime = 1.0f / 60.0f; private float[] recordTimetArray = new float[0]; public float startTime = 0.0f; private string facialFileString = ""; //object names public string faceObjectGroupName = ""; public string headBoneName = ""; public string rightEyeBoneName = ""; public string leftEyeBoneName = ""; public string headPositionObjectName = ""; private SkinnedMeshRenderer meshTarget; private List meshTargetList = new List(); private List meshTargetObjectArray = new List(); private List headObjectArray = new List(); private List rightEyeObjectArray = new List(); private List leftEyeObjectArray = new List(); private List headPositionObjectArray = new List(); private Dictionary> BlendShapeNameAndValueList; private List blendShapeNameList = new List(); private List HeadRotationXList = new List(); private List HeadRotationYList = new List(); private List HeadRotationZList = new List(); private List RightEyeRotationXList = new List(); private List RightEyeRotationYList = new List(); private List RightEyeRotationZList = new List(); private List LeftEyeRotationXList = new List(); private List LeftEyeRotationYList = new List(); private List LeftEyeRotationZList = new List(); private List HeadPositionXList = new List(); private List HeadPositionYList = new List(); private List HeadPositionZList = new List(); [MenuItem("Window/FacialReaderFromFile")] public static void ShowWindow () { GetWindow("FacialReaderFromFile"); } private void FindGameObjectsInsideUnitySettings() { //Find BlendShape Objects meshTargetList = new List(); meshTargetObjectArray = new List(); BlendShapeNameAndValueList = new Dictionary>(); blendShapeNameList = new List(); HeadRotationXList = new List(); HeadRotationYList = new List(); HeadRotationZList = new List(); RightEyeRotationXList = new List(); RightEyeRotationYList = new List(); RightEyeRotationZList = new List(); LeftEyeRotationXList = new List(); LeftEyeRotationYList = new List(); LeftEyeRotationZList = new List(); HeadPositionXList = new List(); HeadPositionYList = new List(); HeadPositionZList = new List(); GameObject faceObjGrp = GameObject.Find(faceObjectGroupName); if (faceObjGrp != null) { List list = FacialReaderFromFileWindow_GetAllChildren.GetAll(faceObjGrp); foreach (GameObject obj in list) { meshTarget = obj.GetComponent(); if (meshTarget != null) { if (HasBlendShapes(meshTarget) == true) { meshTargetList.Add(meshTarget); meshTargetObjectArray.Add(obj); } } } } //Find Bone Objects headObjectArray = new List(); foreach (string headString in headBoneName.Split(',')) { GameObject headObject = GameObject.Find(headString); if (headObject != null) { headObjectArray.Add(headObject); } } rightEyeObjectArray = new List(); foreach (string rightEyeString in rightEyeBoneName.Split(',')) { GameObject rightEyeObject = GameObject.Find(rightEyeString); if (rightEyeObject != null) { rightEyeObjectArray.Add(rightEyeObject); } } leftEyeObjectArray = new List(); foreach (string leftEyeString in leftEyeBoneName.Split(',')) { GameObject leftEyeObject = GameObject.Find(leftEyeString); if (leftEyeObject != null) { leftEyeObjectArray.Add(leftEyeObject); } } headPositionObjectArray = new List(); foreach (string headPositionString in headPositionObjectName.Split(',')) { GameObject headPositionObject = GameObject.Find(headPositionString); if (headPositionObject != null) { headPositionObjectArray.Add(headPositionObject); } } } void OnGUI () { GUILayout.Label("Apply facial movement from file", EditorStyles.boldLabel); faceObjectGroupName = EditorGUILayout.TextField("Face Object Group Name: ", faceObjectGroupName); headBoneName = EditorGUILayout.TextField("Head Bone Name: ", headBoneName); rightEyeBoneName = EditorGUILayout.TextField("Right Eye Bone Name: ", rightEyeBoneName); leftEyeBoneName = EditorGUILayout.TextField("Left Eye Bone Name: ", leftEyeBoneName); headPositionObjectName = EditorGUILayout.TextField("Head Position Object Name: ", headPositionObjectName); startTime = EditorGUILayout.FloatField("Start Time(s):", startTime); if (GUILayout.Button("Read from file")) { FindGameObjectsInsideUnitySettings(); ReadFromFile(); } } void ReadFromFile () { string path = EditorUtility.OpenFilePanel("Read from txt file", "", "txt"); if (path.Length != 0) { StreamReader reader = new StreamReader(path); facialFileString = reader.ReadToEnd(); reader.Close(); // StreamReaderを閉じることを忘れないでください string[] txtArray = new string[0]; try { using (StreamReader streamReader = new StreamReader(path)) // 変数名を変更 { string line; int lineNumber = 0; while ((line = streamReader.ReadLine()) != null) { lineNumber++; if (lineNumber == 3) // line3 { txtArray = line.Split(new[] { "\",\"" }, StringSplitOptions.None); break; } if (lineNumber == 4) // line4 { recordTimetArray = ConvertStringToFloatArray(line); break; } } } } catch (IOException e) { Debug.LogError("File reading error: " + e.Message); } foreach (var txt in txtArray) { try { string txt_remove = txt.Replace("\"", ""); string[] strArray1 = txt.Split('='); if (strArray1.Length == 2) { //blendShapes foreach (string message in strArray1[0].Split('|')) { try { string[] strArray2 = message.Split('&'); if (strArray2.Length == 2) { var bs_name = strArray2[0].Replace("_L","Left").Replace("_R","Right"); //if key exists if ( BlendShapeNameAndValueList.ContainsKey(bs_name) ) { List blendShape_values = new List(); blendShape_values = BlendShapeNameAndValueList[bs_name]; blendShape_values.Add(float.Parse(strArray2[1], CultureInfo.InvariantCulture)); BlendShapeNameAndValueList[bs_name] = blendShape_values; } else { BlendShapeNameAndValueList.Add(bs_name,new List(){float.Parse(strArray2[1], CultureInfo.InvariantCulture)}); blendShapeNameList.Add(bs_name); } } } catch(System.NullReferenceException e) { Debug.Log(e); } } foreach (string message in strArray1[1].Split('|')) { try { string[] strArray2 = message.Split('#'); if (strArray2.Length == 2) { string[] commaList = strArray2[1].Split(','); if (commaList.Length > 1) { if (strArray2[0] == "head") { HeadRotationXList.Add(float.Parse(commaList[0], CultureInfo.InvariantCulture)); HeadRotationYList.Add(-float.Parse(commaList[1], CultureInfo.InvariantCulture)); HeadRotationZList.Add(-float.Parse(commaList[2], CultureInfo.InvariantCulture)); HeadPositionXList.Add(-float.Parse(commaList[3], CultureInfo.InvariantCulture)); HeadPositionYList.Add(float.Parse(commaList[4], CultureInfo.InvariantCulture)); HeadPositionZList.Add(float.Parse(commaList[5], CultureInfo.InvariantCulture)); } else if (strArray2[0] == "rightEye") { RightEyeRotationXList.Add(float.Parse(commaList[0], CultureInfo.InvariantCulture)); RightEyeRotationYList.Add(-float.Parse(commaList[1], CultureInfo.InvariantCulture)); RightEyeRotationZList.Add(0.0f); } else if (strArray2[0] == "leftEye") { LeftEyeRotationXList.Add(float.Parse(commaList[0], CultureInfo.InvariantCulture)); LeftEyeRotationYList.Add(-float.Parse(commaList[1], CultureInfo.InvariantCulture)); LeftEyeRotationZList.Add(0.0f); } } } } catch(System.NullReferenceException e) { Debug.Log(e); } } } } catch(System.NullReferenceException e) { Debug.Log(e); } } try { foreach (var meshTargetObj in meshTargetObjectArray) { AnimationCurve curve; Keyframe[] keys; var _animation = meshTargetObj.GetComponent(); if (!_animation) _animation = meshTargetObj.AddComponent(); var clip = _animation.clip; var clip_exists_flag = true; if (!clip) { clip_exists_flag = false; clip = new AnimationClip(); } meshTarget = meshTargetObj.GetComponent(); if (meshTarget != null) { if (HasBlendShapes(meshTarget) == true) { var shared_mesh = meshTarget.sharedMesh; for (int b_count = 0; b_count < shared_mesh.blendShapeCount; b_count++) { string sharedMeshBlendShapeName = shared_mesh.GetBlendShapeName(b_count); foreach (var mappedShapeName in BlendShapeNameAndValueList.Keys) { if (sharedMeshBlendShapeName.Contains(mappedShapeName)) { List blendShape_values = new List(); blendShape_values = BlendShapeNameAndValueList[mappedShapeName]; keys = new Keyframe[blendShape_values.Count]; for ( int i = 0 ; i < blendShape_values.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, blendShape_values[i]); } curve = new AnimationCurve(keys); clip.SetCurve("", typeof(SkinnedMeshRenderer), "blendShape."+sharedMeshBlendShapeName, curve); break; } } } } } if(clip_exists_flag == false) { clip.name = meshTargetObj.name+"_AnimClip"; // set name clip.legacy = true; // change to legacy _animation.clip = clip; // set default clip _animation.AddClip(clip, clip.name); // add clip to animation component AssetDatabase.CreateAsset(clip, "Assets/"+clip.name+".anim"); // to create asset } } } catch(System.NullReferenceException e) { Debug.Log(e); } try { foreach (var headObject in headObjectArray) { var _animation = headObject.GetComponent(); if (!_animation) _animation = headObject.AddComponent(); var clip = _animation.clip; var clip_exists_flag = true; if (!clip) { clip_exists_flag = false; clip = new AnimationClip(); } AnimationCurve curve_x; Keyframe[] keys; keys = new Keyframe[HeadRotationXList.Count]; for ( int i = 0 ; i < HeadRotationXList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, HeadRotationXList[i]); } curve_x = new AnimationCurve(keys); AnimationCurve curve_y; keys = new Keyframe[HeadRotationYList.Count]; for ( int i = 0 ; i < HeadRotationYList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, HeadRotationYList[i]); } curve_y = new AnimationCurve(keys); AnimationCurve curve_z; keys = new Keyframe[HeadRotationZList.Count]; for ( int i = 0 ; i < HeadRotationZList.Count ; i++ ) { keys[i] = new Keyframe(startTime+ oneFrameTime * i, HeadRotationZList[i]); } curve_z = new AnimationCurve(keys); clip.SetCurve("", typeof(Transform), "localEulerAngles.x", curve_x); clip.SetCurve("", typeof(Transform), "localEulerAngles.y", curve_y); clip.SetCurve("", typeof(Transform), "localEulerAngles.z", curve_z); if(clip_exists_flag == false) { clip.name = headObject.name+"_AnimClip"; // set name clip.legacy = true; // change to legacy _animation.clip = clip; // set default clip _animation.AddClip(clip, clip.name); // add clip to animation component AssetDatabase.CreateAsset(clip, "Assets/"+clip.name+".anim"); // to create asset } } foreach (var rightEyeObject in rightEyeObjectArray) { var _animation = rightEyeObject.GetComponent(); if (!_animation) _animation = rightEyeObject.AddComponent(); var clip = _animation.clip; var clip_exists_flag = true; if (!clip) { clip_exists_flag = false; clip = new AnimationClip(); } AnimationCurve curve_x; Keyframe[] keys; keys = new Keyframe[RightEyeRotationXList.Count]; for ( int i = 0 ; i < RightEyeRotationXList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, RightEyeRotationXList[i]); } curve_x = new AnimationCurve(keys); AnimationCurve curve_y; keys = new Keyframe[RightEyeRotationYList.Count]; for ( int i = 0 ; i < RightEyeRotationYList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, RightEyeRotationYList[i]); } curve_y = new AnimationCurve(keys); AnimationCurve curve_z; keys = new Keyframe[RightEyeRotationZList.Count]; for ( int i = 0 ; i < RightEyeRotationZList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, RightEyeRotationZList[i]); } curve_z = new AnimationCurve(keys); clip.SetCurve("", typeof(Transform), "localEulerAngles.x", curve_x); clip.SetCurve("", typeof(Transform), "localEulerAngles.y", curve_y); clip.SetCurve("", typeof(Transform), "localEulerAngles.z", curve_z); if(clip_exists_flag == false) { clip.name = rightEyeObject.name+"_AnimClip"; // set name clip.legacy = true; // change to legacy _animation.clip = clip; // set default clip _animation.AddClip(clip, clip.name); // add clip to animation component AssetDatabase.CreateAsset(clip, "Assets/"+clip.name+".anim"); // to create asset } } foreach (var leftEyeObject in leftEyeObjectArray) { var _animation = leftEyeObject.GetComponent(); if (!_animation) _animation = leftEyeObject.AddComponent(); var clip = _animation.clip; var clip_exists_flag = true; if (!clip) { clip_exists_flag = false; clip = new AnimationClip(); } AnimationCurve curve_x; Keyframe[] keys; keys = new Keyframe[LeftEyeRotationXList.Count]; for ( int i = 0 ; i < LeftEyeRotationXList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, LeftEyeRotationXList[i]); } curve_x = new AnimationCurve(keys); AnimationCurve curve_y; keys = new Keyframe[LeftEyeRotationYList.Count]; for ( int i = 0 ; i < LeftEyeRotationYList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, LeftEyeRotationYList[i]); } curve_y = new AnimationCurve(keys); AnimationCurve curve_z; keys = new Keyframe[LeftEyeRotationZList.Count]; for ( int i = 0 ; i < LeftEyeRotationZList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, LeftEyeRotationZList[i]); } curve_z = new AnimationCurve(keys); clip.SetCurve("", typeof(Transform), "localEulerAngles.x", curve_x); clip.SetCurve("", typeof(Transform), "localEulerAngles.y", curve_y); clip.SetCurve("", typeof(Transform), "localEulerAngles.z", curve_z); if(clip_exists_flag == false) { clip.name = leftEyeObject.name+"_AnimClip"; // set name clip.legacy = true; // change to legacy _animation.clip = clip; // set default clip _animation.AddClip(clip, clip.name); // add clip to animation component AssetDatabase.CreateAsset(clip, "Assets/"+clip.name+".anim"); // to create asset } } foreach (var headPositionObject in headPositionObjectArray) { var _animation = headPositionObject.GetComponent(); if (!_animation) _animation = headPositionObject.AddComponent(); var clip = _animation.clip; var clip_exists_flag = true; if (!clip) { clip_exists_flag = false; clip = new AnimationClip(); } AnimationCurve curve_x; Keyframe[] keys; keys = new Keyframe[HeadPositionXList.Count]; for ( int i = 0 ; i < HeadPositionXList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, HeadPositionXList[i]); } curve_x = new AnimationCurve(keys); AnimationCurve curve_y; keys = new Keyframe[HeadPositionYList.Count]; for ( int i = 0 ; i < HeadPositionYList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, HeadPositionYList[i]); } curve_y = new AnimationCurve(keys); AnimationCurve curve_z; keys = new Keyframe[HeadPositionZList.Count]; for ( int i = 0 ; i < HeadPositionZList.Count ; i++ ) { keys[i] = new Keyframe(startTime + oneFrameTime * i, HeadPositionZList[i]); } curve_z = new AnimationCurve(keys); clip.SetCurve("", typeof(Transform), "localPosition.x", curve_x); clip.SetCurve("", typeof(Transform), "localPosition.y", curve_y); clip.SetCurve("", typeof(Transform), "localPosition.z", curve_z); if(clip_exists_flag == false) { clip.name = headPositionObject.name+"_AnimClip"; // set name clip.legacy = true; // change to legacy _animation.clip = clip; // set default clip _animation.AddClip(clip, clip.name); // add clip to animation component AssetDatabase.CreateAsset(clip, "Assets/"+clip.name+".anim"); // to create asset } } } catch(System.NullReferenceException e) { Debug.Log(e); } } } private static float[] ConvertStringToFloatArray(string jsonString) { string pattern = @"-?\d+(\.\d+)?(e[+-]?\d+)?"; MatchCollection matches = Regex.Matches(jsonString, pattern); float[] floatArray = matches.OfType() .Select(m => float.Parse(m.Value)) .ToArray(); return floatArray; } private bool HasBlendShapes(SkinnedMeshRenderer skin) { if (!skin.sharedMesh) { return false; } if (skin.sharedMesh.blendShapeCount <= 0) { return false; } return true; } } public static class FacialReaderFromFileWindow_GetAllChildren { public static List GetAll(this GameObject obj) { List allChildren = new List(); allChildren.Add(obj); GetChildren(obj, ref allChildren); return allChildren; } public static void GetChildren(GameObject obj, ref List allChildren) { Transform children = obj.GetComponentInChildren(); if (children.childCount == 0) { return; } foreach (Transform ob in children) { allChildren.Add(ob.gameObject); GetChildren(ob.gameObject, ref allChildren); } } }