diff --git a/src/Cinematic/Cells/AnimatorPlayer.cs b/src/Cinematic/Cells/AnimatorPlayer.cs index d65a451..130554f 100644 --- a/src/Cinematic/Cells/AnimatorPlayer.cs +++ b/src/Cinematic/Cells/AnimatorPlayer.cs @@ -62,7 +62,10 @@ namespace UnityExplorer.UI.Panels } public void ResetAnimation(){ - if (bonesManager != null) bonesManager.turnOffAnimatorToggle.isOn = true; + if (bonesManager != null){ + bonesManager.turnOffAnimatorToggle.isOn = true; + bonesManager.DisableGizmos(); + } // Let the game change animations again animator.StopPlayback(); if (originalAnimatorController != null && animator.wrappedObject != null){ diff --git a/src/Cinematic/Cells/BonesCell.cs b/src/Cinematic/Cells/BonesCell.cs index 26357ec..c0b6a1c 100644 --- a/src/Cinematic/Cells/BonesCell.cs +++ b/src/Cinematic/Cells/BonesCell.cs @@ -17,6 +17,7 @@ namespace UnityExplorer.UI.Panels public AxisComponentControl CurrentSlidingAxisControl { get; set; } public BonesManager Owner; private ButtonRef inspectButton; + private Dropdown gizmoDropdown; // ICell public float DefaultHeight => 25f; @@ -52,6 +53,20 @@ namespace UnityExplorer.UI.Panels UIFactory.SetLayoutElement(inspectButton.GameObject, minWidth: 150, minHeight: 25); inspectButton.OnClick += () => InspectorManager.Inspect(bone.gameObject); + GameObject gizmoObj = UIFactory.CreateDropdown(header, "Gizmo_Dropdown", out gizmoDropdown, null, 14, (idx) => { + if (Owner.turnOffAnimatorToggle.isOn) Owner.turnOffAnimatorToggle.isOn = false; + ExplorerBehaviour.GizmoTools.targetTransform = bone; + ExplorerBehaviour.GizmoTools.ChangeGizmo((GizmoType)idx); + } + ); + UIFactory.SetLayoutElement(gizmoObj, minHeight: 25, minWidth: 150); + gizmoDropdown.options.Add(new Dropdown.OptionData("No Gizmo")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Local Position")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Local Rotation")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Global Position")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Global Rotation")); + gizmoDropdown.captionText.text = "No Gizmo"; + GameObject headerButtons = UIFactory.CreateUIObject("BoneHeader", header); UIFactory.SetLayoutGroup(headerButtons, false, false, true, true, 4, childAlignment: TextAnchor.MiddleRight); UIFactory.SetLayoutElement(headerButtons, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800); @@ -75,6 +90,12 @@ namespace UnityExplorer.UI.Panels bone.gameObject.SetActive(value); } + public void DisableGizmo(){ + if (gizmoDropdown.value != 0){ + gizmoDropdown.value = 0; + } + } + // TransformControls-like functions public void UpdateTransformControlValues(bool force){ positionControl.Update(force); diff --git a/src/ExplorerBehaviour.cs b/src/ExplorerBehaviour.cs index c294a7e..2d9b5ac 100644 --- a/src/ExplorerBehaviour.cs +++ b/src/ExplorerBehaviour.cs @@ -18,6 +18,7 @@ namespace UnityExplorer public class ExplorerBehaviour : MonoBehaviour { internal static ExplorerBehaviour Instance { get; private set; } + static public ExtendedTransformTools GizmoTools { get; private set; } #if CPP public ExplorerBehaviour(System.IntPtr ptr) : base(ptr) { } @@ -33,6 +34,7 @@ namespace UnityExplorer DontDestroyOnLoad(obj); obj.hideFlags = HideFlags.HideAndDontSave; Instance = obj.AddComponent(); + GizmoTools = obj.AddComponent(); } internal void Update() diff --git a/src/GizmosLibraryPlugin/ExtendedTransformTools.cs b/src/GizmosLibraryPlugin/ExtendedTransformTools.cs new file mode 100644 index 0000000..82e31b6 --- /dev/null +++ b/src/GizmosLibraryPlugin/ExtendedTransformTools.cs @@ -0,0 +1,126 @@ +// Credits to https://github.com/Vesper-Works/Unity-Explorer-For-Outer-Wilds/ +using System.Collections.Generic; +using UnityEngine; + +using GizmosLibraryPlugin; +using UnityExplorer.Inspectors; +using UnityExplorer.TransformGizmos; + +using UniverseLib.Input; + +#if INTEROP +using Il2CppInterop.Runtime.Injection; +#endif + +namespace UnityExplorer +{ + public enum GizmoType + { + None, + LocalPosition, + LocalRotation, + GlobalPosition, + GlobalRotation + } + + public class ExtendedTransformTools : MonoBehaviour + { +#if CPP + static ExtendedTransformTools() + { + ClassInjector.RegisterTypeInIl2Cpp(); + } + + public ExtendedTransformTools(IntPtr ptr) : base(ptr) { } +#endif + public Transform targetTransform; + + float maxDistanceToSelect = 0.05f; + + private List EnabledGizmos = new(); + + private List NewGizmo(GizmoType type){ + List returnList = new List(); + switch(type){ + case GizmoType.LocalPosition: + returnList.Add(new LocalPositionGizmo()); + break; + case GizmoType.LocalRotation: + returnList.Add(new LocalEulerAngleGizmo()); + break; + case GizmoType.GlobalPosition: + returnList.Add(new TransformOrientationGizmo()); + break; + case GizmoType.GlobalRotation: + returnList.Add(new LocalEulerAngleGizmo()); + returnList.Add(new FromCameraViewRotationGizmo()); + break; + } + return returnList; + } + + public void ChangeGizmo(GizmoType type) { + EnabledGizmos.Clear(); + if (type != GizmoType.None){ + EnabledGizmos.AddRange(NewGizmo(type)); + } + } + + private void Update() + { + if (targetTransform == null){ + EnabledGizmos.ForEach((gizmo) => gizmo.Reset()); + return; + } + + float scale = Vector3.Distance(Camera.current.transform.position, targetTransform.position) / 5f; + + EnabledGizmos.ForEach((gizmo) => + { + gizmo.SetScale(scale); + gizmo.Set(targetTransform); + }); + + Ray ray = Camera.current.ScreenPointToRay(IInputManager.MousePosition); + + if (IInputManager.GetMouseButtonDown(0)) + { + bool noneIsSelected = EnabledGizmos.TrueForAll((gizmo) => !gizmo.IsSelected()); + + if (noneIsSelected) + { + EnabledGizmos.ForEach((gizmo) => gizmo.CheckSelected(ray, maxDistanceToSelect * scale)); + } + } + else if (IInputManager.GetMouseButton(0)) + { + EnabledGizmos.ForEach((gizmo) => + { + if (gizmo.IsSelected()) + { + gizmo.OnSelected(ray); + } + }); + } + else + { + EnabledGizmos.ForEach((gizmo) => gizmo.Reset()); + } + + } + + public void OnRenderObject() + { + if (targetTransform == null) + return; + + GL.wireframe = true; + + GLHelper.SetDefaultMaterialPass(0, true); + + EnabledGizmos.ForEach((gizmo) => gizmo.OnRender()); + + GL.wireframe = false; + } + } +} diff --git a/src/GizmosLibraryPlugin/GLDraw.cs b/src/GizmosLibraryPlugin/GLDraw.cs new file mode 100644 index 0000000..ad94a2c --- /dev/null +++ b/src/GizmosLibraryPlugin/GLDraw.cs @@ -0,0 +1,289 @@ +using System; +using UnityEngine; +using UnityExplorer; + +namespace GizmosLibraryPlugin +{ + public static class GLDraw + { + + //GL.LINES = 1 + + /// + /// Draws a cube that follows the Vector.foward, Vector.up and Vector.right axis with center in offset. + /// + /// + /// + public static void SimpleWireframeCube(Vector3 offset, Color color) + { + WireframeCube(Vector3.forward, Vector3.up, Vector3.right, offset, color); + } + + /// + /// Draws a cube that follows the specified axis with center in offset. + /// + /// + /// + /// + /// + /// + public static void WireframeCube(Vector3 foward, Vector3 up, Vector3 right, Vector3 offset, Color color) + { + Vector3[] vertex = new Vector3[4]; + vertex[0] = (foward + right) / 2f; + vertex[1] = (-foward + right) / 2f; + vertex[2] = (-foward - right) / 2f; + vertex[3] = (foward - right) / 2f; + + //GL.Begin(GL.LINE_STRIP); + GL.Begin(GL.LINES); + GL.Color(color); + for (int i = 0; i < 4; i++) + { + GL.Vertex(vertex[i] + offset - up / 2f); + } + GL.Vertex(vertex[0] + offset - up / 2f); ; + GL.End(); + + //GL.Begin(GL.LINE_STRIP); + GL.Begin(GL.LINES); + GL.Color(color); + for (int i = 0; i < 4; i++) + { + GL.Vertex(vertex[i] + offset + up / 2f); + } + GL.Vertex(vertex[0] + offset + up / 2f); + GL.End(); + + GL.Begin(GL.LINES); + for (int i = 0; i < 4; i++) + { + GL.Color(color); + GL.Vertex(vertex[i] + offset - up / 2f); + GL.Vertex(vertex[i] + offset + up / 2f); + } + GL.End(); + } + + /// + /// Draws a circle on a plane facing the specified normal, centered in offset with the angle 0 meaning the same direction as up. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// If it is a 2 PI circle you then need to set this to true + public static void WireframeCircle(float radius, Vector3 normal, Vector3 up, Vector3 offset, Color color, int resolution = 3, float startAngle = 0f, float endAngle = 2f * Mathf.PI, bool isWholeCircle = true) + { + if (resolution < 3 || radius <= 0f) + return; + normal = normal.normalized; + up = up.normalized; + + //GL.Begin(GL.LINE_STRIP); + GL.Begin(GL.LINES); + + float angleStep = (endAngle - startAngle) / resolution; + int aditionalSteps = isWholeCircle ? 1 : 0; + + GL.Color(color); + Vector3 rotationVector = Vector3MathUtils.GetRotationVector(normal, up); + for (int i = 0; i <= resolution + aditionalSteps; i++) + { + Vector3 radiusVector = Vector3MathUtils.GetRotatedVectorComponent(rotationVector, up, angleStep * i + startAngle); + GL.Vertex(radiusVector * radius + offset); + } + GL.End(); + } + + /// + /// Draws a sphere centered on offset following the Vector.foward, Vector.up and Vector.right axis. + /// + /// + /// + /// + /// + public static void SimpleWireframeSphere(float radius, Vector3 offset, Color color, int resolution) + { + WireframeCircle(radius, Vector3.up, Vector3.forward, offset, color, resolution); + WireframeCircle(radius, Vector3.forward, Vector3.right, offset, color, resolution); + WireframeCircle(radius, Vector3.right, Vector3.up, offset, color, resolution); + } + + /// + /// Draws a sphere centered on offset following the specified axis. + /// + /// + /// + /// + /// + /// + /// + public static void WireframeSphere(float radius, Vector3 offset, Vector3 foward, Vector3 up, Color color, int resolution = 3) + { + Vector3 right = Vector3.Cross(foward, up); + WireframeCircle(radius, up, foward, offset, color, resolution); + WireframeCircle(radius, foward, right, offset, color, resolution); + WireframeCircle(radius, right, up, offset, color, resolution); + } + + /// + /// Draws half a sphere centered on offset facing the up direction. + /// + /// + /// + /// + /// + /// + /// + public static void WireframeHemisphere(float radius, Vector3 offset, Vector3 foward, Vector3 up, Color color, int resolution = 3) + { + Vector3 right = Vector3.Cross(foward, up); + WireframeCircle(radius, up, foward, offset, color, resolution, -Mathf.PI / 2f, Mathf.PI / 2f, false); + WireframeCircle(radius, foward, right, offset, color, resolution); + WireframeCircle(radius, right, foward, offset, color, resolution, -Mathf.PI / 2f, Mathf.PI / 2f, false); + } + + /// + /// Draws a capsule with the startPoint and endPoint being the center of the spheres on its extreme. + /// + /// + /// + /// + /// + /// + public static void WireframeCapsule(float radius, Vector3 startPoint, Vector3 endPoint, Color color, int resolution = 3) + { + Vector3 direction = startPoint - endPoint; + Vector3 randomUpVector = Vector3MathUtils.GetArbitraryPerpendicularVector(direction); + + //Top and bottom Spheres + WireframeHemisphere(radius, startPoint, direction, randomUpVector, color, resolution); + WireframeHemisphere(radius, endPoint, -direction, -randomUpVector, color, resolution); + + GL.Begin(GL.LINES); + float angleStep = 2f * Mathf.PI / resolution; + Vector3 rotationVector = Vector3MathUtils.GetRotationVector(direction.normalized, randomUpVector); + for (int i = 0; i <= resolution; i++) + { + Vector3 radiusVector = Vector3MathUtils.GetRotatedVectorComponent(rotationVector, randomUpVector, angleStep * i); + Vector3 vertex1 = radiusVector * radius + startPoint; + Vector3 vertex2 = radiusVector * radius + endPoint; + + GL.Color(color); + GL.Vertex(vertex1); + GL.Vertex(vertex2); + } + GL.End(); + } + + /// + /// Draws a capsule oriented with the Vector.up direction. + /// + /// + /// + /// + /// + /// + /// + public static void SimpleWireframeCapsule(float radius, float height, Vector3 up, Vector3 offset, Color color, int resolution = 3) + { + Vector3 randomFowardVector = Vector3MathUtils.GetArbitraryPerpendicularVector(up); + //Top and bottom Spheres + WireframeSphere(radius, offset - up * height / 2f, randomFowardVector, up, color, resolution); + WireframeSphere(radius, offset + up * height / 2f, randomFowardVector, up, color, resolution); + + GL.Begin(GL.LINES); + float angleStep = 2f * Mathf.PI / resolution; + Vector3 rotationVector = Vector3MathUtils.GetRotationVector(up.normalized, randomFowardVector); + for (int i = 0; i <= resolution; i++) + { + Vector3 radiusVector = Vector3MathUtils.GetRotatedVectorComponent(rotationVector, randomFowardVector, angleStep * i); + Vector3 vertex1 = radiusVector * radius + offset - up * height / 2f; + Vector3 vertex2 = radiusVector * radius + offset + up * height / 2f; + + GL.Color(color); + GL.Vertex(vertex1); + GL.Vertex(vertex2); + } + GL.End(); + } + + /// + /// Draws a cone with specifiend star and end positions as well as start and end radius (for cone trunks). It can be used to draw cylinder too. + /// + /// + /// + /// + /// + /// + /// + public static void WireframeCone(float coneRadiusStart, float coneRadiusEnd, Vector3 coneStart, Vector3 coneEnd, Color color, int resolution = 3) + { + Vector3 direction = coneEnd - coneStart; + Vector3 randomFowardVector = Vector3MathUtils.GetArbitraryPerpendicularVector(direction); + //Start Circle + WireframeCircle(coneRadiusStart, direction, randomFowardVector, coneStart, color, resolution); + //End Circle + WireframeCircle(coneRadiusEnd, direction, randomFowardVector, coneEnd, color, resolution); + //Connecting Lines + GL.Begin(GL.LINES); + float angleStep = 2f * Mathf.PI / resolution; + Vector3 rotationVector = Vector3MathUtils.GetRotationVector(direction.normalized, randomFowardVector); + for (int i = 0; i <= resolution; i++) + { + Vector3 radiusVector = Vector3MathUtils.GetRotatedVectorComponent(rotationVector, randomFowardVector, angleStep * i); + Vector3 vertex1 = radiusVector * coneRadiusStart + coneStart; + Vector3 vertex2 = radiusVector * coneRadiusEnd + coneEnd; + + GL.Color(color); + GL.Vertex(vertex1); + GL.Vertex(vertex2); + } + GL.End(); + + } + + /// + /// Draws the direction of a vector as a 3d arrow starting on the offset. + /// + /// + /// + /// + /// + public static void Vector(Vector3 vector, float headSize, Vector3 offset, Color color) + { + if (vector.magnitude <= 0f) + return; + + GL.Begin(GL.LINES); + GL.Color(color); + //Main arrow body + GL.Vertex(offset); + Vector3 endPoint = vector + offset; + GL.Vertex(endPoint); + //Head of the arrow + Vector3 direction = vector.normalized; + Vector3 firstHeadDirection = Vector3MathUtils.GetArbitraryPerpendicularVector(vector); + Vector3 secondHeadDirection = Vector3.Cross(direction, firstHeadDirection); + + GL.Vertex(endPoint); + GL.Vertex((firstHeadDirection - direction).normalized * headSize + endPoint); + + GL.Vertex(endPoint); + GL.Vertex(-(firstHeadDirection + direction).normalized * headSize + endPoint); + + GL.Vertex(endPoint); + GL.Vertex((secondHeadDirection - direction).normalized * headSize + endPoint); + + GL.Vertex(endPoint); + GL.Vertex(-(secondHeadDirection + direction).normalized * headSize + endPoint); + + GL.End(); + } + } +} diff --git a/src/GizmosLibraryPlugin/GLHelper.cs b/src/GizmosLibraryPlugin/GLHelper.cs new file mode 100644 index 0000000..1018248 --- /dev/null +++ b/src/GizmosLibraryPlugin/GLHelper.cs @@ -0,0 +1,128 @@ +using System; +using UnityEngine; + +namespace GizmosLibraryPlugin +{ + public static class GLHelper + { + static Material defaultGizmosMaterial; + static Material alwaysDrawOnTopDefaultGizmosMaterial; + public static Material GetDefaultMaterial() + { + if (!defaultGizmosMaterial) + { + Shader shader = Shader.Find("Hidden/Internal-Colored"); + defaultGizmosMaterial = new Material(shader) + { + hideFlags = HideFlags.HideAndDontSave + }; + // Turn on alpha blending + defaultGizmosMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + defaultGizmosMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + // Turn backface culling off + defaultGizmosMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off); + // Turn off depth writes + defaultGizmosMaterial.SetInt("_ZWrite", 0); + } + return defaultGizmosMaterial; + } + public static void SetDefaultMaterialPass(int pass = 0, bool alwaysDrawOnTop = false) + { + if(!alwaysDrawOnTop) + GetDefaultMaterial().SetPass(pass); + else + GetAlwaysDrawOnTopDefaultMaterial().SetPass(pass); + } + public static Material GetAlwaysDrawOnTopDefaultMaterial() + { + if (!alwaysDrawOnTopDefaultGizmosMaterial) + { + Shader shader = Shader.Find("Hidden/Internal-Colored"); + alwaysDrawOnTopDefaultGizmosMaterial = new Material(shader) + { + hideFlags = HideFlags.HideAndDontSave + }; + // Turn on alpha blending + alwaysDrawOnTopDefaultGizmosMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + alwaysDrawOnTopDefaultGizmosMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + // Turn backface culling off + alwaysDrawOnTopDefaultGizmosMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off); + // Turn off depth writes + //From https://answers.unity.com/questions/1674373/rendering-gl-lines-z-order-manipulation.html + alwaysDrawOnTopDefaultGizmosMaterial.SetInt("_ZWrite", -10); + alwaysDrawOnTopDefaultGizmosMaterial.SetInt("_ZTest", (int)UnityEngine.Rendering.CompareFunction.Disabled); + } + return alwaysDrawOnTopDefaultGizmosMaterial; + } + public static void DrawWithReference(Transform reference, Action drawMethod) + { + GL.PushMatrix(); + GL.MultMatrix(reference.localToWorldMatrix); + drawMethod?.Invoke(); + GL.PopMatrix(); + } + public static void DrawOnGlobalReference(Action drawMethod) + { + GL.PushMatrix(); + GL.MultMatrix(Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one)); + drawMethod?.Invoke(); + GL.PopMatrix(); + } + public static void DrawWithOrthoProjection(Action drawMethod) + { + GL.PushMatrix(); + GL.LoadOrtho(); + drawMethod?.Invoke(); + GL.PopMatrix(); + } + + public static void DrawAxis(float headSize, Color color, Vector3 offset) + { + GLDraw.Vector(Vector3.up, headSize, offset, color); + GLDraw.Vector(Vector3.forward, headSize, offset, color); + GLDraw.Vector(Vector3.right, headSize, offset, color); + } + public static void DrawAxis(float headSize, Color upColor, Color fowardColor, Color rightColor, Vector3 offset) + { + GLDraw.Vector(Vector3.up, headSize, offset, upColor); + GLDraw.Vector(Vector3.forward, headSize, offset, fowardColor); + GLDraw.Vector(Vector3.right, headSize, offset, rightColor); + } + public static void DrawTransform(Transform transform, float headSize, Color color) + { + GLDraw.Vector(transform.up * transform.lossyScale.x, headSize, transform.position, color); + GLDraw.Vector(transform.forward * transform.lossyScale.z, headSize, transform.position, color); + GLDraw.Vector(transform.right * transform.lossyScale.y, headSize, transform.position, color); + } + public static void DrawTransform(Transform transform, float headSize, Color upColor, Color fowardColor, Color rightColor) + { + GLDraw.Vector(transform.up * transform.lossyScale.x, headSize, transform.position, upColor); + GLDraw.Vector(transform.forward * transform.lossyScale.z, headSize, transform.position, fowardColor); + GLDraw.Vector(transform.right * transform.lossyScale.y, headSize, transform.position, rightColor); + } + public static void DrawColliderBoundingBox(Collider collider, Color color) + { + Bounds box = collider.bounds; + GLDraw.WireframeCube(Vector3.forward * box.size.z, Vector3.up * box.size.y, Vector3.right * box.size.z, box.center, color); + } + public static void DrawCollider(Collider collider, Color color) + { + const int resolution = 12; + if (collider is BoxCollider) + { + BoxCollider box = collider as BoxCollider; + GLDraw.WireframeCube(Vector3.forward * box.size.z, Vector3.up * box.size.y, Vector3.right * box.size.z, box.center, color); + } + else if (collider is SphereCollider) + { + SphereCollider sphere = collider as SphereCollider; + GLDraw.WireframeSphere(sphere.radius, sphere.center, Vector3.forward, Vector3.up, color, resolution); + } + else if (collider is CapsuleCollider) + { + CapsuleCollider capsule = collider as CapsuleCollider; + GLDraw.WireframeCapsule(capsule.radius, capsule.center + capsule.height * Vector3.up / 2f, capsule.center - capsule.height * Vector3.up / 2f, color, resolution); + } + } + } +} diff --git a/src/GizmosLibraryPlugin/GizmoControls/ArrowControl.cs b/src/GizmosLibraryPlugin/GizmoControls/ArrowControl.cs new file mode 100644 index 0000000..2b345bb --- /dev/null +++ b/src/GizmosLibraryPlugin/GizmoControls/ArrowControl.cs @@ -0,0 +1,56 @@ +using System; +using UnityEngine; +using GizmosLibraryPlugin; +namespace UnityExplorer.GizmoControls +{ + internal class ArrowControl : BaseControl + { + private Vector3 selectedPosition; + + public Transform Transform; + + public Func Direction = (t) => Vector3.up; + + public float Lenght; + public float HeadLenght; + + public float MaxDistanceToSelect; + + public Color Color; + public Color SelectedColor; + + + public override Vector3 GetValue(Ray ray) + { + Vector3 direction = Direction(Transform); + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, direction, Transform.position, out _, out Vector3 currentSelectedPosition); + currentSelectedPosition = Vector3MathUtils.GetPositionWithReferencial(Transform, currentSelectedPosition); + float distanceWihReference = Vector3.Dot(currentSelectedPosition - selectedPosition, Vector3MathUtils.GetDirectionWithReferencial(Transform, direction)); + + return distanceWihReference * Vector3MathUtils.GetDirectionWithReferencial(Transform, direction); + } + + public override bool IsSelected(Ray ray) + { + Vector3 direction = Direction(Transform); + + if (Vector3MathUtils.GetDistanceFromLineToLineSegment(ray.direction, ray.origin, Transform.position, Transform.position + direction * Lenght * Scale) <= MaxDistanceToSelect) + { + Selected = true; + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, direction, Transform.position, out _, out selectedPosition); + selectedPosition = Vector3MathUtils.GetPositionWithReferencial(Transform, selectedPosition); + } + return Selected; + } + public override void Draw() + { + Vector3 direction = Direction(Transform); + + GLHelper.DrawOnGlobalReference(() => + { + Color color = Selected ? SelectedColor : Color; + GLDraw.Vector(direction * Lenght * Scale, HeadLenght * Scale, Transform.position, color); + }); + } + } +} diff --git a/src/GizmosLibraryPlugin/GizmoControls/BaseControl.cs b/src/GizmosLibraryPlugin/GizmoControls/BaseControl.cs new file mode 100644 index 0000000..1108360 --- /dev/null +++ b/src/GizmosLibraryPlugin/GizmoControls/BaseControl.cs @@ -0,0 +1,17 @@ +using UnityEngine; + +namespace UnityExplorer.GizmoControls +{ + public abstract class BaseControl + { + public float Scale = 1f; + public bool Selected { get; protected set; } = false; + public abstract bool IsSelected(Ray ray); + public abstract void Draw(); + public abstract T GetValue(Ray ray); + public virtual void Reset() + { + Selected = false; + } + } +} diff --git a/src/GizmosLibraryPlugin/GizmoControls/CircleControl.cs b/src/GizmosLibraryPlugin/GizmoControls/CircleControl.cs new file mode 100644 index 0000000..bf8ce04 --- /dev/null +++ b/src/GizmosLibraryPlugin/GizmoControls/CircleControl.cs @@ -0,0 +1,64 @@ +using System; +using UnityEngine; +using GizmosLibraryPlugin; + +namespace UnityExplorer.GizmoControls +{ + public class CircleControl : BaseControl + { + private Vector3 selectedPosition; + + private Vector3 selectedFowardDirection; + + public Transform Transform; + + public float DistanceForFullRevolution = 4f; + + public Func Normal = (t) => Vector3.up; + + public float Radius; + + public float MaxDistanceToSelect; + + public Color Color; + public Color SelectedColor; + + + public override float GetValue(Ray ray) + { + Vector3 foward = Vector3MathUtils.ReturnDirectionWithReferencial(Transform, selectedFowardDirection); + + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, foward, Transform.position, out _, out Vector3 currentXSelectedPosition); + currentXSelectedPosition = Vector3MathUtils.GetPositionWithReferencial(Transform, currentXSelectedPosition); + float distanceWihReference = Vector3.Dot(currentXSelectedPosition - selectedPosition, selectedFowardDirection); + return distanceWihReference * 360f / DistanceForFullRevolution; + } + + public override bool IsSelected(Ray ray) + { + Vector3 normal = Normal(Transform); + if (Vector3MathUtils.GetClosestPointFromLineToCircle(ray.direction, ray.origin, Transform.position, normal, Radius * Scale, out bool lineParalelToCircle, out bool isEquidistant, out Vector3 selectedPoint) <= MaxDistanceToSelect) + { + if (!lineParalelToCircle && !isEquidistant) + { + selectedPosition = selectedPoint; + selectedPosition = Vector3MathUtils.GetPositionWithReferencial(Transform, selectedPosition); + selectedFowardDirection = Vector3.Cross(normal, (selectedPoint - Transform.position).normalized); + selectedFowardDirection = Vector3MathUtils.GetDirectionWithReferencial(Transform, selectedFowardDirection); + Selected = true; + } + } + return Selected; + } + public override void Draw() + { + Vector3 normal = Normal(Transform); + GLHelper.DrawOnGlobalReference(() => + { + Vector3 randomUp = Vector3MathUtils.GetArbitraryPerpendicularVector(normal); + Color color = Selected ? SelectedColor : Color; + GLDraw.WireframeCircle(Radius * Scale, normal, randomUp, Transform.position, color, 16); + }); + } + } +} diff --git a/src/GizmosLibraryPlugin/GizmosAPI.cs b/src/GizmosLibraryPlugin/GizmosAPI.cs new file mode 100644 index 0000000..4168b72 --- /dev/null +++ b/src/GizmosLibraryPlugin/GizmosAPI.cs @@ -0,0 +1,40 @@ +using System; +using UnityEngine; + +namespace GizmosLibraryPlugin +{ + + public static class GizmosAPI + { + //GLHelper + public static Material GetDefaultMaterial() => GLHelper.GetDefaultMaterial(); + public static void SetDefaultMaterialPass(int pass = 0) => GLHelper.SetDefaultMaterialPass(pass); + public static void DrawWithReference(Transform reference, Action drawMethod) => GLHelper.DrawWithReference(reference, drawMethod); + public static void DrawOnGlobalReference(Action drawMethod) => GLHelper.DrawOnGlobalReference(drawMethod); + public static void DrawWithOrthoProjection(Action drawMethod) => GLHelper.DrawWithOrthoProjection(drawMethod); + public static void DrawAxis(float headSize, Color color, Vector3 offset) => GLHelper.DrawAxis(headSize, color, offset); + public static void DrawAxis(float headSize, Color upColor, Color fowardColor, Color rightColor, Vector3 offset) => GLHelper.DrawAxis(headSize, upColor, fowardColor, rightColor, offset); + public static void DrawTransform(Transform transform, float headSize, Color color) => GLHelper.DrawTransform(transform, headSize, color); + public static void DrawTransform(Transform transform, float headSize, Color upColor, Color fowardColor, Color rightColor) => GLHelper.DrawTransform(transform, headSize, upColor, fowardColor, rightColor); + public static void DrawCollider(Collider collider, Color color) => GLHelper.DrawCollider(collider, color); + public static void DrawColliderBoundingBox(Collider collider, Color color) => GLHelper.DrawColliderBoundingBox(collider, color); + //GLDraw + public static void DrawSimpleWireframeCube(Vector3 offset, Color color) => GLDraw.SimpleWireframeCube(offset, color); + public static void DrawWireframeCube(Vector3 foward, Vector3 up, Vector3 right, Vector3 offset, Color color) => GLDraw.WireframeCube(foward, up, right, offset, color); + public static void DrawWireframeCircle(float radius, Vector3 normal, Vector3 up, Vector3 offset, Color color, int resolution = 3, float startAngle = 0f, float endAngle = 2f * Mathf.PI, bool isWholeCircle = true) + => GLDraw.WireframeCircle(radius, normal, up, offset, color, resolution, startAngle, endAngle, isWholeCircle); + public static void DrawSimpleWireframeSphere(float radius, Vector3 offset, Color color, int resolution) + => GLDraw.SimpleWireframeSphere(radius, offset, color, resolution); + public static void DrawWireframeSphere(float radius, Vector3 offset, Vector3 foward, Vector3 up, Color color, int resolution = 3) + => GLDraw.WireframeSphere(radius, offset, foward, up, color, resolution); + public static void DrawWireframeHemisphere(float radius, Vector3 offset, Vector3 foward, Vector3 up, Color color, int resolution = 3) + => GLDraw.WireframeHemisphere(radius, offset, foward, up, color, resolution); + public static void DrawWireframeCapsule(float radius, Vector3 startPoint, Vector3 endPoint, Color color, int resolution = 3) + => GLDraw.WireframeCapsule(radius, startPoint, endPoint, color, resolution); + public static void DrawSimpleWireframeCapsule(float radius, float height, Vector3 up, Vector3 offset, Color color, int resolution = 3) + => GLDraw.SimpleWireframeCapsule(radius, height, up, offset, color, resolution); + public static void DrawWireframeCone(float coneRadiusStart, float coneRadiusEnd, Vector3 coneStart, Vector3 coneEnd, Color color, int resolution = 3) + => GLDraw.WireframeCone(coneRadiusStart, coneRadiusEnd, coneStart, coneEnd, color, resolution); + public static void DrawVector(Vector3 vector, float headSize, Vector3 offset, Color color) => GLDraw.Vector(vector, headSize, offset, color); + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/BaseInteractablePositionGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/BaseInteractablePositionGizmo.cs new file mode 100644 index 0000000..877c7f2 --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/BaseInteractablePositionGizmo.cs @@ -0,0 +1,113 @@ +using UnityEngine; +using UnityExplorer.GizmoControls; + +namespace UnityExplorer.TransformGizmos +{ + public abstract class BaseInteractablePositionGizmo : BaseTransformGizmo + { + ArrowControl x = new(); + ArrowControl y = new(); + ArrowControl z = new(); + + Vector3 initialPosition; + + protected float LineLenght; + protected float HeadLenght; + + public override void SetScale(float scale) + { + base.SetScale(scale); + x.Scale = scale; + y.Scale = scale; + z.Scale = scale; + } + + public void SetLineLenght(float lenght, float headLenght) + { + LineLenght = lenght; + HeadLenght = headLenght; + + x.Lenght = lenght; + x.HeadLenght = headLenght; + y.Lenght = lenght; + y.HeadLenght = headLenght; + z.Lenght = lenght; + z.HeadLenght = headLenght; + } + public abstract Vector3 GetXDirection(); + public abstract Vector3 GetYDirection(); + public abstract Vector3 GetZDirection(); + + public override void Set(Transform transform) + { + base.Set(transform); + + x.Lenght = LineLenght; + x.HeadLenght = HeadLenght; + x.Transform = transform; + x.Color = Color.red; + x.SelectedColor = Color.Lerp(Color.red, Color.white, 0.8f); + x.Direction = (t) => GetXDirection(); + + y.Lenght = LineLenght; + y.HeadLenght = HeadLenght; + y.Transform = transform; + y.Color = Color.yellow; + y.SelectedColor = Color.Lerp(Color.yellow, Color.white, 0.8f); + y.Direction = (t) => GetYDirection(); + + z.Lenght = LineLenght; + z.HeadLenght = HeadLenght; + z.Transform = transform; + z.Color = Color.cyan; + z.SelectedColor = Color.Lerp(Color.cyan, Color.white, 0.8f); + z.Direction = (t) => GetZDirection(); + } + + public override bool CheckSelected(Ray ray, float maxDistanceToSelect) + { + x.MaxDistanceToSelect = maxDistanceToSelect; + y.MaxDistanceToSelect = maxDistanceToSelect; + z.MaxDistanceToSelect = maxDistanceToSelect; + + x.IsSelected(ray); + y.IsSelected(ray); + z.IsSelected(ray); + + initialPosition = Vector3MathUtils.GetPositionWithReferencial(transform, transform.position); + + return IsSelected(); + } + + public override void OnRender() + { + x.Draw(); + y.Draw(); + z.Draw(); + } + public override void OnSelected(Ray ray) + { + Vector3 aditionToPosition = Vector3.zero; + + if (x.Selected) + aditionToPosition += x.GetValue(ray); + if (y.Selected) + aditionToPosition += y.GetValue(ray); + if (z.Selected) + aditionToPosition += z.GetValue(ray); + + transform.position = Vector3MathUtils.ReturnPositionFromReferencial(transform, initialPosition + aditionToPosition); + } + public override bool IsSelected() + { + return x.Selected || y.Selected || z.Selected; + } + + public override void Reset() + { + x.Reset(); + y.Reset(); + z.Reset(); + } + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/BaseTransformGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/BaseTransformGizmo.cs new file mode 100644 index 0000000..30cf33e --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/BaseTransformGizmo.cs @@ -0,0 +1,27 @@ +using UnityEngine; + +namespace UnityExplorer.TransformGizmos +{ + public abstract class BaseTransformGizmo + { + protected Transform transform; + + protected float Scale; + public virtual void SetScale(float scale) + { + Scale = scale; + } + + public virtual void Set(Transform transform) + { + this.transform = transform; + } + public abstract void Reset(); + + public abstract bool CheckSelected(Ray ray,float maxDistanceToSelect); + + public abstract void OnSelected(Ray ray); + public abstract bool IsSelected(); + public abstract void OnRender(); + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/FromCameraViewRotationGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/FromCameraViewRotationGizmo.cs new file mode 100644 index 0000000..a61869b --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/FromCameraViewRotationGizmo.cs @@ -0,0 +1,74 @@ +using UnityEngine; +using UnityExplorer.GizmoControls; + +namespace UnityExplorer.TransformGizmos +{ + public class FromCameraViewRotationGizmo : BaseTransformGizmo + { + CircleControl control = new(); + + float distanceForFullRevolution = 4f; + + Vector3 rotationAxis; + + Quaternion initialLocalRotation; + + public override void SetScale(float scale) + { + base.SetScale(scale); + control.Scale = scale; + } + + public override void Set(Transform transform) + { + base.Set(transform); + + control.DistanceForFullRevolution = distanceForFullRevolution; + control.Color = Color.blue; + control.SelectedColor = Color.Lerp(Color.blue, Color.white, 0.8f); + control.Radius = 1.5f; + control.Transform = transform; + control.Normal = (t) => rotationAxis; + } + + public override bool CheckSelected(Ray ray, float maxDistanceToSelect) + { + control.MaxDistanceToSelect = maxDistanceToSelect; + control.IsSelected(ray); + + rotationAxis = (Camera.current.transform.position - transform.position).normalized; + initialLocalRotation = transform.localRotation; + + return IsSelected(); + } + + public override bool IsSelected() + { + return control.Selected; + } + + public override void OnRender() + { + if (!control.Selected) + { + rotationAxis = (Camera.current.transform.position - transform.position).normalized; + } + + control.Draw(); + } + + public override void OnSelected(Ray ray) + { + float angle = control.GetValue(ray); + + transform.localRotation = initialLocalRotation * Quaternion.AngleAxis(angle, Vector3MathUtils.GetDirectionWithReferencial(transform, rotationAxis)); + } + + + public override void Reset() + { + control.Reset(); + } + } +} + diff --git a/src/GizmosLibraryPlugin/TransformGizmos/GlobalEulerAnglesGIzmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/GlobalEulerAnglesGIzmo.cs new file mode 100644 index 0000000..ed83b99 --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/GlobalEulerAnglesGIzmo.cs @@ -0,0 +1,50 @@ +using UnityEngine; +using GizmosLibraryPlugin; + +namespace UnityExplorer.TransformGizmos +{ + public class GlobalEulerAnglesGizmo : BaseTransformGizmo + { + public override bool CheckSelected(Ray ray, float maxDistanceToSelect) + { + return false; + } + + public override bool IsSelected() + { + return false; + } + + public override void OnRender() + { + GLHelper.DrawOnGlobalReference(() => + { + //Global position axis + Vector3 localFowardAxis = transform.forward; + Vector3 localRightAxis = transform.right; + + //Local Euler Rotation + Vector3 yRotationAxis = Vector3.Cross(Vector3.up, localFowardAxis); + //if (yRotationAxis.ApproxEquals(Vector3.zero)) + if (Math.Abs(yRotationAxis.x) < 0.0001 && Math.Abs(yRotationAxis.y) < 0.0001 && Math.Abs(yRotationAxis.z) < 0.0001) + { + yRotationAxis = localRightAxis; + } + + GLDraw.WireframeCircle(1f, yRotationAxis, localFowardAxis, transform.position, Color.red, 16); + GLDraw.WireframeCircle(1f, Vector3.up, Vector3.forward, transform.position, Color.yellow, 16); + GLDraw.WireframeCircle(1f, localFowardAxis, transform.up, transform.position, Color.cyan, 16); + }); + } + + public override void OnSelected(Ray ray) + { + return; + } + + public override void Reset() + { + return; + } + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/GlobalPositionGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/GlobalPositionGizmo.cs new file mode 100644 index 0000000..591eafa --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/GlobalPositionGizmo.cs @@ -0,0 +1,38 @@ +using UnityEngine; +using GizmosLibraryPlugin; + +namespace UnityExplorer.TransformGizmos +{ + public class GlobalPositionGizmo : BaseTransformGizmo + { + public override bool CheckSelected(Ray ray, float maxDistanceToSelect) + { + return false; + } + + public override bool IsSelected() + { + return false; + } + + public override void OnRender() + { + GLHelper.DrawOnGlobalReference(() => + { + GLDraw.Vector(Vector3.right, 0.25f, transform.position, Color.red); + GLDraw.Vector(Vector3.up, 0.25f, transform.position, Color.yellow); + GLDraw.Vector(Vector3.forward, 0.25f, transform.position, Color.cyan); + }); + } + + public override void OnSelected(Ray ray) + { + return; + } + + public override void Reset() + { + return; + } + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/LocalEulerAngleGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/LocalEulerAngleGizmo.cs new file mode 100644 index 0000000..099421a --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/LocalEulerAngleGizmo.cs @@ -0,0 +1,115 @@ +using UnityEngine; +using UnityExplorer.GizmoControls; + +namespace UnityExplorer.TransformGizmos +{ + internal class LocalEulerAngleGizmo : BaseTransformGizmo + { + CircleControl x = new(); + CircleControl y = new(); + CircleControl z = new(); + + float distanceForFullRevolution = 4f; + + Vector3 initialLocalEulerRotation; + + public override void SetScale(float scale) + { + base.SetScale(scale); + x.Scale = scale; + y.Scale = scale; + z.Scale = scale; + } + public override void Set(Transform transform) + { + base.Set(transform); + + x.DistanceForFullRevolution = distanceForFullRevolution; + x.Color = Color.red; + x.SelectedColor = Color.Lerp(Color.red, Color.white, 0.8f); + x.Radius = 1f; + x.Transform = transform; + x.Normal = (t) => + { + Vector3 localFowardAxis = t.forward; + Vector3 localUp = Vector3MathUtils.ReturnDirectionWithReferencial(t, Vector3.up); + Vector3 yRotationAxis = Vector3.Cross(localUp, localFowardAxis); + //if (yRotationAxis.ApproxEquals(Vector3.zero)) + if (Math.Abs(yRotationAxis.x) < 0.0001 && Math.Abs(yRotationAxis.y) < 0.0001 && Math.Abs(yRotationAxis.z) < 0.0001) + { + yRotationAxis = t.right; + } + return yRotationAxis; + }; + + y.DistanceForFullRevolution = distanceForFullRevolution; + y.Color = Color.yellow; + y.SelectedColor = Color.Lerp(Color.yellow, Color.white, 0.8f); + y.Radius = 1f; + y.Transform = transform; + y.Normal = (t) => + { + Vector3 localUp = Vector3MathUtils.ReturnDirectionWithReferencial(t, Vector3.up); + return localUp; + }; + + z.DistanceForFullRevolution = distanceForFullRevolution; + z.Color = Color.cyan; + z.SelectedColor = Color.Lerp(Color.cyan, Color.white, 0.8f); + z.Radius = 1f; + z.Transform = transform; + z.Normal = (t) => + { + return t.forward; + }; + } + + public override bool CheckSelected(Ray ray, float maxDistanceToSelect) + { + x.MaxDistanceToSelect = maxDistanceToSelect; + y.MaxDistanceToSelect = maxDistanceToSelect; + z.MaxDistanceToSelect = maxDistanceToSelect; + + x.IsSelected(ray); + y.IsSelected(ray); + z.IsSelected(ray); + + initialLocalEulerRotation = transform.localEulerAngles; + + return IsSelected(); + } + + public override bool IsSelected() + { + return x.Selected || y.Selected || z.Selected; + } + + public override void OnRender() + { + x.Draw(); + y.Draw(); + z.Draw(); + } + + public override void OnSelected(Ray ray) + { + Vector3 aditionToAngle = Vector3.zero; + + if (x.Selected) + aditionToAngle.x = x.GetValue(ray); + if (y.Selected) + aditionToAngle.y = y.GetValue(ray); + if (z.Selected) + aditionToAngle.z = z.GetValue(ray); + + transform.localEulerAngles = initialLocalEulerRotation + aditionToAngle; + } + + public override void Reset() + { + x.Reset(); + y.Reset(); + z.Reset(); + } + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/LocalPositionGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/LocalPositionGizmo.cs new file mode 100644 index 0000000..3f6e145 --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/LocalPositionGizmo.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +namespace UnityExplorer.TransformGizmos +{ + public class LocalPositionGizmo : BaseInteractablePositionGizmo + { + public override void Set(Transform transform) + { + LineLenght = 1.1f; + HeadLenght = 0.25f; + base.Set(transform); + } + public override Vector3 GetXDirection() + { + if (transform.parent == null) + return Vector3.right; + + return transform.parent.right; + } + + public override Vector3 GetYDirection() + { + if (transform.parent == null) + return Vector3.up; + + return transform.parent.up; + } + + public override Vector3 GetZDirection() + { + if (transform.parent == null) + return Vector3.forward; + + return transform.parent.forward; + } + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/LocalPositionSphericalCoordinates.cs b/src/GizmosLibraryPlugin/TransformGizmos/LocalPositionSphericalCoordinates.cs new file mode 100644 index 0000000..4e763a5 --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/LocalPositionSphericalCoordinates.cs @@ -0,0 +1,191 @@ +using UnityEngine; +using GizmosLibraryPlugin; + +namespace UnityExplorer.TransformGizmos +{ + //TODO recreate this with GizmoControls and fix bug when radius = 0 -> position = NaN + public class LocalPositionCylindricalCoordinates : BaseTransformGizmo + { + // x = radius + // y = y + // z = theta + + bool selectedRadius = false; + bool selectedTheta = false; + bool selectedY = false; + + float lineSegmentDistance = 0.25f; + + Vector3 selectedRadiusPosition; + float aditionalThetaRotation; + Vector3 selectedYPosition; + + Vector3 initialLocalPositionCylindrical; + + protected Vector3 GetPositionWithReferencial(Transform transform, Vector3 position) + { + if (transform.parent == null) + return position; + + return transform.parent.InverseTransformPoint(position); + } + protected Vector3 ReturnPositionFromReferencial(Transform transform, Vector3 position) + { + if (transform.parent == null) + return position; + + return transform.parent.TransformPoint(position); + } + protected Vector3 GetDirectionWithReferencial(Transform transform, Vector3 direction) + { + if (transform.parent == null) + return direction; + + return transform.parent.InverseTransformDirection(direction); + } + protected Vector3 ReturnDirectionFromReferencial(Transform transform, Vector3 direction) + { + if (transform.parent == null) + return direction; + + return transform.parent.TransformDirection(direction); + } + + public Vector3 GetRadiusDirection(Transform transform) + { + if (transform.parent == null) + return new Vector3(transform.position.x, 0f, transform.position.y).normalized; + + return transform.parent.TransformDirection(new Vector3(transform.localPosition.x, 0f, transform.localPosition.z).normalized); + } + public Vector3 GetUpDirection(Transform transform) + { + Vector3 up = Vector3.up; + if (transform.parent != null) + { + up = transform.parent.up; + } + return up; + } + public Vector3 GetThetaDirection(Vector3 radiusDirection, Vector3 upDirection) + { + return Vector3.Cross(upDirection, radiusDirection); + } + + + public static Vector3 FromCylindricalToCartesian(Vector3 cylindrical) + { + if(cylindrical.x < 0f) + { + cylindrical.x = 0f; + //cylindrical.z += Mathf.PI; + } + return new Vector3(cylindrical.x * Mathf.Cos(cylindrical.z), cylindrical.y, cylindrical.x * Mathf.Sin(cylindrical.z)); + } + public static Vector3 FromCartesianToCylindrical(Vector3 cartesian) + { + float y = cartesian.y; + Vector2 polar = new(cartesian.z, cartesian.x); + float radius = polar.magnitude; + float theta = 0f; + if (!( Mathf.Abs(polar.x) < 1e-2f || Mathf.Abs(polar.y) < 1e-2f)) + { + theta = Mathf.Atan2(polar.x, polar.y); + } + return new Vector3(radius, y, theta); + } + + public override bool CheckSelected(Ray ray, float maxDistanceToSelect) + { + Vector3 radius = GetRadiusDirection(transform); + Vector3 up = GetUpDirection(transform); + Vector3 centerAxis = ReturnPositionFromReferencial(transform, new Vector3(0f, transform.localPosition.y, 0f)); + float radiusValue = ReturnDirectionFromReferencial(transform, new Vector3(transform.localPosition.x, 0f, transform.localPosition.z)).magnitude; + + if (Vector3MathUtils.GetDistanceFromLineToLineSegment(ray.direction, ray.origin, transform.position, transform.position + radius * lineSegmentDistance) <= maxDistanceToSelect) + { + selectedRadius = true; + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, radius, transform.position, out _, out selectedRadiusPosition); + selectedRadiusPosition = GetPositionWithReferencial(transform, selectedRadiusPosition); + } + if (Vector3MathUtils.GetClosestPointFromLineToCircle(ray.direction, ray.origin, centerAxis, up, radiusValue, out bool lineParalelToCircle, out _, out _) <= maxDistanceToSelect) + { + if (!lineParalelToCircle) + { + selectedTheta = true; + aditionalThetaRotation = 0f; + } + } + if (Vector3MathUtils.GetDistanceFromLineToLineSegment(ray.direction, ray.origin, transform.position, transform.position + up * lineSegmentDistance) <= maxDistanceToSelect) + { + selectedY = true; + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, up, transform.position, out _, out selectedYPosition); + selectedYPosition = GetPositionWithReferencial(transform, selectedYPosition); + } + + initialLocalPositionCylindrical = FromCartesianToCylindrical(GetPositionWithReferencial(transform, transform.position)); + + return IsSelected(); + } + + public override void OnRender() + { + Vector3 radius = GetRadiusDirection(transform); + Vector3 up = GetUpDirection(transform); + Vector3 centerAxis = ReturnPositionFromReferencial(transform, new Vector3(0f, transform.localPosition.y, 0f)); + + float radiusValue = ReturnDirectionFromReferencial(transform, new Vector3(transform.localPosition.x, 0f, transform.localPosition.z)).magnitude; + + GLHelper.DrawOnGlobalReference(() => + { + GLDraw.Vector(radius, lineSegmentDistance, transform.position, selectedRadius ? Color.Lerp(Color.red, Color.white, 0.8f) : Color.red); + GLDraw.Vector(up, lineSegmentDistance, transform.position, selectedY ? Color.Lerp(Color.yellow, Color.white, 0.8f) : Color.yellow); + GLDraw.WireframeCircle(radiusValue, up, radius, centerAxis, selectedTheta ? Color.Lerp(Color.cyan, Color.white, 0.8f) : Color.cyan, 16); + }); + } + public override void OnSelected(Ray ray) + { + Vector3 radius = GetRadiusDirection(transform); + Vector3 up = GetUpDirection(transform); + + float mouseScrollDelta = UniverseLib.Input.InputManager.MouseScrollDelta.y * 0.1f; + + Vector3 cylindricalAditionToPosition = Vector3.zero; + if (selectedRadius) + { + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, radius, transform.position, out _, out Vector3 currentRadiusSelectedPosition); + currentRadiusSelectedPosition = GetPositionWithReferencial(transform, currentRadiusSelectedPosition); + + float distanceWihReference = Vector3.Dot(currentRadiusSelectedPosition - selectedRadiusPosition, GetDirectionWithReferencial(transform, radius)); + + cylindricalAditionToPosition.x = distanceWihReference; + } + if (selectedTheta) + { + aditionalThetaRotation += mouseScrollDelta * Mathf.Deg2Rad; + cylindricalAditionToPosition.z = aditionalThetaRotation; + } + if (selectedY) + { + Vector3MathUtils.GetClosestPointFromLines(ray.direction, ray.origin, up, transform.position, out _, out Vector3 currentYSelectedPosition); + currentYSelectedPosition = GetPositionWithReferencial(transform, currentYSelectedPosition); + float distanceWihReference = Vector3.Dot(currentYSelectedPosition - selectedYPosition, GetDirectionWithReferencial(transform, up)); + cylindricalAditionToPosition.y = distanceWihReference; + } + transform.position = ReturnPositionFromReferencial(transform, FromCylindricalToCartesian(initialLocalPositionCylindrical + cylindricalAditionToPosition)); + } + public override bool IsSelected() + { + return selectedRadius || selectedTheta || selectedY; + } + + public override void Reset() + { + aditionalThetaRotation = 0f; + + selectedRadius = false; + selectedTheta = false; + selectedY = false; + } + } +} diff --git a/src/GizmosLibraryPlugin/TransformGizmos/TransformOrientationGizmo.cs b/src/GizmosLibraryPlugin/TransformGizmos/TransformOrientationGizmo.cs new file mode 100644 index 0000000..15eca38 --- /dev/null +++ b/src/GizmosLibraryPlugin/TransformGizmos/TransformOrientationGizmo.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace UnityExplorer.TransformGizmos +{ + public class TransformOrientationGizmo : BaseInteractablePositionGizmo + { + public override void Set(Transform transform) + { + LineLenght = 1.1f; + HeadLenght = 0.25f; + base.Set(transform); + } + public override Vector3 GetXDirection() + { + return transform.right; + } + + public override Vector3 GetYDirection() + { + return transform.up; + } + + public override Vector3 GetZDirection() + { + return transform.forward; + } + } +} diff --git a/src/GizmosLibraryPlugin/Vector3MathUtils.cs b/src/GizmosLibraryPlugin/Vector3MathUtils.cs new file mode 100644 index 0000000..8593290 --- /dev/null +++ b/src/GizmosLibraryPlugin/Vector3MathUtils.cs @@ -0,0 +1,144 @@ +using UnityEngine; + +namespace UnityExplorer +{ + public static class Vector3MathUtils + { + public static Vector3 GetPositionWithReferencial(Transform t, Vector3 position) + { + if (t.parent == null) + return position; + + return t.parent.InverseTransformPoint(position); + } + public static Vector3 ReturnPositionFromReferencial(Transform t, Vector3 position) + { + if (t.parent == null) + return position; + + return t.parent.TransformPoint(position); + } + public static Vector3 GetDirectionWithReferencial(Transform t, Vector3 direction) + { + if (t.parent == null) + return direction; + + return t.parent.InverseTransformDirection(direction); + } + public static Vector3 ReturnDirectionWithReferencial(Transform t, Vector3 direction) + { + if (t.parent == null) + return direction; + + return t.parent.TransformDirection(direction); + } + + //From https://math.stackexchange.com/questions/511370/how-to-rotate-one-vector-about-another + public static void GetParalelAndPerpendicularComponent(Vector3 axis, Vector3 v, out Vector3 paralel, out Vector3 perpendicular) + { + paralel = Vector3.Dot(v, axis) * axis / axis.magnitude; + perpendicular = v - paralel; + } + public static Vector3 GetRotationVector(Vector3 axis, Vector3 perpendicularComponent) + { + return Vector3.Cross(axis, perpendicularComponent); + } + public static Vector3 GetRotatedVectorComponent(Vector3 rotationVector, Vector3 perpendicularComponent, float angle) + { + float x1 = Mathf.Cos(angle) / perpendicularComponent.sqrMagnitude; + float x2 = Mathf.Sin(angle); + + return perpendicularComponent.sqrMagnitude * (x1 * perpendicularComponent + x2 * rotationVector); + } + + //From https://codereview.stackexchange.com/questions/43928/algorithm-to-get-an-arbitrary-perpendicular-vector + public static Vector3 GetArbitraryPerpendicularVector(Vector3 v) + { + if (v.magnitude == 0f) + { + return Vector3.forward; + } + Vector3 firstAttempt = Vector3.Cross(v, Vector3.forward); + if (firstAttempt.magnitude != 0f) + { + return firstAttempt.normalized; + } + else + { + return Vector3.Cross(v, Vector3.up).normalized; + } + } + //From https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line + public static float GetDistanceFromLine(Vector3 point, Vector3 direction, Vector3 lineStartingPoint) + { + return Vector3.Cross(point - lineStartingPoint, direction).magnitude / direction.magnitude; + } + //From https://en.wikipedia.org/wiki/Skew_lines#Distance + public static float GetDistanceFromLineToLineSegment(Vector3 direction, Vector3 lineStartingPoint, Vector3 segmentStartingPoint, Vector3 segmentEndPoint) + { + Vector3 directionOfSegment = segmentEndPoint - segmentStartingPoint; + Vector3 n = Vector3.Cross(direction, directionOfSegment); + Vector3 n2 = Vector3.Cross(directionOfSegment, n); + Vector3 closestPointFromLineToSegment = lineStartingPoint + Vector3.Dot(segmentStartingPoint - lineStartingPoint, n2) / (Vector3.Dot(direction, n2)) * direction; + + return GetDistanceFromLineSegment(closestPointFromLineToSegment, segmentStartingPoint, segmentEndPoint); + } + public static void GetClosestPointFromLines(Vector3 directionOne, Vector3 lineStartingPointOne, Vector3 directionTwo, Vector3 lineStartingPointTwo, out Vector3 closesPointOnLineOne, out Vector3 closesPointOnLineTwo) + { + Vector3 n = Vector3.Cross(directionOne, directionTwo); + Vector3 n2 = Vector3.Cross(directionTwo, n); + closesPointOnLineOne = lineStartingPointOne + Vector3.Dot(lineStartingPointTwo - lineStartingPointOne, n2) / (Vector3.Dot(directionOne, n2)) * directionOne; + + Vector3 n1 = Vector3.Cross(directionOne, n); + closesPointOnLineTwo = lineStartingPointTwo + Vector3.Dot(lineStartingPointOne - lineStartingPointTwo, n1) / (Vector3.Dot(directionTwo, n1)) * directionTwo; + + } + public static float GetDistanceFromLineSegment(Vector3 point, Vector3 startingPoint, Vector3 endPoint) + { + float distanceFromStartingPoint = Vector3.Distance(point, startingPoint); + float distanceFromEndPoint = Vector3.Distance(point, endPoint); + float distanceFromLine = GetDistanceFromLine(point, endPoint - startingPoint, startingPoint); + + return Mathf.Min(distanceFromStartingPoint, distanceFromEndPoint, distanceFromLine); + } + //From https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf + public static float GetClosestPointToCircle(Vector3 point, Vector3 center, Vector3 normal, float radius,out bool isEquidistant, out Vector3 closestPoint) + { + Vector3 delta = point - center; + float dotND = Vector3.Dot(normal, delta); + Vector3 QmC = delta - dotND * normal; + float lenghtQmC = QmC.magnitude; + if (lenghtQmC > 0f) + { + closestPoint = center + radius * (QmC / lenghtQmC); + isEquidistant = false; + return (point - closestPoint).magnitude; + } + else + { + closestPoint = Vector3.zero; + isEquidistant = true; + return delta.sqrMagnitude + radius * radius; + } + } + //From https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection + public static float GetClosestPointFromLineToCircle(Vector3 direction, Vector3 point, Vector3 center, Vector3 normal, float radius, out bool lineParalelToCircle, out bool isEquidistant, out Vector3 closestPoint) + { + float ln = Vector3.Dot(direction, normal); + if(ln == 0f) //Yes, I know we are ignoring when the line and the circle are on the same plane, I just don't want to write a proper GetClosestPointFromLineToCircle + { + lineParalelToCircle = true; + isEquidistant = false; + closestPoint = Vector3.zero; + return 0f; + } + + float d = Vector3.Dot((center - point), normal) / ln; + Vector3 intersectionPoint = point + direction * d; + + lineParalelToCircle = false; + + return GetClosestPointToCircle(intersectionPoint, center, normal, radius, out isEquidistant, out closestPoint); + } + } +} diff --git a/src/Inspectors/GameObjectInspector.cs b/src/Inspectors/GameObjectInspector.cs index dd1442b..ac99ee5 100644 --- a/src/Inspectors/GameObjectInspector.cs +++ b/src/Inspectors/GameObjectInspector.cs @@ -64,6 +64,7 @@ namespace UnityExplorer.Inspectors public override void CloseInspector() { + Controls.TransformControl.DisableGizmo(); InspectorManager.ReleaseInspector(this); } diff --git a/src/UI/Panels/BonesPanel.cs b/src/UI/Panels/BonesPanel.cs index a297a5c..4d6e936 100644 --- a/src/UI/Panels/BonesPanel.cs +++ b/src/UI/Panels/BonesPanel.cs @@ -39,6 +39,10 @@ namespace UnityExplorer.UI.Panels boneScrollPool.Initialize(this); DoneScrollPoolInit = true; } + + if (!active){ + DisableGizmos(); + } } protected override void ConstructPanelContent() @@ -79,6 +83,13 @@ namespace UnityExplorer.UI.Panels animator.enabled = value; } + public void DisableGizmos(){ + // TODO: Create a structure for each cell data instead + foreach (BonesCell cell in boneScrollPool.CellPool){ + cell.DisableGizmo(); + } + } + public void RestoreBoneState(string boneName) { foreach (Transform bone in bones){ diff --git a/src/UI/Widgets/GameObjects/TransformControls.cs b/src/UI/Widgets/GameObjects/TransformControls.cs index 979cd22..33adaeb 100644 --- a/src/UI/Widgets/GameObjects/TransformControls.cs +++ b/src/UI/Widgets/GameObjects/TransformControls.cs @@ -12,6 +12,8 @@ namespace UnityExplorer.UI.Widgets public AxisControl CurrentSlidingAxisControl { get; set; } + Dropdown gizmoDropdown; + Vector3Control PositionControl; Vector3Control LocalPositionControl; Vector3Control RotationControl; @@ -98,6 +100,25 @@ namespace UnityExplorer.UI.Widgets LocalPositionControl = Vector3Control.Create(this, transformGroup, "Local Position:", TransformType.LocalPosition); RotationControl = Vector3Control.Create(this, transformGroup, "Rotation:", TransformType.Rotation); ScaleControl = Vector3Control.Create(this, transformGroup, "Scale:", TransformType.Scale); + + GameObject gizmoObj = UIFactory.CreateDropdown(transformGroup, "Gizmo_Dropdown", out gizmoDropdown, null, 14, (idx) => { + ExplorerBehaviour.GizmoTools.targetTransform = Target.transform; + ExplorerBehaviour.GizmoTools.ChangeGizmo((GizmoType)idx); + } + ); + UIFactory.SetLayoutElement(gizmoObj, minHeight: 25, minWidth: 150); + gizmoDropdown.options.Add(new Dropdown.OptionData("No Gizmo")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Local Position")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Local Rotation")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Global Position")); + gizmoDropdown.options.Add(new Dropdown.OptionData("Global Rotation")); + gizmoDropdown.captionText.text = "No Gizmo"; + } + + public void DisableGizmo(){ + if (gizmoDropdown.value != 0){ + gizmoDropdown.value = 0; + } } } }