using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HoloToolkit.Unity;
using UnityEngine.VR.WSA.Sharing;
using UnityEngine.VR.WSA.Persistence;
using UnityEngine.VR.WSA;
using V3dSharing;
using SimpleJSON;
using BestHTTP.SocketIO;
using System;
public class IEAnchorManager : Singleton<IEAnchorManager>
{
/// <summary>
/// Enum to track the progress through establishing a shared coordinate system.
/// </summary>
public enum ImportExportState
{
// Overall states
Start,
Ready,
Failed,
Reset,
// AnchorStore values
AnchorStore_Initializing,
AnchorStore_Initialized,
RoomApiInitialized,
// Anchor creation values
InitialAnchorRequired,
CreatingInitialAnchor,
ReadyToExportInitialAnchor,
UploadingInitialAnchor,
// Anchor values
DataRequested,
DataReady,
Importing
}
public ImportExportState currentState = ImportExportState.Start;
public string StateName
{
get
{
return currentState.ToString();
}
}
public bool AnchorEstablished
{
get
{
return currentState == ImportExportState.Ready;
}
}
/// <summary>
/// WorldAnchorTransferBatch is the primary object in serializing/deserializing anchors.
/// </summary>
WorldAnchorTransferBatch sharedAnchorInterface;
/// <summary>
/// Keeps track of stored anchor data blob.
/// </summary>
byte[] rawAnchorData = null;
/// <summary>
/// Keeps track of locally stored anchors.
/// </summary>
WorldAnchorStore anchorStore = null;
/// <summary>
/// Keeps track of the name of the anchor we are exporting.
/// </summary>
string exportingAnchorName { get; set; }
public string oldAnchorName { get; set; }
/// <summary>
/// The datablob of the anchor.
/// </summary>
List<byte> exportingAnchorBytes = new List<byte>();
/// <summary>
/// Keeps track of if the sharing service is ready.
/// We need the sharing service to be ready so we can
/// upload and download data for sharing anchors.
/// </summary>
bool sharingServiceReady = false;
/// <summary>
/// Sometimes we'll see a really small anchor blob get generated.
/// These tend to not work, so we have a minimum trustable size.
/// </summary>
const uint minTrustworthySerializedAnchorDataSize = 100000;
SocketManager AnchorSocket;
string upLoadAnchor = "UPLOAD_ANCHOR";
void InitSocket()
{
AnchorSocket = BestSocketIOComponent.Instance.BestSocket;
AnchorSocket.Socket.On("ar", OnArReceive);
sharingServiceReady = true;
}
// Use this for initialization
void Start()
{
exportingAnchorName = "visual-anchor-123456";
currentState = ImportExportState.Ready;
// We need to get our local anchor store started up.
currentState = ImportExportState.AnchorStore_Initializing;
WorldAnchorStore.GetAsync(AnchorStoreReady);
}
/// <summary>
/// Called when the local anchor store is ready.
/// </summary>
/// <param name="store"></param>
void AnchorStoreReady(WorldAnchorStore store)
{
anchorStore = store;
currentState = ImportExportState.AnchorStore_Initialized;
}
/// <summary>
/// Attempts to attach to an anchor by anchorName in the local store..
/// </summary>
/// <returns>True if it attached, false if it could not attach</returns>
bool AttachToCachedAnchor(string AnchorName)
{
Debug.Log("Looking for " + AnchorName);
string[] ids = anchorStore.GetAllIds();
for (int index = 0; index < ids.Length; index++)
{
if (ids[index] == AnchorName)
{
Debug.Log("Using what we have");
//debug("Using what we have");
WorldAnchor wa = anchorStore.Load(ids[index], gameObject);
if (wa.isLocated)
{
currentState = ImportExportState.Ready;
}
else
{
wa.OnTrackingChanged += IEAnchorManager_OnTrackingChanged_Attaching;
}
return true;
}
}
// Didn't find the anchor.
return false;
}
/// <summary>
/// Called when tracking changes for a 'cached' anchor.
/// </summary>
/// <param name="self"></param>
/// <param name="located"></param>
private void IEAnchorManager_OnTrackingChanged_Attaching(WorldAnchor self, bool located)
{
Debug.Log("anchor: " + located);
if (located)
{
currentState = ImportExportState.Ready;
}
else
{
Debug.Log("Failed to find local anchor from cache.");
MakeAnchorDataRequest();
}
self.OnTrackingChanged -= IEAnchorManager_OnTrackingChanged_Attaching;
}
/// <summary>
/// Kicks off getting the datablob required to import the shared anchor.
/// </summary>
void MakeAnchorDataRequest()
{
DownLoadAnchorRequest();
currentState = ImportExportState.DataRequested;
}
void InitRoomApi()
{
//Debug.Log("InitRoomApi" + BestSocketIOComponent.Instance.anchorCmd);
// If we have a room, we'll join the first room we see.
// If we are the user with the lowest user ID, we will create the room.
// Otherwise we will wait for the room to be created.
if (LoadObj.Instance.load)
{
if (BestSocketIOComponent.Instance)
{
exportingAnchorName = BestSocketIOComponent.Instance.roomID;
}
if (BestSocketIOComponent.Instance.anchorCmd == upLoadAnchor)
{
//debug("创建房间中请等待......");
//Debug.Log("Creating room ");
currentState = ImportExportState.InitialAnchorRequired;
}
else
{
//debug("找到房间并加入请等待......");
Debug.Log("Joining room");
currentState = ImportExportState.RoomApiInitialized;
}
}
else
{
//Debug.Log("Waiting for load obj");
}
}
/// <summary>
/// Kicks off the process of creating the shared space.
/// 启动创建共享空间的过程。
/// </summary>
void StartAnchorProcess()
{
// First, are there any anchors in this room?
// If there are anchors, we should attach to the first one.
if (BestSocketIOComponent.Instance.anchorCmd == "DOWNLOAD_ANCHOR")
{
// Extract the name of the anchor.
string storedAnchorName = exportingAnchorName;
//清除内存中的Anchor
anchorStore.Clear();
// Attempt to attach to the anchor in our local anchor store.
//尝试在本地锚存储附加锚。
if (AttachToCachedAnchor(storedAnchorName) == false)
{
Debug.Log("Starting room download");
// If we cannot find the anchor by name, we will need the full data blob.
MakeAnchorDataRequest();
}
}
}
/// <summary>
/// Called when a remote anchor has been deserialized
/// </summary>
/// <param name="status"></param>
/// <param name="wat"></param>
void ImportComplete(SerializationCompletionReason status, WorldAnchorTransferBatch wat)
{
if (status == SerializationCompletionReason.Succeeded && wat.GetAllIds().Length > 0)
{
bool hasAnchorName = false;
BestSocketIOComponent.Instance.debug("Information matching, please wait a moment");
Debug.Log("ImportComplete:" + exportingAnchorName);
string[] anchorNames = wat.GetAllIds();
foreach (var an in anchorNames)
{
if (an == exportingAnchorName)
{
hasAnchorName = true;
break;
}
}
if (!hasAnchorName)
{
currentState = ImportExportState.DataReady;
return;
}
WorldAnchor anchor = wat.LockObject(exportingAnchorName, gameObject);
if (anchor.isLocated)
{
if (anchorStore.Save(exportingAnchorName, anchor))
{
currentState = ImportExportState.Ready;
Debug.Log("LockObject anchor");
}
else
{
currentState = ImportExportState.DataReady;
}
}
else
{
anchor.OnTrackingChanged += WorldAnchorForImport_OnTrackingChanged;
}
}
else
{
Debug.Log("Import fail");
BestSocketIOComponent.Instance.debug("Invalid information, please reload");
currentState = ImportExportState.DataReady;
}
}
private void WorldAnchorForImport_OnTrackingChanged(WorldAnchor self, bool located)
{
if (located)
{
WorldAnchor anchor = GetComponent<WorldAnchor>();
if (anchorStore.Save(exportingAnchorName, anchor))
{
currentState = ImportExportState.Ready;
}
else
{
currentState = ImportExportState.DataReady;
}
}
else
{
currentState = ImportExportState.Failed;
}
self.OnTrackingChanged -= WorldAnchorForImport_OnTrackingChanged;
}
/// <summary>
/// Called by the WorldAnchorTransferBatch as anchor data is available.
/// </summary>
/// <param name="data"></param>
public void WriteBuffer(byte[] data)
{
exportingAnchorBytes.AddRange(data);
}
/// <summary>
/// Called by the WorldAnchorTransferBatch when anchor exporting is complete.
/// </summary>
/// <param name="status"></param>
public void ExportComplete(SerializationCompletionReason status)
{
Debug.Log("ExportComplete....");
if (status == SerializationCompletionReason.Succeeded && exportingAnchorBytes.Count > minTrustworthySerializedAnchorDataSize)
{
//debug("Uploading anchor: " + exportingAnchorName);
//Debug.Log("Uploading anchor: " + exportingAnchorName);
//BestSocketIOComponent.Instance.debug("Uploading anchor:" + exportingAnchorBytes.ToArray());
AnchorSocket.Socket.Emit("ar", UploadAnchor(exportingAnchorName, exportingAnchorBytes.ToArray()));
BestSocketIOComponent.Instance.debug("Information matching, please wait a moment");
currentState = ImportExportState.Ready;
}
else
{
Debug.Log("This anchor didn't work, trying again2");
BestSocketIOComponent.Instance.debug("The information is incomplete, please restart App");
currentState = ImportExportState.InitialAnchorRequired;
}
}
/// <summary>
/// Exports the currently created anchor.
/// </summary>
void Export()
{
WorldAnchor anchor = GetComponent<WorldAnchor>();
if (anchor == null)
{
Debug.Log("We should have made an anchor by now...");
return;
}
//清除内存中的Anchor
anchorStore.Clear();
//string guidString = Guid.NewGuid().ToString();
//exportingAnchorName = guidString;
// Save the anchor to our local anchor store.
if (anchorStore.Save(exportingAnchorName, anchor))
{
sharedAnchorInterface = new WorldAnchorTransferBatch();
sharedAnchorInterface.AddWorldAnchor(exportingAnchorName, anchor);
WorldAnchorTransferBatch.ExportAsync(sharedAnchorInterface, WriteBuffer, ExportComplete);
}
else
{
Debug.Log("This anchor didn't work, trying again1");
//debug("This anchor didn't work, trying again");
BestSocketIOComponent.Instance.debug("Room information incomplete, please try restart App");
currentState = ImportExportState.InitialAnchorRequired;
}
}
/// <summary>
/// Starts establishing a new anchor.
/// </summary>
void CreateAnchorLocally()
{
WorldAnchor anchor = GetComponent<WorldAnchor>();
if (anchor == null)
{
anchor = gameObject.AddComponent<WorldAnchor>();
}
if (anchor.isLocated)
{
Debug.Log("anchor.isLocated...");
currentState = ImportExportState.ReadyToExportInitialAnchor;
}
else
{
anchor.OnTrackingChanged += Anchor_OnTrackingChanged_InitialAnchor;
}
}
/// <summary>
/// Callback to trigger when an anchor has been 'found'.
/// </summary>
private void Anchor_OnTrackingChanged_InitialAnchor(WorldAnchor self, bool located)
{
if (located)
{
Debug.Log("Found anchor, ready to export");
currentState = ImportExportState.ReadyToExportInitialAnchor;
}
else
{
Debug.Log("Failed to locate local anchor (super bad!)");
currentState = ImportExportState.Failed;
}
self.OnTrackingChanged -= Anchor_OnTrackingChanged_InitialAnchor;
}
// Update is called once per frame
void Update()
{
if (BestSocketIOComponent.Instance.ready && !sharingServiceReady)
{
InitSocket();
}
switch (currentState)
{
// If the local anchor store is initialized.
case ImportExportState.AnchorStore_Initialized:
if (sharingServiceReady)
{
InitRoomApi();
}
break;
case ImportExportState.RoomApiInitialized:
StartAnchorProcess();
break;
case ImportExportState.DataReady:
BestSocketIOComponent.Instance.debug("Start matching space information, please wait a moment");
// DataReady is set when the anchor download completes.
currentState = ImportExportState.Importing;
WorldAnchorTransferBatch.ImportAsync(rawAnchorData, ImportComplete);
break;
case ImportExportState.InitialAnchorRequired:
currentState = ImportExportState.CreatingInitialAnchor;
CreateAnchorLocally();
break;
case ImportExportState.ReadyToExportInitialAnchor:
// We've created an anchor locally and it is ready to export.
currentState = ImportExportState.UploadingInitialAnchor;
BestSocketIOComponent.Instance.debug("Start matching space information, please wait a moment");
Export();
break;
case ImportExportState.Reset:
ResetAnchor();
break;
}
}
#region Control funtion
void OnArReceive(Socket socket, Packet packet, params object[] args)
{
string json_str = packet.RemoveEventName(true);
JSONNode value = JSON.Parse(json_str);
ParseJsonMsg(value);
}
void ParseJsonMsg(JSONNode node)
{
string msg_type = node["type"];
switch (msg_type)
{
case "DOWNLOAD_ANCHOR":
OnDownloadAnchor(node);
break;
default:
break;
}
}
string UploadAnchor(string anchorName, byte[] anchor)
{
//上传字节流
return JsonConstract.JsonConstract.CreateUpLoadAnchorMsg(anchorName, anchor);
}
void OnDownloadAnchor(JSONNode node)
{
string anchor = node["payload"][exportingAnchorName];
//rawAnchorData = System.Text.Encoding.Default.GetBytes(anchor);
rawAnchorData = Convert.FromBase64String(anchor);
// If we downloaded anchor data successfully we should import the data.
currentState = ImportExportState.DataReady;
//获取字节流
Debug.Log("OnDownloadAnchor:" + rawAnchorData.Length);
BestSocketIOComponent.Instance.debug("OnDownloadAnchor:" + rawAnchorData.Length);
//BestSocketIOComponent.Instance.debug("同步信息,请等待");
}
void DownLoadAnchorRequest()
{
AnchorSocket.Socket.Emit("ar", AnchorRequest(exportingAnchorName));
}
string AnchorRequest(string anchorName)
{
return JsonConstract.JsonConstract.CreateDownLoadAnchorRequestMsg(anchorName);
}
public void Reset()
{
//Debug.Log("AnchorReset....");
currentState = ImportExportState.Reset;
oldAnchorName = exportingAnchorName;
}
//再次进入同一房间发现有anchor 不用下载了
public void ResetAnchor()
{
if (LoadObj.Instance != null && LoadObj.Instance.load)
{
Debug.Log("ResetAnchor");
if (oldAnchorName == BestSocketIOComponent.Instance.roomID)
{
currentState = ImportExportState.Ready;
return;
}
WorldAnchor anchor = gameObject.GetComponent<WorldAnchor>();
if (anchor != null)
{
DestroyImmediate(anchor);
}
string[] ids = anchorStore.GetAllIds();
for (int index = 0; index < ids.Length; index++)
{
//Debug.Log(ids[index]);
if (ids[index] == oldAnchorName)
{
bool deleted = anchorStore.Delete(ids[index]);
Debug.Log("deleted: " + deleted);
break;
}
}
currentState = ImportExportState.AnchorStore_Initialized;
}
}
#endregion
}