/*
* 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;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
///
/// Controls anchor data and anchor control panel behavior.
///
public class SharedAnchor : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI anchorName;
[SerializeField]
private Image saveIcon;
[SerializeField]
private GameObject shareButton;
[SerializeField]
private Image shareIcon;
[SerializeField]
private Image alignIcon;
[SerializeField]
private Color grayColor;
[SerializeField]
private Color greenColor;
private OVRSpatialAnchor _spatialAnchor;
#if OVR_INTERNAL_CODE
private LocalGroupController localGroupControllerRef;
#endif
public const string SHARE_POINT_CLOUD_DATA_ERROR = "Share Point Cloud Data is disabled on your device. Settings -> Privacy & Safety -> Device Permissions";
public const string SHARE_POINT_CLOUD_DATA_INFO_URL = "https://www.meta.com/help/quest/articles/in-vr-experiences/oculus-features/point-cloud/";
private const uint MAX_ANCHOR_SAVE_ATTEMPTS = 3;
private uint numAnchorSaveAttempts = 0;
public bool IsSavedLocally
{
set
{
if (saveIcon != null)
{
saveIcon.color = value ? greenColor : grayColor;
}
}
}
private bool IsSelectedForShare
{
set
{
if (shareIcon != null)
{
shareIcon.color = value ? greenColor : grayColor;
}
}
}
public bool IsSelectedForAlign
{
set
{
if (alignIcon != null)
{
alignIcon.color = value ? greenColor : grayColor;
}
}
}
private void Awake()
{
_spatialAnchor = GetComponent();
}
private IEnumerator Start()
{
while (_spatialAnchor && !_spatialAnchor.Created)
{
yield return null;
}
if (_spatialAnchor != null)
{
anchorName.text = _spatialAnchor.Uuid.ToString("D");
}
else
{
Destroy(gameObject);
}
if (SampleController.Instance.automaticCoLocation)
transform.Find("Canvas").gameObject.SetActive(false);
#if OVR_INTERNAL_CODE
if (SampleController.Instance.localGroupSample)
localGroupControllerRef = FindObjectOfType();
#endif
}
public void OnSaveLocalButtonPressed()
{
if (_spatialAnchor == null)
{
return;
}
_spatialAnchor.SaveAsync().ContinueWith((isSuccessful) =>
{
if (isSuccessful)
{
SampleController.Instance.Log($"Successfully Saved Spatial Anchor");
IsSavedLocally = true;
SharedAnchorLoader.Instance?.AddLocallySavedAnchor(_spatialAnchor);
}
else
{
SampleController.Instance.LogError($"Failed to save spatial anchor to local storage");
}
});
}
public void OnHideButtonPressed()
{
SampleController.Instance.Log($"{nameof(OnHideButtonPressed)}: Hiding Spatial Anchor");
Destroy(gameObject);
}
public void OnEraseButtonPressed()
{
if (_spatialAnchor == null)
{
return;
}
_spatialAnchor.EraseAsync().ContinueWith((isSuccessful) =>
{
SampleController.Instance.Log($"Successfully Erased Spatial Anchor : {isSuccessful}");
if (isSuccessful)
{
SharedAnchorLoader.Instance?.RemoveLocallySavedAnchor(_spatialAnchor);
Destroy(gameObject);
}
});
}
private bool IsReadyToShare()
{
if (!Photon.Pun.PhotonNetwork.IsConnected)
{
SampleController.Instance.Log("Can't share - no users to share with because you are no longer connected to the Photon network");
return false;
}
var userIds = PhotonAnchorManager.GetUserList().Select(userId => userId.ToString()).ToArray();
if (userIds.Length == 0)
{
SampleController.Instance.Log("Can't share - no users to share with or can't get the user ids through photon custom properties");
return false;
}
if (_spatialAnchor == null)
{
SampleController.Instance.Log("Can't share - no associated spatial anchor");
return false;
}
return true;
}
public void OnShareButtonPressed()
{
SampleController.Instance.Log(nameof(OnShareButtonPressed));
#if OVR_INTERNAL_CODE
if (SampleController.Instance.localGroupSample)
{
IsSelectedForShare = true;
if (localGroupControllerRef.peerNetworkingEnabled)
{
SampleController.Instance.Log("Sharing anchor over p2p Local Group");
localGroupControllerRef.GetComponent().ShareAnchorsWithLocalGroup(new List { GetComponent() });
}
else
{
SampleController.Instance.Log("Sharing anchor over cloud Local Group");
numAnchorSaveAttempts = 0;
SaveToCloudThenShare();
}
}
else
#endif
{
if (!IsReadyToShare())
{
return;
}
IsSelectedForShare = true;
numAnchorSaveAttempts = 0;
SaveToCloudThenShare();
}
}
private void SaveToCloudThenShare()
{
OVRSpatialAnchor.SaveOptions saveOptions;
saveOptions.Storage = OVRSpace.StorageLocation.Cloud;
_spatialAnchor.SaveAsync(saveOptions).ContinueWith((isSuccessful) =>
{
if (isSuccessful)
{
SampleController.Instance.Log("Successfully saved anchor(s) to the cloud");
#if OVR_INTERNAL_CODE
if (SampleController.Instance.localGroupSample && localGroupControllerRef)
{
localGroupControllerRef.GetComponent().ShareAnchorsWithLocalGroup(new List { GetComponent() });
}
else
#endif
{
var userIds = PhotonAnchorManager.GetUserList().Select(userId => userId.ToString()).ToArray();
ICollection spaceUserList = new List();
foreach (string strUsername in userIds)
{
SampleController.Instance.Log($"Sharing Anchor with {strUsername}");
spaceUserList.Add(new OVRSpaceUser(ulong.Parse(strUsername)));
}
_spatialAnchor.ShareAsync(spaceUserList).ContinueWith(OnShareComplete);
}
SampleController.Instance.AddSharedAnchorToLocalPlayer(this);
}
else
{
SampleController.Instance.Log($"Saving Spatial Anchor Failed");
numAnchorSaveAttempts++;
if (numAnchorSaveAttempts < MAX_ANCHOR_SAVE_ATTEMPTS)
{
SampleController.Instance.Log("Retrying anchor save to cloud...");
SaveToCloudThenShare();
}
}
});
}
public void ReshareAnchor()
{
if (!IsReadyToShare())
{
return;
}
SampleController.Instance.Log("ReshareAnchor: re-sharing anchor with all users in the room");
IsSelectedForShare = true;
OVRSpatialAnchor.SaveOptions saveOptions;
saveOptions.Storage = OVRSpace.StorageLocation.Cloud;
ICollection spaceUserList = new List();
foreach (string strUsername in PhotonAnchorManager.GetUsers())
{
spaceUserList.Add(new OVRSpaceUser(ulong.Parse(strUsername)));
}
_spatialAnchor.ShareAsync(spaceUserList).ContinueWith(OnShareComplete);
}
private void OnShareComplete(OVRSpatialAnchor.OperationResult result)
{
SampleController.Instance.Log(nameof(OnShareComplete) + " Result: " + result);
if (result != OVRSpatialAnchor.OperationResult.Success)
{
shareIcon.color = Color.red;
if (result == OVRSpatialAnchor.OperationResult.Failure_SpaceCloudStorageDisabled)
{
SampleController.Instance.Log(SHARE_POINT_CLOUD_DATA_ERROR);
Application.OpenURL(SHARE_POINT_CLOUD_DATA_INFO_URL);
}
return;
}
SampleController.Instance.Log($"{nameof(OnShareComplete)} - UUID: {_spatialAnchor.Uuid}");
PhotonAnchorManager.Instance.PublishAnchorUuids(new Guid[] { _spatialAnchor.Uuid }, 1, true);
}
public void OnAlignButtonPressed()
{
SampleController.Instance.Log("OnAlignButtonPressed: aligning to anchor");
AlignPlayer.Instance.SetAlignmentAnchor(this);
}
public void DisableShareIcon()
{
if (shareButton)
{
shareButton.SetActive(false);
}
}
}