/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
using static OVRPlugin;
///
/// Represents the Triangle Mesh component of an .
///
///
/// This component can be accessed from an that supports it by calling
/// from the anchor.
///
///
///
///
public readonly partial struct OVRTriangleMesh
{
///
/// Gets the number of vertices and triangles in the mesh.
///
///
/// Use this method to get the required sizes of the vertex and triangle index buffers. The length of the `indices`
/// array passed to and should be three times
/// .
///
/// This method is thread-safe.
///
/// The number of vertices in the mesh.
/// The number of triangles in the mesh. There are three times as many indices.
/// True if the counts were retrieved; otherwise, false.
public bool TryGetCounts(out int vertexCount, out int triangleCount)
=> GetSpaceTriangleMeshCounts(Handle, out vertexCount, out triangleCount);
///
/// Gets the raw, untransformed triangle mesh.
///
///
/// ## Thread safety
/// This method is thread-safe.
///
/// ## Memory ownership
/// The caller owns the memory of the input arrays and is responsible for allocating them to the appropriate size
/// before passing them to this method. Use to determine the required size of each array.
/// Note that should be three times the number of triangles (`triangleCount`) indicated
/// by .
///
/// ## Coordinate space
/// The mesh data provided by this method must be transformed into the appropriate coordinate space before being
/// used with a `UnityEngine.Mesh`. Use or to get mesh
/// data in the correct coordinate space. This method is typically for advanced use cases where you want to perform
/// the conversion at a later time, or combine it with your own jobs.
///
/// The are provided in the right-handed coordinate
/// space defined by OpenXR, with X to the right, Y up, and Z backward. is an array of
/// index triplets which define the triangles in counter-clockwise order.
///
/// To convert to the coordinate space used by a Scene anchor in Unity's coordinate system, you must
/// - Negate each vertex's X coordinate
/// - Reverse the triangle winding by swapping each index triplet (a, b, c) => (a, c, b)
///
/// The vertex positions of the mesh.
/// The triangle indices of the mesh.
/// True if the mesh data was retrieved; otherwise, false.
public bool TryGetMeshRawUntransformed(NativeArray positions, NativeArray indices)
=> GetSpaceTriangleMesh(Handle, positions, indices);
///
/// Gets the triangle mesh.
///
///
/// The caller owns the memory of the input arrays and is responsible for allocating them to the appropriate size
/// before passing them to this method. Use to determine the required size of each array.
/// Note that should be three times the number of triangles (`triangleCount`) indicated
/// by .
///
/// This method is thread-safe.
///
/// The vertex positions of the mesh.
/// The triangle indices of the mesh.
/// True if the mesh data was retrieved; otherwise, false.
public bool TryGetMesh(NativeArray positions, NativeArray indices)
{
if (!TryGetMeshRawUntransformed(positions, indices)) return false;
for (var i = 0; i < positions.Length; i++)
{
var p = positions[i];
// Necessary due to the coordinate space difference between OpenXR (right-handed) and Unity (left-handed)
positions[i] = new Vector3(-p.x, p.y, p.z);
}
var triangles = indices.Reinterpret(
expectedTypeSize: sizeof(int));
for (var i = 0; i < triangles.Length; i++)
{
var triangle = triangles[i];
triangles[i] = new Triangle
{
A = triangle.A,
B = triangle.C,
C = triangle.B
};
}
return true;
}
///
/// Schedules a job to get an anchor's triangle mesh.
///
///
/// This schedules jobs with the Unity Job system to retrieve the mesh data and then perform the necessary
/// conversion to Unity's coordinate space (see ).
///
/// The caller owns the memory of the input arrays and is responsible for allocating them to the appropriate size
/// before passing them to this method. Use to determine the required size of each array.
/// Note that should be three times the number of triangles (`triangleCount`) indicated
/// by .
///
/// If the triangle mesh cannot be retrieved, all will be set to zero. Use this to check
/// for success after the job completes. For example, if the first three indices are zero, then the mesh is not
/// valid.
///
/// The vertex positions of the triangle mesh.
/// The triangle indices of the triangle mesh.
/// (Optional) A job on which the new jobs will depend.
/// Returns the handle associated with the new job.
public JobHandle ScheduleGetMeshJob(NativeArray positions, NativeArray indices,
JobHandle dependencies = default)
{
var getMeshJob = new GetMeshJob
{
Positions = positions,
Indices = indices,
Space = Handle
}.Schedule(dependencies);
var triangles =
indices.Reinterpret(expectedTypeSize: sizeof(int));
return JobHandle.CombineDependencies(
new NegateXJob
{
Positions = positions
}.Schedule(positions.Length, 32, getMeshJob),
new FlipTriangleWindingJob
{
Triangles = triangles
}.Schedule(triangles.Length, 32, getMeshJob));
}
private struct GetMeshJob : IJob
{
public ulong Space;
public NativeArray Positions;
public NativeArray Indices;
public unsafe void Execute()
{
if (!GetSpaceTriangleMesh(Space, Positions, Indices))
{
UnsafeUtility.MemSet(Indices.GetUnsafePtr(), 0, Indices.Length * sizeof(int));
}
}
}
private struct Triangle
{
public int A, B, C;
}
private struct FlipTriangleWindingJob : IJobParallelFor
{
public NativeArray Triangles;
public void Execute(int index)
{
var triangle = Triangles[index];
Triangles[index] = new Triangle
{
A = triangle.A,
B = triangle.C,
C = triangle.B
};
}
}
// Necessary due to the coordinate space difference between OpenXR (right-handed) and Unity (left-handed)
private struct NegateXJob : IJobParallelFor
{
public NativeArray Positions;
public void Execute(int index)
{
var p = Positions[index];
Positions[index] = new Vector3(-p.x, p.y, p.z);
}
}
}