/*
* 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;
using System.Collections.Generic;
using System.Diagnostics;
using Meta.XR.Util;
using UnityEngine;
using Debug = UnityEngine.Debug;
///
/// Represents a type Scene anchor.
///
///
/// This component provides properties for accessing information on the Walls, Floors, and Ceiling.
///
/// and associated classes are deprecated (v65), please use [MR Utility Kit](https://developer.oculus.com/documentation/unity/unity-mr-utility-kit-overview)" instead.
///
[DisallowMultipleComponent]
[RequireComponent(typeof(OVRSceneAnchor))]
[HelpURL("https://developer.oculus.com/documentation/unity/unity-scene-use-scene-anchors/#further-scene-model-unity-components")]
[Obsolete(OVRSceneManager.DeprecationMessage)]
[Feature(Feature.Scene)]
public class OVRSceneRoom : MonoBehaviour, IOVRSceneComponent
{
///
/// The representing the floor of the room.
///
///
/// A room contains only a single Floor.
///
public OVRScenePlane Floor { get; private set; }
///
/// The representing the ceiling of the room.
///
///
/// A room contains only a single Ceiling.
///
public OVRScenePlane Ceiling { get; private set; }
///
/// The set of representing the walls of the room.
///
///
/// A room may contain a single Wall, but typically there is more than one.
///
public OVRScenePlane[] Walls { get; private set; } = Array.Empty();
private OVRSceneAnchor _sceneAnchor;
private OVRSceneManager _sceneManager;
private Guid _uuid;
internal static readonly Dictionary SceneRooms = new();
internal static readonly List SceneRoomsList = new();
private void Awake()
{
_sceneAnchor = GetComponent();
_sceneManager = FindAnyObjectByType();
_uuid = _sceneAnchor.Uuid;
if (_sceneAnchor.Space.Valid)
{
((IOVRSceneComponent)this).Initialize();
}
}
void IOVRSceneComponent.Initialize()
{
SceneRooms[_uuid] = this;
SceneRoomsList.Add(this);
}
internal async OVRTask LoadRoom(Guid floor, Guid ceiling, Guid[] walls)
{
using (new OVRObjectPool.HashSetScope(out var uuids))
using (new OVRObjectPool.ListScope(out var anchors))
{
uuids.Add(floor);
uuids.Add(ceiling);
foreach (var wall in walls)
{
uuids.Add(wall);
}
if (_sceneAnchor.Anchor.TryGetComponent(out var container))
{
foreach (var uuid in container.Uuids)
{
uuids.Add(uuid);
}
}
else
{
LogWarning($"{name} has no anchor container. Some elements may be missing.");
}
var result = await OVRSceneManager.FetchAnchorsAsync(uuids, anchors);
if (!result)
{
LogError($"Failed to fetch the {uuids.Count} anchors belonging to {name}");
return false;
}
Log($"{name} has {anchors.Count} anchors.");
using (new OVRObjectPool.ListScope(out var results))
using (new OVRObjectPool.ListScope>(out var tasks))
{
foreach (var anchor in anchors)
{
if (anchor.TryGetComponent(out var locatable))
{
tasks.Add(locatable.SetEnabledAsync(true));
}
}
await OVRTask.WhenAll(tasks, results);
}
foreach (var anchor in anchors)
{
if (!anchor.TryGetComponent(out var locatable) || !locatable.IsEnabled)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
var labels = anchor.TryGetComponent(out var component)
&& component.IsEnabled ? component.Labels : null;
LogWarning($"Anchor {anchor}{(string.IsNullOrEmpty(labels) ? "" : $" ({labels})")} could not be localized. Ignoring.");
#endif
continue;
}
OVRPlugin.GetSpaceComponentStatus(anchor.Handle, OVRPlugin.SpaceComponentType.Bounded2D,
out var bounded2dEnabled, out _);
OVRPlugin.GetSpaceComponentStatus(anchor.Handle, OVRPlugin.SpaceComponentType.Bounded3D,
out var bounded3dEnabled, out _);
OVRPlugin.GetSpaceComponentStatus(anchor.Handle, OVRPlugin.SpaceComponentType.TriangleMesh,
out var triangleMeshEnabled, out _);
var isStrictly2d = bounded2dEnabled && !(bounded3dEnabled || triangleMeshEnabled);
// The plane prefab is for anchors that are only 2D, i.e. they only have
// a 2D component. If a volume component exists, we use a volume prefab,
// else we pass null (prefab overrides may be used)
var prefab = isStrictly2d
? _sceneManager.PlanePrefab
: bounded3dEnabled ? _sceneManager.VolumePrefab : null;
var sceneAnchor = _sceneManager.InstantiateSceneAnchor(anchor, prefab);
if (sceneAnchor)
{
sceneAnchor.transform.parent = transform;
sceneAnchor.IsTracked = true;
}
}
bool TryGetPlane(Guid uuid, out OVRScenePlane plane)
{
plane = null;
return OVRSceneAnchor.SceneAnchors.TryGetValue(uuid, out var sceneAnchor) &&
sceneAnchor.TryGetComponent(out plane);
}
OVRScenePlane GetPlane(Guid uuid) => TryGetPlane(uuid, out var plane) ? plane : null;
Floor = GetPlane(floor);
Ceiling = GetPlane(ceiling);
using (new OVRObjectPool.ListScope(out var planes))
{
foreach (var wall in walls)
{
if (TryGetPlane(wall, out var plane))
{
planes.Add(plane);
}
}
Walls = planes.ToArray();
}
}
return true;
}
private void OnDestroy()
{
SceneRooms.Remove(_uuid);
SceneRoomsList.Remove(this);
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
private void Log(string message) => Debug.Log($"[{nameof(OVRSceneRoom)}] {message}", gameObject);
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
private void LogWarning(string message) => Debug.LogWarning($"[{nameof(OVRSceneRoom)}] {message}", gameObject);
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
private void LogError(string message) => Debug.LogError($"[{nameof(OVRSceneRoom)}] {message}", gameObject);
}