void Selector(SerializedProperty property) { SpineBone attrib = (SpineBone)attribute; SkeletonData data = skeletonDataAsset.GetSkeletonData(true); if (data == null) { return; } GenericMenu menu = new GenericMenu(); menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); menu.AddSeparator(""); for (int i = 0; i < data.Bones.Count; i++) { string name = data.Bones[i].Name; if (name.StartsWith(attrib.startsWith)) { menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } menu.ShowAsContext(); }
/// <summary> /// Pushing spine segment from detected collider /// </summary> public void PushIfSegmentInsideCollider(SpineBone bone, ref Vector3 targetPoint) { // We must translate phantom/reference skeleton positions to true position in world space for collisions Vector3 offset; if (UseTruePosition) { Vector3 theTarget = targetPoint; Vector3 truePosition = bone.FinalPosition; offset = truePosition - theTarget + bone.transform.TransformVector(bone.ColliderOffset + OffsetAllColliders); } else { offset = bone.transform.TransformVector(bone.ColliderOffset + OffsetAllColliders); } if (!DetailedCollision) { for (int i = 0; i < CollidersDataToCheck.Count; i++) { if (CollidersDataToCheck[i].PushIfInside(ref targetPoint, bone.GetCollisionRadiusScaled(), offset)) { return; } } } else { for (int i = 0; i < CollidersDataToCheck.Count; i++) { CollidersDataToCheck[i].PushIfInside(ref targetPoint, bone.GetCollisionRadiusScaled(), offset); } } }
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType != SerializedPropertyType.String) { EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); return; } SpineBone attrib = (SpineBone)attribute; var dataProperty = property.serializedObject.FindProperty(attrib.dataField); if (dataProperty != null) { if (dataProperty.objectReferenceValue is SkeletonDataAsset) { skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; if (renderer != null) { skeletonDataAsset = renderer.skeletonDataAsset; } } else { EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); return; } } else if (property.serializedObject.targetObject is Component) { var component = (Component)property.serializedObject.targetObject; if (component.GetComponentInChildren <SkeletonRenderer>() != null) { var skeletonRenderer = component.GetComponentInChildren <SkeletonRenderer>(); skeletonDataAsset = skeletonRenderer.skeletonDataAsset; } } if (skeletonDataAsset == null) { EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); return; } position = EditorGUI.PrefixLabel(position, label); if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { Selector(property); } }
void Start() { if (speedReference == null) { speedReference = transform; } skeletonAnimation = GetComponent <SkeletonAnimation>(); bone = SpineBone.GetBone(boneName, skeletonAnimation); skeletonAnimation.UpdateLocal += UpdateLocal; lastPosition = speedReference.position; }
/// <summary> /// Should be called by inspector window, but you can also do it during playmode for procedural creations /// </summary> public void CreateSpineChain(Transform start, Transform end) { if (start == null || end == null) { Debug.Log("[SPINE ANIMATOR] Can't create spine chain if one of the bones is null!"); return; } List <Transform> fullChain = new List <Transform>(); Transform p = end; while (p != null) { if (p == start) { break; } fullChain.Add(p); p = p.parent; } if (p == null) { Debug.Log("[SPINE ANIMATOR] '" + start.name + "' is not child of '" + end.name + "' !"); return; } fullChain.Add(start); fullChain.Reverse(); SpineBones = new List <SpineBone>(); for (int i = 0; i < fullChain.Count; i++) { SpineBone bone = new SpineBone(fullChain[i]); SpineBones.Add(bone); } // After creating chain we checking if some auto corrections can be done }
/// <summary> /// Calculation target animation position for bone /// </summary> void CalculateTargetBonePosition(int index) { SpineBone otherBone = SpineBones[index - chainIndexDirection]; SpineBone currentBone = SpineBones[index]; // Target position calculation Vector3 targetPosition = otherBone.ProceduralPosition - (currentBone.ProceduralRotation * ModelForwardAxisScaled) * (currentBone.BoneLength * DistancesMultiplier); if (currentBone.Collide) { targetPosition += gravityScale; } #region Springiness Stuff if (Springiness > 0f) { if (!LastBoneLeading) { Vector3 backPosDiff = currentBone.ProceduralPosition - currentBone.PreviousPosition; Vector3 newPos = currentBone.ProceduralPosition; currentBone.PreviousPosition = currentBone.ProceduralPosition; newPos += backPosDiff * (1 - Mathf.Lerp(.05f, .25f, Springiness)); float restDistance = (otherBone.ProceduralPosition - newPos).magnitude; Matrix4x4 otherLocalToWorld = otherBone.transform.localToWorldMatrix; otherLocalToWorld.SetColumn(3, otherBone.ProceduralPosition); Vector3 restPos = otherLocalToWorld.MultiplyPoint3x4(currentBone.transform.localPosition); Vector3 diffPosVector = restPos - newPos; newPos += diffPosVector * Mathf.Lerp(0.05f, 0.2f, Springiness); diffPosVector = restPos - newPos; float distance = diffPosVector.magnitude; float maxDistance = restDistance * (1 - Mathf.Lerp(0.0f, 0.2f, Springiness)) * 2; if (distance > maxDistance) { newPos += diffPosVector * ((distance - maxDistance) / distance); } if (MaxStretching < 1f) { float dist = Vector3.Distance(currentBone.ProceduralPosition, newPos); if (dist > 0f) { float maxDist = currentBone.BoneLength * 4 * MaxStretching; if (dist > maxDist) { newPos = Vector3.Lerp(newPos, targetPosition, Mathf.InverseLerp(dist, 0f, maxDist)); } } } targetPosition = Vector3.Lerp(targetPosition, newPos, Mathf.Lerp(0.3f, 0.9f, Springiness)); } } #endregion // Target Position Stretching Prevention if (PosSmoother > 0f) { if (MaxStretching < 1f) { float dist = Vector3.Distance(currentBone.ProceduralPosition, targetPosition); if (dist > 0f) { float maxDist = currentBone.BoneLength * 4 * MaxStretching; if (dist > maxDist) { currentBone.ProceduralPosition = Vector3.Lerp(currentBone.ProceduralPosition, targetPosition, Mathf.InverseLerp(dist, 0f, maxDist)); } } } } if (UseCollisions) { if (currentBone.Collide) { PushIfSegmentInsideCollider(currentBone, ref targetPosition); } } // Smoothing Position if (PosSmoother == 0f) { currentBone.ProceduralPosition = targetPosition; } else { currentBone.ProceduralPosition = Vector3.LerpUnclamped(currentBone.ProceduralPosition, targetPosition, Mathf.LerpUnclamped(1f, unifiedDelta, PosSmoother)); } }
/// <summary> /// Calculating rotation with limitations and stuff for single segment /// </summary> void CalculateTargetBoneRotation(int index) { // Preparation for calculations SpineBone otherBone = SpineBones[index - chainIndexDirection]; SpineBone currentBone = SpineBones[index]; Quaternion targetLookRotation, backRotationRef; // Slithery calculation if (Slithery >= 1f) { backRotationRef = otherBone.ProceduralRotation; } else if (Slithery > 0f) { backRotationRef = Quaternion.LerpUnclamped(currentBone.ReferenceRotation, otherBone.ProceduralRotation, Slithery); } else { backRotationRef = currentBone.ReferenceRotation; } // Initial target rotation calculation targetLookRotation = Quaternion.LookRotation(otherBone.ProceduralPosition - currentBone.ProceduralPosition, otherBone.ProceduralRotation * ModelUpAxis); #region Calculations to limit rotations in order to adjust animation behaviour for project needs if (AngleLimit < 91) { float lookDiff = Quaternion.Angle(targetLookRotation, backRotationRef); // Limiting rotation to correct state with elastic range if (lookDiff > AngleLimit) { float limiting = 0f; limiting = Mathf.InverseLerp(0f, lookDiff, lookDiff - AngleLimit); Quaternion limitRange = Quaternion.LerpUnclamped(targetLookRotation, backRotationRef, limiting); float elasticPush = Mathf.Min(1f, lookDiff / (AngleLimit / 0.75f)); elasticPush = Mathf.Sqrt(Mathf.Pow(elasticPush, 4)) * elasticPush; // sqrt and power will make this value increase slower but reaching 1f at the end if (LimitSmoother == 0f) { targetLookRotation = Quaternion.LerpUnclamped(targetLookRotation, limitRange, elasticPush); } else { //targetLookRotation = Quaternion.LerpUnclamped(targetLookRotation, limitRange, Mathf.LerpUnclamped(1f, unifiedDelta * elasticPush, LimitSmoother)); targetLookRotation = Quaternion.LerpUnclamped(targetLookRotation, limitRange, unifiedDelta * (1f - LimitSmoother) * 50f * elasticPush); } } } if (GoBackSpeed <= 0f) { // When position in previous frame was different, we straigtening a little rotation of spine if (StraightenSpeed > 0f) { if (previousPos != RoundPosDiff(SpineBones[leadingBoneIndex].ProceduralPosition)) { currentBone.TargetStraightenFactor = 1f; } else if (currentBone.TargetStraightenFactor > 0f) { currentBone.TargetStraightenFactor -= delta * (5f + StraightenSpeed); } currentBone.StraightenFactor = Mathf.Lerp(currentBone.StraightenFactor, currentBone.TargetStraightenFactor, unifiedDelta * (1f + StraightenSpeed)); if (currentBone.StraightenFactor > 0.025f) { targetLookRotation = Quaternion.Lerp(targetLookRotation, backRotationRef, unifiedDelta * currentBone.StraightenFactor * StraightenSpeed * (TurboStraighten ? 6f : 1f)); } } } else // When we set GoBackSpeed variable spine is going back to straight pose continously so diff would be detected all the time and we don't want this { // If we use straigtening at the same time when using GoBack variable float straightenVal = 0f; // When position in previous frame was different, we straigtening a little rotation of spine if (StraightenSpeed > 0f) { if (previousPos != RoundPosDiff(SpineBones[leadingBoneIndex].ProceduralPosition)) { currentBone.TargetStraightenFactor = 1f; } else if (currentBone.TargetStraightenFactor > 0f) { currentBone.TargetStraightenFactor -= delta * (5f + StraightenSpeed); } //currentBone.StraightenFactor = Mathf.Lerp(currentBone.StraightenFactor, currentBone.TargetStraightenFactor, Mathf.LerpUnclamped(unifiedDelta, 1f, StraightenSpeed / 15f)); currentBone.StraightenFactor = Mathf.Lerp(currentBone.StraightenFactor, currentBone.TargetStraightenFactor, unifiedDelta * (1f + StraightenSpeed)); if (currentBone.StraightenFactor > 0.025f) { straightenVal = currentBone.StraightenFactor * StraightenSpeed * (TurboStraighten ? 6f : 1f); } } //targetLookRotation = Quaternion.Lerp(targetLookRotation, backRotationRef, Mathf.LerpUnclamped(unifiedDelta + straightenVal, 1f, GoBackSpeed)); targetLookRotation = Quaternion.Lerp(targetLookRotation, backRotationRef, unifiedDelta * (Mathf.Lerp(0f, 55f, GoBackSpeed) + straightenVal)); } #endregion // If we want some smooth motion for follower if (RotSmoother == 0f) { currentBone.ProceduralRotation = targetLookRotation; } else { currentBone.ProceduralRotation = Quaternion.LerpUnclamped(currentBone.ProceduralRotation, targetLookRotation, Mathf.LerpUnclamped(0f, Mathf.LerpUnclamped(1f, unifiedDelta, RotSmoother), MotionInfluence)); } }
private void ViewBodyMenu(HumanMuscle script) { GUILayout.BeginHorizontal(); EditorGUILayout.Space(); BoneNameButton(HeadBone.GetInstance(), 100); EditorGUILayout.Space(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUILayout.Space(); BoneNameButton(ShoulderLeftBone.GetInstance()); BoneNameButton(NeckBone.GetInstance(), 80); BoneNameButton(ShoulderRightBone.GetInstance()); EditorGUILayout.Space(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); BoneNameButton(UpperArmLeftBone.GetInstance()); EditorGUILayout.Space(); BoneNameButton(UpperChestBone.GetInstance(), 100); EditorGUILayout.Space(); BoneNameButton(UpperArmRightBone.GetInstance()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); BoneNameButton(LowerArmLeftBone.GetInstance()); EditorGUILayout.Space(); BoneNameButton(ChestBone.GetInstance(), 100); EditorGUILayout.Space(); BoneNameButton(LowerArmRightBone.GetInstance()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); BoneNameButton(HandLeftBone.GetInstance(), 100); EditorGUILayout.Space(); BoneNameButton(SpineBone.GetInstance(), 100); EditorGUILayout.Space(); BoneNameButton(HandRightBone.GetInstance(), 100); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUILayout.Space(); BoneNameButton(RootBone.GetInstance(), 100); EditorGUILayout.Space(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUILayout.Space(); BoneNameButton(UpperLegLeftBone.GetInstance(), 150); BoneNameButton(UpperLegRightBone.GetInstance(), 150); EditorGUILayout.Space(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); EditorGUILayout.Space(); BoneNameButton(LowerLegLeftBone.GetInstance(), 150); EditorGUILayout.Space(); BoneNameButton(LowerLegRightBone.GetInstance(), 150); EditorGUILayout.Space(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); BoneNameButton(FootLeftBone.GetInstance(), 150); EditorGUILayout.Space(); BoneNameButton(FootRightBone.GetInstance(), 150); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); BoneNameButton(ToesLeftBone.GetInstance(), 150); EditorGUILayout.Space(); BoneNameButton(ToesRightBone.GetInstance(), 150); GUILayout.EndHorizontal(); }