/// <summary>Searches the second vertex needed for Find2VerticesForVertexSnapping method amongst SELECTED GameObject PIVOTS.</summary> /// <param name="transform">The parent</param> /// <param name="lastClosestDistanceGUIspace">The distance to cursor from the closest vertex in GUI space so far</param> /// <param name="meshFilter">The reusable mesh filter variable</param> /// <param name="renderer">The reusable renderer variable</param> /// <param name="vertices">The reusable vertices array</param> /// <param name="closestVertexGUISpace">INPUT, the previously found closest vertex to the mouse cursor amongst NON-selected meshes</param> /// <param name="closestSelectedVertexWorldSpace">OUTPUT Is returned as the closest vertex to the mouse cursor amongst SELECTED /// models world space</param> public static void IterateChildGameObjects(Transform transform, ref GameObject ClosestGO, ref float lastClosestDistanceGUIspace, MeshFilter meshFilter, MeshRenderer renderer, Vector3[] vertices, ref Vector2 closestVertexGUISpace, ref Vector3 closestSelectedVertexWorldSpace, ref VertexSnappingResult methodResult) { //#colreg(red*0.3); if (transform != null && transform.gameObject != null && transform.gameObject.activeInHierarchy && transform.gameObject.activeSelf) { Vector3 worldPoint = transform.position; float distance = Vector2.SqrMagnitude(closestVertexGUISpace - HandleUtility.WorldToGUIPoint(worldPoint)); if (lastClosestDistanceGUIspace < 0 || distance < lastClosestDistanceGUIspace) // find the closest pivot GUI-space { lastClosestDistanceGUIspace = distance; ClosestGO = transform.gameObject; closestSelectedVertexWorldSpace = worldPoint; // our second ultimate goal methodResult = VertexSnappingResult.BothVectorsFound; } int children = transform.childCount; for (int i = 0; i < children; ++i) { IterateChildGameObjects(transform.GetChild(i), ref ClosestGO, ref lastClosestDistanceGUIspace, meshFilter, renderer, vertices, ref closestVertexGUISpace, ref closestSelectedVertexWorldSpace, ref methodResult); } } //#endcolreg }
/// <summary>Finds 2 3d points in World space, where 1st is the closest vertex to the mouse cursor /// amongst NON-selected models, and the 2nd one is the closest vertex to the mouse cursor amongst /// SELECTED models.</summary> /// <param name="sceneCam">The camera of the editor window</param> /// <param name="mousePos">The screen space position of the mouse</param> /// <param name="closestVertexWorldSpace">Is returned as the closest vertex to the mouse cursor amongst NON-selected models</param> /// <param name="closestSelectedVertexWorldSpace">Is returned as the closest vertex to the mouse cursor amongst SELECTED models</param> /// <param name="returnOnly1stVertex">If set, ignores whether there is any selected models and only searches for 1 vertex /// which is the closest vertex to the mouse cursor amongst ALL models</param> /// <param name="ignoreZeroesOnX">When searching for a vertex among SELECTED models, /// ignore those that have zero values in x variable in local space</param> /// <param name="ignoreZeroesOnY">When searching for a vertex among SELECTED models, /// ignore those that have zero values in y variable in local space</param> /// <param name="ignoreZeroesOnZ">When searching for a vertex among SELECTED models, /// ignore those that have zero values in z variable in local space</param> /// <param name="pivot">Transformation pivot moved to local space which is subtracted before we check for zeroes</param> public static VertexSnappingResult Find2VerticesForVertexSnapping(Camera sceneCam, Vector2 mousePos, out Vector3 closestVertexWorldSpace, out Vector3 closestSelectedVertexWorldSpace, out GameObject ClosestGO, bool returnOnly1stVertex = false, bool ignoreZeroesOnX = false, bool ignoreZeroesOnY = false, bool ignoreZeroesOnZ = false, Vector3 pivot = default(Vector3)) { //#colreg(red*0.3); ClosestGO = null; VertexSnappingResult methodResult = VertexSnappingResult.NoVectorsFound; closestVertexWorldSpace = Vector3.zero; closestSelectedVertexWorldSpace = Vector3.zero; Object[] allMeshRenderers = Object.FindObjectsOfType(typeof(MeshRenderer)) as Object[]; Object[] allSkinnedMeshes = Object.FindObjectsOfType(typeof(SkinnedMeshRenderer)) as Object[]; Object[] allMeshes = new Object[allMeshRenderers.Length + allSkinnedMeshes.Length]; allMeshRenderers.CopyTo(allMeshes, 0); allSkinnedMeshes.CopyTo(allMeshes, allMeshRenderers.Length); // Find world space things Ray mouseRayWorldSpace = sceneCam.ScreenPointToRay(mousePos); Bounds mouseBoundsWorldSpace = new Bounds(mouseRayWorldSpace.origin, new Vector3(1f, 1f, 1f)); // Find GUI space things Vector2 mouseGUIPos = HandleUtility.WorldToGUIPoint(mouseRayWorldSpace.origin); Rect mouseRect = WorldSpaceBoundsToGUISpaceRect(mouseBoundsWorldSpace); mouseRect.xMin -= sceneCam.pixelWidth * 0.01f; mouseRect.yMin -= sceneCam.pixelHeight * 0.01f; mouseRect.xMax += sceneCam.pixelWidth * 0.01f; mouseRect.yMax += sceneCam.pixelHeight * 0.01f; Vector3[] vertices = null; MeshFilter filter = null; Rect rect; MeshRenderer meshRenderer = null; SkinnedMeshRenderer skinnedRenderer = null; Transform currentTransform = null; Vector2 closestVertexGUISpace = Vector2.zero; float lastClosestDistanceGUIspace = 700; // This is the radius of the search. Hardcoded, no need to make it modular. Vector3 vertex = Vector3.zero; // Find a vertex, among non-selected meshes, that is closest to the mouse. for (int i = 0; i < allMeshes.Length; i++) { meshRenderer = allMeshes[i] as MeshRenderer; bool checkSuccessful = false; if (meshRenderer != null && meshRenderer.gameObject != null && meshRenderer.gameObject.transform != null && meshRenderer.enabled && meshRenderer.isVisible && meshRenderer.gameObject.activeInHierarchy && meshRenderer.gameObject.activeSelf && (!Selection.Contains(meshRenderer.gameObject) || returnOnly1stVertex)) { filter = null; filter = meshRenderer.gameObject.GetComponent <MeshFilter>(); if (filter != null && filter.sharedMesh != null) { rect = WorldSpaceBoundsToGUISpaceRect(meshRenderer.bounds); if (rect.Overlaps(mouseRect, true)) { vertices = null; vertices = filter.sharedMesh.vertices; // Get local-space vertices if (vertices != null && vertices.Length > 0) { currentTransform = null; currentTransform = filter.transform; if (currentTransform != null) { checkSuccessful = true; } } } } } if (!checkSuccessful) { skinnedRenderer = allMeshes[i] as SkinnedMeshRenderer; if (skinnedRenderer != null && skinnedRenderer.gameObject != null && skinnedRenderer.gameObject.transform != null && skinnedRenderer.enabled && skinnedRenderer.isVisible && skinnedRenderer.gameObject.activeInHierarchy && skinnedRenderer.gameObject.activeSelf && (!Selection.Contains(skinnedRenderer.gameObject) || returnOnly1stVertex) && skinnedRenderer.sharedMesh != null) { rect = WorldSpaceBoundsToGUISpaceRect(skinnedRenderer.bounds); if (rect.Overlaps(mouseRect, true)) { vertices = null; vertices = skinnedRenderer.sharedMesh.vertices; // Get local-space vertices if (vertices != null && vertices.Length > 0) { currentTransform = null; currentTransform = skinnedRenderer.transform; if (currentTransform != null) { checkSuccessful = true; } } } checkSuccessful = true; } } if (checkSuccessful) { if (vertices != null) { int n = vertices.Length; while (--n > -1) { vertex = vertices[n]; Vector3 worldPoint = currentTransform.TransformPoint(vertex); // transform world-space Vector2 vertexGUISpace = HandleUtility.WorldToGUIPoint(worldPoint); // transform GUI-space float distance = Vector2.SqrMagnitude(mouseGUIPos - vertexGUISpace); // Check if it's close enough to the mouse in GUI space. All comparisons // are in GUI space because that's how the user sees the working area. if (distance < lastClosestDistanceGUIspace) { lastClosestDistanceGUIspace = distance; closestVertexWorldSpace = worldPoint; // our first ultimate goal. closestVertexGUISpace = vertexGUISpace; // save for later methodResult = VertexSnappingResult.FirstVectorFound; } } } } } // ONLY if the first (NON-selected) vertex is found we search for the second (SELECTED) vertex if (!returnOnly1stVertex && Selection.gameObjects.Length > 0 && // We have a vertex from a non-selected mesh near our mouse. lastClosestDistanceGUIspace < 700) { lastClosestDistanceGUIspace = -1; // Reuse this variable for (int i = 0; i < Selection.gameObjects.Length; i++) { if (Selection.gameObjects[i] != null) { IterateChildMeshes(Selection.gameObjects[i].transform, ref ClosestGO, ref lastClosestDistanceGUIspace, meshRenderer, filter, skinnedRenderer, vertices, currentTransform, ref closestVertexGUISpace, ref closestSelectedVertexWorldSpace, ref methodResult, ignoreZeroesOnX, ignoreZeroesOnY, ignoreZeroesOnZ, pivot); } } if (lastClosestDistanceGUIspace < 0) // We have no MeshFilter-s. Use GameObjects' pivots instead { lastClosestDistanceGUIspace = -1; // Reuse this variable for (int i = 0; i < Selection.gameObjects.Length; i++) { if (Selection.gameObjects[i] != null) { IterateChildGameObjects(Selection.gameObjects[i].transform, ref ClosestGO, ref lastClosestDistanceGUIspace, filter, meshRenderer, vertices, ref closestVertexGUISpace, ref closestSelectedVertexWorldSpace, ref methodResult); } } } } return(methodResult); //#endcolreg }
/// <summary>Searches the second vertex needed for Find2VerticesForVertexSnapping method amongst SELECTED MESHES.</summary> /// <param name="transform">The parent</param> /// <param name="lastClosestDistanceGUIspace">The distance to cursor from the closest vertex in GUI space so far</param> /// <param name="meshRenderer">The reusable mesh renderer variable</param> /// <param name="meshFilter">The reusable mesh filter variable</param> /// <param name="skinnedMesh">The reusable skinned mesh renderer variable</param> /// <param name="vertices">The reusable vertices array</param> /// <param name="curTransform">The reusable transform variable that is used to translate vertices from local to world space</param> /// <param name="closestVertexGUISpace">INPUT, the previously found closest vertex to the mouse cursor amongst NON-selected meshes</param> /// <param name="closestSelectedVertexWorldSpace">OUTPUT Is returned as the closest vertex to the mouse cursor amongst SELECTED /// models world space</param> /// <param name="ignoreZeroesOnX">When searching for a vertex among SELECTED models, /// ignore those that have zero values in x variable in local space</param> /// <param name="ignoreZeroesOnY">When searching for a vertex among SELECTED models, /// ignore those that have zero values in y variable in local space</param> /// <param name="ignoreZeroesOnZ">When searching for a vertex among SELECTED models, /// ignore those that have zero values in z variable in local space</param> /// <param name="pivot">Transformation pivot moved to local space which is subtracted before we check for zeroes</param> public static void IterateChildMeshes(Transform transform, ref GameObject ClosestGO, ref float lastClosestDistanceGUIspace, MeshRenderer meshRenderer, MeshFilter meshFilter, SkinnedMeshRenderer skinnedMesh, Vector3[] vertices, Transform curTransform, ref Vector2 closestVertexGUISpace, ref Vector3 closestSelectedVertexWorldSpace, ref VertexSnappingResult methodResult, bool ignoreZeroesOnX = false, bool ignoreZeroesOnY = false, bool ignoreZeroesOnZ = false, Vector3 pivot = default(Vector3)) { //#colreg(red*0.3); if (transform != null && transform.gameObject != null && transform.gameObject.activeInHierarchy && transform.gameObject.activeSelf) { bool checkSuccesfull = false; Vector3 vertex = Vector3.zero; meshFilter = null; meshFilter = transform.gameObject.GetComponent <MeshFilter>(); meshRenderer = transform.gameObject.GetComponent <MeshRenderer>(); // We don't check for mouse Rect here, because it is intentional that the // tool works even from across the globe for SELECTED meshes if (meshFilter != null && meshRenderer != null && meshRenderer.enabled && meshRenderer.isVisible && meshFilter.sharedMesh != null) { vertices = null; vertices = meshFilter.sharedMesh.vertices; // Get local-space vertices if (vertices != null && vertices.Length > 0) { curTransform = null; curTransform = meshFilter.transform; if (curTransform != null) { checkSuccesfull = true; } } } if (!checkSuccesfull) { skinnedMesh = transform.gameObject.GetComponent <SkinnedMeshRenderer>(); if (skinnedMesh != null && skinnedMesh.enabled && skinnedMesh.isVisible && skinnedMesh.sharedMesh != null) { vertices = null; vertices = skinnedMesh.sharedMesh.vertices; // Get local-space vertices if (vertices != null && vertices.Length > 0) { curTransform = null; curTransform = skinnedMesh.transform; if (curTransform != null) { checkSuccesfull = true; } } } } if (checkSuccesfull) { if (vertices != null) { int n = vertices.Length; while (--n > -1) { vertex = vertices[n]; // Zero values in vertices in local space can produce glitches during scaling // when there is a transform lock set. This way we try to filter them out. // Blender does this better somehow, this is my way of doing it. if ((!ignoreZeroesOnX || Mathf.Abs((vertex - pivot).x) > 0.000001f) && (!ignoreZeroesOnY || Mathf.Abs((vertex - pivot).y) > 0.000001f) && (!ignoreZeroesOnZ || Mathf.Abs((vertex - pivot).z) > 0.000001f)) { Vector3 worldPoint = curTransform.TransformPoint(vertex); // transform world-space float distance = Vector2.SqrMagnitude(closestVertexGUISpace - HandleUtility.WorldToGUIPoint(worldPoint)); // find the closest vertex GUI-space if (lastClosestDistanceGUIspace < 0 || distance < lastClosestDistanceGUIspace) { lastClosestDistanceGUIspace = distance; ClosestGO = transform.gameObject; closestSelectedVertexWorldSpace = worldPoint; // our second ultimate goal methodResult = VertexSnappingResult.BothVectorsFound; } } } } } int children = transform.childCount; for (int i = 0; i < children; ++i) { IterateChildMeshes(transform.GetChild(i), ref ClosestGO, ref lastClosestDistanceGUIspace, meshRenderer, meshFilter, skinnedMesh, vertices, curTransform, ref closestVertexGUISpace, ref closestSelectedVertexWorldSpace, ref methodResult); } } //#endcolreg }