public float GetSpeedMod(float a_startTime, MotionTimingPresets a_presets, IMxMAnim a_mxmAnim) { MotionPreset motionDef = null; if (a_presets != null) motionDef = a_presets.GetDefenition(MotionPresetId); if (motionDef == null || !UsePresets) //Raw { switch (ModType) { case EMotionModType.PlaybackSpeed: { return 1f / RawModValue; } case EMotionModType.Duration: { return RawModValue / ((EndTime - a_startTime)); } case EMotionModType.LinearSpeed: { if (a_mxmAnim != null) { float averageSpeedOriginal = a_mxmAnim.GetAverageRootSpeed(a_startTime, EndTime); return averageSpeedOriginal / RawModValue; } return 1f / RawModValue; } } } else //from Database { switch (motionDef.MotionType) { case EMotionModType.PlaybackSpeed: { return 1f / motionDef.MotionTiming; } case EMotionModType.Duration: { return motionDef.MotionTiming / (EndTime - a_startTime); } case EMotionModType.LinearSpeed: { if (a_mxmAnim != null) { float averageSpeedOriginal = a_mxmAnim.GetAverageRootSpeed(a_startTime, EndTime); return averageSpeedOriginal / motionDef.MotionTiming; } return 1f / motionDef.MotionTiming; } } } return RawModValue; }
//============================================================================================ /** * @brief * *********************************************************************************************/ public MotionModifyData(MotionModifyData a_copy, IMxMAnim a_targetMxMAnim) { m_targetMxMAnim = a_targetMxMAnim; MotionSections = new List<MotionSection>(); foreach(MotionSection motionSection in a_copy.MotionSections) { MotionSections.Add(new MotionSection(motionSection)); } }
//============================================================================================ /** * @brief * *********************************************************************************************/ public void OnEnable(IMxMAnim m_targetMxMAnim) { if (MotionSections == null) MotionSections = new List<MotionSection>(); if (m_targetMxMAnim == null) return; AnimationClip primaryClip = m_targetMxMAnim.TargetClip; if (MotionSections.Count == 0 && m_targetMxMAnim != null && primaryClip != null) { MotionSections.Add(new MotionSection(0, 0, 0, primaryClip.length)); } }
//============================================================================================ /** * @brief Copy constructor * *********************************************************************************************/ public void DrawEvent(Rect _trackRect, float _zoom, MxMTaggingWindow _taggingWindow, IMxMAnim a_mxmAnim) { Texture markerIcon = EditorGUIUtility.IconContent("Animation.EventMarker").image; if (m_selected) { Handles.BeginGUI(); Handles.color = Color.green; Vector3 start = new Vector3(EventTime * 75f * _zoom - (markerIcon.width / 2f), _trackRect.y + markerIcon.height / 2f, 0f); Vector3 end = new Vector3(start.x - Actions[0] * 75f * _zoom + markerIcon.width / 2f, start.y, 0f); Vector3 up = new Vector3(end.x, end.y - markerIcon.height / 4f); Vector3 down = new Vector3(up.x, up.y + markerIcon.height / 2f); Handles.DrawLine(start, end); Handles.DrawLine(up, down); start = end; end = new Vector3(start.x - Windup * 75f * _zoom, start.y, 0f); up.x = down.x = end.x; Handles.color = Color.blue; Handles.DrawLine(start, end); Handles.DrawLine(up, down); start = new Vector3(EventTime * 75f * _zoom + (markerIcon.width / 2f), _trackRect.y + markerIcon.height / 2f, 0f); Handles.color = Color.green; for (int i = 1; i < Actions.Count; ++i) { float markerWidth = 0; if (i == 1) { markerWidth = markerIcon.width / 2f; } end = new Vector3(start.x + Actions[i] * 75f * _zoom - markerWidth, start.y, 0f); up.x = down.x = end.x; Handles.DrawLine(start, end); Handles.DrawLine(up, down); //Draw a circle maybe start = end; } end = new Vector3(start.x + FollowThrough * 75f * _zoom - markerIcon.width / 2f, start.y, 0f); up.x = down.x = end.x; Handles.color = Color.red; Handles.DrawLine(start, end); Handles.DrawLine(up, down); start = end; end = new Vector3(start.x + Recovery * 75f * _zoom, start.y, 0f); up.x = down.x = end.x; Handles.color = Color.yellow; Handles.DrawLine(start, end); Handles.DrawLine(up, down); Handles.EndGUI(); } Rect markerRect = new Rect(EventTime * 75f * _zoom - (markerIcon.width / 2f), _trackRect.y, markerIcon.width, markerIcon.height); GUI.DrawTexture(markerRect, markerIcon); if (m_selected) { GUI.DrawTexture(markerRect, EditorUtil.EditorFunctions.GetHighlightTex()); } Event evt = Event.current; markerRect.x -= 3f; markerRect.height = _trackRect.height; markerRect.width += 6f; if (evt.isMouse) { if (evt.button == 0) { switch (evt.type) { case EventType.MouseDown: { if (markerRect.Contains(evt.mousePosition)) { m_selected = true; m_dragging = true; _taggingWindow.SelectEvent(this, EventTime); evt.Use(); } } break; case EventType.MouseUp: { m_dragging = false; } break; case EventType.MouseDrag: { if (m_dragging && m_selected) { float desiredValueDelta = ((evt.delta.x / _zoom)) / 75f; EventTime += desiredValueDelta; EventTime = Mathf.Clamp(EventTime, 0f, _taggingWindow.TargetClip.length); _taggingWindow.Modified(EventTime); evt.Use(); } } break; } } else if (evt.button == 1) { //Begin Context menu GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Delete"), false, a_mxmAnim.OnDeleteEventMarker, this); menu.ShowAsContext(); } } }
public void CopyTagsAndEvents(IMxMAnim a_target, bool a_mirrored) { }
public void CopyTagsAndEvents(IMxMAnim a_target, bool a_mirrored) { //Copy Require tag tracks List<TagTrack> targetTagTracks = a_target.AnimTagTracks; if (targetTagTracks != null) { TagTracks = new List<TagTrack>(targetTagTracks.Count + 1); foreach (TagTrack track in targetTagTracks) { if (track != null) { TagTracks.Add(new TagTrack(track)); } } } //Copy Favour tag tracks List<TagTrack> targetFavourTracks = a_target.AnimFavourTagTracks; if (FavourTagTracks != null) { FavourTagTracks = new List<TagTrack>(targetFavourTracks.Count + 1); foreach (TagTrack track in targetFavourTracks) { if (track != null) FavourTagTracks.Add(new TagTrack(track)); } } //Copy User Tags List<TagTrackBase> userTagTracks = a_target.UserTagTracks; if (userTagTracks != null) { UserBoolTracks = new List<TagTrackBase>(userTagTracks.Count + 1); foreach (TagTrackBase track in userTagTracks) { if (track != null) UserBoolTracks.Add(track); } } //Copy Utility Tags List<TagTrackBase> utilityTagTracks = a_target.GenericTagTracks; if (utilityTagTracks != null) { if (a_mirrored) { if (utilityTagTracks.Count > 0) { RightFootStepTrack = new FootStepTagTrack(utilityTagTracks[0] as FootStepTagTrack); RightFootStepTrack.Name = "Footstep Right"; } if (utilityTagTracks.Count > 1) { LeftFootStepTrack = new FootStepTagTrack(utilityTagTracks[1] as FootStepTagTrack); LeftFootStepTrack.Name = "Footstep Left"; } } else { if (utilityTagTracks.Count > 0) LeftFootStepTrack = new FootStepTagTrack(utilityTagTracks[0] as FootStepTagTrack); if (utilityTagTracks.Count > 1) RightFootStepTrack = new FootStepTagTrack(utilityTagTracks[1] as FootStepTagTrack); } if (utilityTagTracks.Count > 2) WarpPositionTrack = new TagTrackBase(utilityTagTracks[2]); if (utilityTagTracks.Count > 3) WarpRotationTrack = new TagTrackBase(utilityTagTracks[3]); if (utilityTagTracks.Count > 4) EnableRootMotionTrack = new TagTrackBase(utilityTagTracks[4]); if (utilityTagTracks.Count > 5) PoseFavourTrack = new FloatTagTrack(utilityTagTracks[5] as FloatTagTrack); if (utilityTagTracks.Count > 6) WarpTrajLatTrack = new TagTrackBase(utilityTagTracks[6]); if (utilityTagTracks.Count > 7) WarpTrajLongTrack = new TagTrackBase(utilityTagTracks[7]); } }
public static void DetectFootsteps(IMxMAnim a_mxmAnim, GameObject a_targetModel, MxMPreProcessData a_preProcessData, AnimationModule a_animModule, float a_groundingThreshold, float a_minSpacing, float a_minDuration, float a_maxFootSpeed) { if ((a_preProcessData == null && a_animModule == null) || a_mxmAnim == null) { return; } AnimationClip targetClip = a_mxmAnim.TargetClip; if (targetClip == null) { return; } if (a_targetModel == null) { if (a_preProcessData == null) { a_targetModel = a_preProcessData.Prefab; } else if (a_animModule == null) { a_targetModel = a_animModule.Prefab; } if (a_targetModel == null) { Debug.LogError("MxM Footstep Detection - The MxMAnim you are trying to detect footsteps for has no target" + "model. This could occur if your target model is not set on the pre-processor, or your animation module" + "doesn't have a MotionMatch Config referenced."); return; } } List <FootStepData> leftFootStepData = new List <FootStepData>(); List <Vector2> leftFootStepPositions = new List <Vector2>(); List <FootStepData> rightFootStepData = new List <FootStepData>(); List <Vector2> rightFootStepPositions = new List <Vector2>(); GameObject model = GameObject.Instantiate(a_targetModel); model.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); Animator animator = model.GetComponent <Animator>(); if (animator == null) { animator = model.AddComponent <Animator>(); } animator.applyRootMotion = true; animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; Transform leftToeJoint = null; Transform rightToeJoint = null; bool getBonesByName = false; if (a_preProcessData != null) { getBonesByName = a_preProcessData.GetBonesByName; } else if (a_animModule != null) { getBonesByName = a_animModule.GetBonesByName; } if (!getBonesByName) { leftToeJoint = animator.GetBoneTransform(HumanBodyBones.LeftToes); rightToeJoint = animator.GetBoneTransform(HumanBodyBones.RightToes); } else { //Get generic joints? Debug.LogWarning("Automatic detection of footsteps is not currently supported for generic rigs"); return; } PlayableGraph playableGraph = PlayableGraph.Create(); playableGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual); var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", animator); var animationMixer = AnimationMixerPlayable.Create(playableGraph, 1, true); playableOutput.SetSourcePlayable(animationMixer); var clipPlayable = AnimationClipPlayable.Create(playableGraph, targetClip); animationMixer.ConnectInput(0, clipPlayable, 0); animationMixer.SetInputWeight(0, 1f); clipPlayable.SetTime(0.0); clipPlayable.SetTime(0.0); playableGraph.Evaluate(0f); model.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); const float SixtyHz = 1f / 60f; float leftFootLowestY = leftToeJoint.position.y; float rightFootLowestY = rightToeJoint.position.y; for (float time = 0f; time <= targetClip.length; time += SixtyHz) { float leftFootY = leftToeJoint.position.y; float rightFootY = rightToeJoint.position.y; if (leftFootY < leftFootLowestY) { leftFootLowestY = leftFootY; } if (rightFootY < rightFootLowestY) { rightFootLowestY = rightFootY; } playableGraph.Evaluate(SixtyHz); } clipPlayable.SetTime(0.0); clipPlayable.SetTime(0.0); playableGraph.Evaluate(0f); bool leftFootGrounded = false; bool rightFootGrounded = false; float leftStepStartTime = 0f; float leftStepEndTime = 0f; float rightStepStartTime = 0f; float rightStepEndTime = 0f; model.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); float leftToeSpeed = 0f; float rightToeSpeed = 0f; Vector3 leftToeLastPos = leftToeJoint.position; Vector3 rightToeLastPos = rightToeJoint.position; for (float time = 0f; time <= targetClip.length; time += SixtyHz) { //Velocities leftToeSpeed = Vector3.Distance(leftToeLastPos, leftToeJoint.position) / SixtyHz; rightToeSpeed = Vector3.Distance(rightToeLastPos, rightToeJoint.position) / SixtyHz; leftToeLastPos = leftToeJoint.position; rightToeLastPos = rightToeJoint.position; //LEFT FOOT float leftFootDif = leftToeJoint.position.y - leftFootLowestY; if (leftFootDif < a_groundingThreshold && leftToeSpeed < a_maxFootSpeed) { if (leftFootGrounded == false) { leftStepStartTime = time; } leftFootGrounded = true; } else { if (leftFootGrounded == true) { leftStepEndTime = time; leftFootStepData.Add(new FootStepData()); leftFootStepPositions.Add(new Vector2(leftStepStartTime, leftStepEndTime)); } leftFootGrounded = false; } //RIGHT FOOT float rightFootDif = rightToeJoint.position.y - rightFootLowestY; if (rightFootDif < a_groundingThreshold && rightToeSpeed < a_maxFootSpeed) { if (rightFootGrounded == false) { rightStepStartTime = time; } rightFootGrounded = true; } else { if (rightFootGrounded == true) { rightStepEndTime = time; rightFootStepData.Add(new FootStepData()); rightFootStepPositions.Add(new Vector2(rightStepStartTime, rightStepEndTime)); } rightFootGrounded = false; } playableGraph.Evaluate(SixtyHz); } List <TagTrackBase> genericTagTracks = a_mxmAnim.GenericTagTracks; FootStepTagTrack leftFootTagTrack = genericTagTracks[0] as FootStepTagTrack; leftFootTagTrack.RemoveAllTags(); for (int i = 0; i < leftFootStepPositions.Count; ++i) { Vector2 footStepPosition = leftFootStepPositions[i]; //Combine footsteps that are too close to be real (This should be recursive) if (i + 1 < leftFootStepPositions.Count) { for (int k = i + 1; k < leftFootStepPositions.Count; ++k) { Vector2 nextFootStepPos = leftFootStepPositions[k]; if (nextFootStepPos.x - footStepPosition.y < a_minSpacing) { footStepPosition.y = nextFootStepPos.y; leftFootStepPositions.RemoveAt(k); --k; } else { break; } } } //Ignore footsteps that are too short to be real if (footStepPosition.y - footStepPosition.x < a_minDuration) { continue; } leftFootTagTrack.AddTag(footStepPosition.x, footStepPosition.y); } FootStepTagTrack rightFootTagTrack = genericTagTracks[1] as FootStepTagTrack; rightFootTagTrack.RemoveAllTags(); for (int i = 0; i < rightFootStepPositions.Count; ++i) { Vector3 footStepPosition = rightFootStepPositions[i]; //Combine footsteps that are too close to be real (This should be recursive) if (i + 1 < rightFootStepPositions.Count) { for (int k = i + 1; k < rightFootStepPositions.Count; ++k) { Vector2 nextFootStepPos = rightFootStepPositions[k]; if (nextFootStepPos.x - footStepPosition.y < a_minSpacing) { footStepPosition.y = nextFootStepPos.y; rightFootStepPositions.RemoveAt(k); --k; } else { break; } } } //Ignore footsteps that are too short to be real if (footStepPosition.y - footStepPosition.x < a_minDuration) { continue; } rightFootTagTrack.AddTag(footStepPosition.x, footStepPosition.y); } GameObject.DestroyImmediate(model); }
//============================================================================================ /** * @brief * *********************************************************************************************/ public void DrawSections(ref Rect a_trackRect, ref Rect a_timelineRect, float a_zoom, MxMTaggingWindow a_taggingWindow) { Texture markerIcon = EditorGUIUtility.IconContent("Animation.EventMarker").image; Rect markerRect; Event evt = Event.current; float lastTime = 0f; MotionTimingPresets motionTimingPresets = null; if (a_taggingWindow != null) m_targetMxMAnim = a_taggingWindow.TargetMxMAnim; var targetPreProcess = m_targetMxMAnim.TargetPreProcess; if (m_targetMxMAnim != null && targetPreProcess != null) { motionTimingPresets = targetPreProcess.MotionTimingPresets; } string[] defenitionNames = null; if(motionTimingPresets != null) { defenitionNames = motionTimingPresets.GetDefenitionNames(); } float maxSpeedVariance = 0.5f; //Determine max variance for (int i = 0; i < MotionSections.Count; ++i) { MotionSection curSection = MotionSections[i]; float speedMod = 1f; speedMod = curSection.GetSpeedMod(lastTime, motionTimingPresets, m_targetMxMAnim); float speedVariance = 0f; if (speedMod > 1f) speedVariance = (speedMod / 1f) - 1f; else if (speedMod < 1f) speedVariance = (1f / speedMod) - 1f; if (speedVariance > maxSpeedVariance) maxSpeedVariance = speedVariance; lastTime = curSection.EndTime; } lastTime = 0f; for (int i = 0; i < MotionSections.Count; ++i) { MotionSection curSection = MotionSections[i]; markerRect = new Rect(curSection.EndTime * 75f * a_zoom - (markerIcon.width / 2f), a_trackRect.y, markerIcon.width, markerIcon.height); Vector3 start = new Vector3(markerRect.x + markerRect.width / 2f, markerRect.y + markerRect.height); Vector3 end = new Vector3(start.x, a_timelineRect.height); if (i != MotionSections.Count - 1) { GUI.DrawTexture(markerRect, markerIcon); Handles.color = Color.black; Handles.DrawLine(start, end); if (curSection.Selected) GUI.DrawTexture(markerRect, EditorUtil.EditorFunctions.GetHighlightTex()); markerRect.x -= 3f; markerRect.height = a_trackRect.height; markerRect.width += 6f; if (evt.isMouse && evt.button == 0) { switch (evt.type) { case EventType.MouseDown: { if (markerRect.Contains(evt.mousePosition)) { curSection.Selected = true; curSection.Dragging = true; a_taggingWindow.SelectSection(curSection, curSection.EndTime); } } break; case EventType.MouseUp: { curSection.Dragging = false; } break; case EventType.MouseDrag: { if (curSection.Dragging && curSection.Selected) { float desiredValueDelta = ((evt.delta.x / a_zoom)) / 75f; curSection.EndTime += desiredValueDelta; curSection.EndTime = Mathf.Clamp(curSection.EndTime, 0f, a_taggingWindow.TargetClip.length); a_taggingWindow.Modified(curSection.EndTime); } } break; } } } float speedMod = 1f; speedMod = curSection.GetSpeedMod(lastTime, motionTimingPresets, m_targetMxMAnim); float speedVariance = 0f; if (speedMod > 1f) { speedVariance = (speedMod / 1f) - 1f; } else if (speedMod < 1f) { speedVariance = (1f / speedMod) - 1f; } float heightRatio = speedVariance / maxSpeedVariance; if (speedMod < 1f) heightRatio *= -1; if (maxSpeedVariance < 0.001f) heightRatio = 0f; //Draw Baseline end = new Vector3(curSection.EndTime * 75f * a_zoom, a_timelineRect.height / 2f + 9f); start = new Vector3(lastTime * 75f * a_zoom, end.y); Handles.color = new Color(0f, 0f, 0f, 0.5f); Handles.DrawLine(start, end); //Draw limit line Handles.color = new Color(0f, 0f, 0f, 0.3f); if (speedMod > 1f + Mathf.Epsilon) { end.y = a_timelineRect.height / 2f + (a_timelineRect.height / 2f - 44f) + 9f; start.y = end.y; Handles.DrawLine(start, end); } else { //Draw limit line end.y = a_timelineRect.height / 2f - (a_timelineRect.height / 2f - 44f) + 9f; start.y = end.y; Handles.DrawLine(start, end); } //Draw Green Line end.y = a_timelineRect.height / 2f + heightRatio * (a_timelineRect.height / 2f - 44f) + 9f; start.y = end.y; Handles.color = Color.green; Handles.DrawLine(start, end); float invertedSpeedMod = 1f / speedMod; string speedString = invertedSpeedMod.ToString("F2"); speedString += "x"; float width = GUI.skin.label.CalcSize(new GUIContent(speedString)).x; GUI.Label(new Rect(start.x + (end.x - start.x) / 2f - width / 2f, end.y - 18f, width, 18f), speedString); Rect dataRect = new Rect(lastTime * 75f * a_zoom + 4, a_timelineRect.height / 1.75f, end.x - start.x - 8, a_timelineRect.height - (a_timelineRect.height / 1.75f) - 18f); if (speedMod > 1f + Mathf.Epsilon) { dataRect.y = 40f; } //Draw Drop Down Boxes GUI.Box(dataRect, "Section " + i); dataRect.x += 3f; dataRect.width -= 6f; GUILayout.BeginArea(dataRect); GUILayout.Space(18f); if (motionTimingPresets != null) { curSection.UsePresets = GUILayout.Toggle(curSection.UsePresets, new GUIContent("Use Preset")); } if (curSection.UsePresets && motionTimingPresets != null) { curSection.MotionPresetId = EditorGUILayout.Popup(curSection.MotionPresetId, defenitionNames); } else { float defaultLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 40f; curSection.ModType = (EMotionModType)EditorGUILayout.EnumPopup(new GUIContent("Type"), curSection.ModType); curSection.RawModValue = EditorGUILayout.FloatField(new GUIContent("Value"), curSection.RawModValue); if (curSection.RawModValue < 0.01f) curSection.RawModValue = 0.01f; EditorGUIUtility.labelWidth = defaultLabelWidth; } GUILayout.FlexibleSpace(); float originalDurationF = (curSection.EndTime - lastTime); float finalDurationF = ((curSection.EndTime - lastTime) * speedMod); string originalDuration = originalDurationF.ToString("F2"); string finalDuration = finalDurationF.ToString("F2"); float originalSpeedF = m_targetMxMAnim.GetAverageRootSpeed(lastTime, curSection.EndTime); float finalSpeedF = originalSpeedF * (originalDurationF / finalDurationF); string originalSpeed = originalSpeedF.ToString("F2"); string finalSpeed = finalSpeedF.ToString("F2"); EditorGUILayout.LabelField("Original: " + originalDuration + " sec | " + originalSpeed + "m/s"); EditorGUILayout.LabelField("Final: " + finalDuration + " sec | " + finalSpeed + "m/s"); GUILayout.EndArea(); lastTime = curSection.EndTime; } }