//============================================================================================ /** * @brief * *********************************************************************************************/ public PoseData(PoseData a_pose) { PoseId = a_pose.PoseId; AnimId = a_pose.AnimId; TracksId = a_pose.TracksId; PrimaryClipId = a_pose.PrimaryClipId; Time = a_pose.Time; LocalVelocity = a_pose.LocalVelocity; NextPoseId = a_pose.NextPoseId; LastPoseId = a_pose.LastPoseId; Favour = a_pose.Favour; JointsData = new JointData[a_pose.JointsData.Length]; for (int i = 0; i < a_pose.JointsData.Length; ++i) { JointsData[i] = a_pose.JointsData[i]; } Trajectory = new TrajectoryPoint[a_pose.Trajectory.Length]; for (int i = 0; i < Trajectory.Length; ++i) { Trajectory[i] = a_pose.Trajectory[i]; } Tags = a_pose.Tags; FavourTags = a_pose.FavourTags; GenericTags = a_pose.GenericTags; UserTags = a_pose.UserTags; AnimType = a_pose.AnimType; }
//=========================================================================================== /** * @brief * * * This function is usually called automatically by the MxMAnimator following an action event * dependent on the 'PostEventTrajectoryHandling' property (see MxMAnimator.cs). * *********************************************************************************************/ public virtual void CopyGoalFromPose(ref PoseData a_poseData) { if (a_poseData.Trajectory.Length != p_goal.Length) { return; } SetGoal(a_poseData.Trajectory); }
//============================================================================================ /** * @brief Sets the status of the Playable State to chosen with the passed pose data * * @param [ref PoseData] a_pose - the pose to set as chosen * *********************************************************************************************/ public void SetAsChosenWithPose(ref PoseData a_pose) { Weight = 0f; //This may be wrong, start blend weight of 0? HighestWeight = 0f; AnimType = a_pose.AnimType; AnimId = a_pose.AnimId; StartPoseId = a_pose.PoseId; StartTime = a_pose.Time; Age = 0f; DecayAge = 0f; BlendStatus = EBlendStatus.Chosen; }
//============================================================================================ /** * @brief Sets up a pose for inertial blending method (Experimental) * * In the case of inertial blending, playable clips are never destroyed. Rather each clip is * setup at load time as a playable in a slot id which is the same as their clip Id. Therefore, * no slot id is required * * @param [ref PoseData] a_pose - the pose to setup * @param [float] a_speedMod - the speed modification to the clip playabck (default 1f) * *********************************************************************************************/ private void SetupPose(ref PoseData a_pose, float a_speedMod = 1f) { ClearActiveSlotWeights(); switch (a_pose.AnimType) { case EMxMAnimtype.Composite: { SetupComposite (ref a_pose, a_speedMod); } break; case EMxMAnimtype.IdleSet: { SetupClip (ref a_pose, a_speedMod); } break; case EMxMAnimtype.BlendSpace: { SetupBlendSpace (ref a_pose, a_speedMod); } break; case EMxMAnimtype.Clip: { SetupClip (ref a_pose, a_speedMod); } break; case EMxMAnimtype.BlendClip: { SetupBlendClip (ref a_pose, a_speedMod); } break; case EMxMAnimtype.Sequence: { } break; case EMxMAnimtype.BlendSpace1D: { } break; } }
//============================================================================================ /** * @brief Copies the data from one pose to another * * @param [ref PoseData] a_fromPose - the pose to copy data from * @param [ref PoseData] a_toPose - the pose to copy data to * *********************************************************************************************/ public static void CopyPose(ref PoseData a_fromPose, ref PoseData a_toPose) { a_toPose.PoseId = a_fromPose.PoseId; a_toPose.AnimId = a_fromPose.AnimId; a_toPose.Time = a_fromPose.Time; a_toPose.NextPoseId = a_fromPose.NextPoseId; a_toPose.LastPoseId = a_fromPose.LastPoseId; a_toPose.Favour = a_fromPose.Favour; a_toPose.LocalVelocity = a_fromPose.LocalVelocity; a_toPose.Tags = a_fromPose.Tags; a_toPose.GenericTags = a_fromPose.GenericTags; a_toPose.AnimType = a_fromPose.AnimType; if (a_toPose.JointsData == null) a_toPose.JointsData = new JointData[a_fromPose.JointsData.Length]; if (a_toPose.Trajectory == null) a_toPose.Trajectory = new TrajectoryPoint[a_fromPose.Trajectory.Length]; System.Array.Copy(a_fromPose.JointsData, a_toPose.JointsData, a_fromPose.JointsData.Length); System.Array.Copy(a_fromPose.Trajectory, a_toPose.Trajectory, a_fromPose.Trajectory.Length); }
private List<int> m_activeSlots = null; //A list of inputs to the animation mixer that are active //============================================================================================ /** * @brief Sets up a pose in a specific input on the Motion Matching mixer playable * * @param [ref PoseData] a_pose - the pose to extract the animation data from * @param [int] a_slotId - the Id of the slot (or in put) to use * @param [float] a_speedMod - any speed modification for animation playback (default 1f) * *********************************************************************************************/ private void SetupPoseInSlot(ref PoseData a_pose, int a_slotId, float a_speedMod = 1f) { #if UNITY_2019_1_OR_NEWER if (p_riggingIntegration != null) p_riggingIntegration.FixRigTransforms(); #endif var playable = m_animationMixer.GetInput(a_slotId); if(playable.IsValid()) ClearPlayablesInSlot(a_slotId); switch (a_pose.AnimType) { case EMxMAnimtype.Composite: { SetupCompositeInSlot(ref a_pose, a_slotId, a_speedMod); } break; case EMxMAnimtype.IdleSet: { SetupClipInSlot(ref a_pose, a_slotId, a_speedMod); } break; case EMxMAnimtype.BlendSpace: { SetupBlendSpaceInSlot(ref a_pose, a_slotId, a_speedMod); } break; case EMxMAnimtype.Clip: { SetupClipInSlot(ref a_pose, a_slotId, a_speedMod); } break; case EMxMAnimtype.BlendClip: { SetupBlendClipInSlot(ref a_pose, a_slotId, a_speedMod); } break; case EMxMAnimtype.Sequence: { } break; case EMxMAnimtype.BlendSpace1D: { } break; } }
//============================================================================================ /** * @brief Stops pose debugging and destroys the playable graph * *********************************************************************************************/ public void StopPoseDebug() { if (!m_debugPosesActive) { return; } if (m_animData.Length > 0 && m_animData[0] != null) { CurrentAnimData = m_animData[0]; } if (CurrentAnimData != null) { m_debugPosesActive = false; PoseData pose = CurrentAnimData.Poses[0]; AnimationClip clip = CurrentAnimData.Clips[pose.PrimaryClipId]; var clipPlayable = m_animationMixer.GetInput(0); if (!clipPlayable.IsNull()) { m_animationMixer.DisconnectInput(0); clipPlayable.Destroy(); } clipPlayable = AnimationClipPlayable.Create(MxMPlayableGraph, clip); MxMPlayableGraph.Connect(clipPlayable, 0, m_animationMixer, 0); clipPlayable.SetTime(0.0); clipPlayable.SetTime(0.0); MxMPlayableGraph.Evaluate(0f); SceneView.RepaintAll(); MxMPlayableGraph.Destroy(); transform.SetPositionAndRotation(basePosition, baseRotation); } }
//============================================================================================ /** * @brief Draws the debugging gizmos for the MxMAnimator. * * This includes drawing of the desired trajectory, animation trajectory and pose joint data. * *********************************************************************************************/ private void OnDrawGizmos() { if (p_trajectoryGenerator != null && CurrentAnimData != null && m_animMixerConnected && enabled) { if (m_fsm.CurrentStateId == (uint)EMxMStates.Event) { Gizmos.color = Color.green; Gizmos.DrawSphere(m_desiredEventRootWorld.Position, 0.1f); Quaternion rotation = Quaternion.AngleAxis(m_desiredEventRootWorld.RotationY, Vector3.up); DrawArrow.ForGizmo(m_desiredEventRootWorld.Position, rotation * Vector3.forward, 0.8f, 20f); Gizmos.color = Color.blue; Gizmos.DrawSphere(m_currentEventRootWorld.Position, 0.1f); rotation = Quaternion.AngleAxis(m_currentEventRootWorld.RotationY, Vector3.up); DrawArrow.ForGizmo(m_currentEventRootWorld.Position, rotation * Vector3.forward, 0.8f, 20f); Quaternion contactRotation = Quaternion.AngleAxis(m_desiredEventRootWorld.RotationY, Vector3.up); if (CurEventContacts != null && CurEventContacts.Length > m_curEventContactIndex) { Gizmos.color = Color.red; Gizmos.DrawSphere(CurEventContacts[m_curEventContactIndex].Position, 0.05f); } Gizmos.color = Color.cyan; Gizmos.DrawSphere(transform.position, 0.05f); DrawArrow.ForGizmo(transform.position, transform.rotation * Vector3.forward, 0.4f, 20f); } if (m_debugGoal) { Array.Copy(m_desiredGoal, m_debugDesiredGoal, m_debugDesiredGoal.Length); float rot = transform.rotation.eulerAngles.y; for (int i = 0; i < m_debugDesiredGoal.Length; ++i) { Vector3 newPos = transform.TransformVector(m_debugDesiredGoal[i].Position); float newRot = m_debugDesiredGoal[i].FacingAngle + rot; m_debugDesiredGoal[i] = new TrajectoryPoint(newPos, newRot); } //Draw Desired Trajectory int doneCenter = 0; for (int i = 0; i < m_debugDesiredGoal.Length; ++i) { TrajectoryPoint curPoint = m_debugDesiredGoal[i]; if (CurrentAnimData.PosePredictionTimes[i] < 0f) { Gizmos.color = new Color(0f, 0.5f, 0f); } else { if (doneCenter == 0) { Gizmos.color = Color.cyan; Handles.color = Color.cyan; Handles.DrawWireDisc(transform.position, Vector3.up, 0.3f); Gizmos.color = Color.blue; Vector3 centerPos = transform.position; Vector3 centerPosUp = centerPos + Vector3.up * 0.14f; if (m_debugArrowMesh != null) { Gizmos.DrawMesh(m_debugArrowMesh, centerPos, Quaternion.LookRotation(transform.forward, Vector3.up)); } else { Gizmos.DrawSphere(centerPos, 0.06f); Gizmos.DrawLine(centerPos, centerPosUp); DrawArrow.ForGizmo(centerPosUp, transform.forward * 0.2f, 0.05f, 20f); } doneCenter = 1; Vector3 point1; Vector3 point2; if (m_transformGoal) { point1 = transform.position + m_debugDesiredGoal[i - 1].Position; point2 = transform.position + m_debugDesiredGoal[i].Position; } else { point1 = transform.TransformPoint(m_debugDesiredGoal[i - 1].Position); point2 = transform.TransformPoint(m_debugDesiredGoal[i].Position); } Gizmos.color = new Color(0f, 0.5f, 0f); Gizmos.DrawLine(centerPos, point1); Gizmos.color = Color.green; Gizmos.DrawLine(centerPos, point2); } Gizmos.color = Color.green; } Vector3 pointPos; float angle = curPoint.FacingAngle * Mathf.Deg2Rad; var direction = new Vector3(0.2f * Mathf.Sin(angle), 0f, 0.2f * Mathf.Cos(angle)); if (m_transformGoal) { pointPos = transform.position + curPoint.Position; } else { pointPos = transform.TransformPoint(curPoint.Position); direction = transform.TransformDirection(direction); } if (m_debugArrowMesh != null) { Gizmos.DrawMesh(m_debugArrowMesh, pointPos, Quaternion.LookRotation(direction, Vector3.up)); } else { Gizmos.DrawSphere(pointPos, 0.06f); Gizmos.DrawLine(pointPos, new Vector3(pointPos.x, pointPos.y + 0.14f, pointPos.z)); DrawArrow.ForGizmo(new Vector3(pointPos.x, pointPos.y + 0.14f, pointPos.z), direction, 0.05f, 20f); } if (i != 0) { Vector3 pointPosPrev; if (m_transformGoal) { pointPosPrev = transform.position + m_debugDesiredGoal[i - 1].Position; } else { pointPosPrev = transform.TransformPoint(m_debugDesiredGoal[i - 1].Position); } if (doneCenter != 1) { Gizmos.DrawLine(pointPos, pointPosPrev); } else { doneCenter = 2; } } } } if (m_debugCurrentPose) { Gizmos.color = Color.yellow; for (int i = 0; i < m_curInterpolatedPose.JointsData.Length; ++i) { Vector3 pos = transform.TransformPoint(m_curInterpolatedPose.JointsData[i].Position); Gizmos.DrawWireSphere(pos, 0.04f); DrawArrow.ForGizmo(pos, transform.TransformVector(m_curInterpolatedPose.JointsData[i].Velocity), 0.05f); } } if (m_debugChosenTrajectory) { TrajectoryPoint[] curGoal = CurrentAnimData.Poses[m_curInterpolatedPose.PoseId].Trajectory; int doneCenter = 0; for (int i = 0; i < curGoal.Length; ++i) { TrajectoryPoint curPoint = curGoal[i]; if (CurrentAnimData.PosePredictionTimes[i] < 0f) { Gizmos.color = new Color(0.5f, 0f, 0f); } else { if (doneCenter == 0) { Gizmos.color = Color.cyan; Handles.color = Color.cyan; Handles.DrawWireDisc(transform.position, Vector3.up, 0.3f); DrawArrow.ForGizmo(transform.position, transform.TransformVector(m_curInterpolatedPose.LocalVelocity), 0.4f, 20f); Gizmos.color = Color.blue; Vector3 centerPos = transform.position; Vector3 centerPosUp = centerPos + Vector3.up * 0.14f; if (m_debugArrowMesh != null) { Gizmos.DrawMesh(m_debugArrowMesh, centerPos, Quaternion.LookRotation(transform.forward, Vector3.up)); } else { Gizmos.DrawSphere(centerPos, 0.06f); Gizmos.DrawLine(centerPos, centerPosUp); DrawArrow.ForGizmo(centerPosUp, transform.forward * 0.2f, 0.05f, 20f); } doneCenter = 1; Gizmos.color = new Color(0.5f, 0f, 0f); Gizmos.DrawLine(centerPos, transform.TransformPoint(curGoal[i - 1].Position)); Gizmos.color = Color.red; Gizmos.DrawLine(centerPos, transform.TransformPoint(curGoal[i].Position)); } Gizmos.color = Color.red; } Vector3 pointPos = transform.TransformPoint(curPoint.Position); float angle = curPoint.FacingAngle * Mathf.Deg2Rad; Vector3 direction = transform.TransformDirection(new Vector3(0.2f * Mathf.Sin(angle), 0f, 0.2f * Mathf.Cos(angle))); if (m_debugArrowMesh != null) { Gizmos.DrawMesh(m_debugArrowMesh, pointPos, Quaternion.LookRotation(direction, Vector3.up)); } else { Gizmos.DrawSphere(pointPos, 0.06f); Gizmos.DrawLine(pointPos, new Vector3(pointPos.x, pointPos.y + 0.14f, pointPos.z)); DrawArrow.ForGizmo(new Vector3(pointPos.x, pointPos.y + 0.14f, pointPos.z), direction, 0.05f, 20f); } if (i != 0) { if (doneCenter != 1) { Gizmos.DrawLine(pointPos, transform.TransformPoint(curGoal[i - 1].Position)); } else { doneCenter = 2; } } } } } else { //Draw Chosen Pose Trajectory if (m_debugPoses && m_debugPosesActive && CurrentAnimData != null) { if (m_debugPoseId >= CurrentAnimData.Poses.Length) { m_debugPoseId = CurrentAnimData.Poses.Length - 1; } PoseData pose = CurrentAnimData.Poses[m_debugPoseId]; UpdatePoseDebug(); Gizmos.color = Color.yellow; for (int i = 0; i < pose.JointsData.Length; ++i) { Vector3 pos = transform.TransformPoint(pose.JointsData[i].Position); Gizmos.DrawWireSphere(pos, 0.04f); DrawArrow.ForGizmo(pos, transform.TransformVector( pose.JointsData[i].Velocity), 0.05f); } if (m_debugPoseId < CurrentAnimData.Poses.Length) { m_chosenPose = CurrentAnimData.Poses[m_debugPoseId]; } else { m_chosenPose = CurrentAnimData.Poses[CurrentAnimData.Poses.Length - 1]; } TrajectoryPoint[] curGoal = m_chosenPose.Trajectory; int doneCenter = 0; for (int i = 0; i < curGoal.Length; ++i) { TrajectoryPoint curPoint = curGoal[i]; if (CurrentAnimData.PosePredictionTimes[i] < 0f) { Gizmos.color = new Color(0.5f, 0f, 0f); } else { if (doneCenter == 0) { Gizmos.color = Color.cyan; Handles.color = Color.cyan; Handles.DrawWireDisc(transform.position, Vector3.up, 0.3f); DrawArrow.ForGizmo(transform.position, transform.TransformVector(m_chosenPose.LocalVelocity), 0.4f, 20f); Gizmos.color = Color.blue; Vector3 centerPos = transform.position; Vector3 centerPosUp = centerPos + Vector3.up * 0.14f; if (m_debugArrowMesh != null) { Gizmos.DrawMesh(m_debugArrowMesh, centerPos, Quaternion.LookRotation(transform.forward, Vector3.up)); } else { Gizmos.DrawSphere(centerPos, 0.06f); Gizmos.DrawLine(centerPos, centerPosUp); DrawArrow.ForGizmo(centerPosUp, transform.forward * 0.2f, 0.05f, 20f); } doneCenter = 1; Gizmos.color = new Color(0.5f, 0f, 0f); Gizmos.DrawLine(centerPos, transform.TransformPoint(curGoal[i - 1].Position)); Gizmos.color = Color.red; Gizmos.DrawLine(centerPos, transform.TransformPoint(curGoal[i].Position)); } Gizmos.color = Color.red; } Vector3 pointPos = transform.TransformPoint(curPoint.Position); float angle = curPoint.FacingAngle * Mathf.Deg2Rad; Gizmos.DrawSphere(pointPos, 0.06f); DrawArrow.ForGizmo(new Vector3(pointPos.x, pointPos.y + 0.14f, pointPos.z), transform.TransformDirection(new Vector3(0.2f * Mathf.Sin(angle), 0f, 0.2f * Mathf.Cos(angle))), 0.05f, 20f); Gizmos.DrawLine(pointPos, new Vector3(pointPos.x, pointPos.y + 0.14f, pointPos.z)); if (i != 0) { if (doneCenter != 1) { Gizmos.DrawLine(pointPos, transform.TransformPoint(curGoal[i - 1].Position)); } else { doneCenter = 2; } } } } } }
//============================================================================================ /** * @brief Prints all used pose analytics data to the console * *********************************************************************************************/ public void DumpUsedPoseAnalytics() { if (m_usedPoses != null) { AnimationClip currentClip = null; int maxUseCount = 0; foreach (KeyValuePair <int, int> kvp in m_usedPoses) { if (kvp.Value > maxUseCount) { maxUseCount = kvp.Value; } } StringBuilder clipStats = new StringBuilder(); int uniqueUsedPoses = 0; int clipTotalUsedPoses = 0; MxMAnimData curAnimData = m_targetAnimator.CurrentAnimData; for (int index = 0; index < curAnimData.Poses.Length; index++) { PoseData animDataPose = curAnimData.Poses[index]; int clipId = 0; switch (animDataPose.AnimType) { case EMxMAnimtype.Composite: { clipId = curAnimData.Composites[animDataPose.AnimId].ClipIdA; } break; case EMxMAnimtype.BlendSpace: { clipId = curAnimData.BlendSpaces[animDataPose.AnimId].ClipIds[0]; } break; case EMxMAnimtype.Clip: { clipId = curAnimData.ClipsData[animDataPose.AnimId].ClipId; } break; } var newClip = curAnimData.Clips[clipId] != currentClip; if (newClip) { if (currentClip) { clipStats.Append(clipTotalUsedPoses.ToString()); Debug.Log(clipStats.ToString(), currentClip); clipTotalUsedPoses = 0; clipStats.Clear(); } currentClip = curAnimData.Clips[clipId]; clipStats.Append(currentClip.name); } int usedCount; if (m_usedPoses.TryGetValue(index, out usedCount)) { uniqueUsedPoses++; clipTotalUsedPoses++; int usedDigit = usedCount * 9 / maxUseCount; clipStats.Append(usedDigit); } else { clipStats.Append('_'); } } if (currentClip) { clipStats.Append(clipTotalUsedPoses.ToString()); Debug.Log(clipStats.ToString(), currentClip); clipTotalUsedPoses = 0; clipStats.Clear(); } Debug.Log(string.Format("used {0}/{1} poses ", uniqueUsedPoses, curAnimData.Poses.Length)); } }
//============================================================================================ /** * @brief Sets up a single clip in an input slot on the motion matching mixer. * * @param [ref PoseData] a_pose - a reference to the pose data containing the information on the clip * @param [int] a_slotId - the input slot id to set the clip up in. * @param [float] a_speedMod - the speed modification on the clip playback (default 1f) * *********************************************************************************************/ private void SetupClipInSlot(ref PoseData a_pose, int a_slotId, float a_speedMod = 1f) { ref MxMPlayableState playableState = ref m_animationStates[a_slotId];