HoloLens WorldAnchor ImportExport

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
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值