Added support for additive expressions to OpenSeeVRMDriver

This commit is contained in:
Emiliana 2020-06-30 21:44:37 +02:00
parent 892ba54ba8
commit 7197b37a2a

View file

@ -73,12 +73,12 @@ public class OpenSeeVRMDriver : MonoBehaviour {
public bool hotkeys = true; public bool hotkeys = true;
[Tooltip("This component lets configure your VRM expressions.")] [Tooltip("This component lets configure your VRM expressions.")]
public OpenSeeVRMExpression[] expressions = new OpenSeeVRMExpression[]{ public OpenSeeVRMExpression[] expressions = new OpenSeeVRMExpression[]{
new OpenSeeVRMExpression("neutral", BlendShapePreset.Neutral, 1f, 1f, 1f, true, true, 0x70, true, true, false, true, 50f, 120f), new OpenSeeVRMExpression("neutral", BlendShapePreset.Neutral, true, 1f, 1f, 1f, true, true, 0x70, true, true, false, true, 50f, 120f),
new OpenSeeVRMExpression("fun", BlendShapePreset.Fun, 1f, 1f, 1f, true, true, 0x71, true, true, false, true, 1f, 120f), new OpenSeeVRMExpression("fun", BlendShapePreset.Fun, true, 1f, 1f, 1f, true, true, 0x71, true, true, false, true, 1f, 120f),
new OpenSeeVRMExpression("joy", BlendShapePreset.Joy, 1f, 1f, 1f, true, true, 0x72, true, true, false, true, 1f, 120f), new OpenSeeVRMExpression("joy", BlendShapePreset.Joy, true, 1f, 1f, 1f, true, true, 0x72, true, true, false, true, 1f, 120f),
new OpenSeeVRMExpression("angry", BlendShapePreset.Angry, 1f, 1f, 0f, true, true, 0x73, true, true, false, true, 1f, 120f), new OpenSeeVRMExpression("angry", BlendShapePreset.Angry, true, 1f, 1f, 0f, true, true, 0x73, true, true, false, true, 1f, 120f),
new OpenSeeVRMExpression("sorrow", BlendShapePreset.Sorrow, 1f, 1f, 0f, true, true, 0x74, true, true, false, true, 1f, 120f), new OpenSeeVRMExpression("sorrow", BlendShapePreset.Sorrow, true, 1f, 1f, 0f, true, true, 0x74, true, true, false, true, 1f, 120f),
new OpenSeeVRMExpression("surprise", "Surprised", 1f, 1f, 0f, true, true, 0x75, true, true, false, true, 1f, 120f) new OpenSeeVRMExpression("surprise", "Surprised", true, 1f, 1f, 0f, true, true, 0x75, true, true, false, true, 1f, 120f)
}; };
[Tooltip("The expression configuration is initialized on startup. If it is changed and needs to be reinitialized, this can be triggered by using this flag or calling InitExpressionMap. This flag is reset to false afterwards.")] [Tooltip("The expression configuration is initialized on startup. If it is changed and needs to be reinitialized, this can be triggered by using this flag or calling InitExpressionMap. This flag is reset to false afterwards.")]
public bool reloadExpressions = false; public bool reloadExpressions = false;
@ -146,7 +146,6 @@ public class OpenSeeVRMDriver : MonoBehaviour {
private OpenSeeVRMExpression currentExpression = null; private OpenSeeVRMExpression currentExpression = null;
private OpenSee.OpenSee.OpenSeeData openSeeData = null; private OpenSee.OpenSee.OpenSeeData openSeeData = null;
private OpenSeeVRMExpression lastExpression = null; private OpenSeeVRMExpression lastExpression = null;
private float expressionChangeTime = -1f;
private float turnLeftBoundaryAngle = -30f; private float turnLeftBoundaryAngle = -30f;
private float turnRightBoundaryAngle = 20f; private float turnRightBoundaryAngle = 20f;
@ -181,6 +180,10 @@ public class OpenSeeVRMDriver : MonoBehaviour {
private bool overridden = false; private bool overridden = false;
private OpenSeeVRMExpression toggledExpression = null; private OpenSeeVRMExpression toggledExpression = null;
private HashSet<OpenSeeVRMExpression> continuedPress = new HashSet<OpenSeeVRMExpression>(); private HashSet<OpenSeeVRMExpression> continuedPress = new HashSet<OpenSeeVRMExpression>();
private bool currentExpressionEnableBlinking;
private bool currentExpressionEnableVisemes;
private float currentExpressionEyebrowWeight;
private float currentExpressionVisemeFactor;
private VRMBlendShapeProxy lastAvatar = null; private VRMBlendShapeProxy lastAvatar = null;
private int eyebrowIsMoving = 0; private int eyebrowIsMoving = 0;
@ -245,8 +248,8 @@ public class OpenSeeVRMDriver : MonoBehaviour {
return; return;
float expressionFactor = 1f; float expressionFactor = 1f;
if (currentExpression != null) if (currentExpression != null)
expressionFactor = currentExpression.visemeFactor; expressionFactor = currentExpressionVisemeFactor;
if (currentExpression != null && !currentExpression.enableVisemes) { if (currentExpression != null && !currentExpressionEnableVisemes) {
return; return;
} }
float weight; float weight;
@ -367,12 +370,12 @@ public class OpenSeeVRMDriver : MonoBehaviour {
float t = Mathf.Clamp((float)mouthInterpolationCount / openSeeIKTarget.averageInterpolations, 0f, 0.985f); float t = Mathf.Clamp((float)mouthInterpolationCount / openSeeIKTarget.averageInterpolations, 0f, 0.985f);
mouthInterpolationCount++; mouthInterpolationCount++;
if (currentExpression != null && !currentExpression.enableVisemes) if (currentExpression != null && !currentExpressionEnableVisemes)
return; return;
float expressionFactor = 1f; float expressionFactor = 1f;
if (currentExpression != null) if (currentExpression != null)
expressionFactor = currentExpression.visemeFactor; expressionFactor = currentExpressionVisemeFactor;
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
float interpolated = Mathf.Lerp(lastMouthStates[i], currentMouthStates[i], t); float interpolated = Mathf.Lerp(lastMouthStates[i], currentMouthStates[i], t);
@ -483,7 +486,7 @@ public class OpenSeeVRMDriver : MonoBehaviour {
float strength = eyebrowStrength; float strength = eyebrowStrength;
if (currentExpression != null) if (currentExpression != null)
strength *= currentExpression.eyebrowWeight; strength *= currentExpressionEyebrowWeight;
if (browUpIndex > -1 && strength > 0f) if (browUpIndex > -1 && strength > 0f)
faceMesh.SetBlendShapeWeight(browUpIndex, strength * Mathf.Lerp(lastBrowStates[0], currentBrowStates[0], t)); faceMesh.SetBlendShapeWeight(browUpIndex, strength * Mathf.Lerp(lastBrowStates[0], currentBrowStates[0], t));
@ -583,6 +586,8 @@ public class OpenSeeVRMDriver : MonoBehaviour {
public BlendShapePreset blendShapePreset; public BlendShapePreset blendShapePreset;
[Tooltip("When this name is set, it will be used to create a custom blendshape key.")] [Tooltip("When this name is set, it will be used to create a custom blendshape key.")]
public string customBlendShapeName = ""; public string customBlendShapeName = "";
[Tooltip("When disabled, this expression is considered a base expression. Only one base expression can be active at a time. Otherwise it can be added on top of other expressions.")]
public bool additive = false;
[Tooltip("The weight determines the value the expression blend shape should be set to.")] [Tooltip("The weight determines the value the expression blend shape should be set to.")]
[Range(0, 1)] [Range(0, 1)]
public float weight = 1f; public float weight = 1f;
@ -614,13 +619,63 @@ public class OpenSeeVRMDriver : MonoBehaviour {
[HideInInspector] [HideInInspector]
public BlendShapeKey blendShapeKey; public BlendShapeKey blendShapeKey;
[HideInInspector] [HideInInspector]
public float maxWeight = 0f; public bool toggled = false;
[HideInInspector] [HideInInspector]
public float lastTimeWeightOld = 0f; public bool triggered = false;
[HideInInspector]
public bool reached = true;
[HideInInspector]
public bool lastActive = false;
private float stateChangedTime = -1f;
private float stateChangedWeight = 0f;
private float lastWeight = 0f;
private float targetWeight = 0f;
private float remainingWeight = 0f;
private float currentTransitionTime = 0f;
public float GetWeight(float baseTransitionTime) {
bool active = triggered || toggled;
if (reached) {
if (active) {
lastWeight = 1f;
return 1f;
} else {
lastWeight = 0f;
return 0f;
}
}
if (active != lastActive) {
stateChangedTime = Time.time;
stateChangedWeight = lastWeight;
currentTransitionTime = transitionTime;
lastActive = active;
if (active) {
targetWeight = 1f;
remainingWeight = 1f - lastWeight;
} else {
targetWeight = 0f;
remainingWeight = lastWeight;
}
}
if (baseTransitionTime < currentTransitionTime)
currentTransitionTime = baseTransitionTime;
lastWeight = Mathf.Lerp(stateChangedWeight, targetWeight, Mathf.Min((Time.time - stateChangedTime) / (currentTransitionTime * 0.001f * remainingWeight), 1f));
if (lastWeight >= 0.9999f && active) {
lastWeight = 1f;
reached = true;
}
if (lastWeight <= 0.0001f && !active) {
lastWeight = 0f;
reached = true;
}
return lastWeight;
}
public OpenSeeVRMExpression(string trigger, BlendShapePreset preset, float weight, float factor, float eyebrows, bool visemes, bool blinking, int hotkey, bool shiftKey, bool ctrlKey, bool altKey, bool toggle, float errorWeight, float transitionTime) { public OpenSeeVRMExpression(string trigger, BlendShapePreset preset, bool additive, float weight, float factor, float eyebrows, bool visemes, bool blinking, int hotkey, bool shiftKey, bool ctrlKey, bool altKey, bool toggle, float errorWeight, float transitionTime) {
this.trigger = trigger; this.trigger = trigger;
this.blendShapePreset = preset; this.blendShapePreset = preset;
this.additive = additive;
this.weight = weight; this.weight = weight;
this.visemeFactor = factor; this.visemeFactor = factor;
this.eyebrowWeight = eyebrows; this.eyebrowWeight = eyebrows;
@ -635,9 +690,10 @@ public class OpenSeeVRMDriver : MonoBehaviour {
this.transitionTime = transitionTime; this.transitionTime = transitionTime;
} }
public OpenSeeVRMExpression(string trigger, string name, float weight, float factor, float eyebrows, bool visemes, bool blinking, int hotkey, bool shiftKey, bool ctrlKey, bool altKey, bool toggle, float errorWeight, float transitionTime) { public OpenSeeVRMExpression(string trigger, string name, bool additive, float weight, float factor, float eyebrows, bool visemes, bool blinking, int hotkey, bool shiftKey, bool ctrlKey, bool altKey, bool toggle, float errorWeight, float transitionTime) {
this.trigger = trigger; this.trigger = trigger;
this.customBlendShapeName = name; this.customBlendShapeName = name;
this.additive = additive;
this.weight = weight; this.weight = weight;
this.visemeFactor = factor; this.visemeFactor = factor;
this.eyebrowWeight = eyebrows; this.eyebrowWeight = eyebrows;
@ -689,36 +745,64 @@ public class OpenSeeVRMDriver : MonoBehaviour {
return; return;
} }
if (overridden)
currentExpression = toggledExpression;
bool trigger = false; bool trigger = false;
currentExpression = null;
if (hotkeys) { if (hotkeys) {
bool shiftKey = (GetAsyncKeyState(0x10) & 0x8000U) != 0; bool shiftKey = (GetAsyncKeyState(0x10) & 0x8000U) != 0;
bool ctrlKey = (GetAsyncKeyState(0x11) & 0x8000U) != 0; bool ctrlKey = (GetAsyncKeyState(0x11) & 0x8000U) != 0;
bool altKey = (GetAsyncKeyState(0x12) & 0x8000U) != 0; bool altKey = (GetAsyncKeyState(0x12) & 0x8000U) != 0;
foreach (var expression in expressionMap.Values) { foreach (var expression in expressionMap.Values) {
if (expression == toggledExpression && expression.toggled)
expression.triggered = true;
else
expression.triggered = false;
if (expression.hotkey >= 0 && expression.hotkey < 256 && (GetAsyncKeyState(expression.hotkey) & 0x8000) != 0 && shiftKey == expression.shiftKey && ctrlKey == expression.ctrlKey && altKey == expression.altKey) { if (expression.hotkey >= 0 && expression.hotkey < 256 && (GetAsyncKeyState(expression.hotkey) & 0x8000) != 0 && shiftKey == expression.shiftKey && ctrlKey == expression.ctrlKey && altKey == expression.altKey) {
if (!continuedPress.Contains(expression) && overridden && toggledExpression == expression) {
overridden = false;
toggledExpression = null;
if (currentExpression == expression)
currentExpression = null;
continuedPress.Add(expression);
}
if (!continuedPress.Contains(expression)) { if (!continuedPress.Contains(expression)) {
// Generally, turn on expressions when their hotkey is pressed
expression.triggered = true;
if (expression.toggle) { if (expression.toggle) {
overridden = true; // If can be toggled, toggle it and update the triggered value accordingly
toggledExpression = expression; expression.toggled = !expression.toggled;
expression.triggered = expression.toggled;
// If it is a base expression
if (!expression.additive) {
if (expression.toggled) {
// Override expression detection
overridden = true;
// Mark it as the current base expression while unmarking any other
toggledExpression = expression;
foreach (var otherExpression in expressionMap.Values) {
if (otherExpression.toggle && !otherExpression.additive && otherExpression != expression) {
otherExpression.toggled = false;
otherExpression.triggered = false;
}
}
} else {
// Unmark it as the current base expression if it was untoggled
if (toggledExpression == expression) {
toggledExpression = null;
overridden = false;
}
}
}
continuedPress.Add(expression); continuedPress.Add(expression);
} else } else if (!expression.additive) {
// If it's a base expression being triggered on, override expression detection
trigger = true; trigger = true;
currentExpression = expression; currentExpression = expression;
}
} }
} else { } else {
continuedPress.Remove(expression); continuedPress.Remove(expression);
} }
if (expression.lastActive != expression.triggered)
expression.reached = false;
} }
} }
if (overridden)
currentExpression = toggledExpression;
if (!overridden && !trigger) { if (!overridden && !trigger) {
if (openSeeExpression.expression == null || openSeeExpression.expression == "" || !expressionMap.ContainsKey(openSeeExpression.expression)) { if (openSeeExpression.expression == null || openSeeExpression.expression == "" || !expressionMap.ContainsKey(openSeeExpression.expression)) {
@ -731,13 +815,38 @@ public class OpenSeeVRMDriver : MonoBehaviour {
if (currentExpression == null && expressionMap.ContainsKey("neutral")) { if (currentExpression == null && expressionMap.ContainsKey("neutral")) {
currentExpression = expressionMap["neutral"]; currentExpression = expressionMap["neutral"];
} }
// Reset limits on expressions
currentExpressionEnableBlinking = true;
currentExpressionEnableVisemes = true;
currentExpressionEyebrowWeight = 1f;
currentExpressionVisemeFactor = 1f;
float baseTransitionTime = 1000f;
if (currentExpression != null && !currentExpression.reached && currentExpression.transitionTime < baseTransitionTime)
baseTransitionTime = currentExpression.transitionTime;
if (lastExpression != currentExpression && lastExpression != null && !lastExpression.reached && lastExpression.transitionTime < baseTransitionTime)
baseTransitionTime = lastExpression.transitionTime;
foreach (var expression in expressionMap.Values) {
float weight = expression.GetWeight(baseTransitionTime);
if (weight > 0f) {
vrmBlendShapeProxy.AccumulateValue(expression.blendShapeKey, weight);
// Accumulate limits on expressions
currentExpressionEnableBlinking = currentExpressionEnableBlinking && expression.enableBlinking;
currentExpressionEnableVisemes = currentExpressionEnableVisemes && expression.enableVisemes;
currentExpressionEyebrowWeight = Mathf.Min(currentExpressionEyebrowWeight, expression.eyebrowWeight);
currentExpressionVisemeFactor = Mathf.Min(currentExpressionVisemeFactor, expression.visemeFactor);
}
}
lastExpression = currentExpression;
if (currentExpression != lastExpression) { /*if (currentExpression != lastExpression) {
expressionChangeTime = Time.time; expressionChangeTime = Time.time;
lastExpression = currentExpression; lastExpression = currentExpression;
} }*/
float timeWeight = 1f; /*float timeWeight = 1f;
if (currentExpression.transitionTime > 0f) if (currentExpression.transitionTime > 0f)
timeWeight = Mathf.Clamp((Time.time - expressionChangeTime) / (currentExpression.transitionTime * 0.001f), 0f, 1f); timeWeight = Mathf.Clamp((Time.time - expressionChangeTime) / (currentExpression.transitionTime * 0.001f), 0f, 1f);
if (timeWeight >= 1f) { if (timeWeight >= 1f) {
@ -765,8 +874,9 @@ public class OpenSeeVRMDriver : MonoBehaviour {
currentExpression.maxWeight = timeWeight; currentExpression.maxWeight = timeWeight;
currentExpression.lastTimeWeightOld = 0f; currentExpression.lastTimeWeightOld = 0f;
if (currentExpression != null) if (currentExpression != null)
vrmBlendShapeProxy.AccumulateValue(currentExpression.blendShapeKey, currentExpression.weight * currentExpression.maxWeight); vrmBlendShapeProxy.AccumulateValue(currentExpression.blendShapeKey, currentExpression.weight * currentExpression.maxWeight);*/
} }
void GetLookParameters(ref float lookLeftRight, ref float lookUpDown, bool update, int gazePoint, int right, int left, int topRight, int topLeft, int bottomRight, int bottomLeft) { void GetLookParameters(ref float lookLeftRight, ref float lookUpDown, bool update, int gazePoint, int right, int left, int topRight, int topLeft, int bottomRight, int bottomLeft) {
@ -867,13 +977,13 @@ public class OpenSeeVRMDriver : MonoBehaviour {
vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_R), 0f); vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_R), 0f);
vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_L), 0f); vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_L), 0f);
if (autoBlink) { if (autoBlink) {
if (currentExpression == null || currentExpression.enableBlinking) { if (currentExpressionEnableBlinking) {
float blink = eyeBlinker.Blink(); float blink = eyeBlinker.Blink();
vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_R), blink); vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_R), blink);
vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_L), blink); vrmBlendShapeProxy.AccumulateValue(new BlendShapeKey(BlendShapePreset.Blink_L), blink);
} }
} else if (openSeeExpression != null && openSeeExpression.openSee != null) { } else if (openSeeExpression != null && openSeeExpression.openSee != null) {
if (currentExpression != null && !currentExpression.enableBlinking) if (!currentExpressionEnableBlinking)
return; return;
float right = 1f; float right = 1f;