using UnityEngine; using Lean.Common; using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute; namespace Lean.Touch { /// This script allows you to drag mesh vertices using finger drags. [RequireComponent(typeof(MeshFilter))] [HelpURL(LeanTouch.PlusHelpUrlPrefix + "LeanDragDeformMesh")] [AddComponentMenu(LeanTouch.ComponentPathPrefix + "Drag Deform Mesh")] public class LeanDragDeformMesh : MonoBehaviour { /// The method used to find fingers to use with this component. See LeanFingerFilter documentation for more information. public LeanFingerFilter Use = new LeanFingerFilter(true); /// Radius around the finger the vertices will be moved in scaled screen space. public float ScaledRadius { set { scaledRadius = value; } get { return scaledRadius; } } [FSA("ScaledRadius")] [SerializeField] private float scaledRadius = 50.0f; /// Should mesh deformation be applied to an attached MeshCollider? public bool ApplyToMeshCollider { set { applyToMeshCollider = value; } get { return applyToMeshCollider; } } [FSA("ApplyToMeshCollider")] [SerializeField] private bool applyToMeshCollider; /// The camera this component will calculate using. /// None/null = MainCamera. public Camera Camera { set { _camera = value; } get { return _camera; } } [FSA("Camera")] [SerializeField] private Camera _camera; // The cached mesh filter [System.NonSerialized] private MeshFilter cachedMeshFilter; // The cached mesh collider [System.NonSerialized] private MeshCollider cachedMeshCollider; // Stores a duplicate of the MeshFilter's mesh private Mesh deformedMesh; // Stores the current vertex position array private Vector3[] deformedVertices; /// If you've set Use to ManuallyAddedFingers, then you can call this method to manually add a finger. public void AddFinger(LeanFinger finger) { Use.AddFinger(finger); } /// If you've set Use to ManuallyAddedFingers, then you can call this method to manually remove a finger. public void RemoveFinger(LeanFinger finger) { Use.RemoveFinger(finger); } /// If you've set Use to ManuallyAddedFingers, then you can call this method to manually remove all fingers. public void RemoveAllFingers() { Use.RemoveAllFingers(); } #if UNITY_EDITOR protected virtual void Reset() { Use.UpdateRequiredSelectable(gameObject); } #endif protected virtual void Awake() { Use.UpdateRequiredSelectable(gameObject); } protected virtual void Update() { // Get the fingers we want to use to translate var fingers = Use.UpdateAndGetFingers(); // Make sure the camera exists var camera = LeanHelper.GetCamera(_camera, gameObject); if (camera != null) { if (cachedMeshFilter == null) cachedMeshFilter = GetComponent(); if (cachedMeshFilter.sharedMesh != null) { // Duplicate mesh? if (deformedMesh == null) { deformedMesh = cachedMeshFilter.sharedMesh = Instantiate(cachedMeshFilter.sharedMesh); } // Duplicate vertices if (deformedVertices == null || deformedVertices.Length != cachedMeshFilter.sharedMesh.vertexCount) { deformedVertices = cachedMeshFilter.sharedMesh.vertices; } var scalingFactor = LeanTouch.ScalingFactor; var deformed = false; // Go through all vertices and find the screen point for (var i = deformedVertices.Length - 1; i >= 0; i--) { var worldPoint = transform.TransformPoint(deformedVertices[i]); var screenPoint = camera.WorldToScreenPoint(worldPoint); // Go through all fingers for this vertex for (var j = fingers.Count - 1; j >= 0; j--) { var finger = fingers[j]; var scaledDist = Vector2.Distance(screenPoint, finger.ScreenPosition) * scalingFactor; // Is this finger within the required scaled radius of the vertex? if (scaledDist <= scaledRadius) { deformed = true; // Shift screen point screenPoint.x += finger.ScreenDelta.x; screenPoint.y += finger.ScreenDelta.y; // Un-transform it back to local space and write worldPoint = camera.ScreenToWorldPoint(screenPoint); deformedVertices[i] = transform.InverseTransformPoint(worldPoint); } } } // If deformed, apply changes if (deformed == true) { deformedMesh.vertices = deformedVertices; deformedMesh.RecalculateBounds(); deformedMesh.RecalculateNormals(); if (applyToMeshCollider == true) { if (cachedMeshCollider == null) cachedMeshCollider = GetComponent(); if (cachedMeshCollider != null) { cachedMeshCollider.sharedMesh = null; // Set to null first to force it to update cachedMeshCollider.sharedMesh = deformedMesh; } } } } } else { Debug.LogError("Failed to find camera. Either tag your cameras MainCamera, or set one in this component.", this); } } } } #if UNITY_EDITOR namespace Lean.Touch.Editor { using TARGET = LeanDragDeformMesh; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET))] public class LeanDragDeformMesh_Editor : LeanEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("Use"); Draw("scaledRadius", "Radius around the finger the vertices will be moved in scaled screen space."); Draw("applyToMeshCollider", "Should mesh deformation be applied to an attached MeshCollider?"); Draw("_camera", "The camera this component will calculate using.\n\nNone/null = MainCamera."); } } } #endif