void Gizmos_DrawEndBoneJoint() { // Drawing auto end joint offset if (FEngineering.VIsZero(EndBoneJointOffset)) { Transform t = _TransformsGhostChain[_TransformsGhostChain.Count - 1]; Transform p = _TransformsGhostChain[_TransformsGhostChain.Count - 1].parent; if (p) // Reference from parent { Vector3 worldOffset = t.position - p.position; Handles.color = new Color(0.3f, .3f, 1f, 0.8f); FGUI_Handles.DrawBoneHandle(t.position, t.position + worldOffset, BaseTransform.forward, 1f); } else // Reference to child { if (t.childCount > 0) { Transform ch = _TransformsGhostChain[0].GetChild(0); Vector3 worldOffset = ch.position - t.position; FGUI_Handles.DrawBoneHandle(t.position, t.position + worldOffset, BaseTransform.forward, 1f); } } } // Drawing custom joint offset else { Transform t = _TransformsGhostChain[_TransformsGhostChain.Count - 1]; Handles.color = new Color(0.3f, .3f, 1f, 0.8f); FGUI_Handles.DrawBoneHandle(t.position, t.position + t.TransformVector(EndBoneJointOffset), BaseTransform.forward, 1f); } }
public void AngleLimiting() { Quaternion localRotation = Quaternion.Inverse(LastKeyLocalRotation) * transform.localRotation; Quaternion limitedRotation = localRotation; if (FEngineering.VIsZero(HingeLimits)) { if (AngleLimit < 180) { limitedRotation = LimitSpherical(limitedRotation); } if (TwistAngleLimit < 180) { limitedRotation = LimitZ(limitedRotation); } } else { limitedRotation = LimitHinge(limitedRotation); } if (FEngineering.QIsSame(limitedRotation, localRotation)) { return; } transform.localRotation = LastKeyLocalRotation * limitedRotation; }
/// <summary> /// Limiting movement of tail bones in selected axis /// </summary> void Axis2DLimit(TailSegment child) { child.ProceduralPosition -= FEngineering.VAxis2DLimit( child.ParentBone.transform, child.ParentBone.ProceduralPosition, child.ProceduralPosition, Axis2D); }
/// <summary> /// Offsset to default segment position from parent relation /// </summary> internal Vector3 ParentToFrontOffset() { return(FEngineering.TransformVector ( ParentBone.LastKeyframeLocalRotation, ParentBone.transform.lossyScale, LastKeyframeLocalPosition )); }
//public static Vector3 CalculateLocalLook(Transform parent, Transform child) //{ return -(parent.InverseTransformPoint(child.position) - parent.InverseTransformPoint(parent.position)).normalized; } /// <summary> /// Blend toward target position /// </summary> internal Vector3 BlendMotionWeight(Vector3 newPosition) { return(Vector3.LerpUnclamped ( // Blending from default pose to new position ParentBone.ProceduralPosition + FEngineering.TransformVector(ParentBone.LastKeyframeLocalRotation, ParentBone.transform.lossyScale, LastKeyframeLocalPosition), newPosition, BlendValue )); }
private Quaternion LimitSpherical(Quaternion rotation) { if (FEngineering.QIsZero(rotation)) { return(rotation); } Vector3 currentForward = rotation * ForwardOrientation; Quaternion limitAngle = Quaternion.RotateTowards(Quaternion.identity, Quaternion.FromToRotation(ForwardOrientation, currentForward), AngleLimit); return(Quaternion.FromToRotation(currentForward, limitAngle * ForwardOrientation) * rotation); }
/// <summary> /// Processing calculated simple segment position with special effects like limiting / collision / smoothing etc. /// Calling methods using bone's parent variables /// </summary> void TailCalculations_SegmentPreProcessingStack(TailSegment child) { if (!UseCollision) // Basic motion without collision // Different update order with enable/disable collisions to avoid jittering on angle limiting { // Limit segments angles if (AngleLimit < 181) { child.ProceduralPosition = AngleLimiting(child, child.ProceduralPosition); } // Smoothing motion if (child.PositionSpeed < 1f) { child.ProceduralPosition = TailCalculations_SmoothPosition(child.PreviousPosition /*+ _limiting_influenceOffset*/, child.ProceduralPosition, child); } } else { // Smoothing motion if (child.PositionSpeed < 1f) { child.ProceduralPosition = TailCalculations_SmoothPosition(child.PreviousPosition /*+ _limiting_influenceOffset*/, child.ProceduralPosition, child); } // Computing collision offset as first thing TailCalculations_ComputeSegmentCollisions(child, ref child.ProceduralPosition); // Limit segments angles if (AngleLimit < 181) { child.ProceduralPosition = AngleLimiting(child, child.ProceduralPosition); } } // Control stretching if (MaxStretching < 1f) { StretchingLimiting(child); } // Apply gravity if (!FEngineering.VIsZero(child.Gravity) || UseWind) { CalculateGravityPositionOffsetForSegment(child); } if (Axis2D > 0) { Axis2DLimit(child); } }
/// <summary> /// Calculations for stiff tail motion reference parent rotation /// </summary> Quaternion TailSegment_RotationStiff(TailSegment child) { // Curving feature if (!FEngineering.QIsZero(child.Curving)) { return (MultiplyQ(child.Curving, child.Index * 2f) // Curving multiplier * child.ParentBone.transform.rotation); // Parent bone rotation } else // No Curving { return(child.ParentBone.transform.rotation); // Parent bone rotation } }
/// <summary> /// Calculations for slithery tail motion reference parent rotation /// </summary> Quaternion TailSegment_RotationSlithery(TailSegment child) { if (!FEngineering.QIsZero(child.Curving)) { return(GetSlitheryReferenceRotation(child) // Back rotation for parent of parent bone * child.Curving // Curving * child.ParentBone.LastKeyframeLocalRotation); // Sync with animator } else { return(GetSlitheryReferenceRotation(child) // Back rotation for parent of parent bone * child.ParentBone.LastKeyframeLocalRotation); // Sync with animator } }
void ComputeWind() { Vector3 newWind; if (overrideWind != Vector3.zero) { newWind = overrideWind; } else // Procedural wind { for (int i = 0; i < 4; i++) { randTimes[i] += Time.deltaTime * randSpeeds[i] * turbulenceSpeed; } Quaternion windDir = windOrientation; float x = -1f + Mathf.PerlinNoise(randTimes[0], 256f + randTimes[1]) * 2f; float y = -1f + Mathf.PerlinNoise(-randTimes[1], 55f + randTimes[2]) * 2f; float z = -1f + Mathf.PerlinNoise(-randTimes[3], 55f + randTimes[0]) * 2f; windDir *= Quaternion.Euler(new Vector3(0, y, 0) * changesPower); windDir = Quaternion.Euler(x * (changesPower / 6f), windDir.eulerAngles.y, z * (changesPower / 6f)); smoothWindOrient = FEngineering.SmoothDampRotation(smoothWindOrient, windDir, ref smoothWindOrientHelper, 1f - rapidness, Time.deltaTime); transform.rotation = smoothWindOrient; newWind = smoothWindOrient * Vector3.forward; } // Additional turbulence smoothAddTurbulence = Vector3.SmoothDamp(smoothAddTurbulence, GetAddTurbulence() * additionalTurbulence, ref addTurbHelper, 0.05f, Mathf.Infinity, Time.deltaTime); // Smooth out smoothWind = Vector3.SmoothDamp(smoothWind, newWind, ref windVeloHelper, 0.1f, Mathf.Infinity, Time.deltaTime); for (int i = 7; i < 10; i++) { randTimes[i] += Time.deltaTime * randSpeeds[i] * turbulenceSpeed; } float turbulencedPower = power * 0.015f; turbulencedPower *= 0.5f + Mathf.PerlinNoise(randTimes[7] * 2f, 25 + randTimes[8] * 0.5f); finalAddTurbulence = smoothAddTurbulence * turbulencedPower; targetWind = smoothWind * turbulencedPower; }
/// <summary> /// Skinning target mesh with helper vertex datas which you can get with CalculateVertexWeightingData() method /// Using transforms as guidement for bones positions and rotations /// </summary> /// <returns> Skinned mesh can't be returned as 'Mesh' type because skinned mesh is mesh + bones transforms etc. </returns> public static SkinnedMeshRenderer SkinMesh(Mesh baseMesh, Transform skinParent, Transform[] bonesStructure, FTail_SkinningVertexData[] vertData) { Vector3[] pos = new Vector3[bonesStructure.Length]; Quaternion[] rot = new Quaternion[bonesStructure.Length]; // We must reset bones structure to identity space for (int i = 0; i < bonesStructure.Length; i++) { // Transforming from world to local space coords pos[i] = skinParent.InverseTransformPoint(bonesStructure[i].position); rot[i] = FEngineering.QToLocal(skinParent.rotation, bonesStructure[i].rotation); } SkinnedMeshRenderer skin = SkinMesh(baseMesh, pos, rot, vertData); return(skin); }
protected virtual void OnSceneGUI() { if (Application.isPlaying) { return; } if (!Get.DrawGizmos) { return; } if (Get._Editor_Category == TailAnimator2.ETailCategory.Setup) { if (Get.BaseTransform) { if (!FEngineering.VIsZero(Get.EndBoneJointOffset)) { Get.RefreshTransformsList(); if (Get._TransformsGhostChain.Count > 0) { Undo.RecordObject(Get, "Changing position of tail joint offset"); Transform root = Get._TransformsGhostChain[Get._TransformsGhostChain.Count - 1]; Vector3 off = root.TransformVector(Get.EndBoneJointOffset); Vector3 pos = root.position + off; Vector3 transformed = FEditor_TransformHandles.PositionHandle(pos, Get.BaseTransform.rotation, .3f, true, false); if (Vector3.Distance(transformed, pos) > 0.00001f) { Vector3 diff = transformed - pos; Get.EndBoneJointOffset = root.InverseTransformVector(off + diff); SerializedObject obj = new SerializedObject(Get); if (obj != null) { obj.ApplyModifiedProperties(); obj.Update(); } } } } } } }
/// <summary> /// Begin update operations for additionaly genrated child bone of chain /// </summary> public void TailCalculations_UpdateArtificialChildBone(TailSegment child) { // Pre processing with limiting, gravity etc. //TailCalculations_SegmentPreProcessingStack(lastChild); //// Blending animation weight //TailSegment_PreRotationPositionBlend(lastChild); TailSegment_BaseSwingProcessing(child); if (child.PositionSpeed < 1f) { child.ProceduralPosition = TailCalculations_SmoothPosition(child.PreviousPosition /*+ _limiting_influenceOffset*/, child.ProceduralPosition, child); } if (MaxStretching < 1f) { StretchingLimiting(child); } if (!FEngineering.VIsZero(child.Gravity) || UseWind) { CalculateGravityPositionOffsetForSegment(child); } if (Axis2D > 0) { Axis2DLimit(child); } child.CollisionContactRelevancy = -1f; // Blending or just setting target position if (child.BlendValue * conditionalWeight < 1f) { child.ProceduralPositionWeightBlended = Vector3.LerpUnclamped( child.ParentBone.transform.TransformPoint(child.LastKeyframeLocalPosition), child.ProceduralPosition, child.BlendValue * conditionalWeight); } else { child.ProceduralPositionWeightBlended = child.ProceduralPosition; } }
/// <summary> /// Checking for null keyframes to prevent spinning error /// </summary> public void ZeroKeyframeCheck() { // Null keyframe detected if (FEngineering.QIsSame(lastFinalLocalRotation, transform.localRotation)) { transform.localRotation = lastKeyframeRotation; } else // Remembering rotation to check for null keyframe { lastKeyframeRotation = transform.localRotation; } if (FEngineering.VIsSame(lastFinalLocalPosition, transform.localPosition)) { transform.localPosition = lastKeyframePosition; } else // Remembering position to check for null keyframe { lastKeyframePosition = transform.localPosition; } }
/// <summary> /// Calculating base vertices datas for provided bones setup /// </summary> /// <param name="baseMesh"> Mesh to be weighted </param> /// <param name="bonesCoords"> Required local positions and rotations for bones </param> /// <param name="spreadOffset"> Origin weighting offset which can be helpful in some cases, it can be Vector3.zero in most cases </param> /// <param name="weightBoneLimit"> To how many bones vertex can be weighted to create smooth weight effect </param> /// <param name="spreadValue"> Smoothing weights on the edges of bones if lower then more smooth but don't oversmooth it </param> /// <param name="spreadPower"> Making smoothing more sharp on edges </param> public static FTail_SkinningVertexData[] CalculateVertexWeightingData(Mesh baseMesh, Transform[] bonesCoords, Vector3 spreadOffset, int weightBoneLimit = 2, float spreadValue = 0.8f, float spreadPower = 0.185f) { Vector3[] pos = new Vector3[bonesCoords.Length]; Quaternion[] rot = new Quaternion[bonesCoords.Length]; //for (int i = 0; i < bonesCoords.Length; i++) //{ // pos[i] = bonesCoords[i].position; // rot[i] = bonesCoords[i].rotation; //} // We must reset bones structure to identity space for (int i = 0; i < bonesCoords.Length; i++) { // Transforming from world to local space coords pos[i] = bonesCoords[0].parent.InverseTransformPoint(bonesCoords[i].position); rot[i] = FEngineering.QToLocal(bonesCoords[0].parent.rotation, bonesCoords[i].rotation); } return(CalculateVertexWeightingData(baseMesh, pos, rot, spreadOffset, weightBoneLimit, spreadValue, spreadPower)); }
void Shaping_UpdateGravity() { if (!FEngineering.VIsZero(Gravity)) { if (UseGravityCurve) // Operations with curve { if (!FEngineering.VIsSame(Gravity, lastGravity) || KeysChanged(GravityCurve.keys, lastGravityKeys)) { for (int i = 0; i < TailSegments.Count; i++) { TailSegments[i].Gravity = Gravity * GetValueFromCurve(i, GravityCurve) / 40f; TailSegments[i].Gravity *= (1 + ((TailSegments[i].Index / 2f) * (1f - TailSegments[i].Slithery))); } } } else // Operations without curve { if (!FEngineering.VIsSame(Gravity, lastGravity)) { for (int i = 0; i < TailSegments.Count; i++) { TailSegments[i].Gravity = Gravity / 40f; TailSegments[i].Gravity *= (1 + ((TailSegments[i].Index / 2f) * (1f - TailSegments[i].Slithery))); } } } } else // Gravity reset { if (!FEngineering.VIsSame(Gravity, lastGravity)) { for (int i = 0; i < TailSegments.Count; i++) { TailSegments[i].Gravity = Vector3.zero; TailSegments[i].GravityLookOffset = Vector3.zero; } } } }
public void CheckForZeroKeyframes() { if (FEngineering.QIsSame(lastFinalLocalRotation, Transform.localRotation)) { Transform.localRotation = lastKeyframeLocalRotation; compensatedRotation = Transform.rotation; } else { lastKeyframeLocalRotation = Transform.localRotation; } if (FEngineering.VIsSame(lastFinalLocalPosition, Transform.localPosition)) { Transform.localPosition = lastKeyframeLocalPosition; compensatedPosition = Transform.position; } else { lastKeyframeLocalPosition = Transform.localPosition; } }
void Shaping_UpdateCurving() { if (!FEngineering.QIsZero(Curving)) { if (UseCurvingCurve) // Operations with curve { if (!FEngineering.QIsSame(Curving, lastCurving) || KeysChanged(CurvCurve.keys, lastCurvingKeys)) { for (int i = 0; i < TailSegments.Count; i++) { TailSegments[i].Curving = Quaternion.LerpUnclamped(Quaternion.identity, Curving, GetValueFromCurve(i, CurvCurve)); } } } else // Operations without curve { if (!FEngineering.QIsSame(Curving, lastCurving)) { for (int i = 0; i < TailSegments.Count; i++) { TailSegments[i].Curving = Curving; } } } } else // Curving reset { if (!FEngineering.QIsSame(Curving, lastCurving)) { for (int i = 0; i < TailSegments.Count; i++) { TailSegments[i].Curving = Quaternion.identity; } } } }
public override void RefreshColliderData() { if (IsStatic) { return; // No need to refresh collider data if it is static } if (Collider2D == null) // 3D Refresh { bool diff = false; if (!FEngineering.VIsSame(Transform.position, previousPosition)) { diff = true; } else if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) { diff = true; } if (diff) { right = Box.transform.TransformVector((Vector3.right / 2f) * Box.size.x); up = Box.transform.TransformVector((Vector3.up / 2f) * Box.size.y); forward = Box.transform.TransformVector((Vector3.forward / 2f) * Box.size.z); rightN = right.normalized; upN = up.normalized; forwardN = forward.normalized; boxCenter = GetBoxCenter(Box); scales = Vector3.Scale(Box.size, Box.transform.lossyScale); scales.Normalize(); } } else // 2D Refresh { bool diff = false; if (Vector2.Distance(Transform.position, previousPosition) > Mathf.Epsilon) { diff = true; } else if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) { diff = true; } if (diff) { right = Box2D.transform.TransformVector((Vector3.right / 2f) * Box2D.size.x); up = Box2D.transform.TransformVector((Vector3.up / 2f) * Box2D.size.y); rightN = right.normalized; upN = up.normalized; boxCenter = GetBoxCenter(Box2D); boxCenter.z = 0f; Vector3 scale = Transform.lossyScale; scale.z = 1f; scales = Vector3.Scale(Box2D.size, scale); scales.Normalize(); } } base.RefreshColliderData(); previousPosition = Transform.position; previousRotation = Transform.rotation; }
private void Tab_DrawShaping() { FGUI_Inspector.VSpace(-2, -4); GUILayout.BeginVertical(FGUI_Resources.ViewBoxStyle); GUILayout.BeginVertical(FGUI_Resources.BGInBoxBlankStyle); GUILayout.Space(2f); EditorGUIUtility.labelWidth = 120; if (FEngineering.QIsZero(Get.RotationOffset)) { GUI.color = defaultValC; } else { GUI.color = c; } EditorGUI.BeginChangeCheck(); Get.RotationOffset = Quaternion.Euler(EditorGUILayout.Vector3Field(new GUIContent(sp_tailRotOff.displayName, sp_tailRotOff.tooltip), FEngineering.WrapVector(Get.RotationOffset.eulerAngles))); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); GetSelectedTailAnimators(); for (int i = 0; i < lastSelected.Count; i++) { lastSelected[i].RotationOffset = Get.RotationOffset; new SerializedObject(lastSelected[i]).ApplyModifiedProperties(); } } // Curving Tail GUILayout.Space(2f); if (FEngineering.QIsZero(Get.Curving) && !Get.UseCurlingCurve) { GUI.color = defaultValC; } else { GUI.color = c; } EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); Get.Curving = Quaternion.Euler(EditorGUILayout.Vector3Field(new GUIContent(sp_curving.displayName, sp_curving.tooltip), FEngineering.WrapVector(Get.Curving.eulerAngles))); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); GetSelectedTailAnimators(); for (int i = 0; i < lastSelected.Count; i++) { lastSelected[i].Curving = Get.Curving; new SerializedObject(lastSelected[i]).ApplyModifiedProperties(); } } if (Get.UseCurvingCurve) { EditorGUILayout.LabelField(new GUIContent("*", "Curving offset value weight for tail segments multiplied by curve"), GUILayout.Width(9)); EditorGUILayout.PropertyField(sp_CurvCurve, new GUIContent("", sp_curving.tooltip), GUILayout.MaxWidth(32)); } else { GUILayout.Space(4f); } EditorGUI.BeginChangeCheck(); SwitchButton(ref Get.UseCurvingCurve, "Spread curving rotation offset weight over tail segments", curveIcon); if (EditorGUI.EndChangeCheck()) { GetSelectedTailAnimators(); for (int i = 0; i < lastSelected.Count; i++) { lastSelected[i].UseCurvingCurve = Get.UseCurvingCurve; new SerializedObject(lastSelected[i]).ApplyModifiedProperties(); } } EditorGUILayout.EndHorizontal(); GUILayout.Space(3f); // Length Stretch if (Get.LengthMultiplier == 1f && !Get.UseLengthMulCurve) { GUI.color = defaultValC; } else { GUI.color = c; } EditorGUILayout.BeginHorizontal(); if (!Get.UseLengthMulCurve) { EditorGUILayout.PropertyField(sp_LengthMultiplier); GUILayout.Space(4f); EditorGUI.BeginChangeCheck(); SwitchButton(ref Get.UseLengthMulCurve, "Spread length multiplier weight over tail segments", curveIcon); if (EditorGUI.EndChangeCheck()) { GetSelectedTailAnimators(); for (int i = 0; i < lastSelected.Count; i++) { lastSelected[i].UseLengthMulCurve = Get.UseLengthMulCurve; new SerializedObject(lastSelected[i]).ApplyModifiedProperties(); } } } else { EditorGUILayout.PropertyField(sp_LengthMulCurve, new GUIContent(sp_LengthMultiplier.displayName, sp_LengthMultiplier.tooltip)); GUILayout.Space(4f); EditorGUI.BeginChangeCheck(); SwitchButton(ref Get.UseLengthMulCurve, "Spread length multiplier weight over tail segments", curveIcon); if (EditorGUI.EndChangeCheck()) { GetSelectedTailAnimators(); for (int i = 0; i < lastSelected.Count; i++) { lastSelected[i].UseLengthMulCurve = Get.UseLengthMulCurve; new SerializedObject(lastSelected[i]).ApplyModifiedProperties(); } } } EditorGUILayout.EndHorizontal(); EditorGUIUtility.labelWidth = 0; GUILayout.EndVertical(); GUILayout.EndVertical(); }
/// <summary> /// Method to initialize component, to have more controll than waiting for Start() method, init can be executed before or after start, as programmer need it. /// </summary> protected virtual void Init() { if (initialized) { return; } // Checking if we have transform to create tail chain from if (_TransformsGhostChain == null || _TransformsGhostChain.Count == 0) { _TransformsGhostChain = new List <Transform>(); GetGhostChain(); } // Generating tail instances for procedural animation TailSegments = new List <TailSegment>(); for (int i = 0; i < _TransformsGhostChain.Count; i++) { if (_TransformsGhostChain[i] == null) { Debug.Log("[Tail Animator] Null bones in " + name + " !"); continue; } TailSegment b = new TailSegment(_TransformsGhostChain[i]); b.SetIndex(i, _TransformsGhostChain.Count); TailSegments.Add(b); } // Checking correctness if (TailSegments.Count == 0) { Debug.Log("[Tail Animator] Could not create tail bones chain in " + name + " !"); return; } _TC_TailLength = 0f; _baseTransform = _TransformsGhostChain[0]; //if (_baseTransform.parent) // _baseTransform = _baseTransform.parent; //else // IncludeParent = false; // Setting parent-child relation for tail logics for (int i = 0; i < TailSegments.Count; i++) { TailSegment current = TailSegments[i]; TailSegment parent; #region Defining Parent Bones if (i == 0) { if (current.transform.parent) { // Creating parent and setting safety parent parent = new TailSegment(current.transform.parent); parent.SetParentRef(new TailSegment(parent.transform.parent)); } else #region If first bone is parentless { parent = new TailSegment(current.transform); Vector3 toStartDir; if (_TransformsGhostChain.Count > 1) { toStartDir = _TransformsGhostChain[0].position - _TransformsGhostChain[1].position; if (toStartDir.magnitude == 0) { toStartDir = transform.position - _TransformsGhostChain[1].position; } } else { toStartDir = current.transform.position - _TransformsGhostChain[0].position; } if (toStartDir.magnitude == 0) { toStartDir = transform.position - _TransformsGhostChain[0].position; } if (toStartDir.magnitude == 0) { toStartDir = transform.forward; } parent.LocalOffset = parent.transform.InverseTransformPoint(parent.transform.position + toStartDir); parent.SetParentRef(new TailSegment(current.transform)); } #endregion //current.InitialLocalRotation = Quaternion.Inverse(current.transform.localRotation); GhostParent = parent; GhostParent.Validate(); current.SetParentRef(GhostParent); } else // i != 0 { parent = TailSegments[i - 1]; // If bones are removed manually from chain we support custom length of bone undependent from transform parenting chain structure current.ReInitializeLocalPosRot(parent.transform.InverseTransformPoint(current.transform.position), current.transform.localRotation); } #endregion #region Defining Last Child Bone if (i == TailSegments.Count - 1) { Transform childT = null; if (current.transform.childCount > 0) { childT = current.transform.GetChild(0); } GhostChild = new TailSegment(childT); // Scale ref for ghosting object position offset Vector3 scaleDir; if (FEngineering.VIsZero(EndBoneJointOffset)) { if (current.transform.parent) { scaleDir = current.transform.position - current.transform.parent.position; } else if (current.transform.childCount > 0) { scaleDir = current.transform.GetChild(0).position - current.transform.position; } else { scaleDir = current.transform.TransformDirection(Vector3.forward) * 0.05f; } } else { scaleDir = current.transform.TransformVector(EndBoneJointOffset); } GhostChild.ProceduralPosition = current.transform.position + scaleDir; GhostChild.ProceduralPositionWeightBlended = GhostChild.ProceduralPosition; GhostChild.PreviousPosition = GhostChild.ProceduralPosition; GhostChild.PosRefRotation = Quaternion.identity; GhostChild.PreviousPosReferenceRotation = Quaternion.identity; GhostChild.ReInitializeLocalPosRot(current.transform.InverseTransformPoint(GhostChild.ProceduralPosition), Quaternion.identity); GhostChild.RefreshFinalPos(GhostChild.ProceduralPosition); GhostChild.RefreshFinalRot(GhostChild.PosRefRotation); GhostChild.TrueTargetRotation = GhostChild.PosRefRotation; current.TrueTargetRotation = current.transform.rotation; current.SetChildRef(GhostChild); GhostChild.SetParentRef(current); } else { current.SetChildRef(TailSegments[i + 1]); } #endregion current.SetParentRef(parent); _TC_TailLength += Vector3.Distance(current.ProceduralPosition, parent.ProceduralPosition); } // List with ghosts for curves etc. GhostParent.SetIndex(-1, TailSegments.Count); GhostChild.SetIndex(TailSegments.Count, TailSegments.Count); GhostParent.SetChildRef(TailSegments[0]); previousWorldPosition = BaseTransform.position; WavingRotationOffset = Quaternion.identity; if (CollidersDataToCheck == null) { CollidersDataToCheck = new List <FImp_ColliderData_Base>(); } DynamicAlwaysInclude = new List <Component>(); if (UseCollision) { SetupSphereColliders(); } // List instance for deflection feature if (_defl_source == null) { _defl_source = new List <TailSegment>(); } Waving_Initialize(); if (DetachChildren) { DetachChildrenTransforms(); } initialized = true; if (TailSegments.Count == 1) { if (TailSegments[0].transform.parent == null) { Debug.Log("[Tail Animator] Can't initialize one-bone length chain on bone which don't have any parent!"); Debug.LogError("[Tail Animator] Can't initialize one-bone length chain on bone which don't have any parent!"); TailAnimatorAmount = 0f; initialized = false; return; } } if (UseWind) { TailAnimatorWind.Refresh(this); } if (PostProcessingNeeded()) { if (!_pp_initialized) { InitializePostProcessing(); } } #region Prewarming tail to target state if (Prewarm) { ShapingParamsUpdate(); ExpertParamsUpdate(); Update(); LateUpdate(); justDelta = rateDelta; secPeriodDelta = 1f; deltaForLerps = secPeriodDelta; rateDelta = 1f / 60f; CheckIfTailAnimatorShouldBeUpdated(); if (updateTailAnimator) { int loopCount = 60 + TailSegments.Count / 2; for (int d = 0; d < loopCount; d++) { PreCalibrateBones(); LateUpdate(); } } } #endregion }
public override void RefreshColliderData() { if (IsStatic) { return; // No need to refresh collider data if it is static } bool diff = false; if (!FEngineering.VIsSame(previousPosition, Transform.position)) { diff = true; } else if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) { diff = true; } else { if (Is2D == false) { if (preRadius != Capsule.radius || !FEngineering.VIsSame(previousScale, Transform.lossyScale)) { CalculateCapsuleParameters(Capsule, ref Direction, ref radius, ref scaleFactor); } } else { if (preRadius != GetCapsule2DRadius(Capsule2D) || !FEngineering.VIsSame(previousScale, Transform.lossyScale)) { CalculateCapsuleParameters(Capsule2D, ref Direction, ref radius, ref scaleFactor); } } } if (diff) { if (Is2D == false) { GetCapsuleHeadsPositions(Capsule, ref Top, ref Bottom, Direction, radius, scaleFactor); } else { GetCapsuleHeadsPositions(Capsule2D, ref Top, ref Bottom, Direction, radius, scaleFactor); } } base.RefreshColliderData(); previousPosition = Transform.position; previousRotation = Transform.rotation; previousScale = Transform.lossyScale; if (Is2D == false) { preRadius = Capsule.radius; } else { preRadius = GetCapsule2DRadius(Capsule2D); } }
/// <summary> /// If segment rotation is in too big angle we straighten it /// </summary> protected Vector3 AngleLimiting(TailSegment child, Vector3 targetPos) { float angleFactor = 0f; _limiting_limitPosition = targetPos; _limiting_angle_ToTargetRot = ( Quaternion.FromToRotation ( child.ParentBone.transform.TransformDirection(child.LastKeyframeLocalPosition), targetPos - child.ParentBone.ProceduralPosition) ) * child.ParentBone.transform.rotation; _limiting_angle_targetInLocal = FEngineering.QToLocal(child.ParentBone.transform.rotation, _limiting_angle_ToTargetRot); // Quaternion.Inverse(child.ParentBone.PreviousRotation) * _limiting_angle_ToTargetRot; // Limiting all axis or one float angleDiffToInitPose = 0f; if (AngleLimitAxis.sqrMagnitude == 0f) // All axis limit angle { angleDiffToInitPose = Quaternion.Angle(_limiting_angle_targetInLocal, child.LastKeyframeLocalRotation); } else // Selective axis { #region Selective axis limits AngleLimitAxis.Normalize(); if (LimitAxisRange.x == LimitAxisRange.y) { angleDiffToInitPose = Mathf.DeltaAngle( Vector3.Scale(child.InitialLocalRotation.eulerAngles, AngleLimitAxis).magnitude, Vector3.Scale(_limiting_angle_targetInLocal.eulerAngles, AngleLimitAxis).magnitude); if (angleDiffToInitPose < 0f) { angleDiffToInitPose = -angleDiffToInitPose; } } else { angleDiffToInitPose = Mathf.DeltaAngle( Vector3.Scale(child.InitialLocalRotation.eulerAngles, AngleLimitAxis).magnitude, Vector3.Scale(_limiting_angle_targetInLocal.eulerAngles, AngleLimitAxis).magnitude); if (angleDiffToInitPose > LimitAxisRange.x && angleDiffToInitPose < LimitAxisRange.y) { angleDiffToInitPose = 0f; } if (angleDiffToInitPose < 0) { angleDiffToInitPose = -angleDiffToInitPose; } } #endregion } #region Debug //Debug.Log("Atarget in local = " + // FEngineering.WrapVector(_limiting_angle_targetInLocal.eulerAngles) + " last key local = " + // FEngineering.WrapVector(child.lastKeyframeLocalRotation.eulerAngles) + " angle = " + angleDiffToInitPose); #endregion // Finding rotate back to limited angle coordinates if (angleDiffToInitPose > AngleLimit) { float exceededAngle = Mathf.Abs(Mathf.DeltaAngle(angleDiffToInitPose, AngleLimit)); angleFactor = Mathf.InverseLerp(0f, AngleLimit, exceededAngle); // percentage value (0-1) from target rotation to limit #region Debug //Debug.DrawLine(child.ParentBone.ParentBone.transform.position + child.ParentBone.ParentBone.ProceduralRotation * child.ParentBone.transform.localPosition, //child.ProceduralPosition, Color.red, 1f); //Debug.Log("[" + child.Index + "] diff = " // + angleDiffToInitPose + " exc = " // + exceededAngle + " fact = " // + angleFactor); #endregion if (LimitSmoothing > Mathf.Epsilon) { float smooth = Mathf.Lerp(55f, 15f, LimitSmoothing); _limiting_angle_newLocal = Quaternion.SlerpUnclamped(_limiting_angle_targetInLocal, child.LastKeyframeLocalRotation, deltaForLerps * smooth * angleFactor); } else { _limiting_angle_newLocal = Quaternion.SlerpUnclamped(_limiting_angle_targetInLocal, child.LastKeyframeLocalRotation, angleFactor); } _limiting_angle_ToTargetRot = FEngineering.QToWorld(child.ParentBone.transform.rotation, _limiting_angle_newLocal); _limiting_limitPosition = child.ParentBone.ProceduralPosition + _limiting_angle_ToTargetRot * Vector3.Scale(child.transform.lossyScale, child.LastKeyframeLocalPosition); } if (angleFactor > Mathf.Epsilon) { return(_limiting_limitPosition); } else { return(targetPos); } }
/// <summary> Smoothing rotation for _tc_bone with smooth damp method changing _tc_smoothRot variable in iteration towards _tc_targetRot </summary> Quaternion TailCalculations_SmoothRotationSmoothDamp(Quaternion from, Quaternion to, ref Quaternion velo, float speed) { return(FEngineering.SmoothDampRotation(from, to, ref velo, Mathf.LerpUnclamped(0.25f, 0.0001f, Mathf.Sqrt(Mathf.Sqrt(speed))), rateDelta)); }
/// <summary> /// Refreshing position for additionaly generated parent bone /// </summary> public void Editor_TailCalculations_RefreshArtificialParentBone() { GhostParent.ProceduralPosition = GhostParent.transform.position + FEngineering.TransformVector(GhostParent.transform.rotation, GhostParent.transform.lossyScale, GhostParent.LocalOffset); }