diff --git a/NodesConstraints.Core/NodesConstraints.cs b/NodesConstraints.Core/NodesConstraints.cs index 38a54a6..d48204c 100644 --- a/NodesConstraints.Core/NodesConstraints.cs +++ b/NodesConstraints.Core/NodesConstraints.cs @@ -94,8 +94,12 @@ private class Constraint public GuideObject child; public Transform childTransform; public bool position = true; + public bool mirrorPosition = false; public bool rotation = true; + public bool mirrorRotation = false; + public bool lookAt = false; public bool scale = true; + public bool mirrorScale = false; public Vector3 positionOffset = Vector3.zero; public Quaternion rotationOffset = Quaternion.identity; public Vector3 scaleOffset = Vector3.one; @@ -108,6 +112,10 @@ private class Constraint public bool destroyed = false; private VectorLine _debugLine; + public Vector3 originalParentPosition; + public Quaternion originalParentRotation; + public Vector3 originalParentScale; + public Constraint() { _debugLine = VectorLine.SetLine(Color.white, Vector3.zero, Vector3.one); @@ -133,6 +141,9 @@ public Constraint(Constraint other) : this() originalChildScale = other.originalChildScale; alias = other.alias; fixDynamicBone = other.fixDynamicBone; + originalParentPosition = other.parentTransform.position; + originalParentRotation = other.parentTransform.rotation; + originalParentScale = other.parentTransform.lossyScale; } public void SetActiveDebugLines(bool active) @@ -148,6 +159,74 @@ public void UpdateDebugLines() _debugLine.Draw(); } + public Vector3 GetMirroredPosition() + { + var movement = parentTransform.position - originalParentPosition; + return originalParentPosition - movement + positionOffset; + } + + public void UpdatePosition() + { + if (mirrorPosition) + childTransform.position = GetMirroredPosition(); + else + childTransform.position = parentTransform.TransformPoint(positionOffset); + if (child != null) + child.changeAmount.pos = child.transformTarget.localPosition; + } + + public Quaternion GetMirroredRotation() + { + var movement = Quaternion.Inverse(originalParentRotation) * parentTransform.rotation; + return originalParentRotation * Quaternion.Inverse(movement) * rotationOffset; + } + + public void UpdateRotation() + { + if (lookAt) + { + if (mirrorRotation) + { + var lookAt = Quaternion.LookRotation(parentTransform.position); + childTransform.rotation = new Quaternion(lookAt.x * -1f, lookAt.y * -1f, lookAt.z, lookAt.w); + } + else + childTransform.LookAt(parentTransform); + childTransform.rotation *= rotationOffset; + } + else if (mirrorRotation) + childTransform.rotation = GetMirroredRotation(); + else + childTransform.rotation = parentTransform.rotation * rotationOffset; + if (child != null) + child.changeAmount.rot = child.transformTarget.localEulerAngles; + } + + public Vector3 GetMirroredScale() + { + return new Vector3( + // Multiply the original scale (+ any offsets) times the mirrored change factor compared to the original scale + // mirroring the change factor is done by applying the power of -1 to the change factor + (originalParentScale.x * scaleOffset.x) * Mathf.Pow((parentTransform.lossyScale.x - originalParentScale.x) / originalParentScale.x + 1, -1), + (originalParentScale.y * scaleOffset.y) * Mathf.Pow((parentTransform.lossyScale.y - originalParentScale.y) / originalParentScale.y + 1, -1), + (originalParentScale.z * scaleOffset.z) * Mathf.Pow((parentTransform.lossyScale.z - originalParentScale.z) / originalParentScale.z + 1, -1) + ); + } + + public void UpdateScale() + { + if (mirrorScale) + childTransform.localScale = GetMirroredScale(); + else + childTransform.localScale = new Vector3( + (parentTransform.lossyScale.x * scaleOffset.x) / childTransform.parent.lossyScale.x, + (parentTransform.lossyScale.y * scaleOffset.y) / childTransform.parent.lossyScale.y, + (parentTransform.lossyScale.z * scaleOffset.z) / childTransform.parent.lossyScale.z + ); + if (child != null) + child.changeAmount.scale = child.transformTarget.localScale; + } + public void Destroy() { VectorLine.Destroy(ref _debugLine); @@ -218,9 +297,9 @@ private class AnimationControllerInfo private string _rotationXStr = "0.000"; private string _rotationYStr = "0.000"; private string _rotationZStr = "0.000"; - private string _scaleXStr = "0.0000"; - private string _scaleYStr = "0.0000"; - private string _scaleZStr = "0.0000"; + private string _scaleXStr = "1.0000"; + private string _scaleYStr = "1.0000"; + private string _scaleZStr = "1.0000"; private bool _debugMode; private Vector3 _debugLocalPosition; private Vector3 _debugWorldPosition; @@ -522,10 +601,14 @@ private static void Prefix() parentObjectDestination.guideObject.transformTarget.Find(constraint.parentTransform.GetPathFrom(parentObjectSource.guideObject.transformTarget)), childObjectDestination.guideObject.transformTarget.Find(constraint.childTransform.GetPathFrom(childObjectSource.guideObject.transformTarget)), constraint.position, + constraint.mirrorPosition, constraint.positionOffset, constraint.rotation, + constraint.mirrorRotation, + constraint.lookAt, constraint.rotationOffset, constraint.scale, + constraint.mirrorScale, constraint.scaleOffset, constraint.alias ); @@ -617,27 +700,11 @@ private void ApplyNodesConstraints() if (constraint.enabled && (constraint.child != null || constraint.parent != null)) { if (constraint.position && (constraint.child == null || constraint.child.enablePos)) - { - constraint.childTransform.position = constraint.parentTransform.TransformPoint(constraint.positionOffset); - if (constraint.child != null) - constraint.child.changeAmount.pos = constraint.child.transformTarget.localPosition; - } + constraint.UpdatePosition(); if (constraint.rotation && (constraint.child == null || constraint.child.enableRot)) - { - constraint.childTransform.rotation = constraint.parentTransform.rotation * constraint.rotationOffset; - if (constraint.child != null) - constraint.child.changeAmount.rot = constraint.child.transformTarget.localEulerAngles; - } + constraint.UpdateRotation(); if (constraint.scale && (constraint.child == null || constraint.child.enableScale)) - { - constraint.childTransform.localScale = new Vector3( - (constraint.parentTransform.lossyScale.x * constraint.scaleOffset.x) / constraint.childTransform.parent.lossyScale.x, - (constraint.parentTransform.lossyScale.y * constraint.scaleOffset.y) / constraint.childTransform.parent.lossyScale.y, - (constraint.parentTransform.lossyScale.z * constraint.scaleOffset.z) / constraint.childTransform.parent.lossyScale.z - ); - if (constraint.child != null) - constraint.child.changeAmount.scale = constraint.child.transformTarget.localScale; - } + constraint.UpdateScale(); } } if (toDelete != null) @@ -674,27 +741,11 @@ private void ApplyConstraints() continue; if (constraint.position) - { - constraint.childTransform.position = constraint.parentTransform.TransformPoint(constraint.positionOffset); - if (constraint.child != null) - constraint.child.changeAmount.pos = constraint.child.transformTarget.localPosition; - } + constraint.UpdatePosition(); if (constraint.rotation) - { - constraint.childTransform.rotation = constraint.parentTransform.rotation * constraint.rotationOffset; - if (constraint.child != null) - constraint.child.changeAmount.rot = constraint.child.transformTarget.localEulerAngles; - } + constraint.UpdateRotation(); if (constraint.scale) - { - constraint.childTransform.localScale = new Vector3( - (constraint.parentTransform.lossyScale.x * constraint.scaleOffset.x) / constraint.childTransform.parent.lossyScale.x, - (constraint.parentTransform.lossyScale.y * constraint.scaleOffset.y) / constraint.childTransform.parent.lossyScale.y, - (constraint.parentTransform.lossyScale.z * constraint.scaleOffset.z) / constraint.childTransform.parent.lossyScale.z - ); - if (constraint.child != null) - constraint.child.changeAmount.scale = constraint.child.transformTarget.localScale; - } + constraint.UpdateScale(); } if (toDelete != null) for (int i = toDelete.Count - 1; i >= 0; --i) @@ -785,6 +836,7 @@ private void WindowFunction(int id) GUI.enabled = _displayedConstraint.parentTransform != null && _displayedConstraint.childTransform != null; _displayedConstraint.position = GUILayout.Toggle(_displayedConstraint.position && _displayedConstraint.childTransform != null, "Link position"); GUILayout.FlexibleSpace(); + _displayedConstraint.mirrorPosition = GUILayout.Toggle(_displayedConstraint.mirrorPosition, "Mirror"); GUILayout.Label("X", GUILayout.ExpandWidth(false)); _positionXStr = GUILayout.TextField(_positionXStr, GUILayout.Width(50)); GUILayout.Label("Y"); @@ -811,6 +863,8 @@ private void WindowFunction(int id) GUI.enabled = _displayedConstraint.parentTransform != null && _displayedConstraint.childTransform != null; _displayedConstraint.rotation = GUILayout.Toggle(_displayedConstraint.rotation && _displayedConstraint.childTransform != null, "Link rotation"); GUILayout.FlexibleSpace(); + _displayedConstraint.lookAt = GUILayout.Toggle(_displayedConstraint.lookAt, "Look At"); + _displayedConstraint.mirrorRotation = GUILayout.Toggle(_displayedConstraint.mirrorRotation, "Mirror"); GUILayout.Label("X", GUILayout.ExpandWidth(false)); _rotationXStr = GUILayout.TextField(_rotationXStr, GUILayout.Width(50)); GUILayout.Label("Y", GUILayout.ExpandWidth(false)); @@ -821,7 +875,16 @@ private void WindowFunction(int id) { _onPreCullAction = () => { - _displayedConstraint.rotationOffset = Quaternion.Inverse(_displayedConstraint.parentTransform.rotation) * _displayedConstraint.childTransform.rotation; + if (_displayedConstraint.lookAt) + { + // https://docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html + var relativePos = _displayedConstraint.parentTransform.position - _displayedConstraint.childTransform.position; + var lookAtRot = Quaternion.LookRotation(relativePos); + // Same as the no LookAt version below, just using the LookAt rotation the child would have without offsets as baseline for getting the offset + _displayedConstraint.rotationOffset = Quaternion.Inverse(lookAtRot) * _displayedConstraint.childTransform.rotation; + } + else + _displayedConstraint.rotationOffset = Quaternion.Inverse(_displayedConstraint.parentTransform.rotation) * _displayedConstraint.childTransform.rotation; UpdateDisplayedRotationOffset(); }; } @@ -839,6 +902,7 @@ private void WindowFunction(int id) GUI.enabled = _displayedConstraint.parentTransform != null && _displayedConstraint.childTransform != null; _displayedConstraint.scale = GUILayout.Toggle(_displayedConstraint.scale && _displayedConstraint.childTransform != null, "Link scale"); GUILayout.FlexibleSpace(); + _displayedConstraint.mirrorScale = GUILayout.Toggle(_displayedConstraint.mirrorScale, "Mirror"); GUILayout.Label("X", GUILayout.ExpandWidth(false)); _scaleXStr = GUILayout.TextField(_scaleXStr, GUILayout.Width(50)); GUILayout.Label("Y"); @@ -882,12 +946,12 @@ private void WindowFunction(int id) ValidateDisplayedRotationOffset(); ValidateDisplayedScaleOffset(); - var newConstraint = AddConstraint(true, _displayedConstraint.parentTransform, _displayedConstraint.childTransform, _displayedConstraint.position, _displayedConstraint.positionOffset, _displayedConstraint.rotation, _displayedConstraint.rotationOffset, _displayedConstraint.scale, _displayedConstraint.scaleOffset, _displayedConstraint.alias); + var newConstraint = AddConstraint(true, _displayedConstraint.parentTransform, _displayedConstraint.childTransform, _displayedConstraint.position, _displayedConstraint.mirrorPosition, _displayedConstraint.positionOffset, _displayedConstraint.rotation, _displayedConstraint.mirrorRotation, _displayedConstraint.lookAt, _displayedConstraint.rotationOffset, _displayedConstraint.scale, _displayedConstraint.mirrorScale, _displayedConstraint.scaleOffset, _displayedConstraint.alias); if (newConstraint != null) { newConstraint.fixDynamicBone = _displayedConstraint.fixDynamicBone; - } + } } GUI.enabled = _selectedConstraint != null && _displayedConstraint.parentTransform != null && _displayedConstraint.childTransform != null && (_displayedConstraint.position || _displayedConstraint.rotation || _displayedConstraint.scale) && _displayedConstraint.parentTransform != _displayedConstraint.childTransform; if (GUILayout.Button("Update selected")) @@ -922,8 +986,12 @@ private void WindowFunction(int id) if (_allGuideObjects.TryGetValue(_selectedConstraint.childTransform, out _selectedConstraint.child) == false) _selectedConstraint.child = null; _selectedConstraint.position = _displayedConstraint.position; + _selectedConstraint.mirrorPosition = _displayedConstraint.mirrorPosition; _selectedConstraint.rotation = _displayedConstraint.rotation; + _selectedConstraint.mirrorRotation = _displayedConstraint.mirrorRotation; + _selectedConstraint.lookAt = _displayedConstraint.lookAt; _selectedConstraint.scale = _displayedConstraint.scale; + _selectedConstraint.mirrorScale = _displayedConstraint.mirrorScale; _selectedConstraint.positionOffset = _displayedConstraint.positionOffset; _selectedConstraint.rotationOffset = _displayedConstraint.rotationOffset; _selectedConstraint.scaleOffset = _displayedConstraint.scaleOffset; @@ -1012,8 +1080,12 @@ private void WindowFunction(int id) _displayedConstraint.parentTransform = _selectedConstraint.parentTransform; _displayedConstraint.childTransform = _selectedConstraint.childTransform; _displayedConstraint.position = _selectedConstraint.position; + _displayedConstraint.mirrorPosition = _selectedConstraint.mirrorPosition; _displayedConstraint.rotation = _selectedConstraint.rotation; + _displayedConstraint.mirrorRotation = _selectedConstraint.mirrorRotation; + _displayedConstraint.lookAt = _selectedConstraint.lookAt; _displayedConstraint.scale = _selectedConstraint.scale; + _displayedConstraint.mirrorScale = _selectedConstraint.mirrorScale; _displayedConstraint.positionOffset = _selectedConstraint.positionOffset; _displayedConstraint.rotationOffset = _selectedConstraint.rotationOffset; _displayedConstraint.scaleOffset = _selectedConstraint.scaleOffset; @@ -1279,9 +1351,12 @@ private void ValidateDisplayedScaleOffset() UpdateDisplayedScaleOffset(); } - - private Constraint AddConstraint(bool enabled, Transform parentTransform, Transform childTransform, bool linkPosition, Vector3 positionOffset, bool linkRotation, Quaternion rotationOffset, bool linkScale, Vector3 scaleOffset, string alias) + { + return AddConstraint(enabled, parentTransform, childTransform, linkPosition, false, positionOffset, linkRotation, false, false, rotationOffset, linkScale, false, scaleOffset, alias); + } + + private Constraint AddConstraint(bool enabled, Transform parentTransform, Transform childTransform, bool linkPosition, bool mirrorPosition, Vector3 positionOffset, bool linkRotation, bool mirrorRotation, bool lookAt, Quaternion rotationOffset, bool linkScale, bool mirrorScale, Vector3 scaleOffset, string alias) { bool shouldAdd = true; foreach (Constraint constraint in _constraints) @@ -1300,14 +1375,24 @@ private Constraint AddConstraint(bool enabled, Transform parentTransform, Transf newConstraint.parentTransform = parentTransform; newConstraint.childTransform = childTransform; newConstraint.position = linkPosition; + newConstraint.mirrorPosition = mirrorPosition; newConstraint.rotation = linkRotation; + newConstraint.mirrorRotation = mirrorRotation; + newConstraint.lookAt = lookAt; newConstraint.scale = linkScale; + newConstraint.mirrorScale = mirrorScale; newConstraint.positionOffset = positionOffset; newConstraint.rotationOffset = rotationOffset; newConstraint.scaleOffset = scaleOffset; newConstraint.alias = alias; newConstraint.fixDynamicBone = false; + // Use current ParentTransform pos/rot/scale as default to not break backwards compatibility + // Update to after adding if needed (e.g. OnSceneLoad/OnSceneImport + newConstraint.originalParentPosition = parentTransform.position; + newConstraint.originalParentRotation = parentTransform.rotation; + newConstraint.originalParentScale = parentTransform.lossyScale; + if (_allGuideObjects.TryGetValue(newConstraint.parentTransform, out newConstraint.parent) == false) newConstraint.parent = null; if (_allGuideObjects.TryGetValue(newConstraint.childTransform, out newConstraint.child) == false) @@ -1568,7 +1653,6 @@ private void LoadSceneGeneric(XmlNode node, List