private void LateUpdate() { if (_proxy == null || _blendShape.Count == 0) { //オーバーライド不要なケース _proxy?.Apply(); return; } //一回ApplyすることでAccumulateした値を捨てさせる _proxy.Apply(); //実際に適用したい値を入れなおして再度Applyすると完全上書きになる for (int i = 0; i < _allBlendShapeKeys.Length; i++) { var key = _allBlendShapeKeys[i]; //リップシンクをそのままにするかどうかは場合による if (SkipLipSyncKeys && _lipSyncKeys.Contains(key)) { continue; } if (_blendShape.TryGetValue(key, out float value)) { _proxy.AccumulateValue(key, value); } else { //完全な排他制御をしたいので、指定がない値は明示的にゼロ書き込みする _proxy.AccumulateValue(key, 0); } } _proxy.Apply(); _eyeBoneResetter.ReserveReset = true; }
private void LateUpdate() { if (_proxy == null || _blendShape.Count == 0) { //オーバーライド不要なケース _proxy?.Apply(); return; } //一回ApplyすることでAccumulateした値を捨てさせる _proxy.Apply(); //実際に適用したい値を入れなおして再度Applyすると完全上書きになる for (int i = 0; i < _allBlendShapeKeys.Length; i++) { if (_blendShape.TryGetValue(_allBlendShapeKeys[i], out float value)) { _proxy.AccumulateValue(_allBlendShapeKeys[i], value); } else { //完全な排他制御をしたいので、指定がない値は明示的にゼロ書き込みする _proxy.AccumulateValue(_allBlendShapeKeys[i], 0); } } _proxy.Apply(); }
private void SetFaceNeutral() { //表情をデフォルトに戻す if (proxy != null) { var NeutralKey = new BlendShapeKey(BlendShapePreset.Neutral); proxy.SetValues(BlendShapeKeys.Select(d => { var k = new BlendShapeKey(d); return(new KeyValuePair <BlendShapeKey, float>(k, k.Equals(NeutralKey) ? 1.0f : 0.0f)); })); proxy.Apply(); } }
private void Update() { if (_reserveBlendShapeReset) { for (int i = 0; i < _allBlendShapeKeys.Length; i++) { _proxy.AccumulateValue(_allBlendShapeKeys[i], 0f); } _proxy?.Apply(); _reserveBlendShapeReset = false; } }
public void StartSetting() { IsSetting = true; //表情をデフォルトに戻す if (proxy != null) { foreach (var key in BlendShapeKeys) { proxy.SetValue(key, 0.0f, false); } proxy.SetValue(BlendShapePreset.Neutral, 1.0f, false); proxy.Apply(); } }
private void Update() { var headPose = TobiiAPI.GetHeadPose(); if (headPose.IsRecent()) { var rot = headPose.Rotation; if (isMirrored) { rot.y *= -1; rot.z *= -1; } _head.localRotation = Quaternion.Lerp( _head.localRotation, rot, Time.deltaTime * responsiveness ); var pos = headPose.Position; // Debug.Log($"Head Pos = {pos.x:0.00},{pos.y:0.00},{pos.z:0.00}"); _root.localPosition = new Vector3( pos.x * (-0.001f), pos.y * 0.001f, 0 ); } var gazePoint = TobiiAPI.GetGazePoint(); if (gazePoint.IsRecent()) { var fov = _cam.fieldOfView; var mirrorFactor = isMirrored ? -1f : 1f; var eyeRotation = Quaternion.Euler( (gazePoint.Viewport.y - 0.5f) * fov * eyeRotationApplyRate * (-1), (gazePoint.Viewport.x - 0.5f) * fov * _cam.aspect * eyeRotationApplyRate * mirrorFactor, 0 ); _leftEye.localRotation = eyeRotation; _rightEye.localRotation = eyeRotation; } _eyeClosed = TobiiAPI.GetUserPresence().IsUserPresent() && (Time.unscaledTime - gazePoint.Timestamp) > eyeCloseJudgeLimitTime || !gazePoint.IsRecent(); if (_eyeClosed) { _blinkValue = 1.0f; } else { _blinkValue = Mathf.Max(_blinkValue - eyeOpenRate * Time.deltaTime, 0f); } blendShape.AccumulateValue(lBlinkKey, _blinkValue); blendShape.AccumulateValue(rBlinkKey, _blinkValue); blendShape.Apply(); }
public void Update() { if (m_proxy != null) { m_proxy.Apply(); } }
void Update() { available = false; //全ノブを調べる if (blendShapeProxy != null) { for (int i = 0; i < MidiCCWrapper.KNOBS; i++) { //キーが登録されている場合 if (KnobToBlendShape[i] != null && KnobToBlendShape[i] != "") { //表情を反映する blendShapeProxy.AccumulateValue(KnobToBlendShape[i], midiCCWrapper.CCValue[i]); available = true; } } //値が一つでも存在すれば反映、でなければ特に触らない if (available) { blendShapeProxy.Apply(); } } }
/// <summary> /// 確認用の表情設定 /// </summary> void Set(float joy, float angry, float sorrow, float fun) { Proxy.AccumulateValue(BlendShapePreset.Joy, joy); Proxy.AccumulateValue(BlendShapePreset.Angry, angry); Proxy.AccumulateValue(BlendShapePreset.Sorrow, sorrow); Proxy.AccumulateValue(BlendShapePreset.Fun, fun); Proxy.Apply(); }
private void Update() { UIOperation(); if (_proxy != null) { _proxy.Apply(); } }
private void LateUpdate() { if (!_hasModel) { return; } WriteClips(); _blendShape.Apply(); }
void LateUpdate() { orgPose.GetHumanPose(ref hp); vrmPose.SetHumanPose(ref hp); instancedModel.transform.localPosition = Vector3.zero; instancedModel.transform.localRotation = Quaternion.identity; if (blendProxy) { blendProxy.Apply(); } }
private void Start() { _blendShapeProxy = GetComponent <VRMBlendShapeProxy>(); SetupFingers(); SetBlendShape(); SetIK(); SetLipSync(); //LateUpdateで表情を反映 this.LateUpdateAsObservable().Subscribe(_ => _blendShapeProxy.Apply()); }
void OnDataReceived(uOSC.Message message) { if (message.address == "/VMC/Ext/Root/Pos") { Vector3 pos = new Vector3((float)message.values[1], (float)message.values[2], (float)message.values[3]); Quaternion rot = new Quaternion((float)message.values[4], (float)message.values[5], (float)message.values[6], (float)message.values[7]); Model.transform.localPosition = pos; Model.transform.localRotation = rot; } else if (message.address == "/VMC/Ext/Bone/Pos") { //モデルが更新されたときのみ読み込み if (Model != null && OldModel != Model) { animator = Model.GetComponent <Animator>(); blendShapeProxy = Model.GetComponent <VRMBlendShapeProxy>(); OldModel = Model; } HumanBodyBones bone; if (Enum.TryParse <HumanBodyBones>((string)message.values[0], out bone)) { if ((animator != null) && (bone != HumanBodyBones.LastBone)) { Vector3 pos = new Vector3((float)message.values[1], (float)message.values[2], (float)message.values[3]); Quaternion rot = new Quaternion((float)message.values[4], (float)message.values[5], (float)message.values[6], (float)message.values[7]); var t = animator.GetBoneTransform(bone); if (t != null) { t.localPosition = pos; t.localRotation = rot; } } } } else if (message.address == "/VMC/Ext/Blend/Val") { string BlendName = (string)message.values[0]; float BlendValue = (float)message.values[1]; blendShapeProxy.AccumulateValue(BlendName, BlendValue); } else if (message.address == "/VMC/Ext/Blend/Apply") { blendShapeProxy.Apply(); } }
private void AccumulateBlendShapes() { if (proxy == null) { return; } foreach (var shapeKey in CurrentShapeKeys) { proxy.AccumulateValue(shapeKey.Key, shapeKey.Value); } foreach (var presets in AccumulateShapeKeys) { foreach (var preset in presets.Value) { proxy.AccumulateValue(preset.Key, preset.Value); } } proxy.Apply(); }
private void Update() { EnableLipSyncValue = m_enableLipSync.isOn; EnableBlinkValue = m_enableAutoBlink.isOn; if (Input.GetKeyDown(KeyCode.Tab)) { if (Root != null) { Root.SetActive(!Root.activeSelf); } } m_ui.UpdateToggle(EnableBvh, EnableTPose); if (m_proxy != null) { m_proxy.Apply(); } }
void LateUpdate() { orgPose.GetHumanPose(ref hp); if (orgAnim.GetCurrentAnimatorStateInfo(0).shortNameHash == OcAnimHash.Sprint) { // 走るとき足の動きがおかしいのを補正 hp.muscles[22] = -0.05f; hp.muscles[30] = -0.05f; } vrmPose.SetHumanPose(ref hp); instancedModel.transform.localPosition = Vector3.zero; instancedModel.transform.localRotation = Quaternion.identity; if (blendProxy) { blendProxy.Apply(); } }
private void LateUpdate() { if (!_hasModel || string.IsNullOrEmpty(_externalTracker.FaceSwitchClipName) || _config.WordToMotionExpressionActive ) { return; } _proxy.Apply(); _initializer.InitializeBlendShapes(_externalTracker.KeepLipSyncForFaceSwitch); if (_latestClipName != _externalTracker.FaceSwitchClipName) { _latestKey = CreateKey(_externalTracker.FaceSwitchClipName); _latestClipName = _externalTracker.FaceSwitchClipName; } //NOTE: 最終的な適用はWordToMotionBlendShapeがやる。ので、ここではその前処理だけやってればよい _proxy.AccumulateValue(_latestKey, 1.0f); //表情を適用した = 目ボーンは正面向きになってほしい _eyeBoneResetter.ReserveReset = true; }
void LateUpdate() { m_proxy.Apply(); }
//メッセージ処理本体 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) ) { if (BlendShapeSynchronize && blendShapeProxy != null) { //v0.55ブレンドシェープ仕様変更対応 //ワークアラウンドが有効か if (VRMVersion.MAJOR >= 0 && VRMVersion.MINOR > 53 && BlendShapeCaseWorkAround) { //まず、小文字変換テーブルを作成する string RawKey = (string)message.values[0]; string CaseSensitiveKey = ""; string NoCaseSensitiveKey = ""; //小文字変換テーブルに存在しなければ if (!blendShapeProxyCaseBuffer.TryGetValue(RawKey, out NoCaseSensitiveKey)) { //小文字変換テーブルに登録する blendShapeProxyCaseBuffer[RawKey] = RawKey.ToLower(); NoCaseSensitiveKey = RawKey.ToLower(); //Debug.Log("Register to CaseBuffer:" + RawKey + " -> " + NoCaseSensitiveKey); } //小文字変換された結果で、まずプリセットに存在するか確認する string presetName = ""; BlendShapePreset preset = BlendShapePreset.Unknown; //プリセット変換テーブルで見つかるか if (BlendShapePresetCaseConverter.TryGetValue(NoCaseSensitiveKey, out presetName)) { //ENUMテーブルで見つかるか if (BlendShapePresetTryParse(ref presetName, out preset)) { //見つかった blendShapeProxy.AccumulateValue(preset, (float)message.values[1]); //Debug.Log("Key Found: " + NoCaseSensitiveKey + "->" + preset.ToString()); } else { //見つからない preset = BlendShapePreset.Unknown; } } else { //見つからない preset = BlendShapePreset.Unknown; } //プリセットに該当するものがなかった場合 if (preset == BlendShapePreset.Unknown) { //小文字変換された結果を使って、ケースセンシティブな文字列に変換する if (blendShapeProxyCaseConverter.TryGetValue(NoCaseSensitiveKey, out CaseSensitiveKey)) { //独自キーとしてUniVRMにわたす blendShapeProxy.AccumulateValue(CaseSensitiveKey, (float)message.values[1]); //Debug.Log("Key Convert: " + NoCaseSensitiveKey + "->" + CaseSensitiveKey); } else { //Debug.Log("Key not found"); } } } else { //通常通り適用する blendShapeProxy.AccumulateValue((string)message.values[0], (float)message.values[1]); } } } //ブレンドシェープ適用 else if (message.address == "/VMC/Ext/Blend/Apply") { if (BlendShapeSynchronize && blendShapeProxy != null) { blendShapeProxy.Apply(); } } }
// Update is called once per frame void Update() { if (EnableLipSync) { if (Context != 0) { if (proxy == null) { if (VRMmodel != null) { proxy = VRMmodel.GetComponent <VRMBlendShapeProxy>(); } } else { // get the current viseme frame OVRLipSync.Frame frame = GetCurrentPhonemeFrame(); if (frame != null) { //あ OVRLipSync.Viseme.aa; BlendShapePreset.A; //い OVRLipSync.Viseme.ih; BlendShapePreset.I; //う OVRLipSync.Viseme.ou; BlendShapePreset.U; //え OVRLipSync.Viseme.E; BlendShapePreset.E; //お OVRLipSync.Viseme.oh; BlendShapePreset.O; var presets = new BlendShapePreset[] { BlendShapePreset.A, BlendShapePreset.I, BlendShapePreset.U, BlendShapePreset.E, BlendShapePreset.O, }; var visemes = new float[] { frame.Visemes[(int)OVRLipSync.Viseme.aa], frame.Visemes[(int)OVRLipSync.Viseme.ih], frame.Visemes[(int)OVRLipSync.Viseme.ou], frame.Visemes[(int)OVRLipSync.Viseme.E], frame.Visemes[(int)OVRLipSync.Viseme.oh], }; int maxindex = 0; float maxvisemes = 0; for (int i = 0; i < presets.Length; i++) { if (visemes[i] < WeightThreashold) { visemes[i] = 0; } if (maxvisemes < visemes[i]) { maxindex = i; maxvisemes = visemes[i]; } } if (MaxWeightEmphasis) { visemes[maxindex] = Mathf.Clamp(visemes[maxindex] * 3, 0.0f, 1.0f); } if (MaxWeightEnable) { for (int i = 0; i < presets.Length; i++) { if (i != maxindex) { visemes[i] = 0.0f; } } } for (int i = 0; i < presets.Length; i++) { visemes[i] *= MaxLevel; proxy.AccumulateValue(presets[i], visemes[i]); } proxy.Apply(); //Debug.Log("Visemes:" + string.Join(",", frame.Visemes.Select(d => d.ToString()))); } } } if (string.IsNullOrEmpty(selectedDevice) == false) { audioSource.volume = (sourceVolume / 100); if (!Microphone.IsRecording(selectedDevice)) { StartMicrophone(); } if (EnableLowLatency) { var position = Microphone.GetPosition(selectedDevice); if (position < 0 || head == position) { return; } audioSource.clip.GetData(microphoneBuffer, 0); while (GetDataLength(microphoneBuffer.Length, head, position) > processBuffer.Length) { var remain = microphoneBuffer.Length - head; if (remain < processBuffer.Length) { Array.Copy(microphoneBuffer, head, processBuffer, 0, remain); Array.Copy(microphoneBuffer, 0, processBuffer, remain, processBuffer.Length - remain); } else { Array.Copy(microphoneBuffer, head, processBuffer, 0, processBuffer.Length); } OVRLipSync.ProcessFrame(Context, processBuffer, Frame, OVRLipSync.AudioDataType.F32_Mono); head += processBuffer.Length; if (head > microphoneBuffer.Length) { head -= microphoneBuffer.Length; } } } } } }
void OnDataReceived(uOSC.Message message) { //有効なとき以外処理しない if (this.isActiveAndEnabled) { //仮想コントローラー V2.3 if (message.address == "/VMC/Ext/Con/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 serial = (string)message.values[0]; var rigidTransform = SetTransform(ref pos, ref rot, ref message); lock (LockObject) { if (virtualController.ContainsKey(serial)) { virtualController[serial] = rigidTransform; } else { virtualController.Add(serial, rigidTransform); virtualControllerFiltered.Add(serial, rigidTransform); } } } //仮想トラッカー V2.3 else if ((message.address == "/VMC/Ext/Hmd/Pos" || message.address == "/VMC/Ext/Tra/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 serial = (string)message.values[0]; var rigidTransform = SetTransform(ref pos, ref rot, ref message); lock (LockObject) { if (virtualTracker.ContainsKey(serial)) { virtualTracker[serial] = rigidTransform; } else { virtualTracker.Add(serial, rigidTransform); virtualTrackerFiltered.Add(serial, rigidTransform); } } } //フレーム設定 V2.3 else if (message.address == "/VMC/Ext/Set/Period" && (message.values[0] is int) && (message.values[1] is int) && (message.values[2] is int) && (message.values[3] is int) && (message.values[4] is int) && (message.values[5] is int) ) { externalSender.periodStatus = (int)message.values[0]; externalSender.periodRoot = (int)message.values[1]; externalSender.periodBone = (int)message.values[2]; externalSender.periodBlendShape = (int)message.values[3]; externalSender.periodCamera = (int)message.values[4]; externalSender.periodDevices = (int)message.values[5]; } //Virtual MIDI CC V2.3 else if (message.address == "/VMC/Ext/Midi/CC/Val" && (message.values[0] is int) && (message.values[1] is float) ) { MIDICCWrapper.KnobUpdated(0, (int)message.values[0], (float)message.values[1]); } //Camera Control V2.3 else if (message.address == "/VMC/Ext/Cam" && (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) && (message.values[8] is float) ) { 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]; float fov = (float)message.values[8]; //FreeCameraじゃなかったらFreeCameraにする if (ControlWPFWindow.CurrentSettings.CameraType != UnityMemoryMappedFile.CameraTypes.Free) { window.ChangeCamera(UnityMemoryMappedFile.CameraTypes.Free); } //カメラ制御を切る window.FreeCamera.GetComponent <CameraMouseControl>().enabled = false; //座標とFOVを適用 window.FreeCamera.transform.position = pos; window.FreeCamera.transform.rotation = rot; window.FreeCamera.fieldOfView = fov; } //ブレンドシェープ同期 else if (message.address == "/VMC/Ext/Blend/Val" && (message.values[0] is string) && (message.values[1] is float) ) { if (blendShapeProxy != null) { blendShapeProxy.AccumulateValue((string)message.values[0], (float)message.values[1]); } } //ブレンドシェープ適用 else if (message.address == "/VMC/Ext/Blend/Apply") { if (blendShapeProxy != null) { blendShapeProxy.Apply(); } }//外部アイトラ V2.3 else if (message.address == "/VMC/Ext/Set/Eye" && (message.values[0] is int) && (message.values[1] is float) && (message.values[2] is float) && (message.values[3] is float) ) { bool enable = ((int)message.values[0]) != 0; pos.x = (float)message.values[1]; pos.y = (float)message.values[2]; pos.z = (float)message.values[3]; if (enable) { //ターゲットが存在しなければ作る if (lookTargetOSC == null) { lookTargetOSC = new GameObject(); lookTargetOSC.transform.parent = null; lookTargetOSC.name = "lookTargetOSC"; } //位置を書き込む if (lookTargetOSC.transform != null) { lookTargetOSC.transform.position = pos; } //視線に書き込む if (vrmLookAtHead != null) { vrmLookAtHead.Target = lookTargetOSC.transform; } } else { //視線を止める if (vrmLookAtHead != null) { vrmLookAtHead.Target = null; } } } //情報要求 V2.4 else if (message.address == "/VMC/Ext/Set/Req") { externalSender.SendPerLowRate(); //即時送信 } //情報表示 V2.4 else if (message.address == "/VMC/Ext/Set/Res" && (message.values[0] is string)) { statusString = (string)message.values[0]; } //キャリブレーション準備 V2.5 else if (message.address == "/VMC/Ext/Set/Calib/Ready") { if (File.Exists(ControlWPFWindow.CurrentSettings.VRMPath)) { window.ImportVRM(ControlWPFWindow.CurrentSettings.VRMPath, true, true, true); } } //キャリブレーション実行 V2.5 else if (message.address == "/VMC/Ext/Set/Calib/Exec" && (message.values[0] is int)) { PipeCommands.CalibrateType calibrateType = PipeCommands.CalibrateType.Default; switch ((int)message.values[0]) { case 0: calibrateType = PipeCommands.CalibrateType.Default; break; case 1: calibrateType = PipeCommands.CalibrateType.FixedHand; break; case 2: calibrateType = PipeCommands.CalibrateType.FixedHandWithGround; break; default: return; //無視 } StartCoroutine(window.Calibrate(calibrateType)); Invoke("EndCalibrate", 2f); } //設定読み込み V2.5 else if (message.address == "/VMC/Ext/Set/Config" && (message.values[0] is string)) { string path = (string)message.values[0]; if (File.Exists(path)) { //なぜか時間がかかる window.LoadSettings(path); } } } }
//メッセージ処理本体 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; scale = Vector3.one; offset = Vector3.zero; } } //ボーン姿勢 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) ) { if (BlendShapeSynchronize && blendShapeProxy != null) { blendShapeProxy.AccumulateValue((string)message.values[0], (float)message.values[1]); } } //ブレンドシェープ適用 else if (message.address == "/VMC/Ext/Blend/Apply") { if (BlendShapeSynchronize && blendShapeProxy != null) { blendShapeProxy.Apply(); } } }
void OnDataReceived(uOSC.Message message) { //有効なとき以外処理しない if (this.isActiveAndEnabled) { //仮想コントローラー V2.3 if (message.address == "/VMC/Ext/Con/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 serial = (string)message.values[0]; var rigidTransform = SetTransform(ref pos, ref rot, ref message); if (virtualController.ContainsKey(serial)) { virtualController[serial] = rigidTransform; } else { virtualController.Add(serial, rigidTransform); } } //仮想トラッカー V2.3 else if ((message.address == "/VMC/Ext/Hmd/Pos" || message.address == "/VMC/Ext/Tra/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 serial = (string)message.values[0]; var rigidTransform = SetTransform(ref pos, ref rot, ref message); if (virtualTracker.ContainsKey(serial)) { virtualTracker[serial] = rigidTransform; } else { virtualTracker.Add(serial, rigidTransform); } } //フレーム設定 V2.3 else if (message.address == "/VMC/Ext/Set/Period" && (message.values[0] is int) && (message.values[1] is int) && (message.values[2] is int) && (message.values[3] is int) && (message.values[4] is int) && (message.values[5] is int) ) { externalSender.periodStatus = (int)message.values[0]; externalSender.periodRoot = (int)message.values[1]; externalSender.periodBone = (int)message.values[2]; externalSender.periodBlendShape = (int)message.values[3]; externalSender.periodCamera = (int)message.values[4]; externalSender.periodDevices = (int)message.values[5]; } //Virtual MIDI CC V2.3 else if (message.address == "/VMC/Ext/Midi/CC/Val" && (message.values[0] is int) && (message.values[1] is float) ) { MIDICCWrapper.KnobUpdated(0, (int)message.values[0], (float)message.values[1]); } //Camera Control V2.3 else if (message.address == "/VMC/Ext/Cam" && (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) && (message.values[8] is float) ) { 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]; float fov = (float)message.values[8]; //FreeCameraじゃなかったらFreeCameraにする if (ControlWPFWindow.CurrentSettings.CameraType != UnityMemoryMappedFile.CameraTypes.Free) { window.ChangeCamera(UnityMemoryMappedFile.CameraTypes.Free); } //カメラ制御を切る window.FreeCamera.GetComponent <CameraMouseControl>().enabled = false; //座標とFOVを適用 window.FreeCamera.transform.position = pos; window.FreeCamera.transform.rotation = rot; window.FreeCamera.fieldOfView = fov; } //ブレンドシェープ同期 else if (message.address == "/VMC/Ext/Blend/Val" && (message.values[0] is string) && (message.values[1] is float) ) { if (blendShapeProxy != null) { blendShapeProxy.AccumulateValue((string)message.values[0], (float)message.values[1]); } } //ブレンドシェープ適用 else if (message.address == "/VMC/Ext/Blend/Apply") { if (blendShapeProxy != null) { blendShapeProxy.Apply(); } }//外部アイトラ V2.3 else if (message.address == "/VMC/Ext/Set/Eye" && (message.values[0] is int) && (message.values[1] is float) && (message.values[2] is float) && (message.values[3] is float) ) { bool enable = ((int)message.values[0]) != 0; pos.x = (float)message.values[1]; pos.y = (float)message.values[2]; pos.z = (float)message.values[3]; if (enable) { //ターゲットが存在しなければ作る if (lookTargetOSC == null) { lookTargetOSC = new GameObject(); lookTargetOSC.transform.parent = null; lookTargetOSC.name = "lookTargetOSC"; } //位置を書き込む if (lookTargetOSC.transform != null) { lookTargetOSC.transform.position = pos; } //視線に書き込む if (vrmLookAtHead != null) { vrmLookAtHead.Target = lookTargetOSC.transform; } } else { //視線を止める if (vrmLookAtHead != null) { vrmLookAtHead.Target = null; } } } } }