KINDNICK_URP/Assets/External/EVMC4U/ExternalReceiver.cs
2025-04-25 21:14:54 +09:00

1447 lines
63 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* ExternalReceiver
* https://sabowl.sakura.ne.jp/gpsnmeajp/
*
* MIT License
*
* Copyright (c) 2019 gpsnmeajp
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma warning disable 0414,0219
using System;
using System.Reflection;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Profiling;
using UniGLTF;
using UniVRM10;
namespace EVMC4U
{
//[RequireComponent(typeof(uOSC.uOscServer))]
public class ExternalReceiver : MonoBehaviour, IExternalReceiver
{
[Header("ExternalReceiver v5.0a")]
[SerializeField, Label("VRMモデルのGameObject")]
public GameObject Model = null;
[SerializeField, Label("一時停止")]
public bool Freeze = false; //すべての同期を止める(撮影向け)
[SerializeField, Label("パケットリミッター")]
public bool PacktLimiter = true; //パケットフレーム数が一定値を超えるとき、パケットを捨てる
#if EVMC4U_JA
[Header("ルート姿勢同期オプション")]
#else
[Header("Root Synchronize Option")]
#endif
[SerializeField, Label("ルート位置反映対象(VR向け)")]
public Transform RootPositionTransform = null; //VR向けroot位置同期オブジェクト指定
[SerializeField, Label("ルート回転反映対象(VR向け)")]
public Transform RootRotationTransform = null; //VR向けroot回転同期オブジェクト指定
[SerializeField, Label("ルート位置反映")]
public bool RootPositionSynchronize = true; //ルート座標同期(ルームスケール移動)
[SerializeField, Label("ルート回転反映")]
public bool RootRotationSynchronize = true; //ルート回転同期
[SerializeField, Label("ルートスケール反映")]
public bool RootScaleOffsetSynchronize = false; //MRスケール適用
#if EVMC4U_JA
[Header("その他の同期オプション")]
#else
[Header("Other Synchronize Option")]
#endif
[SerializeField, Label("表情の同期")]
public bool BlendShapeSynchronize = true; //表情等同期
[SerializeField, Label("ボーン位置の厳密な同期")]
public bool BonePositionSynchronize = true; //ボーン位置適用(回転は強制)
#if EVMC4U_JA
[Header("同期遮断オプション")]
#else
[Header("Synchronize Cutoff Option")]
#endif
[SerializeField, Label("指ボーンを同期しない")]
public bool HandPoseSynchronizeCutoff = false; //指状態反映オフ
[SerializeField, Label("目ボーンを同期しない")]
public bool EyeBoneSynchronizeCutoff = false; //目ボーン反映オフ
#if EVMC4U_JA
[Header("なめらかフィルター")]
#else
[Header("Lowpass Filter Option")]
#endif
[SerializeField, Label("ボーン位置なめらか")]
public bool BonePositionFilterEnable = false; //ボーン位置フィルタ
[SerializeField, Label("ボーン回転なめらか")]
public bool BoneRotationFilterEnable = false; //ボーン回転フィルタ
[SerializeField, Label("なめらか係数(0.5000.999)")]
public float BoneFilter = 0.7f; //ボーンフィルタ係数
[SerializeField, Label("表情なめらか")]
public bool BlendShapeFilterEnable = false; //BlendShapeフィルタ
[SerializeField, Label("表情なめらか係数(0.5000.999)")]
public float BlendShapeFilter = 0.7f; //BlendShapeフィルタ係数
#if EVMC4U_JA
[Header("VRMファイル自動読み込み")]
#else
[Header("VRM Loader Option")]
#endif
[SerializeField, Label("VRMファイル自動読み込み(対応アプリのみ)")]
public bool enableAutoLoadVRM = true; //VRMの自動読み込みの有効可否
#if EVMC4U_JA
[Header("その他")]
#else
[Header("Other Option")]
#endif
[SerializeField, Label("更新タイミングをLateUpdateにする(Animationの後に実行して上書きする)")]
public bool EnableLateUpdateForOverwriteAnimationResult = false; //LateUpdateにするかどうか(Animationの後に実行して上書きする)
[SerializeField, Label("未キャリブレーション時にモデルを隠す")]
public bool HideInUncalibrated = false; //キャリブレーション出来ていないときは隠す
[SerializeField, Label("MRモード連動")]
public bool SyncCalibrationModeWithScaleOffsetSynchronize = true; //キャリブレーションモードとスケール設定を連動させる
public Action<GameObject> BeforeModelDestroyAction = null; //破壊時Action
public Action<GameObject> AfterAutoLoadAction = null; //自動ロード時Action
[SerializeField, Label("VRM1正規化ボーン選択(ロード時)")]
public UniVRM10.ControlRigGenerationOption controlRigGenerationOptionOnLoad = UniVRM10.ControlRigGenerationOption.None; //VRM1正規化ボーン(非推奨)
#if EVMC4U_JA
[Header("現在の状態(表示用)")]
#else
[Header("Status (Read only)")]
#endif
[SerializeField, Label("動作状況")]
private string StatusMessage = ""; //状態メッセージ(Inspector表示用)
[SerializeField, Label("オプション文字列")]
public string OptionString = ""; //VMCから送信されるオプション文字列
[SerializeField, Label("自動読み込みVRMパス")]
public string loadedVRMPath = ""; //読み込み済みVRMパス
[SerializeField, Label("自動読み込みVRM名前")]
public string loadedVRMName = ""; //読み込み済みVRM名前
[SerializeField, Label("読み込んだモデルの親GameObject")]
public GameObject LoadedModelParent = null; //読み込んだモデルの親
[SerializeField, Label("読み込んだVRMの種別")]
public string loadedVRMType = ""; //読み込んだ種別
[SerializeField, Label("1フレームあたりのパケットフレーム数")]
public int LastPacketframeCounterInFrame = 0; //1フレーム中に受信したパケットフレーム数
[SerializeField, Label("廃棄されたパケット数")]
public int DropPackets = 0; //廃棄されたパケット(not パケットフレーム)
public Vector3 HeadPosition = Vector3.zero;
#if EVMC4U_JA
[Header("デイジーチェーン")]
#else
[Header("Daisy Chain")]
#endif
public GameObject[] NextReceivers = new GameObject[6]; //デイジーチェーン
#if EVMC4U_JA
[Header("ボーンの個別遮断(下半身、腕のみなど)")]
#else
[Header("Cut bones")]
#endif
[SerializeField, Label("有効")]
public bool CutBonesEnable = false;
#if EVMC4U_JA
[Header("遮断ボーン (Head)")]
#else
[Header("Cut bones(Head)")]
#endif
[SerializeField, Label("Neck遮断")]
public bool CutBoneNeck = false;
[SerializeField, Label("Head遮断")]
public bool CutBoneHead = false;
[SerializeField, Label("LeftEye遮断")]
public bool CutBoneLeftEye = false;
[SerializeField, Label("RightEye遮断")]
public bool CutBoneRightEye = false;
[SerializeField, Label("Jaw遮断")]
public bool CutBoneJaw = false;
#if EVMC4U_JA
[Header("遮断ボーン (Body)")]
#else
[Header("Cut bones(Body)")]
#endif
[SerializeField, Label("Hips遮断")]
public bool CutBoneHips = true;
[SerializeField, Label("Spine遮断")]
public bool CutBoneSpine = true;
[SerializeField, Label("Chest遮断")]
public bool CutBoneChest = true;
[SerializeField, Label("UpperChest遮断")]
public bool CutBoneUpperChest = true;
#if EVMC4U_JA
[Header("遮断ボーン (Left Arm)")]
#else
[Header("Cut bones(Left Arm)")]
#endif
[SerializeField, Label("LeftShoulder遮断")]
public bool CutBoneLeftShoulder = false;
[SerializeField, Label("LeftUpperArm遮断")]
public bool CutBoneLeftUpperArm = false;
[SerializeField, Label("LeftLowerArm遮断")]
public bool CutBoneLeftLowerArm = false;
[SerializeField, Label("LeftHand遮断")]
public bool CutBoneLeftHand = false;
#if EVMC4U_JA
[Header("遮断ボーン (Right Arm)")]
#else
[Header("Cut bones(Right Arm)")]
#endif
[SerializeField, Label("RightShoulder遮断")]
public bool CutBoneRightShoulder = false;
[SerializeField, Label("RightUpperArm遮断")]
public bool CutBoneRightUpperArm = false;
[SerializeField, Label("RightLowerArm遮断")]
public bool CutBoneRightLowerArm = false;
[SerializeField, Label("RightHand遮断")]
public bool CutBoneRightHand = false;
#if EVMC4U_JA
[Header("遮断ボーン (Left Leg)")]
#else
[Header("Cut bones(Left Leg)")]
#endif
[SerializeField, Label("LeftUpperLeg遮断")]
public bool CutBoneLeftUpperLeg = true;
[SerializeField, Label("LeftLowerLeg遮断")]
public bool CutBoneLeftLowerLeg = true;
[SerializeField, Label("LeftFoot遮断")]
public bool CutBoneLeftFoot = true;
[SerializeField, Label("LeftToes遮断")]
public bool CutBoneLeftToes = true;
#if EVMC4U_JA
[Header("遮断ボーン (Right Leg)")]
#else
[Header("Cut bones(Right Leg)")]
#endif
[SerializeField, Label("RightUpperLeg遮断")]
public bool CutBoneRightUpperLeg = true;
[SerializeField, Label("RightLowerLeg遮断")]
public bool CutBoneRightLowerLeg = true;
[SerializeField, Label("RightFoot遮断")]
public bool CutBoneRightFoot = true;
[SerializeField, Label("RightToes遮断")]
public bool CutBoneRightToes = true;
#if EVMC4U_JA
[Header("遮断ボーン (Left Hand)")]
#else
[Header("Cut bones(Left Hand)")]
#endif
[SerializeField, Label("LeftThumbProximal遮断")]
public bool CutBoneLeftThumbProximal = false;
[SerializeField, Label("LeftThumbIntermediate遮断")]
public bool CutBoneLeftThumbIntermediate = false;
[SerializeField, Label("LeftThumbDistal遮断")]
public bool CutBoneLeftThumbDistal = false;
[SerializeField, Label("LeftIndexProximal遮断")]
public bool CutBoneLeftIndexProximal = false;
[SerializeField, Label("LeftIndexIntermediate遮断")]
public bool CutBoneLeftIndexIntermediate = false;
[SerializeField, Label("LeftIndexDistal遮断")]
public bool CutBoneLeftIndexDistal = false;
[SerializeField, Label("LeftMiddleProximal遮断")]
public bool CutBoneLeftMiddleProximal = false;
[SerializeField, Label("LeftMiddleIntermediate遮断")]
public bool CutBoneLeftMiddleIntermediate = false;
[SerializeField, Label("LeftMiddleDistal遮断")]
public bool CutBoneLeftMiddleDistal = false;
[SerializeField, Label("LeftRingProximal遮断")]
public bool CutBoneLeftRingProximal = false;
[SerializeField, Label("LeftRingIntermediate遮断")]
public bool CutBoneLeftRingIntermediate = false;
[SerializeField, Label("LeftRingDistal遮断")]
public bool CutBoneLeftRingDistal = false;
[SerializeField, Label("LeftLittleProximal遮断")]
public bool CutBoneLeftLittleProximal = false;
[SerializeField, Label("LeftLittleIntermediate遮断")]
public bool CutBoneLeftLittleIntermediate = false;
[SerializeField, Label("LeftLittleDistal遮断")]
public bool CutBoneLeftLittleDistal = false;
#if EVMC4U_JA
[Header("遮断ボーン (Right Hand)")]
#else
[Header("Cut bones(Right Hand)")]
#endif
[SerializeField, Label("RightThumbProximal遮断")]
public bool CutBoneRightThumbProximal = false;
[SerializeField, Label("RightThumbIntermediate遮断")]
public bool CutBoneRightThumbIntermediate = false;
[SerializeField, Label("RightThumbDistal遮断")]
public bool CutBoneRightThumbDistal = false;
[SerializeField, Label("RightIndexProximal遮断")]
public bool CutBoneRightIndexProximal = false;
[SerializeField, Label("RightIndexIntermediate遮断")]
public bool CutBoneRightIndexIntermediate = false;
[SerializeField, Label("RightIndexDistal遮断")]
public bool CutBoneRightIndexDistal = false;
[SerializeField, Label("RightMiddleProximal遮断")]
public bool CutBoneRightMiddleProximal = false;
[SerializeField, Label("RightMiddleIntermediate遮断")]
public bool CutBoneRightMiddleIntermediate = false;
[SerializeField, Label("RightMiddleDistal遮断")]
public bool CutBoneRightMiddleDistal = false;
[SerializeField, Label("RightRingProximal遮断")]
public bool CutBoneRightRingProximal = false;
[SerializeField, Label("RightRingIntermediate遮断")]
public bool CutBoneRightRingIntermediate = false;
[SerializeField, Label("RightRingDistal遮断")]
public bool CutBoneRightRingDistal = false;
[SerializeField, Label("RightLittleProximal遮断")]
public bool CutBoneRightLittleProximal = false;
[SerializeField, Label("RightLittleIntermediate遮断")]
public bool CutBoneRightLittleIntermediate = false;
[SerializeField, Label("RightLittleDistal遮断")]
public bool CutBoneRightLittleDistal = false;
[Header("API")]
public string api1 = "void UpdateDaisyChain()";
public string api2 = "int GetAvailable()";
public string api3 = "float GetRemoteTime()";
public string api4 = "void SetBlend(string key, float value)";
public string api5 = "void ApplyBlend()";
public string api6 = "void DestroyModel()";
public string api7 = "void LoadVRM(string path)";
public string api8 = "void LoadVRMFromData(byte[] VRMdata)";
public string api9 = "void ApplicationQuit()";
//---Const---
//rootパケット長定数(拡張判別)
const int RootPacketLengthOfScaleAndOffset = 8;
//---Private---
private ExternalReceiverManager externalReceiverManager = null;
//フィルタ用データ保持変数
private Vector3[] bonePosFilter = new Vector3[Enum.GetNames(typeof(HumanBodyBones)).Length];
private Quaternion[] boneRotFilter = new Quaternion[Enum.GetNames(typeof(HumanBodyBones)).Length];
private Dictionary<string, float> blendShapeFilterDictionaly = new Dictionary<string, float>();
//通信状態保持変数
private int Available = 0; //データ送信可能な状態か
private float time = 0; //送信時の時刻
//モデル切替検出用reference保持変数
private GameObject OldModel = null;
//ボーン情報取得
Animator animator = null;
//VRMのブレンドシェイププロキシ(VRM0)
VRM.VRMBlendShapeProxy blendShapeProxyV0 = null;
//VRM10のルート(VRM1)
UniVRM10.Vrm10Instance VrmRootV1 = null;
//ボーンENUM情報テーブル
Dictionary<string, HumanBodyBones> HumanBodyBonesTable = new Dictionary<string, HumanBodyBones>();
//ボーン情報テーブル
Dictionary<HumanBodyBones, Vector3> HumanBodyBonesPositionTable = new Dictionary<HumanBodyBones, Vector3>();
Dictionary<HumanBodyBones, Quaternion> HumanBodyBonesRotationTable = new Dictionary<HumanBodyBones, Quaternion>();
//ブレンドシェイプ変換テーブル(VRM0)
Dictionary<string, VRM.BlendShapeKey> StringToBlendShapeKeyDictionaryV0 = new Dictionary<string, VRM.BlendShapeKey>();
Dictionary<VRM.BlendShapeKey, float> BlendShapeToValueDictionaryV0 = new Dictionary<VRM.BlendShapeKey, float>();
//ブレンドシェイプ変換テーブル(VRM1)
Dictionary<string, UniVRM10.ExpressionKey> StringToExpressionKeyDictionaryV1 = new Dictionary<string, UniVRM10.ExpressionKey>();
Dictionary<UniVRM10.ExpressionKey, float> ExpressionToValueDictionaryV1 = new Dictionary<UniVRM10.ExpressionKey, float>();
//uOSCサーバー
uOSC.uOscServer server = null;
//エラー・無限ループ検出フラグ(trueで一切の受信を停止する)
bool shutdown = false;
//フレーム間パケットフレーム数測定
int PacketCounterInFrame = 0;
//1フレームに30パケットフレーム来たら、同一フレーム内でそれ以上は受け取らない。
const int PACKET_LIMIT_MAX = 30;
//読込中は読み込まない
bool isLoading = false;
//メッセージ処理一時変数struct(負荷対策)
Vector3 pos;
Quaternion rot;
Vector3 scale;
Vector3 offset;
//同期コンテキスト
SynchronizationContext synchronizationContext;
public void Start()
{
synchronizationContext = SynchronizationContext.Current;
//nullチェック
if (NextReceivers == null)
{
NextReceivers = new GameObject[0];
}
//NextReciverのインターフェースを取得する
externalReceiverManager = new ExternalReceiverManager(NextReceivers);
//サーバーを取得
server = GetComponent<uOSC.uOscServer>();
if (server)
{
//サーバーを初期化
StatusMessage = "Waiting for VMC...";
server.onDataReceived.AddListener(OnDataReceived);
}
else
{
//デイジーチェーンスレーブモード
StatusMessage = "Waiting for Master...";
}
//初期状態で読み込み済みのモデルが有る場合はVRMの自動読み込みは禁止する
if (Model != null)
{
enableAutoLoadVRM = false;
}
}
//デイジーチェーンを更新
public void UpdateDaisyChain()
{
//nullチェック
if (NextReceivers == null)
{
NextReceivers = new GameObject[0];
}
externalReceiverManager.GetIExternalReceiver(NextReceivers);
}
//外部から通信状態を取得するための公開関数
public int GetAvailable()
{
return Available;
}
//外部から通信時刻を取得するための公開関数
public float GetRemoteTime()
{
return time;
}
public void Update()
{
// 毎フレーム更新処理(前)
if (!EnableLateUpdateForOverwriteAnimationResult)
{
Process();
}
}
public void LateUpdate()
{
// 毎フレーム更新処理(後)
if (EnableLateUpdateForOverwriteAnimationResult)
{
Process();
}
}
public void Process()
{
//エラー・無限ループ時は処理をしない
if (shutdown) { return; }
//Freeze有効時は動きを一切止める
if (Freeze) { return; }
LastPacketframeCounterInFrame = PacketCounterInFrame;
PacketCounterInFrame = 0;
//5.6.3p1などRunInBackgroundが既定で無効な場合Unityが極めて重くなるため対処
Application.runInBackground = true;
//VRMモデルからBlendShapeProxyを取得(タイミングの問題) (VRM0)
if (blendShapeProxyV0 == null && Model != null)
{
blendShapeProxyV0 = Model.GetComponent<VRM.VRMBlendShapeProxy>();
//以降、blendShapeProxyV0 is not nullでVRM0と扱うことができる
}
//VRMモデルからVrmRootを取得(タイミングの問題) (VRM1)
if (VrmRootV1 == null && Model != null)
{
VrmRootV1 = Model.GetComponent<UniVRM10.Vrm10Instance>();
//以降、VrmRootV1 is not nullでVRM1と扱うことができる
}
//判定
if (blendShapeProxyV0 != null && VrmRootV1 != null)
{
loadedVRMType = "VRM0&1";
}
else if (blendShapeProxyV0 != null)
{
loadedVRMType = "VRM0";
}
else if (VrmRootV1 != null)
{
loadedVRMType = "VRM1";
}
else
{
loadedVRMType = "Unknown";
}
//ルート位置がない場合
if (RootPositionTransform == null && Model != null)
{
//モデル姿勢をルート姿勢にする
RootPositionTransform = Model.transform;
}
//ルート回転がない場合
if (RootRotationTransform == null && Model != null)
{
//モデル姿勢をルート姿勢にする
RootRotationTransform = Model.transform;
}
//モデルがない場合はエラー表示をしておく(親切心)
if (Model == null)
{
blendShapeProxyV0 = null;
VrmRootV1 = null;
StatusMessage = "Model not found.";
return;
}
//モデルが更新されたときに関連情報を更新する
if (OldModel != Model && Model != null)
{
blendShapeProxyV0 = Model.GetComponent<VRM.VRMBlendShapeProxy>();
VrmRootV1 = Model.GetComponent<UniVRM10.Vrm10Instance>();
OldModel = Model;
animator = Model?.GetComponent<Animator>();
Debug.Log("[ExternalReceiver] New model detected");
//v0.56 BlendShape仕様変更対応
//Debug.Log("-- Make BlendShapeProxy BSKey Table --");
//BSキー値辞書の初期化(SetValueで無駄なキーが適用されるのを防止する)
BlendShapeToValueDictionaryV0.Clear();
ExpressionToValueDictionaryV1.Clear();
//文字-BSキー辞書の初期化(キー情報の初期化)
StringToBlendShapeKeyDictionaryV0.Clear();
StringToExpressionKeyDictionaryV1.Clear();
//全Clipsを取り出す(VRM0)
if (blendShapeProxyV0)
{
foreach (var c in blendShapeProxyV0.BlendShapeAvatar.Clips)
{
string key = "";
bool unknown = false;
//プリセットかどうかを調べる
if (c.Preset == VRM.BlendShapePreset.Unknown)
{
//非プリセット(Unknown)であれば、Unknown用の名前変数を参照する
key = c.BlendShapeName;
unknown = true;
}
else
{
//プリセットであればENUM値をToStringした値を利用する
key = c.Preset.ToString();
unknown = false;
}
//非ケース化するために小文字変換する
string lowerKey = key.ToLower();
//Debug.Log("Add: [key]->" + key + " [lowerKey]->" + lowerKey + " [clip]->" + c.ToString() + " [bskey]->"+c.Key.ToString() + " [unknown]->"+ unknown);
//小文字名-BSKeyで登録する
StringToBlendShapeKeyDictionaryV0.Add(lowerKey, c.Key);
}
}
//全Clipsを取り出す(VRM1)
if (VrmRootV1)
{
foreach (var c in VrmRootV1.Runtime.Expression.ExpressionKeys)
{
string key = "";
bool unknown = false;
//プリセットかどうかを調べる
if (c.Preset == UniVRM10.ExpressionPreset.custom)
{
//非プリセット(Unknown)であれば、Unknown用の名前変数を参照する
key = c.Name;
unknown = true;
}
else
{
//プリセットであればENUM値をToStringした値を利用する
key = c.Preset.ToString();
unknown = false;
}
//非ケース化するために小文字変換する
string lowerKey = key.ToLower();
//Debug.Log("Add: [key]->" + key + " [lowerKey]->" + lowerKey + " [clip]->" + c.ToString() + " [bskey]->"+c.Key.ToString() + " [unknown]->"+ unknown);
//小文字名-BSKeyで登録する
StringToExpressionKeyDictionaryV1.Add(lowerKey, c);
}
}
//メモ: プリセット同名の独自キー、独自キーのケース違いの重複は、共に区別しないと割り切る
/*
Debug.Log("-- Registered List --");
foreach (var k in StringToBlendShapeKeyDictionaryV0) {
Debug.Log("B [k.Key]" + k.Key + " -> [k.Value.Name]" + k.Value.Name);
}
foreach (var k in StringToExpressionKeyDictionaryV1) {
Debug.Log("E [k.Key]" + k.Key + " -> [k.Value.Name]" + k.Value.Name);
}
*/
//Debug.Log("-- End BlendShapeProxy BSKey Table --");
}
// VRM1において、VRM1によるLateUpdateでの更新はUpdateにずらして(Constraints)
// EVMC4UはLateUpdateにする(視線動かない問題への暫定対処)
if (VrmRootV1?.UpdateType == Vrm10Instance.UpdateTypes.LateUpdate)
{
VrmRootV1.UpdateType = Vrm10Instance.UpdateTypes.Update;
Debug.Log("[ExternalReceiver] [Temporary Fix] Forcefully change the UpdateType of Vrm10Instance to \"Update\".");
}
if (VrmRootV1?.UpdateType == Vrm10Instance.UpdateTypes.Update && EnableLateUpdateForOverwriteAnimationResult == false)
{
EnableLateUpdateForOverwriteAnimationResult = true;
Debug.Log("[ExternalReceiver] [Temporary Fix] Forcefully change the Update timing of ExternalReceiver to LateUpdate.");
}
BoneSynchronizeByTable();
}
//データ受信イベント
private void OnDataReceived(uOSC.Message message)
{
//チェーン数0としてデイジーチェーンを発生させる
MessageDaisyChain(ref message, 0);
}
//デイジーチェーン処理
public void MessageDaisyChain(ref uOSC.Message message, int callCount)
{
//Startされていない場合無視
if (externalReceiverManager == null || enabled == false || gameObject.activeInHierarchy == false)
{
return;
}
//エラー・無限ループ時は処理をしない
if (shutdown)
{
return;
}
//パケットリミッターが有効な場合、一定以上のパケットフレーム/フレーム数を観測した場合、次のフレームまでパケットを捨てる
if (PacktLimiter && (LastPacketframeCounterInFrame > PACKET_LIMIT_MAX))
{
DropPackets++;
return;
}
//メッセージを処理
if (!Freeze)
{
//異常を検出して動作停止
try
{
ProcessMessage(ref message);
}
catch (Exception e)
{
StatusMessage = "Error: Exception";
Debug.LogError(" --- Communication Error ---");
Debug.LogError(e.ToString());
shutdown = true;
return;
}
}
//次のデイジーチェーンへ伝える
if (!externalReceiverManager.SendNextReceivers(message, callCount))
{
//無限ループ対策
StatusMessage = "Infinite loop detected!";
//以降の処理を全部停止
shutdown = true;
}
}
//メッセージ処理本体
private void ProcessMessage(ref uOSC.Message message)
{
//メッセージアドレスがない、あるいはメッセージがない不正な形式の場合は処理しない
if (message.address == null || message.values == null)
{
StatusMessage = "Bad message.";
return;
}
//ルート位置がない場合
if (RootPositionTransform == null && Model != null)
{
//モデル姿勢をルート姿勢にする
RootPositionTransform = Model.transform;
}
//ルート回転がない場合
if (RootRotationTransform == null && Model != null)
{
//モデル姿勢をルート姿勢にする
RootRotationTransform = Model.transform;
}
//モーションデータ送信可否
if (message.address == "/VMC/Ext/OK"
&& (message.values[0] is int))
{
Available = (int)message.values[0];
if (Available == 0)
{
StatusMessage = "Waiting for [Load VRM]";
}
//V2.5 キャリブレーション状態(長さ3以上)
if (message.values.Length >= 3)
{
if ((message.values[1] is int) && (message.values[2] is int))
{
int calibrationState = (int)message.values[1];
int calibrationMode = (int)message.values[2];
//キャリブレーション出来ていないときは隠す
if (HideInUncalibrated && Model != null)
{
Model.SetActive(calibrationState == 3);
}
//スケール同期をキャリブレーションと連動させる
if (SyncCalibrationModeWithScaleOffsetSynchronize)
{
RootScaleOffsetSynchronize = !(calibrationMode == 0); //通常モードならオフ、MR系ならオン
}
}
}
return;
}
//データ送信時刻
else if (message.address == "/VMC/Ext/T"
&& (message.values[0] is float))
{
time = (float)message.values[0];
PacketCounterInFrame++; //フレーム中のパケットフレーム数を測定
return;
}
//VRM自動読み込み
else if (message.address == "/VMC/Ext/VRM"
&& (message.values[0] is string)
&& (message.values[1] is string)
)
{
string path = (string)message.values[0];
string title = (string)message.values[1];
//前回読み込んだパスと違う場合かつ、読み込みが許可されている場合
if (path != loadedVRMPath && enableAutoLoadVRM == true)
{
loadedVRMPath = path;
loadedVRMName = title;
LoadVRM(path);
}
return;
}
//オプション文字列
else if (message.address == "/VMC/Ext/Opt"
&& (message.values[0] is string))
{
OptionString = (string)message.values[0];
return;
}
//モデルがないか、モデル姿勢、ルート姿勢が取得できないなら以降何もしない
if (Model == null || Model.transform == null || RootPositionTransform == null || RootRotationTransform == null)
{
return;
}
//Root姿勢
if (message.address == "/VMC/Ext/Root/Pos"
&& (message.values[0] is string)
&& (message.values[1] is float)
&& (message.values[2] is float)
&& (message.values[3] is float)
&& (message.values[4] is float)
&& (message.values[5] is float)
&& (message.values[6] is float)
&& (message.values[7] is float)
)
{
StatusMessage = "OK";
pos.x = (float)message.values[1];
pos.y = (float)message.values[2];
pos.z = (float)message.values[3];
rot.x = (float)message.values[4];
rot.y = (float)message.values[5];
rot.z = (float)message.values[6];
rot.w = (float)message.values[7];
//位置同期
if (RootPositionSynchronize)
{
RootPositionTransform.localPosition = pos;
}
//回転同期
if (RootRotationSynchronize)
{
RootRotationTransform.localRotation = rot;
}
//スケール同期とオフセット補正(v2.1拡張プロトコルの場合のみ)
if (RootScaleOffsetSynchronize && message.values.Length > RootPacketLengthOfScaleAndOffset
&& (message.values[8] is float)
&& (message.values[9] is float)
&& (message.values[10] is float)
&& (message.values[11] is float)
&& (message.values[12] is float)
&& (message.values[13] is float)
)
{
scale.x = 1.0f / (float)message.values[8];
scale.y = 1.0f / (float)message.values[9];
scale.z = 1.0f / (float)message.values[10];
offset.x = (float)message.values[11];
offset.y = (float)message.values[12];
offset.z = (float)message.values[13];
Model.transform.localScale = scale;
RootPositionTransform.localPosition = Vector3.Scale(RootPositionTransform.localPosition, scale);
//位置同期が有効な場合のみオフセットを反映する
if (RootPositionSynchronize)
{
offset = Vector3.Scale(offset, scale);
RootPositionTransform.localPosition -= offset;
}
}
else
{
Model.transform.localScale = Vector3.one;
}
}
//ボーン姿勢
else if (message.address == "/VMC/Ext/Bone/Pos"
&& (message.values[0] is string)
&& (message.values[1] is float)
&& (message.values[2] is float)
&& (message.values[3] is float)
&& (message.values[4] is float)
&& (message.values[5] is float)
&& (message.values[6] is float)
&& (message.values[7] is float)
)
{
string boneName = (string)message.values[0];
pos.x = (float)message.values[1];
pos.y = (float)message.values[2];
pos.z = (float)message.values[3];
rot.x = (float)message.values[4];
rot.y = (float)message.values[5];
rot.z = (float)message.values[6];
rot.w = (float)message.values[7];
//Humanoidボーンに該当するボーンがあるか調べる
HumanBodyBones bone;
if (HumanBodyBonesTryParse(ref boneName, out bone))
{
//あれば位置と回転をキャッシュする
if (HumanBodyBonesPositionTable.ContainsKey(bone))
{
HumanBodyBonesPositionTable[bone] = pos;
}
else
{
HumanBodyBonesPositionTable.Add(bone, pos);
}
if (HumanBodyBonesRotationTable.ContainsKey(bone))
{
HumanBodyBonesRotationTable[bone] = rot;
}
else
{
HumanBodyBonesRotationTable.Add(bone, rot);
}
}
//受信と更新のタイミングは切り離した
}
//ブレンドシェイプ同期
else if (message.address == "/VMC/Ext/Blend/Val"
&& (message.values[0] is string)
&& (message.values[1] is float)
)
{
//一旦変数に格納する
string key = (string)message.values[0];
float value = (float)message.values[1];
if (BlendShapeSynchronize)
{
SetBlend(key, value);
}
}
//ブレンドシェープ適用
else if (message.address == "/VMC/Ext/Blend/Apply")
{
if (BlendShapeSynchronize)
{
ApplyBlend();
}
}
}
//表情の設定(VRM0,VRM1共通)
public void SetBlend(string key, float value)
{
//BlendShapeフィルタが有効なら
if (BlendShapeFilterEnable)
{
//フィルタテーブルに存在するか確認する
if (blendShapeFilterDictionaly.ContainsKey(key))
{
//存在する場合はフィルタ更新して値として反映する
blendShapeFilterDictionaly[key] = (blendShapeFilterDictionaly[key] * BlendShapeFilter) + value * (1.0f - BlendShapeFilter);
value = blendShapeFilterDictionaly[key];
}
else
{
//存在しない場合はフィルタに登録する。値はそのまま
blendShapeFilterDictionaly.Add(key, value);
}
}
// VRM0
if (blendShapeProxyV0 != null)
{
//v0.56 BlendShape仕様変更対応
//辞書からKeyに変換し、Key値辞書に値を入れる
//通信で受信したキーを小文字に変換して非ケース化
string lowerKey = key.ToLower();
//キーに該当するBSKeyが存在するかチェックする
VRM.BlendShapeKey bskey;
if (StringToBlendShapeKeyDictionaryV0.TryGetValue(lowerKey, out bskey))
{
//キーに対して値を登録する
BlendShapeToValueDictionaryV0[bskey] = value;
//Debug.Log("[lowerKey]->"+ lowerKey+" [bskey]->"+bskey.ToString()+" [value]->"+value);
}
else
{
//VRM1.x -> VRM0.x逆変換テーブル(前方互換性)
switch (lowerKey)
{
case "happy": lowerKey = "joy"; break;
case "sad": lowerKey = "sorrow"; break;
case "relaxed": lowerKey = "fun"; break;
case "aa": lowerKey = "a"; break;
case "ih": lowerKey = "i"; break;
case "ou": lowerKey = "u"; break;
case "ee": lowerKey = "e"; break;
case "oh": lowerKey = "o"; break;
case "blinkleft": lowerKey = "blink_l"; break;
case "blinkright": lowerKey = "blink_r"; break;
}
if (StringToBlendShapeKeyDictionaryV0.TryGetValue(lowerKey, out bskey))
{
//キーに対して値を登録する
BlendShapeToValueDictionaryV0[bskey] = value;
//Debug.Log("[lowerKey]->"+ lowerKey+" [bskey]->"+bskey.ToString()+" [value]->"+value);
}
else
{
//そんなキーは無い
//Debug.LogError("[lowerKey]->" + lowerKey + " is not found");
}
}
}
// VRM1
if (VrmRootV1 != null)
{
//v0.56 BlendShape仕様変更対応
//辞書からKeyに変換し、Key値辞書に値を入れる
//通信で受信したキーを小文字に変換して非ケース化
string lowerKey = key.ToLower();
//キーに該当するBSKeyが存在するかチェックする
UniVRM10.ExpressionKey exkey;
if (StringToExpressionKeyDictionaryV1.TryGetValue(lowerKey, out exkey))
{
//キーに対して値を登録する
ExpressionToValueDictionaryV1[exkey] = value;
//Debug.Log("[lowerKey]->"+ lowerKey+" [exkey]->"+exkey.ToString()+" [value]->"+value);
}
else
{
//VRM0.x -> VRM1.x変換テーブル(後方互換性)
switch (lowerKey)
{
case "joy": lowerKey = "happy"; break;
case "sorrow": lowerKey = "sad"; break;
case "fun": lowerKey = "relaxed"; break;
case "a": lowerKey = "aa"; break;
case "i": lowerKey = "ih"; break;
case "u": lowerKey = "ou"; break;
case "e": lowerKey = "ee"; break;
case "o": lowerKey = "oh"; break;
case "blink_l": lowerKey = "blinkleft"; break;
case "blink_r": lowerKey = "blinkright"; break;
}
if (StringToExpressionKeyDictionaryV1.TryGetValue(lowerKey, out exkey))
{
//キーに対して値を登録する
ExpressionToValueDictionaryV1[exkey] = value;
//Debug.Log("[lowerKey]->"+ lowerKey+" [exkey]->"+exkey.ToString()+" [value]->"+value);
}
else
{
//そんなキーは無い
//Debug.LogError("[lowerKey]->" + lowerKey + " is not found");
}
}
}
}
//表情の確定(VRM0,VRM1共通)
public void ApplyBlend()
{
if (blendShapeProxyV0 != null)
{
blendShapeProxyV0.SetValues(BlendShapeToValueDictionaryV0);
}
if (VrmRootV1 != null)
{
VrmRootV1.Runtime.Expression.SetWeights(ExpressionToValueDictionaryV1);
}
}
//モデル破棄
public void DestroyModel()
{
//存在すれば即破壊(異常顔防止)
if (Model != null)
{
BeforeModelDestroyAction?.Invoke(Model);
Destroy(Model);
Model = null;
}
if (LoadedModelParent != null)
{
Destroy(LoadedModelParent);
LoadedModelParent = null;
}
}
//ファイルからモデルを読み込む
public void LoadVRM(string path)
{
DestroyModel();
//バイナリの読み込み
if (File.Exists(path))
{
byte[] VRMdata = File.ReadAllBytes(path);
LoadVRMFromData(VRMdata);
}
else
{
Debug.LogError("VRM load failed.");
}
}
//ファイルからモデルを読み込む
public void LoadVRMFromData(byte[] VRMdata)
{
if (isLoading)
{
Debug.LogError("Now Loading! load request is rejected.");
return;
}
DestroyModel();
//読み込み
GlbLowLevelParser glbLowLevelParser = new GlbLowLevelParser(null, VRMdata);
GltfData gltfData = glbLowLevelParser.Parse();
// VRM0 読み込み処理
try
{
VRM.VRMData vrm = new VRM.VRMData(gltfData);
VRM.VRMImporterContext vrmImporter = new VRM.VRMImporterContext(vrm);
isLoading = true;
synchronizationContext.Post(async (arg) => {
RuntimeGltfInstance instance = await vrmImporter.LoadAsync(new VRMShaders.ImmediateCaller());
isLoading = false;
Model = instance.Root;
//ExternalReceiverの下にぶら下げる
LoadedModelParent = new GameObject();
LoadedModelParent.transform.SetParent(transform, false);
LoadedModelParent.name = "LoadedModelParent";
//その下にモデルをぶら下げる
Model.transform.SetParent(LoadedModelParent.transform, false);
instance.EnableUpdateWhenOffscreen();
instance.ShowMeshes();
//カメラなどの移動補助のため、頭の位置を格納する
animator = Model.GetComponent<Animator>();
HeadPosition = animator.GetBoneTransform(HumanBodyBones.Head).position;
//開放
vrmImporter.Dispose();
gltfData.Dispose();
//読み込み後アクションを実行
AfterAutoLoadAction?.Invoke(Model);
}, null);
return; //VRM0 Loaded
}
catch (VRM.NotVrm0Exception)
{
//continue loading
}
// VRM1 読み込み処理
synchronizationContext.Post(async (_) =>
{
var instance = await UniVRM10.Vrm10.LoadBytesAsync(VRMdata, canLoadVrm0X: false, showMeshes: true, controlRigGenerationOption: controlRigGenerationOptionOnLoad);
await Task.Delay(100); //VRM1不具合対策
Model = instance.gameObject;
Model.transform.parent = this.transform;
//ExternalReceiverの下にぶら下げる
LoadedModelParent = new GameObject();
LoadedModelParent.transform.SetParent(transform, false);
LoadedModelParent.name = "LoadedModelParent";
//その下にモデルをぶら下げる
Model.transform.SetParent(LoadedModelParent.transform, false);
//カメラなどの移動補助のため、頭の位置を格納する
animator = Model.GetComponent<Animator>();
HeadPosition = animator.GetBoneTransform(HumanBodyBones.Head).position;
var meshes = Model.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var m in meshes)
{
m.updateWhenOffscreen = true;
}
//開放
gltfData?.Dispose();
//読み込み後アクションを実行
AfterAutoLoadAction?.Invoke(Model);
}, null);
// Debug.LogError("Failed to load VRM1 and VRM0.");
}
//ボーン位置をキャッシュテーブルに基づいて更新
private void BoneSynchronizeByTable()
{
//キャッシュテーブルを参照
foreach (var bone in HumanBodyBonesTable)
{
//キャッシュされた位置・回転を適用
if (HumanBodyBonesPositionTable.ContainsKey(bone.Value) && HumanBodyBonesRotationTable.ContainsKey(bone.Value))
{
BoneSynchronize(bone.Value, HumanBodyBonesPositionTable[bone.Value], HumanBodyBonesRotationTable[bone.Value]);
}
}
}
//ボーン位置同期
private void BoneSynchronize(HumanBodyBones bone, Vector3 pos, Quaternion rot)
{
//操作可能な状態かチェック
if (animator != null && bone != HumanBodyBones.LastBone)
{
//ボーンによって操作を分ける
Transform t;
t = animator.GetBoneTransform(bone);
if (t != null)
{
//個別ボーン遮断
if (CutBonesEnable)
{
if (bone == HumanBodyBones.Hips && CutBoneHips) { return; }
if (bone == HumanBodyBones.LeftUpperLeg && CutBoneLeftUpperLeg) { return; }
if (bone == HumanBodyBones.RightUpperLeg && CutBoneRightUpperLeg) { return; }
if (bone == HumanBodyBones.LeftLowerLeg && CutBoneLeftLowerLeg) { return; }
if (bone == HumanBodyBones.RightLowerLeg && CutBoneRightLowerLeg) { return; }
if (bone == HumanBodyBones.LeftFoot && CutBoneLeftFoot) { return; }
if (bone == HumanBodyBones.RightFoot && CutBoneRightFoot) { return; }
if (bone == HumanBodyBones.Spine && CutBoneSpine) { return; }
if (bone == HumanBodyBones.Chest && CutBoneChest) { return; }
if (bone == HumanBodyBones.Neck && CutBoneNeck) { return; }
if (bone == HumanBodyBones.Head && CutBoneHead) { return; }
if (bone == HumanBodyBones.LeftShoulder && CutBoneLeftShoulder) { return; }
if (bone == HumanBodyBones.RightShoulder && CutBoneRightShoulder) { return; }
if (bone == HumanBodyBones.LeftUpperArm && CutBoneLeftUpperArm) { return; }
if (bone == HumanBodyBones.RightUpperArm && CutBoneRightUpperArm) { return; }
if (bone == HumanBodyBones.LeftLowerArm && CutBoneLeftLowerArm) { return; }
if (bone == HumanBodyBones.RightLowerArm && CutBoneRightLowerArm) { return; }
if (bone == HumanBodyBones.LeftHand && CutBoneLeftHand) { return; }
if (bone == HumanBodyBones.RightHand && CutBoneRightHand) { return; }
if (bone == HumanBodyBones.LeftToes && CutBoneLeftToes) { return; }
if (bone == HumanBodyBones.RightToes && CutBoneRightToes) { return; }
if (bone == HumanBodyBones.LeftEye && CutBoneLeftEye) { return; }
if (bone == HumanBodyBones.RightEye && CutBoneRightEye) { return; }
if (bone == HumanBodyBones.Jaw && CutBoneJaw) { return; }
if (bone == HumanBodyBones.LeftThumbProximal && CutBoneLeftThumbProximal) { return; }
if (bone == HumanBodyBones.LeftThumbIntermediate && CutBoneLeftThumbIntermediate) { return; }
if (bone == HumanBodyBones.LeftThumbDistal && CutBoneLeftThumbDistal) { return; }
if (bone == HumanBodyBones.LeftIndexProximal && CutBoneLeftIndexProximal) { return; }
if (bone == HumanBodyBones.LeftIndexIntermediate && CutBoneLeftIndexIntermediate) { return; }
if (bone == HumanBodyBones.LeftIndexDistal && CutBoneLeftIndexDistal) { return; }
if (bone == HumanBodyBones.LeftMiddleProximal && CutBoneLeftMiddleProximal) { return; }
if (bone == HumanBodyBones.LeftMiddleIntermediate && CutBoneLeftMiddleIntermediate) { return; }
if (bone == HumanBodyBones.LeftMiddleDistal && CutBoneLeftMiddleDistal) { return; }
if (bone == HumanBodyBones.LeftRingProximal && CutBoneLeftRingProximal) { return; }
if (bone == HumanBodyBones.LeftRingIntermediate && CutBoneLeftRingIntermediate) { return; }
if (bone == HumanBodyBones.LeftRingDistal && CutBoneLeftRingDistal) { return; }
if (bone == HumanBodyBones.LeftLittleProximal && CutBoneLeftLittleProximal) { return; }
if (bone == HumanBodyBones.LeftLittleIntermediate && CutBoneLeftLittleIntermediate) { return; }
if (bone == HumanBodyBones.LeftLittleDistal && CutBoneLeftLittleDistal) { return; }
if (bone == HumanBodyBones.RightThumbProximal && CutBoneRightThumbProximal) { return; }
if (bone == HumanBodyBones.RightThumbIntermediate && CutBoneRightThumbIntermediate) { return; }
if (bone == HumanBodyBones.RightThumbDistal && CutBoneRightThumbDistal) { return; }
if (bone == HumanBodyBones.RightIndexProximal && CutBoneRightIndexProximal) { return; }
if (bone == HumanBodyBones.RightIndexIntermediate && CutBoneRightIndexIntermediate) { return; }
if (bone == HumanBodyBones.RightIndexDistal && CutBoneRightIndexDistal) { return; }
if (bone == HumanBodyBones.RightMiddleProximal && CutBoneRightMiddleProximal) { return; }
if (bone == HumanBodyBones.RightMiddleIntermediate && CutBoneRightMiddleIntermediate) { return; }
if (bone == HumanBodyBones.RightMiddleDistal && CutBoneRightMiddleDistal) { return; }
if (bone == HumanBodyBones.RightRingProximal && CutBoneRightRingProximal) { return; }
if (bone == HumanBodyBones.RightRingIntermediate && CutBoneRightRingIntermediate) { return; }
if (bone == HumanBodyBones.RightRingDistal && CutBoneRightRingDistal) { return; }
if (bone == HumanBodyBones.RightLittleProximal && CutBoneRightLittleProximal) { return; }
if (bone == HumanBodyBones.RightLittleIntermediate && CutBoneRightLittleIntermediate) { return; }
if (bone == HumanBodyBones.RightLittleDistal && CutBoneRightLittleDistal) { return; }
if (bone == HumanBodyBones.UpperChest && CutBoneUpperChest) { return; }
}
//指ボーン
if (bone == HumanBodyBones.LeftIndexDistal ||
bone == HumanBodyBones.LeftIndexIntermediate ||
bone == HumanBodyBones.LeftIndexProximal ||
bone == HumanBodyBones.LeftLittleDistal ||
bone == HumanBodyBones.LeftLittleIntermediate ||
bone == HumanBodyBones.LeftLittleProximal ||
bone == HumanBodyBones.LeftMiddleDistal ||
bone == HumanBodyBones.LeftMiddleIntermediate ||
bone == HumanBodyBones.LeftMiddleProximal ||
bone == HumanBodyBones.LeftRingDistal ||
bone == HumanBodyBones.LeftRingIntermediate ||
bone == HumanBodyBones.LeftRingProximal ||
bone == HumanBodyBones.LeftThumbDistal ||
bone == HumanBodyBones.LeftThumbIntermediate ||
bone == HumanBodyBones.LeftThumbProximal ||
bone == HumanBodyBones.RightIndexDistal ||
bone == HumanBodyBones.RightIndexIntermediate ||
bone == HumanBodyBones.RightIndexProximal ||
bone == HumanBodyBones.RightLittleDistal ||
bone == HumanBodyBones.RightLittleIntermediate ||
bone == HumanBodyBones.RightLittleProximal ||
bone == HumanBodyBones.RightMiddleDistal ||
bone == HumanBodyBones.RightMiddleIntermediate ||
bone == HumanBodyBones.RightMiddleProximal ||
bone == HumanBodyBones.RightRingDistal ||
bone == HumanBodyBones.RightRingIntermediate ||
bone == HumanBodyBones.RightRingProximal ||
bone == HumanBodyBones.RightThumbDistal ||
bone == HumanBodyBones.RightThumbIntermediate ||
bone == HumanBodyBones.RightThumbProximal)
{
//指ボーンカットオフが有効でなければ
if (!HandPoseSynchronizeCutoff)
{
//ボーン同期する。ただしフィルタはかけない
BoneSynchronizeSingle(t, ref bone, ref pos, ref rot, false, false);
}
}
//目ボーン
else if (bone == HumanBodyBones.LeftEye ||
bone == HumanBodyBones.RightEye)
{
//目ボーンカットオフが有効でなければ
if (!EyeBoneSynchronizeCutoff)
{
//ボーン同期する。ただしフィルタはかけない
BoneSynchronizeSingle(t, ref bone, ref pos, ref rot, false, false);
}
}
else
{
//ボーン同期する。フィルタは設定依存
BoneSynchronizeSingle(t, ref bone, ref pos, ref rot, BonePositionFilterEnable, BoneRotationFilterEnable);
}
}
}
}
//1本のボーンの同期
private void BoneSynchronizeSingle(Transform t, ref HumanBodyBones bone, ref Vector3 pos, ref Quaternion rot, bool posFilter, bool rotFilter)
{
BoneFilter = Mathf.Clamp(BoneFilter, 0f, 1f);
//ボーン位置同期が有効か
if (BonePositionSynchronize)
{
//ボーン位置フィルタが有効か
if (posFilter)
{
bonePosFilter[(int)bone] = (bonePosFilter[(int)bone] * BoneFilter) + pos * (1.0f - BoneFilter);
t.localPosition = bonePosFilter[(int)bone];
}
else
{
t.localPosition = pos;
}
}
//ボーン回転フィルタが有効か
if (rotFilter)
{
boneRotFilter[(int)bone] = Quaternion.Slerp(boneRotFilter[(int)bone], rot, 1.0f - BoneFilter);
t.localRotation = boneRotFilter[(int)bone];
}
else
{
t.localRotation = rot;
}
}
//ボーンENUM情報をキャッシュして高速化
private bool HumanBodyBonesTryParse(ref string boneName, out HumanBodyBones bone)
{
//ボーンキャッシュテーブルに存在するなら
if (HumanBodyBonesTable.ContainsKey(boneName))
{
//キャッシュテーブルから返す
bone = HumanBodyBonesTable[boneName];
//ただしLastBoneは発見しなかったことにする(無効値として扱う)
if (bone == HumanBodyBones.LastBone)
{
return false;
}
return true;
}
else
{
//キャッシュテーブルにない場合、検索する
var res = EnumTryParse<HumanBodyBones>(boneName, out bone);
if (!res)
{
//見つからなかった場合はLastBoneとして登録する(無効値として扱う)ことにより次回から検索しない
bone = HumanBodyBones.LastBone;
}
//キャシュテーブルに登録する
HumanBodyBonesTable.Add(boneName, bone);
return res;
}
}
//互換性を持ったTryParse
private static bool EnumTryParse<T>(string value, out T result) where T : struct
{
#if NET_4_6
return Enum.TryParse(value, out result);
#else
try
{
result = (T)Enum.Parse(typeof(T), value, true);
return true;
}
catch
{
result = default(T);
return false;
}
#endif
}
//アプリケーションを終了させる
public void ApplicationQuit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
}