private void FacialFrameToBuffer([NotNull] Dictionary <string, float> map, [NotNull] float[] buffer) { var currentTime = playerControl.relativeTime; foreach (var kv in map) { var index = Array.IndexOf(_blendShapeNamesFastLookup, kv.Key); if (index >= 0) { Debug.Assert(index >= LipSyncCount); float value; do { if (EyesFilteredMorphs.Contains(kv.Key)) { value = 0; break; } if (EyesAffectedMorphs.Contains(kv.Key)) { if (_currEyeClosed) { value = 0; break; } else if (currentTime < _eyeCloseStartTime + HalfEyeBlinkTime) { value = 0; break; } } value = kv.Value * 100; } while (false); buffer[index - LipSyncCount] = value; } } }
private VmdFacialFrame[] CreateFacialExpressionFrames([NotNull] ScenarioObject facialExpr, int formationNumber) { var frameList = new List <VmdFacialFrame>(); var expControls = facialExpr.Scenario.WhereToArray(s => s.Type == ScenarioDataType.FacialExpression && s.Idol == formationNumber - 1); Debug.Assert(expControls.Length > 0, "Expression controls should exist."); var mappings = _conversionConfig.FacialExpressionMappings; // Note that here we don't process blinks (which happens in MLTD) for (var i = 0; i < expControls.Length; i++) { var exp = expControls[i]; var currentTime = (float)exp.AbsoluteTime; bool areEyesOpen; var eyesClosedRatio = exp.EyeClosed ? 1.0f : 0.0f; frameList.Add(CreateFacialFrame(currentTime, "E_metoji_r", eyesClosedRatio)); frameList.Add(CreateFacialFrame(currentTime, "E_metoji_l", eyesClosedRatio)); if (i > 0) { if (expControls[i - 1].EyeClosed != exp.EyeClosed) { frameList.Add(CreateFacialFrame(currentTime - HalfEyeBlinkTime, "E_metoji_r", 1 - eyesClosedRatio)); frameList.Add(CreateFacialFrame(currentTime - HalfEyeBlinkTime, "E_metoji_l", 1 - eyesClosedRatio)); } } if (exp.EyeClosed) { areEyesOpen = false; } else { do { if (i > 0) { if (expControls[i - 1].EyeClosed) { areEyesOpen = false; break; } } if (i < expControls.Length - 1) { if (expControls[i + 1].EyeClosed) { if (currentTime >= expControls[i + 1].AbsoluteTime - HalfEyeBlinkTime) { areEyesOpen = false; break; } } } areEyesOpen = true; } while (false); } { // The key associated with a group of morph values, representing a whole facial expression var expressionKey = exp.Param; if (!mappings.ContainsKey(expressionKey)) { Trace.TraceWarning("Facial expression key {0} is not found (at time {1}), using default emotion instead.", exp.Param, currentTime); expressionKey = 0; } foreach (var kv in mappings[expressionKey]) { var morphName = kv.Key; if (EyesFilteredMorphs.Contains(morphName)) { continue; } if (EyesAffectedMorphs.Contains(morphName) && !areEyesOpen) { continue; } frameList.Add(CreateFacialFrame(currentTime, morphName, kv.Value)); } // TODO: There is still one problem though, if an eye morph (say, E_wink_l) is activated BEFORE blinking, there is still a disaster. // But I am not sure how to handle that case. If that happens, it probably means something went wrong in animation directing. // Logical animations won't let you blink while you are winking. if (i > 0) { if (expControls[i - 1].Param != exp.Param) { var lastExpressionKey = expControls[i - 1].Param; if (!mappings.ContainsKey(lastExpressionKey)) { Trace.TraceWarning("Facial expression key {0} is not found (at time {1}), using default emotion instead.", expControls[i - 1].Param, (float)expControls[i - 1].AbsoluteTime); lastExpressionKey = 0; } var expectedTransitionStartTime = currentTime - FacialExpressionTransitionTime; foreach (var kv in mappings[lastExpressionKey]) { var morphName = kv.Key; if (EyesFilteredMorphs.Contains(morphName)) { continue; } if (EyesAffectedMorphs.Contains(morphName) && !areEyesOpen) { continue; } // So... do we have to "restore" some morphs after blinking? I think not. Otherwise it will become very strange. frameList.Add(CreateFacialFrame(expectedTransitionStartTime, morphName, kv.Value)); } } } } } return(frameList.ToArray()); }