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();
}
}