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(); } }
/// <summary> /// 現在の表情を適用 /// </summary> private void UpdateEmotion() { if (!blendShapeProxy) { return; } if (!randomEmotion) { return; // 現状、ランダムが解除されていたら何もしない(戻さない) } var blendShapes = new List <KeyValuePair <BlendShapeKey, float> >(); int index = 0; foreach (var shape in EmotionPresets) { float val = 0f; // 現在選ばれている表情のみ値を入れ、他はゼロとする if (index == emotionIndex) { val = emotionRate; } blendShapes.Add(new KeyValuePair <BlendShapeKey, float>(BlendShapeKey.CreateFromPreset(shape), val)); index++; } blendShapeProxy.SetValues(blendShapes); UpdateUI(); }
private void Update() { if (_vrmBlendShapeProxy) { var shapeKeyList = new List <KeyValuePair <BlendShapeKey, float> >() { new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.A), BlendShapeClip_A), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.I), BlendShapeClip_I), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.U), BlendShapeClip_U), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.E), BlendShapeClip_E), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.O), BlendShapeClip_O), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Blink), BlendShapeClip_Blink), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Joy), BlendShapeClip_Joy), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Angry), BlendShapeClip_Angry), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Sorrow), BlendShapeClip_Sorrow), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Fun), BlendShapeClip_Fun), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.LookUp), BlendShapeClip_LookUp), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.LookDown), BlendShapeClip_LookDown), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.LookLeft), BlendShapeClip_LookLeft), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.LookRight), BlendShapeClip_LookRight), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Blink_L), BlendShapeClip_Blink_L), new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(BlendShapePreset.Blink_R), BlendShapeClip_Blink_R), }; _vrmBlendShapeProxy.SetValues(shapeKeyList); } }
void Update() { var valuesToSet = new Dictionary <BlendShapeKey, float>(); var keysFinished = new List <BlendShapeKey>(); foreach (var t in transitions) { valuesToSet.Add(t.Key, t.Value.Next(Time.deltaTime)); if (t.Value.IsFinished) { keysFinished.Add(t.Key); } } if (!IsActive) { return; } VRM.SetValues(valuesToSet); // アニメーションが終わったものはリセットする // (リセットさせたくないものは Transition.Hold() でアニメーションを維持すること) foreach (var k in keysFinished) { transitions.Remove(k); if (VRM.GetValue(k) != 0) { TurnDown(k, VRM.GetValue(k) * AutoResetSpeed); } } }
private void AccumulateBlendShapes() { if (proxy == null) { return; } var accumulatedValues = new Dictionary <BlendShapeKey, float>(); //ベースの表情を設定する(使わない表情には全て0が入っている) foreach (var shapeKey in CurrentShapeKeys) { accumulatedValues[shapeKey.Key] = shapeKey.Value; } BeforeApply?.Invoke(); //MixPresetsする最後のチャンス //追加表情を合成する(最大値は超えないようにする) foreach (var presets in AccumulateShapeKeys) { foreach (var preset in presets.Value) { if (accumulatedValues.ContainsKey(preset.Key)) // waidayo等から別のモデルのBlendShapeが送られてくる場合があるので存在チェックする { var value = accumulatedValues[preset.Key]; value += preset.Value; if (value > 1.0f) { value = 1.0f; } accumulatedValues[preset.Key] = value; } } } //上書き表情を合成する(最大値は超えないようにする) foreach (var presets in OverwriteShapeKeys) { foreach (var preset in presets.Value) { if (accumulatedValues.ContainsKey(preset.Key)) // waidayo等から別のモデルのBlendShapeが送られてくる場合があるので存在チェックする { var value = preset.Value; if (value > 1.0f) { value = 1.0f; } accumulatedValues[preset.Key] = value; } } } //全ての表情をSetValuesで1度に反映させる proxy.SetValues(accumulatedValues); //SetValuesは内部でApplyまで行うためApply不要 }
// Update is called once per frame void Update() { latest = source.latest(); // ----- Set Face Rotation ----- // Model can rotate -40~40 degree. // So firstly, I'll clamp float x = Mathf.Clamp(-((float)latest.FaceXRadian) * Mathf.Rad2Deg, -40.0f, 40.0f); float y = Mathf.Clamp(((float)latest.FaceYRadian) * Mathf.Rad2Deg, -40.0f, 40.0f); float z = Mathf.Clamp(((float)latest.FaceZRadian) * Mathf.Rad2Deg, -40.0f, 40.0f); animator.SetFloat(FaceDataServerMenu.XRotationParameterName, (x + 40f) / 80f); animator.SetFloat(FaceDataServerMenu.YRotationParameterName, (y + 40f) / 80f); animator.SetFloat(FaceDataServerMenu.ZRotationParameterName, (z + 40f) / 80f); // ----- Set Facial Expression ----- Dictionary <BlendShapeKey, float> face = new Dictionary <BlendShapeKey, float> { }; // FDS define '0' to 'closing eye', '1' to 'opened eye', but UniVRM is opposit way. // I should convert it. // // I defined 'EyePercent > 100' represents 'surprising'. if (latest.LeftEyePercent > 100 || latest.RightEyePercent > 100) { face.Add(new BlendShapeKey(BlendShapePreset.Blink_L), 0.0f); face.Add(new BlendShapeKey(BlendShapePreset.Blink_R), 0.0f); face.Add(new BlendShapeKey(BlendShapePreset.A), 0.0f); face.Add(new BlendShapeKey(BlendShapePreset.U), 0.0f); // Surprised parameter is affected by bigger one face.Add(new BlendShapeKey("Surprised") , 2.0f * (Mathf.Max(latest.RightEyePercent, latest.LeftEyePercent) - 100.0f) / 100.0f); } else { face.Add(new BlendShapeKey("Surprised"), 0.0f); face.Add(new BlendShapeKey(BlendShapePreset.Blink_L), 1.0f - latest.LeftEyePercent / 100.0f); face.Add(new BlendShapeKey(BlendShapePreset.Blink_R), 1.0f - latest.RightEyePercent / 100.0f); // TODO: Apply MouthWidthPercent too face.Add(new BlendShapeKey(BlendShapePreset.A) , Mathf.Clamp(latest.MouthHeightPercent, 0.0f, 100.0f) / 100.0f); face.Add(new BlendShapeKey(BlendShapePreset.U) , 1.0f - Mathf.Clamp(latest.MouthWidthPercent, 0.0f, 100.0f) / 100.0f); } blenderShapeProxy.SetValues(face); }
void Update() { if (!SelectVRM.IsActive) { Proxy = null; return; } if (Proxy != SelectVRM.Proxy) { Proxy = SelectVRM.Proxy; GetBlendShape(); VRM_Toggle.SetIsOnWithoutNotify(Proxy.enabled); Full_Toggle.SetIsOnWithoutNotify(!Proxy.enabled); VRM.SetActive(Proxy.enabled); Full.SetActive(!Proxy.enabled); return; } if (Proxy.enabled) { var values = new Dictionary <BlendShapeKey, float>(); var clips = Proxy.BlendShapeAvatar.Clips; for (int i = 0; i < clips.Count; i++) { values.Add(clips[i].Key, VRMSliders[i].value); } Proxy.SetValues(values); } else { int index = 0; foreach (var skinned in Skinneds) { for (int i = 0; i < skinned.sharedMesh.blendShapeCount; i++) { var value = FullSliders[index].value; skinned.SetBlendShapeWeight(i, value); index++; } } } }
//メッセージ処理本体 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]; //BlendShapeフィルタが有効なら if (BlendShapeFilterEnable) { //フィルタテーブルに存在するか確認する if (blendShapeFilterDictionaly.ContainsKey(key)) { //存在する場合はフィルタ更新して値として反映する blendShapeFilterDictionaly[key] = (blendShapeFilterDictionaly[key] * BlendShapeFilter) + value * (1.0f - BlendShapeFilter); value = blendShapeFilterDictionaly[key]; } else { //存在しない場合はフィルタに登録する。値はそのまま blendShapeFilterDictionaly.Add(key, value); } } if (BlendShapeSynchronize && blendShapeProxy != null) { //v0.56 BlendShape仕様変更対応 //辞書からKeyに変換し、Key値辞書に値を入れる //通信で受信したキーを小文字に変換して非ケース化 string lowerKey = key.ToLower(); //キーに該当するBSKeyが存在するかチェックする BlendShapeKey bskey; if (StringToBlendShapeKeyDictionary.TryGetValue(lowerKey, out bskey)) { //キーに対して値を登録する BlendShapeToValueDictionary[bskey] = value; //Debug.Log("[lowerKey]->"+ lowerKey+" [bskey]->"+bskey.ToString()+" [value]->"+value); } else { //そんなキーは無い //Debug.LogError("[lowerKey]->" + lowerKey + " is not found"); } } } //ブレンドシェープ適用 else if (message.address == "/VMC/Ext/Blend/Apply") { if (BlendShapeSynchronize && blendShapeProxy != null) { blendShapeProxy.SetValues(BlendShapeToValueDictionary); } } }
private void Update() { if (blendShapeProxy == null) { return; } //口閉じの場合: とにかく閉じるのが良いので閉じて終わり if (ForceClosedMouth) { UpdateToClosedMouth(); return; } if (_context == null || !_context.enabled || !(_context.GetCurrentPhonemeFrame() is OVRLipSync.Frame frame) ) { return; } _transitionTimer += Time.deltaTime; // 最大の重みを持つ音素を探す int maxVisemeIndex = 0; float maxVisemeWeight = 0.0f; // 子音は無視する for (var i = (int)OVRLipSync.Viseme.aa; i < frame.Visemes.Length; i++) { if (frame.Visemes[i] > maxVisemeWeight) { maxVisemeWeight = frame.Visemes[i]; maxVisemeIndex = i; } } // 音素の重みが小さすぎる場合は口を閉じる if (maxVisemeWeight * 100.0f < weightThreshold) { _transitionTimer = 0.0f; } // 音素の切り替わりでタイマーをリセットする if (_previousViseme != (OVRLipSync.Viseme)maxVisemeIndex) { _transitionTimer = 0.0f; _previousViseme = (OVRLipSync.Viseme)maxVisemeIndex; } int visemeIndex = maxVisemeIndex - (int)OVRLipSync.Viseme.aa; bool hasValidMaxViseme = (visemeIndex >= 0); for (int i = 0; i < _keys.Length; i++) { var key = _keys[i]; _blendShapeWeights[key] = Mathf.Clamp(Mathf.Lerp( _blendShapeWeights[key], 0.0f, Time.deltaTime * cancelSpeedFactor ), 0, 0.8f); //減衰中の値のほうが大きければそっちを採用する。 //「あぁあぁぁあ」みたいな声の出し方した場合にEvaluateだけ使うとヘンテコになる可能性が高い為。 if (hasValidMaxViseme && i == visemeIndex) { _blendShapeWeights[key] = Mathf.Clamp(Mathf.Max( _blendShapeWeights[key], transitionCurves.Evaluate(_transitionTimer) ), 0, 0.8f); } } /* * if(keyboardBlendShapeController.IsKeyPressing()) * { * return; * } */ if (EnableSinCurveOnly) { if (!_animating) { var detected = _blendShapeWeights.Values.Where(s => s > 0.1).ToList().Count > 0; if (detected) { _animating = true; Animate(); } } } else { blendShapeProxy.SetValues(_blendShapeWeights); } }
// Update is called once per frame void Update() { if (blendShapeProxy == null) { return; } var sources = SteamVR_Input_Source.GetUpdateSources(); foreach (var source in sources) { if (source == SteamVR_Input_Sources.Any) { continue; } foreach (var actionSet in actionSets) { foreach (var action in actionSet.allActions) //ボタン { if (action is SteamVR_Action_Boolean) { var actionBoolean = (SteamVR_Action_Boolean)action; if (actionBoolean.GetStateDown(source)) { var name = actionBoolean.GetShortName(); if (name == "InteractUI") // トリガー半引き { } else if (name == "GrabPinch") // トリガー全引き { } else if (name == "Teleport") // タッチパッド押し { } else if (name == "GrabGrip") // グリップボタン押し { } } } else if (action is SteamVR_Action_Single) //Axis { var actionSingle = (SteamVR_Action_Single)action; if (actionSingle.GetChanged(source)) { var name = actionSingle.GetShortName(); var axis = actionSingle.GetAxis(source); if (name == "Squeeze") // トリガー { } } } else if (action is SteamVR_Action_Vector2) // Padやスティック { var actionVector2 = (SteamVR_Action_Vector2)action; if (actionVector2.GetChanged(source)) { var name = actionVector2.GetShortName(); var axis = actionVector2.GetAxis(source); // // タッチパッドの座標(両手とも) // (0, 1) // (-1, 0)(0, 0)(1, 0) // (0,-1) // // タッチパッドを離した時は(0,0)が飛んでくる // if (name == "TouchPad") { var isLeft = source == SteamVR_Input_Sources.LeftHand; //全ての表情を一旦無効にする foreach (var key in keyList) { shapeValueDictionary[key] = 0.0f; } if (Mathf.Approximately(axis.x, 0.0f) && Mathf.Approximately(axis.y, 0.0f)) //中心(離した時) { //まばたきとリップシンクを復活させる blinker.enabled = true; animMorphTarget.curveAmplifier = 100f; shapeValueDictionary[GetKey(BlendShapePreset.Neutral)] = 1.0f; } else { //表情とぶつからないようにまばたきを止めてリップシンクを弱くする blinker.enabled = false; animMorphTarget.curveAmplifier = 0.1f; if (axis.x < 0) // 左 { if (axis.y > 0) // 左上 { shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Blink_L : BlendShapePreset.Blink)] = 1.0f; } else if (axis.y < 0) // 左下 { shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Joy : BlendShapePreset.Angry)] = 1.0f; } } else if (axis.x > 0) // 右 { if (axis.y > 0) // 右上 { shapeValueDictionary[isLeft ? GetKey("><") : GetKey(BlendShapePreset.Blink_R)] = 1.0f; } else if (axis.y < 0) // 右下 { shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Sorrow : BlendShapePreset.Fun)] = 1.0f; } } } //表情を適用する blendShapeProxy.SetValues(shapeValueDictionary.ToList()); } } } } } } }
/// <summary> /// Sets the viseme to VRM target. /// </summary> void SetVisemeToVRMTarget(OVRLipSync.Frame frame) { var values = lipsyncClips.Select(_ => new KeyValuePair <BlendShapeKey, float>(new BlendShapeKey(_.Key), frame.Visemes[_.Value])); BlendShapeProxy.SetValues(values); }
void Update() { if (context == null || blendShapeProxy == null || !(context.GetCurrentPhonemeFrame() is OVRLipSync.Frame frame) ) { return; } transitionTimer += Time.deltaTime; // 最大の重みを持つ音素を探す int maxVisemeIndex = 0; float maxVisemeWeight = 0.0f; // 子音は無視する for (var i = (int)OVRLipSync.Viseme.aa; i < frame.Visemes.Length; i++) { if (frame.Visemes[i] > maxVisemeWeight) { maxVisemeWeight = frame.Visemes[i]; maxVisemeIndex = i; } } // 音素の重みが小さすぎる場合は口を閉じる if (maxVisemeWeight * 100.0f < weightThreashold) { transitionTimer = 0.0f; } // 音素の切り替わりでタイマーをリセットする if (previousViseme != (OVRLipSync.Viseme)maxVisemeIndex) { transitionTimer = 0.0f; previousViseme = (OVRLipSync.Viseme)maxVisemeIndex; } int visemeIndex = maxVisemeIndex - (int)OVRLipSync.Viseme.aa; bool hasValidMaxViseme = (visemeIndex >= 0); for (int i = 0; i < keys.Length; i++) { var key = keys[i]; blendShapeWeights[key] = Mathf.Lerp( blendShapeWeights[key], 0.0f, Time.deltaTime * cancelSpeedFactor ); //減衰中の値のほうが大きければそっちを採用する。 //「あぁあぁぁあ」みたいな声の出し方した場合にEvaluateだけ使うとヘンテコになる可能性が高い為。 if (hasValidMaxViseme && i == visemeIndex) { blendShapeWeights[key] = Mathf.Max( blendShapeWeights[key], transitionCurves.Evaluate(transitionTimer) ); } } blendShapeProxy.SetValues(blendShapeWeights); }
void Update() { if (context == null || blendShapeProxy == null) { return; } var frame = context.GetCurrentPhonemeFrame(); if (frame == null) { return; } transitionTimer += Time.deltaTime; // 設定したフレームレートへUpdate関数を低下させる frameRateTimer += Time.deltaTime; if (frameRateTimer < 1.0f / frameRate) { return; } frameRateTimer -= 1.0f / frameRate; // すでに設定されているBlendShapeの重みをリセット foreach (var key in keyList) { defaultValueDictionary[key] = 0.0f; } // 最大の重みを持つ音素を探す var maxVisemeIndex = 0; var maxVisemeWeight = 0.0f; // 子音は無視する for (var i = (int)OVRLipSync.Viseme.aa; i < frame.Visemes.Length; i++) { if (frame.Visemes[i] > maxVisemeWeight) { maxVisemeWeight = frame.Visemes[i]; maxVisemeIndex = i; } } // 音素の重みが小さすぎる場合は口を閉じる if (maxVisemeWeight * 100.0f < weightThreashold) { transitionTimer = 0.0f; } // 音素の切り替わりでタイマーをリセットする if (previousViseme != (OVRLipSync.Viseme)maxVisemeIndex) { transitionTimer = 0.0f; previousViseme = (OVRLipSync.Viseme)maxVisemeIndex; } var visemeIndex = maxVisemeIndex - (int)OVRLipSync.Viseme.aa; if (visemeIndex >= 0) { defaultValueDictionary[keyList[visemeIndex]] = transitionCurves[visemeIndex].Evaluate(transitionTimer) * curveAmplifier; } blendShapeProxy.SetValues(defaultValueDictionary.ToList()); }
// Update is called once per frame void Update() { vrtest.c = 0; vrtest.d = 0; l = false; tmp3 = head.transform.position; tmp4 = waist.transform.position; ScaleHead = (tmp3.y - tmp4.y) / 2; if (vrtest.actionToHaptic2.GetState(SteamVR_Input_Sources.LeftHand)) { tmp2 = lefthand.transform.position; b = 0; } else if (vrtest.actionToHaptic2.GetState(SteamVR_Input_Sources.LeftHand) == false && b == 0) { tmp2 = new Vector3(0f, 0f, 0f); b = 1; } if (vrtest.actionToHaptic2.GetState(SteamVR_Input_Sources.RightHand)) { tmp = righthand.transform.position; c = 0; } else if (vrtest.actionToHaptic2.GetState(SteamVR_Input_Sources.RightHand) == false && c == 0) { tmp = new Vector3(0f, 0f, 0f); c = 1; } if (b == 1 && c == 1) { //全ての表情を一旦無効にする foreach (var key in keyList) { shapeValueDictionary[key] = 0.0f; } Neutral(); l = false; //表情を適用する blendShapeProxy.SetValues(shapeValueDictionary.ToList()); } if (blendShapeProxy == null) { return; } var sources = SteamVR_Input_Source.GetAllSources(); foreach (var source in sources) { if (source == SteamVR_Input_Sources.Any) { continue; } foreach (var actionSet in actionSets) { foreach (var action in actionSet.allActions) //ボタン { if (action is SteamVR_Action_Boolean) { var actionBoolean = (SteamVR_Action_Boolean)action; if (actionBoolean.GetStateDown(source)) { var name = actionBoolean.GetShortName(); if (name == "InteractUI") // トリガー半引き { } else if (name == "GrabPinch") // トリガー全引き { } else if (name == "Teleport") // タッチパッド押し { } else if (name == "GrabGrip") // グリップボタン押し { } else if (name == "Menu") // Menu押し { Debug.Log("A"); var isLeft = source == SteamVR_Input_Sources.LeftHand; if (isLeft) { if (n) { n = false; } else { n = true; m = false; } } else { if (m) { m = false; } else { m = true; n = false; } } } } if (actionBoolean.GetState(source)) { //全ての表情を一旦無効にする foreach (var key in keyList) { shapeValueDictionary[key] = 0.0f; } var name = actionBoolean.GetShortName(); if (name == "GrabPinch") // トリガー全引き { if (tmp.y < tmp3.y && tmp.y >= tmp3.y - ScaleHead) { //表情とぶつからないようにまばたきを止めてリップシンクを弱くする blinker.enabled = false; animMorphTarget.curveAmplifier = 0.1f; l = true; if (tmp2.y >= tmp3.y - ScaleHead && tmp2.y < tmp3.y) { shapeValueDictionary[GetKey(BlendShapePreset.LookUp)] = 1.0f; vrtest.c = 3; } else { if (n == true) { shapeValueDictionary[GetKey(BlendShapePreset.Blink_R)] = 1.0f; vrtest.c = 1; } else if (m == true) { shapeValueDictionary[GetKey(BlendShapePreset.LookDown)] = 1.0f; vrtest.c = 3; } else { shapeValueDictionary[GetKey(BlendShapePreset.Sorrow)] = 1.0f; vrtest.c = 6; } } } else if (tmp2.y < tmp3.y && tmp2.y >= tmp3.y - ScaleHead) { //表情とぶつからないようにまばたきを止めてリップシンクを弱くする blinker.enabled = false; animMorphTarget.curveAmplifier = 0.1f; l = true; if (tmp.y >= tmp3.y - ScaleHead && tmp.y < tmp3.y) { shapeValueDictionary[GetKey(BlendShapePreset.LookUp)] = 1.0f; vrtest.c = 3; } else { if (n == true) { shapeValueDictionary[GetKey(BlendShapePreset.Blink_L)] = 1.0f; vrtest.c = 2; } else if (m == true) { shapeValueDictionary[GetKey(BlendShapePreset.LookRight)] = 1.0f; vrtest.c = 5; } else { shapeValueDictionary[GetKey(BlendShapePreset.Fun)] = 1.0f; vrtest.c = 4; } } } else if (tmp.y >= tmp3.y && tmp2.y >= tmp3.y) { //表情とぶつからないようにまばたきを止めてリップシンクを弱くする blinker.enabled = false; animMorphTarget.curveAmplifier = 0.1f; l = true; shapeValueDictionary[GetKey(BlendShapePreset.Joy)] = 1.0f; } else if (tmp.y - tmp2.y <= 0.1 || (tmp.y - tmp2.y >= -0.1 && tmp.y < tmp3.y - ScaleHead && tmp2.y < tmp3.y - ScaleHead)) { //表情とぶつからないようにまばたきを止めてリップシンクを弱くする blinker.enabled = false; animMorphTarget.curveAmplifier = 0.1f; vrtest.WaistRotationCheck(); Angry(vrtest.i); } else { Neutral(); l = false; } } //表情を適用する blendShapeProxy.SetValues(shapeValueDictionary.ToList()); } } else if (action is SteamVR_Action_Single) //Axis { var actionSingle = (SteamVR_Action_Single)action; if (actionSingle.GetChanged(source)) { var name = actionSingle.GetShortName(); var axis = actionSingle.GetAxis(source); if (name == "Squeeze") // トリガー { } } } else if (action is SteamVR_Action_Vector2) // Padやスティック { var actionVector2 = (SteamVR_Action_Vector2)action; if (actionVector2.GetChanged(source)) { var name = actionVector2.GetShortName(); var axis = actionVector2.GetAxis(source); // // タッチパッドの座標(両手とも) // (0, 1) // (-1, 0)(0, 0)(1, 0) // (0,-1) // // タッチパッドを離した時は(0,0)が飛んでくる // if (name == "TouchPad") { var isLeft = source == SteamVR_Input_Sources.LeftHand; //全ての表情を一旦無効にする foreach (var key in keyList) { shapeValueDictionary[key] = 0.0f; } if (Mathf.Approximately(axis.x, 0.0f) && Mathf.Approximately(axis.y, 0.0f)) //中心(離した時) { //まばたきとリップシンクを復活させる blinker.enabled = true; animMorphTarget.curveAmplifier = 1f; shapeValueDictionary[GetKey(BlendShapePreset.Neutral)] = 1.0f; } else { //表情とぶつからないようにまばたきを止めてリップシンクを弱くする blinker.enabled = false; animMorphTarget.curveAmplifier = 0.1f; if (axis.x < 0) // 左 { if (axis.y > 0) // 左上 { shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Blink_L : BlendShapePreset.Blink)] = 1.0f; } else if (axis.y < 0) // 左下 { shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Joy : BlendShapePreset.Angry)] = 1.0f; } } else if (axis.x > 0) // 右 { if (axis.y > 0) // 右上 { shapeValueDictionary[isLeft ? GetKey("><"): GetKey(BlendShapePreset.Blink_R)] = 1.0f; } else if (axis.y < 0) // 右下 { shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Sorrow : BlendShapePreset.Fun)] = 1.0f; } } } //表情を適用する blendShapeProxy.SetValues(shapeValueDictionary.ToList()); } } } } } } }
private void Update() { if (blendShapeProxy == null) { return; } //口閉じの場合: とにかく閉じるのが良いので閉じて終わり if (ForceClosedMouth) { UpdateToClosedMouth(); return; } if (_context == null || !_context.enabled || !(_context.GetCurrentPhonemeFrame() is OVRLipSync.Frame frame) ) { return; } _transitionTimer += Time.deltaTime; // 最大の重みを持つ音素を探す int maxVisemeIndex = 0; float maxVisemeWeight = 0.0f; // 子音は無視する for (var i = (int)OVRLipSync.Viseme.aa; i < frame.Visemes.Length; i++) { if (frame.Visemes[i] > maxVisemeWeight) { maxVisemeWeight = frame.Visemes[i]; maxVisemeIndex = i; } } // 音素の重みが小さすぎる場合は口を閉じる if (maxVisemeWeight * 100.0f < weightThreshold) { _transitionTimer = 0.0f; } // 音素の切り替わりでタイマーをリセットする if (_previousViseme != (OVRLipSync.Viseme)maxVisemeIndex) { _transitionTimer = 0.0f; _previousViseme = (OVRLipSync.Viseme)maxVisemeIndex; } int visemeIndex = maxVisemeIndex - (int)OVRLipSync.Viseme.aa; bool hasValidMaxViseme = (visemeIndex >= 0); for (int i = 0; i < _keys.Length; i++) { var key = _keys[i]; _blendShapeWeights[key] = Mathf.Lerp( _blendShapeWeights[key], 0.0f, Time.deltaTime * cancelSpeedFactor ); //減衰中の値のほうが大きければそっちを採用する。 //「あぁあぁぁあ」みたいな声の出し方した場合にEvaluateだけ使うとヘンテコになる可能性が高い為。 if (hasValidMaxViseme && i == visemeIndex) { _blendShapeWeights[key] = Mathf.Max( _blendShapeWeights[key], transitionCurves.Evaluate(_transitionTimer) ); } } blendShapeProxy.SetValues(_blendShapeWeights); }