/// <summary> /// Selects the new bone and deselects the old one /// </summary> /// <param name="rBone"></param> private void SelectBone(BoneControllerBone rBone) { // Flag the old bone as deselected if (mSelectedBone != null) { mSelectedBone.OnDisable(); } // Flag the new bone as selected if (rBone == null) { mSelectedBones.Clear(); } else if (mSelectedBones.Count > 0) { mSelectedBones[0] = rBone; } else { mSelectedBones.Add(rBone); } mSelectedBone = rBone; if (mSelectedBone != null) { mSelectedBone.OnEnable(); } }
/// <summary> /// When the skeleton is reloaded, we'll have different references for /// the same bone. So, we need to update our references to point to the skeleton /// </summary> public virtual void RefreshBones() { for (int i = mBones.Count - 1; i >= 0; i--) { bool lFound = false; string lBoneName = mBones[i].Name; string lBoneParentName = (mBones[i].Parent != null ? mBones[i].Parent.Name : ""); BoneControllerBone lNewBone = mSkeleton.GetBone(lBoneName) as BoneControllerBone; if (lNewBone != null) { string lNewBoneParentName = (lNewBone.Parent != null ? lNewBone.Parent.Name : ""); if (lNewBoneParentName == lBoneParentName) { lFound = true; mBones[i] = lNewBone; } } // If we didn't find a matching bone, we need to remove the // old one. Otherwise, the user may think everything is ok if (!lFound) { RemoveBone(i, false); Debug.LogWarning("BoneControllerMotor.RefreshBones() - Matching bone for " + lBoneName + " was not found. " + (Name.Length > 0 ? Name : this.GetType().Name) + " removing old bone."); } } }
/// <summary> /// Renders out bone details specific to the motor /// </summary> /// <param name="rIndex"></param> /// <param name="rBone"></param> /// <returns></returns> protected override bool RenderBone(int rIndex, BoneControllerBone rBone) { bool lIsDirty = false; #if UNITY_EDITOR while (rIndex >= _BoneInfo.Count) { PoseMotorBone lBoneInfo = new PoseMotorBone(); _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); } // Set the bone enable bool lNewIsEnabled = EditorGUILayout.Toggle(new GUIContent("Is Enabled", "Determines if this bone pose is enabled."), _BoneInfo[rIndex].IsEnabled); if (lNewIsEnabled != _BoneInfo[rIndex].IsEnabled) { lIsDirty = true; _BoneInfo[rIndex].IsEnabled = lNewIsEnabled; } // Set the bone weight float lNewWeight = EditorGUILayout.FloatField(new GUIContent("Weight", "Determines how much the motor effects vs. currently animated rotation."), _BoneInfo[rIndex].Weight); if (lNewWeight != _BoneInfo[rIndex].Weight) { lIsDirty = true; _BoneInfo[rIndex].Weight = lNewWeight; } // Set the bone lerp float lNewRotationLerp = EditorGUILayout.FloatField(new GUIContent("Rotation Lerp", "Determines how quickly we rotate to the target."), _BoneInfo[rIndex].RotationLerp); if (lNewRotationLerp != _BoneInfo[rIndex].RotationLerp) { lIsDirty = true; _BoneInfo[rIndex].RotationLerp = lNewRotationLerp; } // Render inspector controls based on the joint type if (rBone != null) { PoseMotorBone lBoneInfo = _BoneInfo[rIndex]; lBoneInfo.IsDirty = false; if (rBone.Joint == null) { rBone.OnInspectorManipulatorGUI(lBoneInfo); } else { rBone.Joint.OnInspectorManipulatorGUI(lBoneInfo); } // Update the actual dirty flag if (_BoneInfo[rIndex].IsDirty) { lIsDirty = true; } } #endif return(lIsDirty); }
/// <summary> /// Spreads the impact through to the children /// </summary> /// <param name="rParent">Parent whose children will be impacted</param> /// <param name="rChange">Change being applied</param> /// <param name="rDepthRemaining">Determines how deep we go</param> /// <param name="rDamping">Determines how much we dull the change on each iteration</param> private void ApplyImpactToChildren(BoneControllerBone rParent, Vector3 rChange, int rDepthRemaining, float rDamping) { rDepthRemaining--; rChange = rChange * rDamping; for (int i = 0; i < rParent.Children.Count; i++) { BoneControllerBone lChild = rParent.Children[i]; if (AddTemporaryBone(lChild)) { // We're going to temper the child response based on the // length compared to the original. float lCompare = Mathf.Min(lChild.Length / rParent.Length, 1f); rChange = rChange * lCompare; mActiveBoneInfo[lChild].State = 1; mActiveBoneInfo[lChild].Time = _ImpactTime; mActiveBoneInfo[lChild].Change = rChange; if (rDepthRemaining > 0) { ApplyImpactToChildren(lChild, rChange, rDepthRemaining, rDamping); } } } }
/// <summary> /// Adds a rotation to the specified bone /// </summary> /// <param name="rBone"></param> /// <param name="rSwing"></param> /// <param name="rTwist"></param> public void AddRotation(BoneControllerBone rBone, Quaternion rSwing, Quaternion rTwist) { Rotations.Add(rBone, rSwing * rTwist); Swings.Add(rBone, rSwing); Twists.Add(rBone, rTwist); }
/// <summary> /// Add information specific to the bone. This helps us manage the bone /// </summary> /// <param name="rBone"></param> private void AddBoneInfo(int rIndex, BoneControllerBone rBone) { // Remember we want one more bone info than // we have bones. while (_BoneInfo.Count <= rIndex) { _BoneInfo.Add(new BoneChainDragBone()); } if (rBone != null) { BoneChainDragBone lBoneInfo = _BoneInfo[rIndex]; // Reset the values lBoneInfo.Position = rBone._Transform.position; lBoneInfo.PrevPosition = rBone._Transform.position; lBoneInfo.RotationTarget = rBone.Transform.rotation * rBone.ToBoneForward; lBoneInfo.Rotation = lBoneInfo.RotationTarget; lBoneInfo.Length = rBone.Length; CapsuleCollider lCollider = rBone._Transform.GetComponent <CapsuleCollider>(); if (lCollider != null) { lBoneInfo.Length = lCollider.height * rBone._Transform.lossyScale.y; } // Record the transforms for collision testing if (!mBoneTransforms.Contains(rBone._Transform)) { mBoneTransforms.Add(rBone._Transform); } } }
/// <summary> /// Renders out bone details specific to the motor /// </summary> /// <param name="rIndex"></param> /// <param name="rBone"></param> /// <returns></returns> protected override bool RenderBone(int rIndex, BoneControllerBone rBone) { bool lIsDirty = false; #if UNITY_EDITOR while (rIndex >= _BoneInfo.Count) { ImpactMotorBone lBoneInfo = new ImpactMotorBone(); if (rBone != null && rBone._Transform != null) { lBoneInfo.EndPosition = rBone._Transform.position + (rBone._Transform.rotation * (rBone.BoneForward * rBone.Length)); } _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); } // Set the bone weight float lOldBoneWeight = (rBone == null ? 0 : _BoneInfo[rIndex].Weight); float lNewBoneWeight = EditorGUILayout.FloatField(new GUIContent("Rotation Weight", "Normalized weight this bone will be responsible for."), lOldBoneWeight); if (lNewBoneWeight != lOldBoneWeight) { if (rBone != null) { lIsDirty = true; _BoneInfo[rIndex].Weight = lNewBoneWeight; } } #endif return(lIsDirty); }
/// <summary> /// Renders out bone details specific to the motor /// </summary> /// <param name="rIndex"></param> /// <param name="rBone"></param> /// <returns></returns> protected override bool RenderBone(int rIndex, BoneControllerBone rBone) { bool lIsDirty = false; #if UNITY_EDITOR // Set the collision flag bool lNewUseBindPosition = EditorGUILayout.Toggle(new GUIContent("Use Bind Position", "Determines if the movement is based on the current position or bind position."), _BoneInfo[rIndex].UseBindPosition); if (lNewUseBindPosition != _BoneInfo[rIndex].UseBindPosition) { lIsDirty = true; _BoneInfo[rIndex].UseBindPosition = lNewUseBindPosition; } // Set the bone yaw float lNewRotationLerp = EditorGUILayout.FloatField(new GUIContent("Drag Lerp", "Determines how quickly we rotate to the target."), _BoneInfo[rIndex].RotationLerp); if (lNewRotationLerp != _BoneInfo[rIndex].RotationLerp) { lIsDirty = true; _BoneInfo[rIndex].RotationLerp = lNewRotationLerp; } // Set the bone yaw float lNewUntwistLerp = EditorGUILayout.FloatField(new GUIContent("Untwist Lerp", "Determines how quickly we return to the bind twist. Setting to 0 disables untwisting."), _BoneInfo[rIndex].UntwistLerp); if (lNewUntwistLerp != _BoneInfo[rIndex].UntwistLerp) { lIsDirty = true; _BoneInfo[rIndex].UntwistLerp = lNewUntwistLerp; } #endif return(lIsDirty); }
/// <summary> /// Allows the motor to process bones temporarily and removes them once done /// </summary> /// <param name="rBone"></param> protected virtual bool AddTemporaryBone(BoneControllerBone rBone) { if (rBone == null || rBone._Transform == null) { return(false); } if (rBone.Length < _MinBoneLength) { return(false); } if (rBone == mSkeleton.Root) { return(false); } if (mActiveBoneInfo.ContainsKey(rBone)) { return(true); } ImpactMotorBone lBoneInfo = ImpactMotorBone.Allocate(); lBoneInfo.IsTemporary = true; lBoneInfo.EndPosition = rBone._Transform.position + (rBone._Transform.rotation * (rBone.BoneForward * rBone.Length)); mActiveBoneInfo.Add(rBone, lBoneInfo); return(true); }
/// <summary> /// Handle used to twist the bone /// </summary> /// <returns></returns> public static bool JointTwistHandle(BoneControllerBone rBone, ref Quaternion rTwist) { bool lIsDirty = false; #if UNITY_EDITOR if (rBone != null) { Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwing = rBone.WorldBindRotation * rBone.Swing; Color lHandleColor = Handles.color; float lHandleSnapSettingsRotation = 1f; float lHandleScale = 0.1f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(lWorldPosition) * HandlesHelper.HandleScale; } // Track the rotation when we start using the handle. Event lEvent = Event.current; switch (lEvent.type) { case EventType.MouseDown: StartRotation = rTwist; break; } // ROLL Handles.color = InactiveColor; Handles.DrawWireArc(lWorldPosition, lWorldSwing * Vector3.forward, lWorldSwing * Vector3.right, 360f, lHandleScale); Handles.color = Handles.zAxisColor; Quaternion lRotation = Quaternion.identity; Quaternion lNewRotation = Handles.Disc(lRotation, lWorldPosition, lWorldSwing * Vector3.forward, lHandleScale, true, lHandleSnapSettingsRotation); if (lNewRotation != lRotation) { float lTwistAngle = 0f; Vector3 lTwistAxis = Vector3.zero; lNewRotation.ToAngleAxis(out lTwistAngle, out lTwistAxis); // When the rotation exceeds 360 degress, our angle starts to // reverse its trending. We can fix that here. float lTwistInvertTest = Vector3.Angle(lWorldSwing * Vector3.forward, lTwistAxis); float lAngle = (Mathf.Abs(lTwistInvertTest) < 1f ? 1 : -1) * lTwistAngle; // We return Unity Forward values rTwist = StartRotation * Quaternion.AngleAxis(lAngle, Vector3.forward); // Flag the skeleton as dirty lIsDirty = true; } // Reset Handles.color = lHandleColor; } #endif return lIsDirty; }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rIndex">Index position of the new bone</param> /// <param name="rBone">New bone that was added</param> public override void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { base.AddBone(rBone, rIncludeChildren); PoseMotorBone lBoneInfo = new PoseMotorBone(); _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); }
/// <summary> /// Renders a bone using the handle draw functions /// </summary> /// <param name="rBone"></param> /// <param name="rColor"></param> public static void DrawBoneCollider(BoneControllerBone rBone, Color rColor) { #if UNITY_EDITOR if (rBone == null) { return; } if (rBone._Transform == null) { return; } if (rBone._ColliderSize.x == 0f && rBone._ColliderSize.y == 0f && rBone._ColliderSize.z == 0f) { return; } Color lHandleColor = Handles.color; Handles.color = rColor; // Sphere type if (rBone._ColliderType == 1) { Handles.DrawWireDisc(rBone._Transform.position, rBone._Transform.rotation * rBone._BoneForward, rBone._ColliderSize.x); Handles.DrawWireDisc(rBone._Transform.position, rBone._Transform.rotation * rBone._BoneUp, rBone._ColliderSize.x); Handles.DrawWireDisc(rBone._Transform.position, rBone._Transform.rotation * rBone._BoneRight, rBone._ColliderSize.x); } // Box type else { float lHalfX = rBone._ColliderSize.x / 2f; float lHalfY = rBone._ColliderSize.y / 2f; float lZ = rBone.ColliderSize.z; Vector3 lPosition = rBone._Transform.position; Quaternion lRotation = rBone._Transform.rotation * rBone._ToBoneForward; Vector3 lPoint0 = lRotation * new Vector3(-lHalfX, lHalfY, 0f); Vector3 lPoint1 = lRotation * new Vector3(-lHalfX, -lHalfY, 0f); Vector3 lPoint2 = lRotation * new Vector3(lHalfX, lHalfY, 0f); Vector3 lPoint3 = lRotation * new Vector3(lHalfX, -lHalfY, 0f); Vector3 lPoint4 = lRotation * new Vector3(-lHalfX, lHalfY, lZ); Vector3 lPoint5 = lRotation * new Vector3(-lHalfX, -lHalfY, lZ); Vector3 lPoint6 = lRotation * new Vector3(lHalfX, lHalfY, lZ); Vector3 lPoint7 = lRotation * new Vector3(lHalfX, -lHalfY, lZ); Handles.DrawLine(lPosition + lPoint0, lPosition + lPoint1); Handles.DrawLine(lPosition + lPoint0, lPosition + lPoint2); Handles.DrawLine(lPosition + lPoint2, lPosition + lPoint3); Handles.DrawLine(lPosition + lPoint1, lPosition + lPoint3); Handles.DrawLine(lPosition + lPoint4, lPosition + lPoint5); Handles.DrawLine(lPosition + lPoint4, lPosition + lPoint6); Handles.DrawLine(lPosition + lPoint6, lPosition + lPoint7); Handles.DrawLine(lPosition + lPoint5, lPosition + lPoint7); Handles.DrawLine(lPosition + lPoint0, lPosition + lPoint4); Handles.DrawLine(lPosition + lPoint1, lPosition + lPoint5); Handles.DrawLine(lPosition + lPoint2, lPosition + lPoint6); Handles.DrawLine(lPosition + lPoint3, lPosition + lPoint7); } Handles.color = lHandleColor; #endif }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rIndex">Index position of the new bone</param> /// <param name="rBone">New bone that was added</param> public override void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { base.AddBone(rBone, rIncludeChildren); while (_BoneInfo.Count < mBones.Count) { FootPlacementMotorBone lBoneInfo = new FootPlacementMotorBone(); _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); } }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been deleted /// </summary> /// <param name="rIndex">Index position the bone was at</param> /// <param name="rBone">Bone that was deleted</param> protected override void RemoveBone(BoneControllerBone rBone, bool rIncludeChildren) { int lIndex = mBones.IndexOf(rBone); if (lIndex >= 0) { _BoneInfo.RemoveAt(lIndex); base.RemoveBone(rBone, rIncludeChildren); } }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rIndex">Index position of the new bone</param> /// <param name="rBone">New bone that was added</param> public override void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { if (rBone == null || rBone._Transform == null) { return; } base.AddBone(rBone, rIncludeChildren); AddBoneInfo(mBones.IndexOf(rBone), rBone); }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been deleted /// </summary> /// <param name="rIndex">Index position the bone was at</param> /// <param name="rBone">Bone that was deleted</param> protected virtual void RemoveBone(int rBoneIndex, bool rIncludeChildren) { if (rBoneIndex < 0 || rBoneIndex >= mBones.Count) { return; } BoneControllerBone rBone = mBones[rBoneIndex]; RemoveBone(rBone, rIncludeChildren); }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rIndex">Index position of the new bone</param> /// <param name="rBone">New bone that was added</param> public override void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { base.AddBone(rBone, rIncludeChildren); RotationBone lBoneInfo = new RotationBone(); if (rBone != null) { lBoneInfo.Euler = (rBone._Transform.rotation * rBone._ToBoneForwardInv).eulerAngles; } _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); }
/// <summary> /// Handle used to twist the bone /// </summary> /// <returns></returns> public static bool JointSwingAxisHandle(BoneControllerBone rBone, Vector3 rAxis, ref Quaternion rSwing) { bool lIsDirty = false; #if UNITY_EDITOR Quaternion lWorldBind = rBone.WorldBindRotation; Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwing = rBone.WorldBindRotation * rSwing; Vector3 lWorldAxis = lWorldBind * rAxis; Color lHandleColor = Handles.color; float lHandleSnapSettingsRotation = 1f; float lHandleScale = 0.1f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(rBone.Transform.position) * HandlesHelper.HandleScale; } // Render the backs Handles.color = InactiveColor; //Handles.DrawWireArc(lWorldPosition, lWorldSwing * Vector3.forward, lWorldSwing * -Vector3.up, 360f, lHandleSize); //Handles.DrawWireArc(lWorldPosition, lWorldSwing * Vector3.right, lWorldSwing * -Vector3.up, 360f, lHandleSize); // Track the rotation when we start using the handle. Event lEvent = Event.current; switch (lEvent.type) { case EventType.MouseDown: StartRotation = lWorldSwing; break; } // Render the rotation handle for the z axis Handles.color = new Color(0.94118f, 0.39608f, 0.13333f, 1f); Quaternion lNewRotationZ = Handles.Disc(StartRotation, lWorldPosition, lWorldAxis, lHandleScale, false, lHandleSnapSettingsRotation); if (lNewRotationZ != StartRotation) { rSwing = rBone.TransformWorldRotationToLocalRotation(lNewRotationZ); lIsDirty = true; } // Reset Handles.color = lHandleColor; #endif return lIsDirty; }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rIndex">Index position of the new bone</param> /// <param name="rBone">New bone that was added</param> public override void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { base.AddBone(rBone, rIncludeChildren); ImpactMotorBone lBoneInfo = new ImpactMotorBone(); lBoneInfo.IsTemporary = false; if (rBone != null && rBone._Transform != null) { lBoneInfo.EndPosition = rBone._Transform.position + (rBone._Transform.rotation * (rBone.BoneForward * rBone.Length)); } _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rIndex">Index position of the new bone</param> /// <param name="rBone">New bone that was added</param> public virtual void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { if (rBone == null || !mBones.Contains(rBone)) { mBones.Add(rBone); } if (rBone != null && rIncludeChildren) { for (int i = 0; i < rBone.Children.Count; i++) { AddBone(rBone.Children[i], rIncludeChildren); } } }
/// <summary> /// Renders out a list of bones that belong to the skeleton /// </summary> private bool RenderBoneList(string rNameFilter, bool rSelectedFilter) { // Cycle through the motions BoneControllerBone lBone = mSkeleton.Root; if (lBone == null) { EditorGUILayout.HelpBox("Select a root transform above that will represent the root of your skeleton. Typically this is the 'hips'.", MessageType.Info); return(false); } else { return(RenderBone(lBone, 0, rNameFilter, rSelectedFilter)); } }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been deleted /// </summary> /// <param name="rIndex">Index position the bone was at</param> /// <param name="rBone">Bone that was deleted</param> protected virtual void RemoveBone(BoneControllerBone rBone, bool rIncludeChildren) { if (mBones.Contains(rBone)) { mBones.Remove(rBone); } if (rBone != null && rIncludeChildren) { for (int i = 0; i < rBone.Children.Count; i++) { RemoveBone(rBone.Children[i], rIncludeChildren); } } }
/// <summary> /// Retrieves the bone index if it exists or -1 if it doesn't. /// </summary> /// <param name="rBoneID"></param> /// <returns></returns> public int GetBoneIndex(HumanBodyBones rBoneID) { if (mBones == null || mBones.Count == 0) { return(-1); } BoneControllerBone lBone = mSkeleton.GetBone(rBoneID) as BoneControllerBone; if (lBone != null) { return(mBones.IndexOf(lBone)); } return(-1); }
/// <summary> /// This function renders out the handles that allow us to edit the twist limits. It /// isn't actually meant to change the twist itself /// </summary> /// <param name="rBone"></param> /// <param name="rMinAngle"></param> /// <param name="rMaxAngle"></param> /// <returns></returns> public static bool JointSwingAxisLimitsHandle(BoneControllerBone rBone, Vector3 rAxis, float rMinAngle, float rMaxAngle) { bool lIsDirty = false; #if UNITY_EDITOR Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwing = rBone.WorldBindRotation * rBone.Swing; Color lGUIColor = GUI.color; Color lHandleColor = Handles.color; float lHandleScale = 0.2f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(rBone.Transform.position) * HandlesHelper.HandleScale; } // Render the border of the angles Quaternion lMinRotation = Quaternion.AngleAxis(rMinAngle, rAxis); Quaternion lMaxRotation = Quaternion.AngleAxis(rMaxAngle, rAxis); // We don't use the world swing since we want the rotation based on the bind position Vector3 lMinOffset = rBone.WorldBindRotation * lMinRotation * Vector3.forward * lHandleScale; Vector3 lMaxOffset = rBone.WorldBindRotation * lMaxRotation * Vector3.forward * lHandleScale; Handles.color = new Color(0.94118f, 0.39608f, 0.13333f, 1f); Handles.DrawLine(lWorldPosition, lWorldPosition + lMinOffset); Handles.DrawLine(lWorldPosition, lWorldPosition + lMaxOffset); // Render the solid of the angles Handles.color = new Color(0.94118f, 0.39608f, 0.13333f, 0.1f); Handles.DrawSolidArc(lWorldPosition, rBone.WorldBindRotation * rAxis, (rBone.WorldBindRotation * lMinRotation * Vector3.forward), Mathf.Abs(rMinAngle) + rMaxAngle, lHandleScale); // Draw text GUI.color = new Color(0.72549f, 0.30588f, 0.10588f, 1f); Handles.Label(lWorldPosition + lMinOffset, "min:\r\n" + rMinAngle.ToString("0.00")); Handles.Label(lWorldPosition + lMaxOffset, "max:\r\n" + rMaxAngle.ToString("0.00")); Vector3 lDirectionAxis = Vector3.Cross(rAxis, rBone._BindRotation * rBone._BoneForward); float lSwingAngle = Vector3Ext.SignedAngle(lDirectionAxis, rBone.Swing * lDirectionAxis, rAxis); Handles.Label(lWorldPosition + (lWorldSwing * (Vector3.forward * (lHandleScale * 1.3f))), " " + lSwingAngle.ToString("0.00")); // Reset GUI.color = lGUIColor; Handles.color = lHandleColor; #endif return lIsDirty; }
/// <summary> /// This function renders out the handles that allow us to edit the twist limits. It /// isn't actually meant to change the twist itself /// </summary> /// <param name="rBone"></param> /// <param name="rMinAngle"></param> /// <param name="rMaxAngle"></param> /// <returns></returns> public static bool JointTwistLimitsHandle(BoneControllerBone rBone, ref float rMinAngle, ref float rMaxAngle) { bool lIsDirty = false; #if UNITY_EDITOR Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwingRotation = rBone.WorldBindRotation * rBone._Swing; Color lGUIColor = GUI.color; Color lHandleColor = Handles.color; float lHandleScale = 0.2f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(rBone.Transform.position) * HandlesHelper.HandleScale; } // Render the border of the angles float lTwistAngle = Vector3Ext.SignedAngle(Vector3.up, rBone._Twist * Vector3.up, Vector3.forward); Quaternion lActualRotation = Quaternion.AngleAxis(lTwistAngle, Vector3.forward); Quaternion lMinRotation = Quaternion.AngleAxis(rMinAngle, Vector3.forward); Quaternion lMaxRotation = Quaternion.AngleAxis(rMaxAngle, Vector3.forward); Vector3 lMinOffset = lWorldSwingRotation * lMinRotation * Vector3.up * lHandleScale; Vector3 lMaxOffset = lWorldSwingRotation * lMaxRotation * Vector3.up * lHandleScale; Handles.color = new Color(0.1f, 0.1f, 0.6f, 1f); Handles.DrawLine(lWorldPosition, lWorldPosition + lMinOffset); Handles.DrawLine(lWorldPosition, lWorldPosition + lMaxOffset); //Handles.DrawLine(lWorldPosition, lWorldPosition + (lWorldSwingRotation * rBone.Twist * Vector3.up * (lHandleScale * 1.1f))); // Render the solid of the angles Handles.color = new Color(0.25f, 0.60f, 0.95f, 0.1f); Handles.DrawSolidArc(lWorldPosition, rBone.Transform.rotation * rBone._BoneForward, lWorldSwingRotation * lMinRotation * Vector3.up, Mathf.Abs(rMinAngle) + rMaxAngle, lHandleScale); // Render text GUI.color = new Color(0.05f, 0.05f, 0.5f, 1f); Handles.Label(lWorldPosition + lMinOffset, "min:\r\n" + rMinAngle.ToString("0.00")); Handles.Label(lWorldPosition + lMaxOffset, "max:\r\n" + rMaxAngle.ToString("0.00")); Handles.Label(lWorldPosition + (lWorldSwingRotation * lActualRotation * Vector3.up * (lHandleScale * 1.3f)), " " + lTwistAngle.ToString("0.00")); // Reset GUI.color = lGUIColor; Handles.color = lHandleColor; #endif return lIsDirty; }
/// <summary> /// Handle used to twist the bone /// </summary> /// <returns></returns> public static bool JointSwingAxisHandle(BoneControllerBone rBone, Vector3 rAxis, IKBoneModifier rModifier) { bool lIsDirty = false; #if UNITY_EDITOR Vector3 lWorldPosition = rBone._Transform.position; Quaternion lWorldSwing = rBone.WorldBindRotation * rModifier.Swing; Vector3 lWorldAxis = rBone.WorldBindRotation * rAxis; Color lHandleColor = Handles.color; float lHandleSnapSettingsRotation = 1f; float lHandleScale = 0.1f; if (rBone.Skeleton.EditorAutoScaleHandles) { lHandleScale = HandleUtility.GetHandleSize(lWorldPosition) * HandlesHelper.HandleScale; } // Track the rotation when we start using the handle. Event lEvent = Event.current; switch (lEvent.type) { case EventType.MouseDown: StartRotation = lWorldSwing; break; } Quaternion lOldValue = StartRotation; Quaternion lNewValue = Quaternion.identity; // AXIS Handles.color = new Color(0.94118f, 0.39608f, 0.13333f, 1f); lNewValue = Handles.Disc(lOldValue, lWorldPosition, lWorldAxis, lHandleScale, false, lHandleSnapSettingsRotation); if (lNewValue != lOldValue) { rModifier.Swing = Quaternion.Inverse(rBone.WorldBindRotation) * lNewValue; lIsDirty = true; lOldValue = lNewValue; } // Reset Handles.color = lHandleColor; #endif return lIsDirty; }
/// <summary> /// Renders out bone details specific to the motor /// </summary> /// <param name="rIndex"></param> /// <param name="rBone"></param> /// <returns></returns> protected override bool RenderBone(int rIndex, BoneControllerBone rBone) { bool lIsDirty = false; #if UNITY_EDITOR while (rIndex >= _BoneInfo.Count) { FootPlacementMotorBone lBoneInfo = new FootPlacementMotorBone(); _BoneInfo.Insert(mBones.IndexOf(rBone), lBoneInfo); } // Set the bone weight float lNewWeight = EditorGUILayout.FloatField(new GUIContent("Weight", "Determines how much the motor effects vs. currently animated rotation."), _BoneInfo[rIndex].Weight); if (lNewWeight != _BoneInfo[rIndex].Weight) { lIsDirty = true; _BoneInfo[rIndex].Weight = lNewWeight; } // Set the bone yaw float lNewRotationLerp = EditorGUILayout.FloatField(new GUIContent("Rotation Lerp", "Determines how quickly we rotate to the target."), _BoneInfo[rIndex].RotationLerp); if (lNewRotationLerp != _BoneInfo[rIndex].RotationLerp) { lIsDirty = true; _BoneInfo[rIndex].RotationLerp = lNewRotationLerp; } // Axis to rotate around Vector3 lNewBendAxis = EditorGUILayout.Vector3Field(new GUIContent("Support Axis", "Axis used for rotating the bone."), _BoneInfo[rIndex].BendAxis); if (lNewBendAxis != _BoneInfo[rIndex].BendAxis) { lIsDirty = true; _BoneInfo[rIndex].BendAxis = lNewBendAxis; } // Final twist to apply to the bone after it's rotated float lNewTwist = EditorGUILayout.FloatField(new GUIContent("Final Twist Adjust", "Twist applied to the bone after movement."), _BoneInfo[rIndex].Twist); if (lNewTwist != _BoneInfo[rIndex].Twist) { lIsDirty = true; _BoneInfo[rIndex].Twist = lNewTwist; } #endif return(lIsDirty); }
/// <summary> /// Associates the bone and allows for any setup /// </summary> /// <param name="rBone">Bone the joint is tied to</param> public override void Initialize(BoneControllerBone rBone) { base.Initialize(rBone); //Log.ConsoleWrite("SwingPointAndTwistJoint.Initialize"); // Use the default boundary points if we need to if (BoundaryPoints == null || BoundaryPoints.Count == 0) { ClearBoundaryPoints(); } // Create the reach points with the existig boundary points else { BuildReachCones(); } }
/// <summary> /// Allows the motor to process any specific bone logic after /// a bone has been added /// </summary> /// <param name="rBone">New bone that was added</param> public override void AddBone(BoneControllerBone rBone, bool rIncludeChildren) { base.AddBone(rBone, rIncludeChildren); LookAtMotorBone lBoneInfo = new LookAtMotorBone(); lBoneInfo.Weight = 1 / Mathf.Pow(2, _BoneInfo.Count); if (rBone == null) { _BoneInfo.Add(lBoneInfo); } else { int lIndex = mBones.IndexOf(rBone); _BoneInfo.Insert(lIndex, lBoneInfo); } }
/// <summary> /// Allow the motor to control the scene GUI /// </summary> /// <returns></returns> public override bool OnSceneGUI(List <BoneControllerBone> rSelectedBones) { bool lIsDirty = false; #if UNITY_EDITOR // Force the selected bone based on the input list BoneControllerBone lSelectedBone = null; int lSelectedBoneIndex = -1; if (rSelectedBones.Count > 0) { lSelectedBone = rSelectedBones[0]; lSelectedBoneIndex = mBones.IndexOf(lSelectedBone); } // If we have a bone index, we can process the motor if (lSelectedBoneIndex >= 0 && lSelectedBoneIndex < mBones.Count) { PoseMotorBone lBoneInfo = _BoneInfo[lSelectedBoneIndex]; lBoneInfo.IsDirty = false; if (lSelectedBone != null) { // Local space rotators if (lSelectedBone.Joint == null) { lSelectedBone.OnSceneManipulatorGUI(lBoneInfo); } else { lSelectedBone.Joint.OnSceneManipulatorGUI(lBoneInfo); } // Update the actual dirty flag if (lBoneInfo.IsDirty) { lIsDirty = true; } } } #endif return(lIsDirty); }