186 lines
6.7 KiB
C#
186 lines
6.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using DynamicSceneManagerHelper;
|
|
|
|
/// <summary>
|
|
/// This sample expands on the snapshot scene manager and adds the ability
|
|
/// to update Unity game objects as the data changes through the snapshots.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// As per the anchor change rules, the UUID of a room changes when a child
|
|
/// item is added or removed, so we match pairs of anchors that have similar
|
|
/// child anchors between snapshots. We also check the geometric bounds of
|
|
/// anchors across snapshots.
|
|
///
|
|
/// In order to achieve this, we save additional info in scene snapshots,
|
|
/// and also provide a way to update existing Unity objects when
|
|
/// their geometry changes.
|
|
///
|
|
/// This sample should serve as a guide only, as it relies on many heap
|
|
/// allocations and inefficient search queries to help readability.
|
|
/// </remarks>
|
|
public class DynamicSceneManager : MonoBehaviour
|
|
{
|
|
public float UpdateFrequencySeconds = 5;
|
|
|
|
SceneSnapshot _snapshot = new SceneSnapshot();
|
|
Dictionary<OVRAnchor, GameObject> _sceneGameObjects = new Dictionary<OVRAnchor, GameObject>();
|
|
|
|
Task _updateSceneTask;
|
|
|
|
void Start()
|
|
{
|
|
SceneManagerHelper.RequestScenePermission();
|
|
StartCoroutine(UpdateScenePeriodically());
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (OVRInput.GetDown(OVRInput.RawButton.A))
|
|
_ = SceneManagerHelper.RequestSceneCapture();
|
|
}
|
|
|
|
IEnumerator UpdateScenePeriodically()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return new WaitForSeconds(UpdateFrequencySeconds);
|
|
|
|
_updateSceneTask = UpdateScene();
|
|
yield return new WaitUntil(() => _updateSceneTask.IsCompleted);
|
|
}
|
|
}
|
|
|
|
async Task UpdateScene()
|
|
{
|
|
// get current snapshot and compare to previous
|
|
var currentSnapshot = await LoadSceneSnapshotAsync();
|
|
var differences = new SnapshotComparer(
|
|
_snapshot, currentSnapshot).Compare();
|
|
|
|
// update unity objects from the differences
|
|
await UpdateUnityObjects(differences, currentSnapshot);
|
|
|
|
// update previous snapshot
|
|
_snapshot = currentSnapshot;
|
|
}
|
|
|
|
async Task<SceneSnapshot> LoadSceneSnapshotAsync()
|
|
{
|
|
// create snapshot from all rooms and their anchors
|
|
// saving some of the anchor data to detect changes
|
|
var snapshot = new SceneSnapshot();
|
|
|
|
var rooms = new List<OVRAnchor>();
|
|
await OVRAnchor.FetchAnchorsAsync<OVRRoomLayout>(rooms);
|
|
foreach (var room in rooms)
|
|
{
|
|
if (!room.TryGetComponent(out OVRAnchorContainer container))
|
|
continue;
|
|
|
|
var children = new List<OVRAnchor>();
|
|
await container.FetchChildrenAsync(children);
|
|
snapshot.Anchors.Add(room, new SceneSnapshot.Data { Children = children});
|
|
|
|
foreach (var child in children)
|
|
{
|
|
var data = new SceneSnapshot.Data();
|
|
if (child.TryGetComponent(out OVRBounded2D b2d) && b2d.IsEnabled)
|
|
data.Rect = b2d.BoundingBox;
|
|
if (child.TryGetComponent(out OVRBounded3D b3d) && b3d.IsEnabled)
|
|
data.Bounds = b3d.BoundingBox;
|
|
snapshot.Anchors.Add(child, data);
|
|
}
|
|
}
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
async Task UpdateUnityObjects(List<(OVRAnchor, SnapshotComparer.ChangeType)> changes,
|
|
SceneSnapshot newSnapshot)
|
|
{
|
|
if (!changes.Any())
|
|
return;
|
|
|
|
var updater = new UnityObjectUpdater();
|
|
|
|
var changesNew = FilterChanges(changes, SnapshotComparer.ChangeType.New);
|
|
var changesMissing = FilterChanges(changes, SnapshotComparer.ChangeType.Missing);
|
|
var changesId = FilterChanges(changes, SnapshotComparer.ChangeType.ChangedId);
|
|
var changesBounds = FilterChanges(changes, SnapshotComparer.ChangeType.ChangedBounds);
|
|
|
|
// create a new game object for all new changes
|
|
foreach (var anchor in changesNew)
|
|
{
|
|
_sceneGameObjects.TryGetValue(GetParentAnchor(anchor, newSnapshot), out var parent);
|
|
_sceneGameObjects.Add(anchor, await updater.CreateUnityObject(anchor, parent));
|
|
}
|
|
|
|
// destroy game objects for all missing anchors
|
|
foreach (var anchor in changesMissing)
|
|
{
|
|
Destroy(_sceneGameObjects[anchor]);
|
|
_sceneGameObjects.Remove(anchor);
|
|
}
|
|
|
|
// ChangedId means we need to find the pairs between the snapshots
|
|
foreach (var (currentAnchor, newAnchor) in FindAnchorPairs(changesId, newSnapshot))
|
|
{
|
|
// we only need to update the reference in our scene game objects
|
|
_sceneGameObjects.Add(newAnchor, _sceneGameObjects[currentAnchor]);
|
|
_sceneGameObjects.Remove(currentAnchor);
|
|
}
|
|
|
|
// geometry bounds means just updating an existing game object
|
|
foreach (var currentAnchor in changesBounds)
|
|
updater.UpdateUnityObject(currentAnchor, _sceneGameObjects[currentAnchor]);
|
|
}
|
|
|
|
List<OVRAnchor> FilterChanges(List<(OVRAnchor, SnapshotComparer.ChangeType)> changes,
|
|
SnapshotComparer.ChangeType changeType) =>
|
|
changes.Where(tuple => tuple.Item2 == changeType).Select(tuple => tuple.Item1).ToList();
|
|
|
|
List<(OVRAnchor, OVRAnchor)> FindAnchorPairs(List<OVRAnchor> allAnchors, SceneSnapshot newSnapshot)
|
|
{
|
|
var currentAnchors = allAnchors.Where(_snapshot.Contains);
|
|
var newAnchors = allAnchors.Where(newSnapshot.Contains);
|
|
|
|
var pairs = new List<(OVRAnchor, OVRAnchor)>();
|
|
foreach (var currentAnchor in currentAnchors)
|
|
{
|
|
foreach (var newAnchor in newAnchors)
|
|
{
|
|
if (AreAnchorsEqual(_snapshot.Anchors[currentAnchor], newSnapshot.Anchors[newAnchor]))
|
|
{
|
|
pairs.Add((currentAnchor, newAnchor));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pairs;
|
|
}
|
|
|
|
bool AreAnchorsEqual(SceneSnapshot.Data anchor1Data, SceneSnapshot.Data anchor2Data)
|
|
{
|
|
// the only equal anchors with different UUIDs are when they are rooms.
|
|
// so we will check if any of their child elements are the same
|
|
if (anchor1Data.Children == null || anchor2Data.Children == null)
|
|
return false;
|
|
|
|
return anchor1Data.Children.Any(anchor2Data.Children.Contains) ||
|
|
anchor2Data.Children.Any(anchor1Data.Children.Contains);
|
|
}
|
|
|
|
OVRAnchor GetParentAnchor(OVRAnchor childAnchor, SceneSnapshot snapshot)
|
|
{
|
|
foreach (var kvp in snapshot.Anchors)
|
|
if (kvp.Value.Children?.Contains(childAnchor) == true)
|
|
return kvp.Key;
|
|
return OVRAnchor.Null;
|
|
}
|
|
}
|