Files
ApexSurgery/Assets/Oculus/SampleFramework/Usage/SceneManager/Scripts/CustomSceneManager/DynamicSceneManagerHelper.cs
T
2025-07-04 20:33:06 +03:00

188 lines
7.2 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
// This namespace contains helper classes for the DynamicSceneManager.
namespace DynamicSceneManagerHelper
{
/// <summary>
/// A class that holds a list of anchors, along with data that
/// can be used to identify when a change has occured.
/// </summary>
class SceneSnapshot
{
public class Data
{
public List<OVRAnchor> Children;
public Rect? Rect;
public Bounds? Bounds;
}
public Dictionary<OVRAnchor, Data> Anchors { get; } = new Dictionary<OVRAnchor, Data>();
public bool Contains(OVRAnchor anchor) => Anchors.ContainsKey(anchor);
}
/// <summary>
/// This class contains the custom logic for scene snapshot comparison.
/// </summary>
class SnapshotComparer
{
public SceneSnapshot BaseSnapshot { get; }
public SceneSnapshot NewSnapshot { get; }
public SnapshotComparer(SceneSnapshot baseSnapshot, SceneSnapshot newSnapshot)
{
BaseSnapshot = baseSnapshot;
NewSnapshot = newSnapshot;
}
public enum ChangeType
{
New,
Missing,
ChangedId,
ChangedBounds
}
public List<(OVRAnchor, ChangeType)> Compare()
{
var changes = new List<(OVRAnchor, ChangeType)>();
// if in base but not in new, then missing
// if in new but not in base, then new
foreach (var anchor1 in BaseSnapshot.Anchors.Keys)
if (!NewSnapshot.Contains(anchor1))
changes.Add((anchor1, ChangeType.Missing));
foreach (var anchor2 in NewSnapshot.Anchors.Keys)
if (!BaseSnapshot.Contains(anchor2))
changes.Add((anchor2, ChangeType.New));
// further checks
CheckRoomChanges(changes);
CheckBoundsChanges(changes);
return changes;
}
void CheckRoomChanges(List<(OVRAnchor, ChangeType)> changes)
{
// a room with new/deleted/edited child anchors is considered
// a NEW anchor, so we will check if any child elements are
// the same and mark both old and new room anchors as CHANGED
for (var i = 0; i < changes.Count; i++)
{
var (anchor, change) = changes[i];
var isRoom = anchor.TryGetComponent(out OVRRoomLayout room) && room.IsEnabled;
if (!isRoom || change == ChangeType.ChangedId)
continue;
var isNewAnchor = NewSnapshot.Contains(anchor);
var isOldAnchor = BaseSnapshot.Contains(anchor);
if (!isNewAnchor && !isOldAnchor)
continue;
var children = isNewAnchor ?
NewSnapshot.Anchors[anchor].Children :
BaseSnapshot.Anchors[anchor].Children;
var comparisonSnapshot = change == ChangeType.New ?
BaseSnapshot : NewSnapshot;
foreach (var child in children)
if (comparisonSnapshot.Contains(child))
changes[i] = (anchor, ChangeType.ChangedId);
}
}
void CheckBoundsChanges(List<(OVRAnchor, ChangeType)> changes)
{
// first match pairs of base and new snapshots
foreach (var baseAnchor in BaseSnapshot.Anchors.Keys)
{
var newAnchor = NewSnapshot.Anchors.Keys.FirstOrDefault(
newAnchor => newAnchor.Uuid == baseAnchor.Uuid);
// we have a pair, now compare bounds data
if (newAnchor.Uuid == baseAnchor.Uuid)
{
var baseData = BaseSnapshot.Anchors[baseAnchor];
var newData = NewSnapshot.Anchors[newAnchor];
var changed2DBounds = Has2DBounds(baseData, newData) && Are2DBoundsDifferent(baseData, newData);
var changed3DBounds = Has3DBounds(baseData, newData) && Are3DBoundsDifferent(baseData, newData);
if (changed2DBounds || changed3DBounds)
changes.Add((baseAnchor, ChangeType.ChangedBounds));
}
}
}
bool Has2DBounds(SceneSnapshot.Data data1, SceneSnapshot.Data data2)
=> data1.Rect.HasValue && data2.Rect.HasValue;
bool Are2DBoundsDifferent(SceneSnapshot.Data data1, SceneSnapshot.Data data2)
=> data1.Rect?.min != data2.Rect?.min || data1.Rect?.max != data2.Rect?.max;
bool Has3DBounds(SceneSnapshot.Data data1, SceneSnapshot.Data data2)
=> data1.Bounds.HasValue && data2.Bounds.HasValue;
bool Are3DBoundsDifferent(SceneSnapshot.Data data1, SceneSnapshot.Data data2)
=> data1.Bounds?.min != data2.Bounds?.min || data1.Bounds?.max != data2.Bounds?.max;
}
/// <summary>
/// This class wraps the logic for interacting with Unity
/// game objects.
/// </summary>
class UnityObjectUpdater
{
public async Task<GameObject> CreateUnityObject(OVRAnchor anchor, GameObject parent)
{
// if this is a room, we only need to make a GameObject
if (anchor.TryGetComponent(out OVRRoomLayout _))
return new GameObject($"Room-{anchor.Uuid}");
// only interested in the anchors which are locatable
if (!anchor.TryGetComponent(out OVRLocatable locatable))
return null;
await locatable.SetEnabledAsync(true);
// get semantic classification for object name
var label = "other";
if (anchor.TryGetComponent(out OVRSemanticLabels labels))
label = labels.Labels;
// create and parent Unity game object if possible
var gameObject = new GameObject(label);
if (parent != null)
gameObject.transform.SetParent(parent.transform);
// set location and create objects for 2D, 3D, triangle mesh
var helper = new SceneManagerHelper(gameObject);
helper.SetLocation(locatable);
if (anchor.TryGetComponent(out OVRBounded2D b2d) && b2d.IsEnabled)
helper.CreatePlane(b2d);
if (anchor.TryGetComponent(out OVRBounded3D b3d) && b3d.IsEnabled)
helper.CreateVolume(b3d);
if (anchor.TryGetComponent(out OVRTriangleMesh mesh) && mesh.IsEnabled)
helper.CreateMesh(mesh);
return gameObject;
}
public void UpdateUnityObject(OVRAnchor anchor, GameObject gameObject)
{
var helper = new SceneManagerHelper(gameObject);
if (anchor.TryGetComponent(out OVRLocatable locatable))
helper.SetLocation(locatable);
if (anchor.TryGetComponent(out OVRBounded2D b2d) && b2d.IsEnabled)
helper.UpdatePlane(b2d);
if (anchor.TryGetComponent(out OVRBounded3D b3d) && b3d.IsEnabled)
helper.UpdateVolume(b3d);
if (anchor.TryGetComponent(out OVRTriangleMesh mesh) && mesh.IsEnabled)
helper.UpdateMesh(mesh);
}
}
static class ObjectPool
{
}
}