using System; using System.Collections.Generic; using UnityEngine; /// /// Generates mesh from a list of vertices. /// internal class OVRMeshGenerator { /// /// Winding order for triangles vertices. /// public enum WindingOrderMode { Clockwise, CounterClockwise } /// /// Returns a mesh generated from a array of vertices. /// /// Source boundary vertices /// If true, source vertices position will /// be transformed from OpenXR to Unity coordinate space. /// Generate Unity mesh object. public static void GenerateMesh(Vector2[] vertices, Mesh mesh) { TransformVertices(vertices, out var verticesV3, out var uvCoords, out var normals); var triangles = GenerateTrianglesFromBoundaryVertices(vertices); mesh.Clear(); mesh.vertices = verticesV3; mesh.triangles = triangles; mesh.normals = normals; mesh.uv = uvCoords; mesh.RecalculateBounds(); } /// /// Transform the position of vertices from OpenXR space to Unity space. /// Create normals and uv arrays from source vertices. /// /// Source vertices /// Transformed vertices /// UV coords /// Vertices normals /// If true, transform vertices position from OpenXR to Unity space. public static void TransformVertices(Vector2[] vertices, out Vector3[] verticesV3, out Vector2[] uvCoords, out Vector3[] normals) { uvCoords = new Vector2[vertices.Length]; verticesV3 = new Vector3[vertices.Length]; normals = new Vector3[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { verticesV3[i] = vertices[i]; verticesV3[i].x *= -1; uvCoords[i] = verticesV3[i]; normals[i] = verticesV3[i]; normals[i].z = 1; } } /// /// This method takes care the triangulation process. /// /// List of input vertices /// A list of triangles to use as a indices in a mesh. /// /// Throws when vertices array is null. /// /// /// Throws when invalid number of vertices are passed. Minimum required vertices are 3. /// public static int[] GenerateTrianglesFromBoundaryVertices(Vector2[] vertices) { if (vertices == null) { throw new ArgumentNullException(); } if (vertices.Length < 3) { throw new ArgumentException("Vertices count cannot be less than 3."); } int totalTriangleCount = (vertices.Length - 2); int[] triangles = new int[totalTriangleCount * 3]; List indexList = new List(); for (int i = 0; i < vertices.Length; i++) { indexList.Add(i); } bool indexListChanged = true; // Find a valid triangle. // Checks: // 1. Connected edges do not form a co-linear or reflex angle. // 2. There's no vertices inside the selected triangle area. int triangleCount = 0; while (indexList.Count > 3) { if (!indexListChanged) { Debug.LogWarning("Infinite loop in vertices."); break; } indexListChanged = false; for (int i = 0; i < indexList.Count; i++) { int a = indexList[i]; int b = Get(i - 1, indexList); int c = Get(i + 1, indexList); Vector2 va = vertices[a]; Vector2 vb = vertices[b]; Vector2 vc = vertices[c]; Vector2 atob = vb - va; Vector2 atoc = vc - va; // reflex angle check if (Cross(atob, atoc) >= 0) { continue; } bool validTriangle = true; for (int j = 0; j < vertices.Length; j++) { if (j == a || j == b || j == c) { continue; } if (PointInTriangle(vertices[j], va, vb, vc)) { validTriangle = false; break; } } // add indices to triangle list if (validTriangle) { triangles[triangleCount++] = c; triangles[triangleCount++] = a; triangles[triangleCount++] = b; indexList.RemoveAt(i); indexListChanged = true; break; } } } triangles[triangleCount++] = indexList[2]; triangles[triangleCount++] = indexList[1]; triangles[triangleCount++] = indexList[0]; return triangles; } /// /// Detect the order of vertices. /// /// /// SUM((x[n] - x[n-1]) * (y[n] + y[n-1])); n is total number of vertices. /// Positive sum refers to the clockwise winding order. /// /// Source vertices /// Clockwise or Counter-Clockwise vertices order. public static WindingOrderMode GetWindingOrder(Vector2[] vertices) { float total = 0; for(int i = 1; i < vertices.Length; i++) { total += ((vertices[i].x - vertices[i - 1].x) * (vertices[i].y + vertices[i - 1].y)); } return total < 0 ? WindingOrderMode.CounterClockwise : WindingOrderMode.Clockwise; } /// /// Checks if point p is always on the right side of the vectors a, b, c /// private static bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c) { return Cross(b - a, p - a) < 0 && Cross(c - b, p - b) < 0 && Cross(a - c, p - c) < 0; } /// /// Cross product for 2d vectors. /// private static float Cross(Vector2 a, Vector2 b) { return a.x * b.y - a.y * b.x; } /// /// Get item in circular order from an array /// private static int Get(int index, List array) { if (index >= array.Count) { return array[index % array.Count]; } else if (index < 0) { return array[index % array.Count + array.Count]; } else { return array[index]; } } }