/*
* 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 System.Collections;
using UnityEngine;
using NUnit.Framework;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEngine.TestTools.Utils;
namespace Meta.XR.MRUtilityKit.Tests
{
public class RayCastTests : MRUKTestBase
{
private MRUKRoom _currentRoom;
[UnitySetUp]
public IEnumerator SetUp()
{
yield return LoadScene("Packages/com.meta.xr.mrutilitykit/Tests/RayCastTests.unity");
_currentRoom = MRUK.Instance.GetCurrentRoom();
}
[UnityTearDown]
public IEnumerator TearDown()
{
yield return UnloadScene();
}
///
/// Test that the ray cast hits a scene anchor plane and that the hit point and normal are as expected.
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Plane_DoesHit()
{
Ray mockRay = new Ray(new Vector3(0f, 1.70f, 1f), Vector3.forward);
bool didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, out RaycastHit hit, out MRUKAnchor anchorInfo);
Assert.IsTrue(didHit);
yield return null;
Assert.IsTrue(hit.point != Vector3.zero);
Assert.IsNotNull(anchorInfo);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.WALL_FACE));
Vector3 expectedHitPoint = new Vector3(0.0000f, 1.7000f, 4.2992f);
Assert.That(hit.point, Is.EqualTo(expectedHitPoint).Using(Vector3EqualityComparer.Instance));
Vector3 expectedHitNormal = new Vector3(-0.0864f, 0.0000f, -0.9963f);
Assert.That(hit.normal, Is.EqualTo(expectedHitNormal).Using(Vector3EqualityComparer.Instance));
}
///
/// Test that the ray cast respects the component type filter. This ray should only hit planes
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Plane_DoesFilterComponentType()
{
Ray mockRay = new Ray(new Vector3(0f, 1.70f, 1f), Vector3.forward);
Assert.IsFalse(_currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(componentTypes: MRUKAnchor.ComponentType.Volume), out _));
Assert.IsTrue(_currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(componentTypes: MRUKAnchor.ComponentType.Plane), out _));
Assert.IsTrue(_currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(componentTypes: MRUKAnchor.ComponentType.Volume | MRUKAnchor.ComponentType.Plane), out _));
yield return null;
}
///
/// Test that the ray cast respects the max distance parameter
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Plane_DoesNotHitMaxDist()
{
Ray mockRay = new Ray(new Vector3(0f, 1.70f, 1f), Vector3.forward);
bool didHit = _currentRoom.Raycast(mockRay, 1f, out RaycastHit _, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsFalse(didHit);
Assert.IsNull(anchorInfo);
}
///
/// Test that the ray cast respects the label filter parameter
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Plane_DoesNotHitFilter()
{
Ray mockRay = new Ray(new Vector3(0f, 1.70f, 1f), Vector3.forward);
bool didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(~MRUKAnchor.SceneLabels.WALL_FACE), out RaycastHit _, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsFalse(didHit);
Assert.IsNull(anchorInfo);
}
///
/// Test that the ray cast hits a scene anchor plane and that the hit point and normal are as expected.
/// The ray cast direction is rotated on all three axis.
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_PlaneAtAnAngle_DoesHit()
{
Ray mockRay = new Ray(new Vector3(0f, 1f, 0f), new Vector3(-0.25f, 0.1f, -0.1f));
bool didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, out RaycastHit hit, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsTrue(didHit);
Assert.IsTrue(hit.point != Vector3.zero);
Assert.IsNotNull(anchorInfo);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.WINDOW_FRAME));
Vector3 expectedHitPoint = new Vector3(-1.7084f, 1.6833f, -0.6833f);
Assert.That(hit.point, Is.EqualTo(expectedHitPoint).Using(Vector3EqualityComparer.Instance));
Vector3 expectedHitNormal = new Vector3(0.9917f, 0.0000f, -0.1288f);
Assert.That(hit.normal, Is.EqualTo(expectedHitNormal).Using(Vector3EqualityComparer.Instance));
}
///
/// Test that the ray cast hits a scene anchor volume and that the hit point and normal are as expected
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Volume_DoesHit()
{
Ray mockRay = new Ray(new Vector3(0f, 0.5f, 2f), Vector3.left);
bool didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, out RaycastHit hit, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsTrue(didHit);
Assert.IsTrue(hit.point != Vector3.zero);
Assert.IsNotNull(anchorInfo);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.TABLE));
Vector3 expectedHitPoint = new Vector3(-3.3530f, 0.500f, 2.0000f);
Assert.That(hit.point, Is.EqualTo(expectedHitPoint).Using(Vector3EqualityComparer.Instance));
Vector3 expectedHitNormal = new Vector3(0.9976f, 0.0000f, -0.0699f);
Assert.That(hit.normal, Is.EqualTo(expectedHitNormal).Using(Vector3EqualityComparer.Instance));
}
///
/// Test that the ray cast ignores the scene anchor volume and hits the wall behind it
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Volume_DoesFilterComponentType()
{
Ray mockRay = new Ray(new Vector3(0f, 0.5f, 2f), Vector3.left);
bool didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(componentTypes: MRUKAnchor.ComponentType.Volume), out _, out MRUKAnchor anchorInfo);
Assert.IsTrue(didHit);
Assert.IsNotNull(anchorInfo);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.TABLE));
didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(componentTypes: MRUKAnchor.ComponentType.Plane), out _, out anchorInfo);
Assert.IsTrue(didHit);
Assert.IsNotNull(anchorInfo);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.WALL_FACE));
yield return null;
}
///
/// Test that the ray cast respects the max distance parameter
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Volume_DoesNotHitMaxDist()
{
Ray mockRay = new Ray(new Vector3(0f, 0.5f, 2f), Vector3.left);
bool didHit = _currentRoom.Raycast(mockRay, 1f, out RaycastHit _, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsFalse(didHit);
Assert.IsNull(anchorInfo);
}
///
/// Test that the ray cast respects the max distance parameter
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Volume_DoesNotHitLabelFilter()
{
Ray mockRay = new Ray(new Vector3(0f, 0.5f, 2f), Vector3.left);
bool didHit = _currentRoom.Raycast(mockRay, Mathf.Infinity, new LabelFilter(MRUKAnchor.SceneLabels.OTHER), out RaycastHit _, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsFalse(didHit);
Assert.IsNull(anchorInfo);
}
///
/// Test that the ray cast hits a scene anchor polygon and that the hit point and normal are as expected
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_Polygon_DoesHit()
{
Ray mockRay = new Ray(new Vector3(0f, 1f, 1f), Vector3.down);
_currentRoom.Raycast(mockRay, Mathf.Infinity, out RaycastHit hit, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsTrue(hit.point != Vector3.zero);
Assert.IsNotNull(anchorInfo);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.FLOOR));
Vector3 expectedHitPoint = new Vector3(0.0000f, 0.0000f, 1.0000f);
Assert.That(hit.point, Is.EqualTo(expectedHitPoint).Using(Vector3EqualityComparer.Instance));
Vector3 expectedHitNormal = new Vector3(0.0000f, 1.0000f, 0.0000f);
Assert.That(hit.normal, Is.EqualTo(expectedHitNormal).Using(Vector3EqualityComparer.Instance));
}
///
/// Test that the ray cast hits a scene anchor,
/// the ray hits the floor.
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_OutOfRoom_HitFloor()
{
Ray mockRay = new Ray(new Vector3(0f, 5f, 0f), Vector3.down);
_currentRoom.Raycast(mockRay, Mathf.Infinity, out RaycastHit hit, out MRUKAnchor anchorInfo);
yield return null;
Assert.IsTrue(hit.point == Vector3.zero);
Assert.IsTrue(anchorInfo.Label.HasFlag(MRUKAnchor.SceneLabels.FLOOR));
Assert.IsNotNull(anchorInfo);
}
///
/// Test that the ray cast does not hit any scene anchors.
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_OutOfRoom_DoesNotHit()
{
Ray mockRay = new Ray(new Vector3(0f, 5f, 0f), Vector3.up);
_currentRoom.Raycast(mockRay, Mathf.Infinity, out RaycastHit hit, out MRUKAnchor anchorInfo);
yield return null;
// Test that the ray cast does not hit any sceen anchors
Assert.IsTrue(hit.point == Vector3.zero);
Assert.IsNull(anchorInfo);
}
///
/// Test that the ray cast does not hit any scene anchors as they are outside the max. distance.
///
[UnityTest]
[Timeout(DefaultTimeoutMs)]
public IEnumerator Raycast_BeyondMaxDistance_DoesNotHit()
{
Ray mockRay = new Ray(new Vector3(0f, 1f, 1f), Vector3.down);
_currentRoom.Raycast(mockRay, 0.01f, out RaycastHit hit, out MRUKAnchor anchorInfo);
yield return null;
// Test that the ray cast does not hit any sceen anchors
Assert.IsTrue(hit.point == Vector3.zero);
Assert.IsNull(anchorInfo);
}
}
}