using System; using System.Collections.Generic; using Unity.Collections; namespace UnityEngine.XR.ARFoundation { /// /// Generator methods for mesh geometery. /// /// /// These static class provides ways to generate different parts of plane geometry, such as vertices, indices, normals, and UVs. /// You can use these methods to build an ARPlane Mesh object. /// public static class ARPlaneMeshGenerators { /// /// Generates a Mesh from the given parameters. The is assumed to be convex. /// /// /// is not checked for its convexness. Concave polygons will produce incorrect results. /// /// The Mesh to write results to. /// The session-space pose of the mesh. /// The vertices of the plane's boundary, in plane-space. /// If any triangle in the generated mesh is less than this, then the entire mesh is ignored. /// This handles an edge case which prevents degenerate or very small triangles. Units are meters-squared. /// True if the was generated, False otherwise. The might /// fail to generate if it is not a valid polygon (too few vertices) or if it contains degenerate triangles (area smaller than ). public static bool GenerateMesh(Mesh mesh, Pose pose, NativeArray convexPolygon, float areaTolerance = 1e-6f) { if (mesh == null) throw new ArgumentNullException("mesh"); if (convexPolygon.Length < 3) return false; // Vertices s_Vertices.Clear(); var center = Vector3.zero; foreach (var point2 in convexPolygon) { var point3 = new Vector3(point2.x, 0, point2.y); center += point3; s_Vertices.Add(point3); } center /= convexPolygon.Length; s_Vertices.Add(center); // If the polygon is too small or degenerate, no mesh is created. if (!GenerateIndices(s_Indices, s_Vertices, areaTolerance)) return false; // We can't fail after this point, so it is safe to mutate the mesh mesh.Clear(); mesh.SetVertices(s_Vertices); // Indices const int subMesh = 0; const bool calculateBounds = true; mesh.SetTriangles(s_Indices, subMesh, calculateBounds); // UVs GenerateUvs(s_Uvs, pose, s_Vertices); mesh.SetUVs(0, s_Uvs); // Normals // Reuse the same list for normals var normals = s_Vertices; for (int i = 0; i < normals.Count; ++i) normals[i] = Vector3.up; mesh.SetNormals(normals); return true; } /// /// Generates a `List` of UVs from the given parameters. /// /// The `List` to write results to. /// /// The session-space pose of the mesh. /// The vertices of the plane's boundary, in plane-space. public static void GenerateUvs(List Uvs, Pose pose, List vertices) { // Get the twist rotation about the plane's normal, then apply // its inverse to the rotation to produce the "untwisted" rotation. // This is similar to Swing-Twist Decomposition. var planeRotation = pose.rotation; var rotationAxis = new Vector3(planeRotation.x, planeRotation.y, planeRotation.z); var projection = Vector3.Project(rotationAxis, planeRotation * Vector3.up); var normalizedTwist = (new Vector4(projection.x, projection.y, projection.z, planeRotation.w)).normalized; var inverseTwist = new Quaternion(normalizedTwist.x, normalizedTwist.y, normalizedTwist.z, -normalizedTwist.w); var untwistedRotation = inverseTwist * pose.rotation; // Compute the basis vectors for the plane in session space. var sessionSpaceRight = untwistedRotation * Vector3.right; var sessionSpaceForward = untwistedRotation * Vector3.forward; Uvs.Clear(); foreach (var vertex in vertices) { var vertexInSessionSpace = pose.rotation * vertex + pose.position; // Project onto each axis var uv = new Vector2( Vector3.Dot(vertexInSessionSpace, sessionSpaceRight), Vector3.Dot(vertexInSessionSpace, sessionSpaceForward)); Uvs.Add(uv); } } /// /// Generates a `List` of indices from the given parameters, forming a triangle fan. /// The is assumed to be convex. /// /// /// is not checked for its convexness. Concave polygons will produce incorrect results. /// /// The `List` to write results to. /// The vertices of the plane's boundary, in plane-space. /// If any triangle in the generated mesh is less than this, then the entire mesh is ignored. /// True if the indices were generated, False if a triangle whose area is less than is found. public static bool GenerateIndices(List indices, List convexPolygon, float areaTolerance = 1e-6f) { indices.Clear(); var numBoundaryVertices = convexPolygon.Count - 1; var centerIndex = numBoundaryVertices; var areaToleranceSquared = areaTolerance * areaTolerance; for (int i = 0; i < numBoundaryVertices; ++i) { int j = (i + 1) % numBoundaryVertices; // Stop if the area of the triangle is too small var a = convexPolygon[i] - convexPolygon[centerIndex]; var b = convexPolygon[j] - convexPolygon[centerIndex]; // Area is the magnitude of the normal / 2, so the // area squared is the magnitude squared / 4 var areaSquared = Vector3.Cross(a, b).sqrMagnitude * 0.25f; if (areaSquared < areaToleranceSquared) return false; indices.Add(centerIndex); indices.Add(i); indices.Add(j); } return true; } // Caches to avoid reallocing Lists during calculations static List s_Indices = new List(); static List s_Uvs = new List(); static List s_Vertices = new List(); } }