ARPlaneMeshGenerators.cs
7.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
using System.Collections.Generic;
using Unity.Collections;
namespace UnityEngine.XR.ARFoundation
{
/// <summary>
/// Generator functions for <see cref="ARPlane"/> mesh geometery.
/// </summary>
/// <remarks>
/// These static class provides ways to generate different parts of plane geometry, such as vertices, indices, normals and UVs.
/// You can use these functions to build an ARPlane Mesh object.
/// </remarks>
public static class ARPlaneMeshGenerators
{
/// <summary>
/// Generates a <c>Mesh</c> from the given parameters. The <paramref name="convexPolygon"/> is assumed to be convex.
/// </summary>
/// <remarks>
/// <paramref name="convexPolygon"/> is not checked for its convexness. Concave polygons will produce incorrect results.
/// </remarks>
/// <param name="mesh">The <c>Mesh</c> to write results to.</param>
/// <param name="pose">The session-space pose of the mesh.</param>
/// <param name="convexPolygon">The vertices of the plane's boundary, in plane-space.</param>
/// <param name="areaTolerance">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.</param>
/// <returns><c>True</c> if the <paramref name="mesh"/> was generated, <c>False</c> otherwise. The <paramref name="mesh"/> may
/// fail to generate if it is not a valid polygon (too few vertices) or if it contains degenerate triangles (area smaller than <paramref name="areaTolerance"/>).</returns>
public static bool GenerateMesh(Mesh mesh, Pose pose, NativeArray<Vector2> 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;
}
/// <summary>
/// Generates a `List` of UVs from the given parameters.
/// </summary>
/// <param name="Uvs">The `List` to write results to.</param>
/// /// <param name="pose">The session-space pose of the mesh.</param>
/// <param name="vertices">The vertices of the plane's boundary, in plane-space.</param>
public static void GenerateUvs(List<Vector2> Uvs, Pose pose, List<Vector3> 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);
}
}
/// <summary>
/// Generates a `List` of indices from the given parameters, forming a triangle fan.
/// The <paramref name="convexPolygon"/> is assumed to be convex.
/// </summary>
/// <remarks>
/// <paramref name="convexPolygon"/> is not checked for its convexness. Concave polygons will produce incorrect results.
/// </remarks>
/// <param name="indices">The `List` to write results to.</param>
/// <param name="convexPolygon">The vertices of the plane's boundary, in plane-space.</param>
/// <param name="areaTolerance">If any triangle in the generated mesh is less than this, then the entire mesh is ignored.</param>
/// <returns><c>True</c> if the indices were generated, <c>False</c> if a triangle whose area is less than <paramref name="areaTolerance"/> is found.</returns>
public static bool GenerateIndices(List<int> indices, List<Vector3> 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<int> s_Indices = new List<int>();
static List<Vector2> s_Uvs = new List<Vector2>();
static List<Vector3> s_Vertices = new List<Vector3>();
}
}