/// <summary> /// Returns the distance this grid is drawing /// </summary> /// <param name="cam"></param> /// <param name="pivot"></param> /// <param name="tangent"></param> /// <param name="bitangent"></param> /// <param name="snapValue"></param> /// <param name="color"></param> /// <returns></returns> internal static float SetPerspective(Camera cam, Vector3 pivot, Vector3 tangent, Vector3 bitangent, float snapValue, Color color) { if (!s_GridMesh || !s_GridMaterial) { Init(); } s_GridMaterial.SetFloat("_AlphaCutoff", .1f); s_GridMaterial.SetFloat("_AlphaFade", .6f); pivot = Snapping.Round(pivot, snapValue); Vector3 p = cam.WorldToViewportPoint(pivot); bool inFrustum = (p.x >= 0f && p.x <= 1f) && (p.y >= 0f && p.y <= 1f) && p.z >= 0f; float[] distances = GetDistanceToFrustumPlanes(cam, pivot, tangent, bitangent, 24f); if (inFrustum) { s_TangentIteration = (int)(Mathf.Ceil((Mathf.Abs(distances[0]) + Mathf.Abs(distances[2])) / snapValue)); s_BitangentIteration = (int)(Mathf.Ceil((Mathf.Abs(distances[1]) + Mathf.Abs(distances[3])) / snapValue)); s_MaxLines = Mathf.Max(s_TangentIteration, s_BitangentIteration); // if the s_MaxLines is around 3x greater than min, we're probably skewing the camera at near-plane // angle, so use the min instead. if (s_MaxLines > Mathf.Min(s_TangentIteration, s_BitangentIteration) * 2) { s_MaxLines = (int)Mathf.Min(s_TangentIteration, s_BitangentIteration) * 2; } s_GridResolution = 1; float dot = Vector3.Dot(cam.transform.position - pivot, Vector3.Cross(tangent, bitangent)); if (s_MaxLines > k_MaxLines) { if (Vector3.Distance(cam.transform.position, pivot) > 50f * snapValue && Mathf.Abs(dot) > .8f) { while (s_MaxLines / s_GridResolution > k_MaxLines) { s_GridResolution += s_GridResolution; } } else { s_MaxLines = k_MaxLines; } } } // origin, tan, bitan, increment, iterations, divOffset, color, primary alpha bump RebuildPlane(cam, pivot, tangent, bitangent, snapValue * s_GridResolution, s_MaxLines / s_GridResolution, s_GridResolution, color); return((snapValue * s_GridResolution) * (s_MaxLines / s_GridResolution)); }
void SnapToGrid(Transform[] transforms) { if (transforms != null && transforms.Length > 0) { Undo.RecordObjects(transforms, "Snap to Grid"); foreach (Transform t in transforms) { t.position = Snapping.Round(t.position, SnapValueInUnityUnits); } } PushToGrid(SnapValueInUnityUnits); DoGridRepaint(); }
void DoTransformSnapping(Event currentEvent, Camera camera) { if (currentEvent.type == EventType.MouseUp) { m_IsFirstMove = true; } Transform selected = m_LastActiveTransform; if (selected == null) { return; } bool positionMoved = !Snapping.Approx(m_LastActiveTransform.position, m_LastPosition); bool scaleMoved = !Snapping.Approx(m_LastActiveTransform.localScale, m_LastScale); if (!GetSnapEnabled() || !EditorUtility.SnapIsEnabled(selected) || GUIUtility.hotControl < 1) { m_LastPosition = m_LastActiveTransform.position; m_LastScale = m_LastActiveTransform.localScale; return; } if (Tools.current == Tool.Move && positionMoved) { Vector3 old = selected.position; Vector3 mask = old - m_LastPosition; bool constraintsOn = SnapMethod == SnapMethod.SnapOnSelectedAxis; if (m_ToggleAxisConstraint) { constraintsOn = !constraintsOn; } Vector3 snapped; if (constraintsOn) { snapped = Snapping.Round(old, mask, m_SnapSettings.SnapValueInUnityUnits()); } else { snapped = Snapping.Round(old, m_SnapSettings.SnapValueInUnityUnits()); } Vector3 snapOffset = snapped - old; if (m_IsFirstMove) { Undo.RecordObjects(Selection.transforms, "Move Objects"); m_IsFirstMove = false; if (m_PredictiveGrid && !FullGridEnabled) { Axis dragAxis = EditorUtility.CalcDragAxis(snapOffset, camera); if (dragAxis != Axis.None && dragAxis != m_RenderPlane) { SetRenderPlane(dragAxis); } } } selected.position = snapped; if (m_SnapSettings.SnapAsGroup) { EditorUtility.OffsetTransforms(Selection.transforms, selected, snapOffset); } else { foreach (Transform t in Selection.transforms) { t.position = constraintsOn ? Snapping.Round(t.position, mask, m_SnapSettings.SnapValueInUnityUnits()) : Snapping.Round(t.position, m_SnapSettings.SnapValueInUnityUnits()); } } } if (Tools.current == Tool.Scale && ScaleSnapEnabled && scaleMoved) { Vector3 old = m_LastActiveTransform.localScale; Vector3 mask = old - m_LastScale; if (m_PredictiveGrid) { Axis dragAxis = EditorUtility.CalcDragAxis(Selection.activeTransform.TransformDirection(mask), camera); if (dragAxis != Axis.None && dragAxis != m_RenderPlane) { SetRenderPlane(dragAxis); } } // scale snapping does not respect the SnapMethod by design foreach (Transform t in Selection.transforms) { t.localScale = Snapping.Round(t.localScale, mask, m_SnapSettings.SnapValueInUnityUnits()); } } m_LastPosition = m_LastActiveTransform.position; m_LastScale = m_LastActiveTransform.localScale; }
void CalculateGridPlacement(SceneView view) { var cam = view.camera; bool wasOrtho = gridIsOrthographic; gridIsOrthographic = cam.orthographic && Snapping.IsRounded(view.rotation.eulerAngles.normalized); m_CameraDirection = Snapping.Sign(m_Pivot - cam.transform.position); if (wasOrtho != gridIsOrthographic) { m_DoGridRepaint = true; if (view == SceneView.lastActiveSceneView && gridIsOrthographic != menuIsOrtho) { if (gridIsOrthographic) { m_savedAxis = (Axis)EditorPrefs.GetInt(PreferenceKeys.GridAxis); m_savedFullGrid = EditorPrefs.GetBool(PreferenceKeys.PerspGrid); } else { SetRenderPlane(m_savedAxis); FullGridEnabled = m_savedFullGrid; } SetMenuIsExtended(menuOpen); } } if (gridIsOrthographic) { return; } if (FullGridEnabled) { m_Pivot = m_GridIsLocked || Selection.activeTransform == null ? m_Pivot : Selection.activeTransform.position; } else { Vector3 sceneViewPlanePivot = m_Pivot; Ray ray = new Ray(cam.transform.position, cam.transform.forward); Plane plane = new Plane(Vector3.up, m_Pivot); // the only time a locked grid should ever move is if it's m_Pivot is out // of the camera's frustum. if ((m_GridIsLocked && !cam.InFrustum(m_Pivot)) || !m_GridIsLocked || view != SceneView.lastActiveSceneView) { float dist; if (plane.Raycast(ray, out dist)) { sceneViewPlanePivot = ray.GetPoint(Mathf.Min(dist, m_PlaneGridDrawDistance / 2f)); } else { sceneViewPlanePivot = ray.GetPoint(Mathf.Min(cam.farClipPlane / 2f, m_PlaneGridDrawDistance / 2f)); } } if (m_GridIsLocked) { m_Pivot = EnumExtension.InverseAxisMask(sceneViewPlanePivot, m_RenderPlane) + EnumExtension.AxisMask(m_Pivot, m_RenderPlane); } else { m_Pivot = Selection.activeTransform == null ? m_Pivot : Selection.activeTransform.position; if (Selection.activeTransform == null || !cam.InFrustum(m_Pivot)) { m_Pivot = EnumExtension.InverseAxisMask(sceneViewPlanePivot, m_RenderPlane) + EnumExtension.AxisMask(Selection.activeTransform == null ? m_Pivot : Selection.activeTransform.position, m_RenderPlane); } } } }
/// <summary> /// Gets the origin point of the grid. If no instance is active, this method returns zero. /// </summary> /// <returns>The origin point of the grid or zero if no instance is active.</returns> public static Vector3 GetPivot() { return(s_Instance == null ? Vector3.zero : Snapping.Round(s_Instance.m_Pivot, s_Instance.GetSnapValue())); }
static void DrawOrthographic(Camera cam, Axis camAxis, Color color, float snapValue, float angle) { Color previousColor = Handles.color; Color primaryColor = new Color(color.r, color.g, color.b, color.a + s_AlphaBump); Vector3 bottomLeft = Snapping.Floor(cam.ScreenToWorldPoint(Vector2.zero), snapValue); Vector3 bottomRight = Snapping.Floor(cam.ScreenToWorldPoint(new Vector2(cam.pixelWidth, 0f)), snapValue); Vector3 topLeft = Snapping.Floor(cam.ScreenToWorldPoint(new Vector2(0f, cam.pixelHeight)), snapValue); Vector3 topRight = Snapping.Floor(cam.ScreenToWorldPoint(new Vector2(cam.pixelWidth, cam.pixelHeight)), snapValue); Vector3 axis = EnumExtension.VectorWithAxis(camAxis); float width = Vector3.Distance(bottomLeft, bottomRight); float height = Vector3.Distance(bottomRight, topRight); // Shift lines to 10m forward of the camera bottomLeft += axis * 10f; topRight += axis * 10f; bottomRight += axis * 10f; topLeft += axis * 10f; // Draw Vertical Lines Vector3 camRight = cam.transform.right; Vector3 camUp = cam.transform.up; float snapValueAtResolution = snapValue; int segs = (int)Mathf.Ceil(width / snapValueAtResolution) + 2; float n = 2f; while (segs > k_MaxLines) { snapValueAtResolution = snapValueAtResolution * n; segs = (int)Mathf.Ceil(width / snapValueAtResolution) + 2; n++; } // Screen start and end Vector3 bl = (camRight.x + camRight.y + camRight.z) > 0 ? Snapping.Floor(bottomLeft, camRight, snapValueAtResolution * k_PrimaryColorIncrement) : Snapping.Ceil(bottomLeft, camRight, snapValueAtResolution * k_PrimaryColorIncrement); Vector3 start = bl - camUp * (height + snapValueAtResolution * 2); Vector3 end = bl + camUp * (height + snapValueAtResolution * 2); segs += k_PrimaryColorIncrement; // The current line start and end Vector3 lineStart; Vector3 lineEnd; for (int i = -1; i < segs; i++) { lineStart = start + (i * (camRight * snapValueAtResolution)); lineEnd = end + (i * (camRight * snapValueAtResolution)); Handles.color = i % k_PrimaryColorIncrement == 0 ? primaryColor : color; Handles.DrawLine(lineStart, lineEnd); } // Draw Horizontal Lines segs = (int)Mathf.Ceil(height / snapValueAtResolution) + 2; n = 2; while (segs > k_MaxLines) { snapValueAtResolution = snapValueAtResolution * n; segs = (int)Mathf.Ceil(height / snapValueAtResolution) + 2; n++; } Vector3 tl = (camUp.x + camUp.y + camUp.z) > 0 ? Snapping.Ceil(topLeft, camUp, snapValueAtResolution * k_PrimaryColorIncrement) : Snapping.Floor(topLeft, camUp, snapValueAtResolution * k_PrimaryColorIncrement); start = tl - camRight * (width + snapValueAtResolution * 2); end = tl + camRight * (width + snapValueAtResolution * 2); segs += (int)k_PrimaryColorIncrement; for (int i = -1; i < segs; i++) { lineStart = start + (i * (-camUp * snapValueAtResolution)); lineEnd = end + (i * (-camUp * snapValueAtResolution)); Handles.color = i % k_PrimaryColorIncrement == 0 ? primaryColor : color; Handles.DrawLine(lineStart, lineEnd); } if (angle > 0f) { Vector3 cen = Snapping.Round(((topRight + bottomLeft) / 2f), snapValue); float half = (width > height) ? width : height; float opposite = Mathf.Tan(Mathf.Deg2Rad * angle) * half; Vector3 up = cam.transform.up * opposite; Vector3 right = cam.transform.right * half; Vector3 bottomLeftAngle = cen - (up + right); Vector3 topRightAngle = cen + (up + right); Vector3 bottomRightAngle = cen + (right - up); Vector3 topLeftAngle = cen + (up - right); Handles.color = primaryColor; // y = 1x+1 Handles.DrawLine(bottomLeftAngle, topRightAngle); // y = -1x-1 Handles.DrawLine(topLeftAngle, bottomRightAngle); } Handles.color = previousColor; }
/// <summary> /// Draws a plane grid using pivot point, the right and forward directions, and how far each direction should extend /// </summary> /// <param name="cam"></param> /// <param name="pivot"></param> /// <param name="tan"></param> /// <param name="bitan"></param> /// <param name="increment"></param> /// <param name="iterations"></param> /// <param name="div"></param> /// <param name="secondary"></param> /// <param name="alphaBump"></param> static void RebuildPlane(Camera cam, Vector3 pivot, Vector3 tan, Vector3 bitan, float increment, int iterations, int div, Color secondary) { Color primary = secondary; primary.a += s_AlphaBump; float len = iterations * increment; iterations++; Vector3 start = pivot - tan * (len / 2f) - bitan * (len / 2f); start = Snapping.Round(start, bitan + tan, increment); float inc = increment; int highlightOffsetTan = (int)((EditorUtility.ValueFromMask(start, tan) % (inc * majorLineIncrement)) / inc); int highlightOffsetBitan = (int)((EditorUtility.ValueFromMask(start, bitan) % (inc * majorLineIncrement)) / inc); Vector3[] lines = new Vector3[iterations * 4]; int[] indices = new int[iterations * 4]; Color[] colors = new Color[iterations * 4]; int v = 0, t = 0; for (int i = 0; i < iterations; i++) { Vector3 a = start + tan * i * increment; Vector3 b = start + bitan * i * increment; lines[v + 0] = a; lines[v + 1] = a + bitan * len; lines[v + 2] = b; lines[v + 3] = b + tan * len; indices[t++] = v; indices[t++] = v + 1; indices[t++] = v + 2; indices[t++] = v + 3; Color col = (i + highlightOffsetTan) % majorLineIncrement == 0 ? primary : secondary; // tan colors[v + 0] = col; colors[v + 1] = col; col = (i + highlightOffsetBitan) % majorLineIncrement == 0 ? primary : secondary; // bitan colors[v + 2] = col; colors[v + 3] = col; v += 4; } Vector3 nrm = Vector3.Cross(tan, bitan); Vector3[] nrms = new Vector3[lines.Length]; for (int i = 0; i < lines.Length; i++) { nrms[i] = nrm; } s_GridMesh.Clear(); s_GridMesh.vertices = lines; s_GridMesh.normals = nrms; s_GridMesh.subMeshCount = 1; s_GridMesh.uv = new Vector2[lines.Length]; s_GridMesh.colors = colors; s_GridMesh.SetIndices(indices, MeshTopology.Lines, 0); }
internal static void SetPerspective3D(Camera cam, Vector3 pivot, float snapValue) { if (!s_GridMesh || !s_GridMaterial) { Init(); } s_GridMaterial.SetFloat("_AlphaCutoff", 0f); s_GridMaterial.SetFloat("_AlphaFade", 0f); Vector3 camDir = (pivot - cam.transform.position).normalized; pivot = Snapping.Round(pivot, snapValue); // Used to flip the grid to match whatever direction the cam is currently // coming at the pivot from Vector3 right = camDir.x < 0f ? Vector3.right : Vector3.right * -1f; Vector3 up = camDir.y < 0f ? Vector3.up : Vector3.up * -1f; Vector3 forward = camDir.z < 0f ? Vector3.forward : Vector3.forward * -1f; // Get intersecting point for each axis, if it exists Ray ray_x = new Ray(pivot, right); Ray ray_y = new Ray(pivot, up); Ray ray_z = new Ray(pivot, forward); float x_dist = 10f, y_dist = 10f, z_dist = 10f; bool x_intersect = false, y_intersect = false, z_intersect = false; Plane[] planes = GeometryUtility.CalculateFrustumPlanes(cam); foreach (Plane p in planes) { float dist; float t = 0; if (p.Raycast(ray_x, out dist)) { t = Vector3.Distance(pivot, ray_x.GetPoint(dist)); if (t < x_dist || !x_intersect) { x_intersect = true; x_dist = t; } } if (p.Raycast(ray_y, out dist)) { t = Vector3.Distance(pivot, ray_y.GetPoint(dist)); if (t < y_dist || !y_intersect) { y_intersect = true; y_dist = t; } } if (p.Raycast(ray_z, out dist)) { t = Vector3.Distance(pivot, ray_z.GetPoint(dist)); if (t < z_dist || !z_intersect) { z_intersect = true; z_dist = t; } } } int x_iter = (int)(Mathf.Ceil(Mathf.Max(x_dist, y_dist)) / snapValue); int y_iter = (int)(Mathf.Ceil(Mathf.Max(x_dist, z_dist)) / snapValue); int z_iter = (int)(Mathf.Ceil(Mathf.Max(z_dist, y_dist)) / snapValue); int max = Mathf.Max(Mathf.Max(x_iter, y_iter), z_iter); int div = 1; while (max / div > k_MaxLines) { div++; } Vector3[] vertices_t = null; Vector3[] normals_t = null; Color[] colors_t = null; int[] indices_t = null; List <Vector3> vertices_m = new List <Vector3>(); List <Vector3> normals_m = new List <Vector3>(); List <Color> colors_m = new List <Color>(); List <int> indices_m = new List <int>(); // X plane RebuildPerspectivePlane(cam, pivot, up, right, snapValue * div, x_iter / div, gridColorX, out vertices_t, out normals_t, out colors_t, out indices_t, 0); vertices_m.AddRange(vertices_t); normals_m.AddRange(normals_t); colors_m.AddRange(colors_t); indices_m.AddRange(indices_t); // Y plane RebuildPerspectivePlane(cam, pivot, right, forward, snapValue * div, y_iter / div, gridColorY, out vertices_t, out normals_t, out colors_t, out indices_t, vertices_m.Count); vertices_m.AddRange(vertices_t); normals_m.AddRange(normals_t); colors_m.AddRange(colors_t); indices_m.AddRange(indices_t); // Z plane RebuildPerspectivePlane(cam, pivot, forward, up, snapValue * div, z_iter / div, gridColorZ, out vertices_t, out normals_t, out colors_t, out indices_t, vertices_m.Count); vertices_m.AddRange(vertices_t); normals_m.AddRange(normals_t); colors_m.AddRange(colors_t); indices_m.AddRange(indices_t); s_GridMesh.Clear(); s_GridMesh.vertices = vertices_m.ToArray(); s_GridMesh.normals = normals_m.ToArray(); s_GridMesh.subMeshCount = 1; s_GridMesh.uv = new Vector2[vertices_m.Count]; s_GridMesh.colors = colors_m.ToArray(); s_GridMesh.SetIndices(indices_m.ToArray(), MeshTopology.Lines, 0); }
void CalculateGridPlacement(SceneView view) { var cam = view.camera; bool wasOrtho = GridIsOrthographic; GridIsOrthographic = cam.orthographic && Snapping.IsRounded(view.rotation.eulerAngles.normalized); m_CameraDirection = Snapping.Sign(m_Pivot - cam.transform.position); if (GridIsOrthographic && !wasOrtho || GridIsOrthographic != menuIsOrtho) { OnSceneBecameOrtho(view == SceneView.lastActiveSceneView); } if (!GridIsOrthographic && wasOrtho) { OnSceneBecamePersp(view == SceneView.lastActiveSceneView); } if (GridIsOrthographic) { return; } if (FullGridEnabled) { m_Pivot = m_GridIsLocked || Selection.activeTransform == null ? m_Pivot : Selection.activeTransform.position; } else { Vector3 sceneViewPlanePivot = m_Pivot; Ray ray = new Ray(cam.transform.position, cam.transform.forward); Plane plane = new Plane(Vector3.up, m_Pivot); // the only time a locked grid should ever move is if it's m_Pivot is out // of the camera's frustum. if ((m_GridIsLocked && !cam.InFrustum(m_Pivot)) || !m_GridIsLocked || view != SceneView.lastActiveSceneView) { float dist; if (plane.Raycast(ray, out dist)) { sceneViewPlanePivot = ray.GetPoint(Mathf.Min(dist, m_PlaneGridDrawDistance / 2f)); } else { sceneViewPlanePivot = ray.GetPoint(Mathf.Min(cam.farClipPlane / 2f, m_PlaneGridDrawDistance / 2f)); } } if (m_GridIsLocked) { m_Pivot = EnumExtension.InverseAxisMask(sceneViewPlanePivot, m_RenderPlane) + EnumExtension.AxisMask(m_Pivot, m_RenderPlane); } else { m_Pivot = Selection.activeTransform == null ? m_Pivot : Selection.activeTransform.position; if (Selection.activeTransform == null || !cam.InFrustum(m_Pivot)) { m_Pivot = EnumExtension.InverseAxisMask(sceneViewPlanePivot, m_RenderPlane) + EnumExtension.AxisMask(Selection.activeTransform == null ? m_Pivot : Selection.activeTransform.position, m_RenderPlane); } } } }