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; }
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); } }
/// <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); } }
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; } }
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; } } } }
/// <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 }