using System.Collections.Generic; using UnityEngine; namespace UnityEngine.ProBuilder { /// /// Snapping functions and ProGrids compatibility. /// static class Snapping { const float k_MaxRaySnapDistance = Mathf.Infinity; /// /// Round value to nearest snpVal increment. /// /// /// /// public static Vector3 SnapValue(Vector3 vertex, float snpVal) { // snapValue is a global setting that comes from ProGrids return new Vector3( snpVal * Mathf.Round(vertex.x / snpVal), snpVal * Mathf.Round(vertex.y / snpVal), snpVal * Mathf.Round(vertex.z / snpVal)); } /// /// Round value to nearest snpVal increment. /// /// /// /// public static float SnapValue(float val, float snpVal) { if (snpVal < Mathf.Epsilon) return val; return snpVal * Mathf.Round(val / snpVal); } /// /// An override that accepts a vector3 to use as a mask for which values to snap. Ex; /// Snap((.3f, 3f, 41f), (0f, 1f, .4f)) only snaps Y and Z values (to 1 & .4 unit increments). /// /// /// /// public static Vector3 SnapValue(Vector3 vertex, Vector3 snap) { float x = vertex.x, y = vertex.y, z = vertex.z; Vector3 v = new Vector3( (Mathf.Abs(snap.x) < 0.0001f ? x : snap.x * Mathf.Round(x / snap.x)), (Mathf.Abs(snap.y) < 0.0001f ? y : snap.y * Mathf.Round(y / snap.y)), (Mathf.Abs(snap.z) < 0.0001f ? z : snap.z * Mathf.Round(z / snap.z)) ); return v; } public static Vector3 Floor(Vector3 vertex, Vector3 snap) { float x = vertex.x, y = vertex.y, z = vertex.z; Vector3 v = new Vector3( (Mathf.Abs(snap.x) < 0.0001f ? x : snap.x * Mathf.Floor(x / snap.x)), (Mathf.Abs(snap.y) < 0.0001f ? y : snap.y * Mathf.Floor(y / snap.y)), (Mathf.Abs(snap.z) < 0.0001f ? z : snap.z * Mathf.Floor(z / snap.z)) ); return v; } public static Vector3 Ceil(Vector3 vertex, Vector3 snap) { float x = vertex.x, y = vertex.y, z = vertex.z; Vector3 v = new Vector3( (Mathf.Abs(snap.x) < 0.0001f ? x : snap.x * Mathf.Ceil(x / snap.x)), (Mathf.Abs(snap.y) < 0.0001f ? y : snap.y * Mathf.Ceil(y / snap.y)), (Mathf.Abs(snap.z) < 0.0001f ? z : snap.z * Mathf.Ceil(z / snap.z)) ); return v; } public static Vector3 Ceil(Vector3 vertex, float snpVal) { return new Vector3( snpVal * Mathf.Ceil(vertex.x / snpVal), snpVal * Mathf.Ceil(vertex.y / snpVal), snpVal * Mathf.Ceil(vertex.z / snpVal)); } public static Vector3 Floor(Vector3 vertex, float snpVal) { // snapValue is a global setting that comes from ProGrids return new Vector3( snpVal * Mathf.Floor(vertex.x / snpVal), snpVal * Mathf.Floor(vertex.y / snpVal), snpVal * Mathf.Floor(vertex.z / snpVal)); } /// /// Snap all vertices to an increment of @snapValue in world space. /// /// /// /// public static void SnapVertices(ProBuilderMesh mesh, IEnumerable indexes, Vector3 snap) { Vector3[] verts = mesh.positionsInternal; foreach (var v in indexes) verts[v] = mesh.transform.InverseTransformPoint(SnapValue(mesh.transform.TransformPoint(verts[v]), snap)); } internal static Vector3 GetSnappingMaskBasedOnNormalVector(Vector3 normal) { return new Vector3( (Mathf.Approximately(Mathf.Abs(normal.x), 1f)) ? 0f : 1f, (Mathf.Approximately(Mathf.Abs(normal.y), 1f)) ? 0f : 1f, (Mathf.Approximately(Mathf.Abs(normal.z), 1f)) ? 0f : 1f); } internal static Vector3 SnapValueOnRay(Ray ray, float distance, float snap, Vector3Mask mask) { var nearest = k_MaxRaySnapDistance; var forwardRay = new Ray(ray.origin, ray.direction); var backwardsRay = new Ray(ray.origin, -ray.direction); for (int i = 0; i < 3; i++) { if (mask[i] > 0f) { var dir = new Vector3Mask(new Vector3Mask((byte) (1 << i))); var prj = Vector3.Project( ray.direction * Math.MakeNonZero(distance), dir * Mathf.Sign(ray.direction[i])); var pnt = ray.origin + prj; var plane = new Plane(dir, SnapValue(pnt, dir * snap)); if(Mathf.Abs(plane.GetDistanceToPoint(ray.origin)) < .0001f) { nearest = 0f; continue; } float d; if (plane.Raycast(forwardRay, out d) && Mathf.Abs(d) < Mathf.Abs(nearest)) nearest = d; if (plane.Raycast(backwardsRay, out d) && Mathf.Abs(d) < Mathf.Abs(nearest)) nearest = -d; } } return ray.origin + ray.direction * (Mathf.Abs(nearest) >= k_MaxRaySnapDistance ? distance : nearest); } } }