Streamingle_URP/Assets/External/Ifacialmocap/UnityRecieve_FACEMOTION3D_and_iFacialMocap.cs

435 lines
10 KiB
C#

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using System;
using System.Collections;
using System.Globalization;
public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour
{
// broadcast address
public bool gameStartWithConnect = true;
public string iOS_IPAddress = "255.255.255.255";
public bool mirrorMode = false; // 좌우 반전 모드 설정
private UdpClient client;
private bool StartFlag = true;
//object references
public SkinnedMeshRenderer[] faceMeshRenderers;
public Transform headBone;
public Transform rightEyeBone;
public Transform leftEyeBone;
public Transform headPositionObject;
private UdpClient udp;
private Thread thread;
private string messageString = "";
public int LOCAL_PORT = 49983;
// Start is called
void StartFunction()
{
if (StartFlag == true)
{
StartFlag = false;
FindGameObjectsInsideUnitySettings();
//Send to iOS
if (gameStartWithConnect == true)
{
Connect_to_iOS_App();
}
//Recieve udp from iOS
CreateUdpServer();
}
}
void Start()
{
StartFunction();
}
void CreateUdpServer()
{
try
{
udp = new UdpClient(LOCAL_PORT);
udp.Client.ReceiveTimeout = 5;
thread = new Thread(new ThreadStart(ThreadMethod));
thread.Start();
}
catch (Exception e)
{
Debug.LogError($"[iFacialMocap] UDP 서버 생성 실패: {e.Message}");
}
}
IEnumerator WaitProcess(float WaitTime)
{
yield return new WaitForSeconds(WaitTime);
}
void Connect_to_iOS_App()
{
try
{
//iFacialMocap
SendMessage_to_iOSapp("iFacialMocap_sahuasouryya9218sauhuiayeta91555dy3719", 49983);
//Facemotion3d
SendMessage_to_iOSapp("FACEMOTION3D_OtherStreaming", 49993);
}
catch (Exception e)
{
Debug.LogError($"[iFacialMocap] iOS 앱 연결 실패: {e.Message}");
}
}
void StopStreaming_iOS_App()
{
SendMessage_to_iOSapp("StopStreaming_FACEMOTION3D", 49993);
}
//iOSアプリに通信開始のメッセージを送信
//Send a message to the iOS application to start streaming
void SendMessage_to_iOSapp(string sendMessage, int send_port)
{
try
{
client = new UdpClient();
client.Connect(iOS_IPAddress, send_port);
byte[] dgram = Encoding.UTF8.GetBytes(sendMessage);
// 메시지 전송 시도
for (int i = 0; i < 5; i++)
{
client.Send(dgram, dgram.Length);
}
}
catch (Exception e)
{
Debug.LogError($"[iFacialMocap] 메시지 전송 실패: {e.Message}");
}
}
// Update is called once per frame
void Update()
{
try
{
SetAnimation_inside_Unity_settings();
}
catch
{ }
}
//BlendShapeの設定
//set blendshapes
void SetBlendShapeWeightFromStrArray(string[] strArray2)
{
string mappedShapeName = strArray2[0].Replace("_R", "Right").Replace("_L", "Left");
float weight = float.Parse(strArray2[1], CultureInfo.InvariantCulture);
if (faceMeshRenderers != null)
{
foreach (var meshRenderer in faceMeshRenderers)
{
if (meshRenderer != null && meshRenderer.sharedMesh != null)
{
// 대소문자 구분 없이 블렌드쉐입 찾기
for (int i = 0; i < meshRenderer.sharedMesh.blendShapeCount; i++)
{
string blendShapeName = meshRenderer.sharedMesh.GetBlendShapeName(i);
if (string.Equals(blendShapeName, mappedShapeName, StringComparison.OrdinalIgnoreCase))
{
meshRenderer.SetBlendShapeWeight(i, weight);
break;
}
}
}
}
}
}
// BlendShape 이름 정규화 함수
string NormalizeBlendShapeName(string name)
{
if (name.EndsWith("_L"))
name = name.Substring(0, name.Length - 2) + "Left";
else if (name.EndsWith("_R"))
name = name.Substring(0, name.Length - 2) + "Right";
// 카멜케이스화: 언더스코어 기준 분리 후 각 파트 첫 글자 대문자
string[] parts = name.Split('_');
for (int i = 0; i < parts.Length; i++)
{
if (parts[i].Length > 0)
parts[i] = char.ToUpper(parts[i][0]) + parts[i].Substring(1);
}
return string.Join("", parts);
}
//BlendShapeとボーンの回転の設定
//set blendshapes & bone rotation
void SetAnimation_inside_Unity_settings()
{
try
{
string[] strArray1 = messageString.Split('=');
if (strArray1.Length >= 2)
{
//blendShapes
foreach (string message in strArray1[0].Split('|'))
{
string[] strArray2 = new string[3];
if (message.Contains("&"))
{
strArray2 = message.Split('&');
}
else
{
strArray2 = message.Split('-');
}
if (strArray2.Length == 2)
{
// 이름 정규화 먼저 적용
strArray2[0] = NormalizeBlendShapeName(strArray2[0]);
if (mirrorMode)
{
string originalShapeName = strArray2[0];
string shapeNameLower = originalShapeName.ToLowerInvariant();
// eye 블렌드쉐입 매핑 (key는 소문자, value는 Unity 카멜케이스)
Dictionary<string, string> eyeMirrorMap = new Dictionary<string, string>() {
{"eyelookupleft", "EyeLookUpRight"},
{"eyelookupright", "EyeLookUpLeft"},
{"eyelookdownleft", "EyeLookDownRight"},
{"eyelookdownright", "EyeLookDownLeft"},
{"eyelookinleft", "EyeLookInRight"},
{"eyelookinright", "EyeLookInLeft"},
{"eyelookoutleft", "EyeLookOutRight"},
{"eyelookoutright", "EyeLookOutLeft"},
{"eyewideleft", "EyeWideRight"},
{"eyewideright", "EyeWideLeft"},
{"eyesquintleft", "EyeSquintRight"},
{"eyesquintright", "EyeSquintLeft"},
{"eyeblinkleft", "EyeBlinkRight"},
{"eyeblinkright", "EyeBlinkLeft"}
};
string mirroredName = originalShapeName;
if (eyeMirrorMap.ContainsKey(shapeNameLower))
{
mirroredName = eyeMirrorMap[shapeNameLower];
}
else if (originalShapeName.Contains("Right"))
{
mirroredName = originalShapeName.Replace("Right", "Left");
}
else if (originalShapeName.Contains("Left"))
{
mirroredName = originalShapeName.Replace("Left", "Right");
}
strArray2[0] = mirroredName;
}
SetBlendShapeWeightFromStrArray(strArray2);
}
}
foreach (string message in strArray1[1].Split('|'))
{
string[] strArray2 = message.Split('#');
if (strArray2.Length == 2)
{
string[] commaList = strArray2[1].Split(',');
if (strArray2[0] == "head" && headBone != null)
{
float x = float.Parse(commaList[0], CultureInfo.InvariantCulture);
float y = float.Parse(commaList[1], CultureInfo.InvariantCulture);
float z = float.Parse(commaList[2], CultureInfo.InvariantCulture);
if (mirrorMode)
{
y = -y; // Y축 회전 반전
}
headBone.localRotation = Quaternion.Euler(x, y, -z);
if (headPositionObject != null)
{
float posX = -float.Parse(commaList[3], CultureInfo.InvariantCulture);
float posY = float.Parse(commaList[4], CultureInfo.InvariantCulture);
float posZ = float.Parse(commaList[5], CultureInfo.InvariantCulture);
if (mirrorMode)
{
posX = -posX; // X축 위치 반전
}
headPositionObject.localPosition = new Vector3(posX, posY, posZ);
}
}
else if (strArray2[0] == "rightEye" && rightEyeBone != null)
{
float x = float.Parse(commaList[0], CultureInfo.InvariantCulture);
float y = float.Parse(commaList[1], CultureInfo.InvariantCulture);
float z = float.Parse(commaList[2], CultureInfo.InvariantCulture);
if (mirrorMode)
{
y = -y; // Y축 회전 반전
}
rightEyeBone.localRotation = Quaternion.Euler(x, y, z);
}
else if (strArray2[0] == "leftEye" && leftEyeBone != null)
{
float x = float.Parse(commaList[0], CultureInfo.InvariantCulture);
float y = float.Parse(commaList[1], CultureInfo.InvariantCulture);
float z = float.Parse(commaList[2], CultureInfo.InvariantCulture);
if (mirrorMode)
{
y = -y; // Y축 회전 반전
}
leftEyeBone.localRotation = Quaternion.Euler(x, y, z);
}
}
}
}
}
catch
{
}
}
void FindGameObjectsInsideUnitySettings()
{
// 모든 Transform 참조는 이미 인스펙터에서 할당되므로 추가 초기화가 필요 없음
}
void ThreadMethod()
{
//Process once every 5ms
long next = DateTime.Now.Ticks + 50000;
long now;
while (true)
{
try
{
IPEndPoint remoteEP = null;
byte[] data = udp.Receive(ref remoteEP);
messageString = Encoding.ASCII.GetString(data);
}
catch (SocketException e)
{
if (e.SocketErrorCode != SocketError.TimedOut)
{
Debug.LogError($"[iFacialMocap] 데이터 수신 오류: {e.Message}");
}
}
catch (Exception e)
{
Debug.LogError($"[iFacialMocap] 예상치 못한 오류: {e.Message}");
}
do
{
now = DateTime.Now.Ticks;
}
while (now < next);
next += 50000;
}
}
public string GetMessageString()
{
return messageString;
}
void OnEnable()
{
StartFunction();
}
void OnDisable()
{
try
{
OnApplicationQuit();
}
catch
{
}
}
void OnApplicationQuit()
{
if (StartFlag == false)
{
StartFlag = true;
StopUDP();
}
}
public void StopUDP()
{
if (gameStartWithConnect == true)
{
StopStreaming_iOS_App();
}
udp.Dispose();
thread.Abort();
}
private bool HasBlendShapes(SkinnedMeshRenderer skin)
{
if (!skin.sharedMesh)
{
return false;
}
if (skin.sharedMesh.blendShapeCount <= 0)
{
return false;
}
return true;
}
}
public static class FM3D_and_iFacialMocap_GetAllChildren
{
public static List<GameObject> GetAll(this GameObject obj)
{
List<GameObject> allChildren = new List<GameObject>();
allChildren.Add(obj);
GetChildren(obj, ref allChildren);
return allChildren;
}
public static void GetChildren(GameObject obj, ref List<GameObject> allChildren)
{
Transform children = obj.GetComponentInChildren<Transform>();
if (children.childCount == 0)
{
return;
}
foreach (Transform ob in children)
{
allChildren.Add(ob.gameObject);
GetChildren(ob.gameObject, ref allChildren);
}
}
}