using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
///
/// This sample expands on the basic scene manager and adds the following features:
/// * Spawn a prefab for the wall, ceiling, floor elements and set 2D dimensions
/// * Spawn a fallback object for all other semantic labels and set 3D dimensions
/// * Update the location of all anchors at some frequency with new tracking info
///
///
/// The prefabs in this class are scaled to dimensions 1, however, you will need to
/// consider the dimension within your prefab to scale correctly. Additionally, in
/// order to avoid stretching, you may consider 9-slicing.
///
/// Scene anchors are tracked independently and can change as new tracking information
/// becomes available. It may be better for your use case to consider a single
/// tracking point and only update that. This will keep the relative positions of
/// all objects consistent, while also updating the location with new tracking data.
///
public class PrefabSceneManager : MonoBehaviour
{
public GameObject WallPrefab;
public GameObject CeilingPrefab;
public GameObject FloorPrefab;
public GameObject FallbackPrefab;
public float UpdateFrequencySeconds = 5;
List<(GameObject,OVRLocatable)> _locatableObjects = new List<(GameObject,OVRLocatable)>();
void Start()
{
SceneManagerHelper.RequestScenePermission();
LoadSceneAsync();
StartCoroutine(UpdateAnchorsPeriodically());
}
async void LoadSceneAsync()
{
// fetch all rooms, with a SceneCapture fallback
var rooms = new List();
await OVRAnchor.FetchAnchorsAsync(rooms);
if (rooms.Count == 0)
{
var sceneCaptured = await SceneManagerHelper.RequestSceneCapture();
if (!sceneCaptured)
return;
await OVRAnchor.FetchAnchorsAsync(rooms);
}
// fetch room elements, create objects for them
var tasks = rooms.Select(async room =>
{
var roomObject = new GameObject($"Room-{room.Uuid}");
if (!room.TryGetComponent(out OVRAnchorContainer container))
return;
if (!room.TryGetComponent(out OVRRoomLayout roomLayout))
return;
var children = new List();
await container.FetchChildrenAsync(children);
await CreateSceneAnchors(roomObject, roomLayout, children);
}).ToList();
await Task.WhenAll(tasks);
}
async Task CreateSceneAnchors(GameObject roomGameObject,
OVRRoomLayout roomLayout, List anchors)
{
roomLayout.TryGetRoomLayout(out var ceilingUuid,
out var floorUuid, out var wallUuids);
// iterate over all anchors as async tasks
var tasks = anchors.Select(async anchor =>
{
// can we locate it in the world?
if (!anchor.TryGetComponent(out OVRLocatable locatable))
return;
await locatable.SetEnabledAsync(true);
// check room layout information and assign prefab
// it would also be possible to use the semantic label
var prefab = FallbackPrefab;
if (anchor.Uuid == floorUuid)
prefab = FloorPrefab;
else if (anchor.Uuid == ceilingUuid)
prefab = CeilingPrefab;
else if (wallUuids.Contains(anchor.Uuid))
prefab = WallPrefab;
// get semantic classification for object name
var label = "other";
if (anchor.TryGetComponent(out OVRSemanticLabels labels))
label = labels.Labels;
// create container object
var gameObject = new GameObject(label);
gameObject.transform.SetParent(roomGameObject.transform);
var helper = new SceneManagerHelper(gameObject);
helper.SetLocation(locatable);
// instantiate prefab & set 2D dimensions
var model = Instantiate(prefab, gameObject.transform);
if (anchor.TryGetComponent(out OVRBounded2D bounds2D) &&
bounds2D.IsEnabled)
{
model.transform.localScale = new Vector3(
bounds2D.BoundingBox.size.x,
bounds2D.BoundingBox.size.y,
0.01f);
}
// we will set volume dimensions for the non-room elements
if (prefab == FallbackPrefab)
{
if (anchor.TryGetComponent(out OVRBounded3D bounds3D) &&
bounds3D.IsEnabled)
{
model.transform.localPosition = new Vector3(0, 0,
-bounds3D.BoundingBox.size.z / 2);
model.transform.localScale = bounds3D.BoundingBox.size;
}
}
// save game object and locatable for updating later
_locatableObjects.Add((gameObject, locatable));
}).ToList();
await Task.WhenAll(tasks);
}
IEnumerator UpdateAnchorsPeriodically()
{
while (true) {
foreach (var (gameObject, locatable) in _locatableObjects)
{
var helper = new SceneManagerHelper(gameObject);
helper.SetLocation(locatable);
}
yield return new WaitForSeconds(UpdateFrequencySeconds);
}
}
}