AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
SharingWorldAnchorManager.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using HoloToolkit.Unity;
5 
6 #if UNITY_WSA
7 using System;
8 using System.Collections.Generic;
9 using UnityEngine;
10 #if UNITY_2017_2_OR_NEWER
11 using UnityEngine.XR.WSA;
12 using UnityEngine.XR.WSA.Persistence;
13 using UnityEngine.XR.WSA.Sharing;
14 #else
15 using UnityEngine.VR.WSA;
16 using UnityEngine.VR.WSA.Persistence;
17 using UnityEngine.VR.WSA.Sharing;
18 #endif
19 #endif
20 
21 
22 namespace HoloToolkit.Sharing
23 {
28  {
29 #if UNITY_WSA
30  public event Action<bool> AnchorUploaded;
34 
38  public event Action<bool, GameObject> AnchorDownloaded;
39 
44  private const uint MinTrustworthySerializedAnchorDataSize = 100000;
45 
50  private WorldAnchorTransferBatch currentAnchorTransferBatch;
51 
52  private bool isExportingAnchors;
53  private bool shouldExportAnchors;
54 
58  private List<byte> rawAnchorUploadData = new List<byte>(0);
59 
60  private bool canUpdate;
61  private bool isImportingAnchors;
62  private bool shouldImportAnchors;
63 
67  private byte[] rawAnchorDownloadData;
68 
69  #region Unity Methods
70 
71  protected override void Start()
72  {
73  base.Start();
74 
75  if (SharingStage.Instance != null)
76  {
77  ShowDetailedLogs = SharingStage.Instance.ShowDetailedLogs;
78 
79  // SharingStage should be valid at this point, but we may not be connected.
80  if (SharingStage.Instance.IsConnected)
81  {
82  Connected();
83  }
84  else
85  {
86  Disconnected();
87  }
88  }
89  else
90  {
91  Debug.LogError("SharingWorldAnchorManager requires the SharingStage.");
92  }
93  }
94 
95  protected override void Update()
96  {
97  if (AnchorStore == null ||
98  SharingStage.Instance == null ||
99  SharingStage.Instance.CurrentRoom == null ||
100  !canUpdate)
101  { return; }
102 
103  if (LocalAnchorOperations.Count > 0)
104  {
105  if (!isExportingAnchors && !isImportingAnchors)
106  {
107  DoAnchorOperation(LocalAnchorOperations.Dequeue());
108  }
109  }
110  else
111  {
112  if (shouldImportAnchors && !isImportingAnchors && !isExportingAnchors)
113  {
114  if (AnchorDebugText != null)
115  {
116  AnchorDebugText.text += "\nStarting Anchor Download...";
117  }
118 
119  isImportingAnchors = true;
120  shouldImportAnchors = false;
121  WorldAnchorTransferBatch.ImportAsync(rawAnchorDownloadData, ImportComplete);
122  }
123 
124  if (shouldExportAnchors && !isExportingAnchors && !isImportingAnchors)
125  {
126  if (AnchorDebugText != null)
127  {
128  AnchorDebugText.text += "\nStarting Anchor Upload...";
129  }
130 
131  isExportingAnchors = true;
132  shouldExportAnchors = false;
133  WorldAnchorTransferBatch.ExportAsync(currentAnchorTransferBatch, WriteBuffer, ExportComplete);
134  }
135  }
136  }
137 
138  protected override void OnDestroy()
139  {
140  Disconnected();
141  base.OnDestroy();
142  }
143 
144  #endregion // Unity Methods
145 
146  #region Event Callbacks
147 
152  protected override void AnchorStoreReady(WorldAnchorStore anchorStore)
153  {
154  AnchorStore = anchorStore;
155 
156  if (!SharingStage.Instance.KeepRoomAlive || !PersistentAnchors)
157  {
158  if (AnchorDebugText != null)
159  {
160  AnchorDebugText.text += "\nClearing Anchor Store...";
161  }
162 
163  anchorStore.Clear();
164  }
165  }
166 
172  private void Connected(object sender = null, EventArgs e = null)
173  {
174  SharingStage.Instance.SharingManagerConnected -= Connected;
175  SharingStage.Instance.SharingManagerDisconnected += Disconnected;
176 
177  SharingStage.Instance.RoomManagerAdapter.UserJoinedRoomEvent += RoomManagerListener_OnRoomJoined;
178  SharingStage.Instance.RoomManagerAdapter.UserLeftRoomEvent += RoomManagerListener_OnLeftRoom;
179 
180  if (ShowDetailedLogs)
181  {
182  Debug.Log("[SharingWorldAnchorManager] Connected to server.");
183  }
184 
185  if (AnchorDebugText != null)
186  {
187  AnchorDebugText.text += "\nConnected to Server.";
188  }
189  }
190 
196  private void Disconnected(object sender = null, EventArgs e = null)
197  {
198  if (SharingStage.Instance != null)
199  {
200  SharingStage.Instance.SharingManagerConnected += Connected;
201  SharingStage.Instance.SharingManagerDisconnected -= Disconnected;
202 
203  SharingStage.Instance.RoomManagerAdapter.UserJoinedRoomEvent -= RoomManagerListener_OnRoomJoined;
204  SharingStage.Instance.RoomManagerAdapter.UserLeftRoomEvent -= RoomManagerListener_OnLeftRoom;
205  }
206 
207  if (ShowDetailedLogs)
208  {
209  Debug.Log("[SharingWorldAnchorManager] Disconnected from server.");
210  }
211 
212  if (AnchorDebugText != null)
213  {
214  AnchorDebugText.text += "\nDisconnected from Server.";
215  }
216  canUpdate = false;
217  }
218 
219  private void RoomManagerListener_OnRoomJoined(Room room, int userId)
220  {
221  if (SharingStage.Instance.Manager.GetLocalUser().GetID() == userId &&
222  SharingStage.Instance.CurrentRoom.GetID() == room.GetID())
223  {
224  SharingStage.Instance.RoomManagerAdapter.AnchorUploadedEvent += RoomManagerListener_AnchorUploaded;
225  SharingStage.Instance.RoomManagerAdapter.AnchorsChangedEvent += RoomManagerListener_AnchorsChanged;
226  SharingStage.Instance.RoomManagerAdapter.AnchorsDownloadedEvent += RoomManagerListener_AnchorDownloaded;
227 
228  canUpdate = true;
229 
230  if (ShowDetailedLogs)
231  {
232  Debug.LogFormat("[SharingWorldAnchorManager] In room {0} with {1} anchors.",
233  SharingStage.Instance.CurrentRoom.GetName().GetString(),
234  SharingStage.Instance.CurrentRoom.GetAnchorCount());
235  }
236 
237  if (AnchorDebugText != null)
238  {
239  AnchorDebugText.text += string.Format("\nIn room {0} with {1} anchors.",
240  SharingStage.Instance.CurrentRoom.GetName().GetString(),
241  SharingStage.Instance.CurrentRoom.GetAnchorCount());
242  }
243  }
244  }
245 
246  private void RoomManagerListener_OnLeftRoom(Room room, int userId)
247  {
248  if (SharingStage.Instance.Manager.GetLocalUser().GetID() == userId)
249  {
250  SharingStage.Instance.RoomManagerAdapter.AnchorUploadedEvent -= RoomManagerListener_AnchorUploaded;
251  SharingStage.Instance.RoomManagerAdapter.AnchorsChangedEvent -= RoomManagerListener_AnchorsChanged;
252  SharingStage.Instance.RoomManagerAdapter.AnchorsDownloadedEvent -= RoomManagerListener_AnchorDownloaded;
253 
254  canUpdate = false;
255 
256  if (ShowDetailedLogs)
257  {
258  Debug.LogFormat("\n[SharingWorldAnchorManager] Left room {0}", room.GetName().GetString());
259  }
260  if (AnchorDebugText != null)
261  {
262  AnchorDebugText.text += string.Format("\nLeft room {0}", room.GetName().GetString());
263  }
264  }
265  }
266 
270  private void RoomManagerListener_AnchorUploaded(bool successful, XString failureReason)
271  {
272  if (successful)
273  {
274  string[] anchorIds = currentAnchorTransferBatch.GetAllIds();
275 
276  for (int i = 0; i < anchorIds.Length; i++)
277  {
278  if (ShowDetailedLogs)
279  {
280  Debug.LogFormat("[SharingWorldAnchorManager] Successfully uploaded anchor \"{0}\".", anchorIds[i]);
281  }
282 
283  if (AnchorDebugText != null)
284  {
285  AnchorDebugText.text += string.Format("\nSuccessfully uploaded anchor \"{0}\".", anchorIds[i]);
286  }
287  }
288  }
289  else
290  {
291  Debug.LogError("[SharingWorldAnchorManager] Upload failed: " + failureReason);
292  if (AnchorDebugText != null)
293  {
294  AnchorDebugText.text += string.Format("\nUpload failed: " + failureReason);
295  }
296  }
297 
298  rawAnchorUploadData.Clear();
299  currentAnchorTransferBatch.Dispose();
300  currentAnchorTransferBatch = null;
301  isExportingAnchors = false;
302 
303  if (AnchorUploaded != null)
304  {
305  AnchorUploaded(successful);
306  }
307  }
308 
312  private void RoomManagerListener_AnchorDownloaded(bool successful, AnchorDownloadRequest request, XString failureReason)
313  {
314  // If we downloaded anchor data successfully we should import the data.
315  if (successful)
316  {
317  int dataSize = request.GetDataSize();
318 
319  if (ShowDetailedLogs)
320  {
321  Debug.LogFormat("[SharingWorldAnchorManager] Downloaded {0} bytes.", dataSize.ToString());
322  }
323 
324  if (AnchorDebugText != null)
325  {
326  AnchorDebugText.text += string.Format("\nDownloaded {0} bytes.", dataSize.ToString());
327  }
328 
329  rawAnchorDownloadData = new byte[dataSize];
330  request.GetData(rawAnchorDownloadData, dataSize);
331  shouldImportAnchors = true;
332  }
333  else
334  {
335  Debug.LogWarning("[SharingWorldAnchorManager] Anchor DL failed " + failureReason);
336  if (AnchorDebugText != null)
337  {
338  AnchorDebugText.text += string.Format("\nAnchor DL failed " + failureReason);
339  }
340  }
341  }
342 
347  private void RoomManagerListener_AnchorsChanged(Room room)
348  {
349  if (SharingStage.Instance.CurrentRoom.GetID() == room.GetID())
350  {
351  if (AnchorDebugText != null)
352  {
353  AnchorDebugText.text += "\nRoom Anchors Updated! Clearing the local Anchor Store and attempting to download the update...";
354  }
355 
356  // Clear our local anchor store, and download all our shared anchors again.
357  // TODO: Only download the anchors that changed. Currently there's no way to know which anchor changed.
358  AnchorStore.Clear();
359 
360  if (ShowDetailedLogs)
361  {
362  Debug.LogFormat("[SharingWorldAnchorManager] Anchors updated for room \"{0}\".\nClearing the local Anchor Store and attempting to download the update...", room.GetName().GetString());
363  }
364 
365  if (AnchorDebugText != null)
366  {
367  AnchorDebugText.text += string.Format("\nAnchors updated for room \"{0}\".\nClearing the local Anchor Store and attempting to download the update...", room.GetName().GetString());
368  }
369 
370  int roomAnchorCount = SharingStage.Instance.CurrentRoom.GetAnchorCount();
371 
372  for (int i = 0; i < roomAnchorCount; i++)
373  {
374  GameObject anchoredObject;
375  string roomAnchorId = SharingStage.Instance.CurrentRoom.GetAnchorName(i).GetString();
376 
377  if (AnchorGameObjectReferenceList.TryGetValue(roomAnchorId, out anchoredObject))
378  {
379  if (ShowDetailedLogs)
380  {
381  Debug.LogFormat("[SharingWorldAnchorManager] Found cached GameObject reference for \"{0}\".", roomAnchorId);
382  }
383 
384  if (AnchorDebugText != null)
385  {
386  AnchorDebugText.text += string.Format("\nFound cached GameObject reference for \"{0}\".", roomAnchorId);
387  }
388 
389  AttachAnchor(anchoredObject, roomAnchorId);
390  }
391  else
392  {
393  anchoredObject = GameObject.Find(roomAnchorId);
394 
395  if (anchoredObject != null)
396  {
397  if (ShowDetailedLogs)
398  {
399  Debug.LogFormat("[SharingWorldAnchorManager] Found a GameObject reference form scene for \"{0}\".", roomAnchorId);
400  }
401 
402  if (AnchorDebugText != null)
403  {
404  AnchorDebugText.text += string.Format("\nFound a GameObject reference form scene for \"{0}\".", roomAnchorId);
405  }
406 
407  AttachAnchor(anchoredObject, roomAnchorId);
408  }
409  else
410  {
411  Debug.LogWarning("[SharingWorldAnchorManager] Unable to find a matching GameObject for anchor!");
412  if (AnchorDebugText != null)
413  {
414  AnchorDebugText.text += "\nUnable to find a matching GameObject for anchor!";
415  }
416  }
417  }
418  }
419  }
420  }
421 
422  #endregion // Event Callbacks
423 
430  protected override bool ImportAnchor(string anchorId, GameObject objectToAnchor)
431  {
432  if (SharingStage.Instance == null ||
433  SharingStage.Instance.Manager == null ||
434  SharingStage.Instance.CurrentRoom == null)
435  {
436  Debug.LogErrorFormat("[SharingWorldAnchorManager] Failed to import anchor \"{0}\"! The sharing service was not ready.", anchorId);
437  if (AnchorDebugText != null)
438  {
439  AnchorDebugText.text += string.Format("\nFailed to import anchor \"{0}\"! The sharing service was not ready.", anchorId);
440  }
441 
442  return false;
443  }
444 
445  int roomAnchorCount = SharingStage.Instance.CurrentRoom.GetAnchorCount();
446 
447  for (int i = 0; i < roomAnchorCount; i++)
448  {
449  XString roomAnchorId = SharingStage.Instance.CurrentRoom.GetAnchorName(i);
450 
451  if (roomAnchorId.GetString().Equals(anchorId))
452  {
453  bool downloadStarted = SharingStage.Instance.CurrentRoomManager.DownloadAnchor(SharingStage.Instance.CurrentRoom, anchorId);
454 
455  if (downloadStarted)
456  {
457  if (ShowDetailedLogs)
458  {
459  Debug.Log("[SharingWorldAnchorManager] Found a match! Attempting to download anchor...");
460  }
461 
462  if (AnchorDebugText != null)
463  {
464  AnchorDebugText.text += "\nFound a match! Attempting to download anchor...";
465  }
466  }
467  else
468  {
469  Debug.LogWarning("[SharingWorldAnchorManager] Found a match, but we've failed to start download!");
470  if (AnchorDebugText != null)
471  {
472  AnchorDebugText.text += "\nFound a match, but we've failed to start download!";
473  }
474  }
475 
476  return downloadStarted;
477  }
478  }
479 
480  if (ShowDetailedLogs)
481  {
482  Debug.LogFormat("[SharingWorldAnchorManager] No matching anchor found for \"{0}\" in room {1}.", anchorId, SharingStage.Instance.CurrentRoom.GetName().GetString());
483  }
484 
485  if (AnchorDebugText != null)
486  {
487  AnchorDebugText.text += string.Format("\nNo matching anchor found for \"{0}\" in room {1}.", anchorId, SharingStage.Instance.CurrentRoom.GetName().GetString());
488  }
489 
490  return false;
491  }
492 
498  protected override void ExportAnchor(WorldAnchor anchor)
499  {
500  if (SharingStage.Instance == null ||
501  SharingStage.Instance.Manager == null ||
502  SharingStage.Instance.CurrentRoom == null)
503  {
504  Debug.LogErrorFormat("[SharingWorldAnchorManager] Failed to export anchor \"{0}\"! The sharing service was not ready.", anchor.name);
505  if (AnchorDebugText != null)
506  {
507  AnchorDebugText.text += string.Format("\nFailed to export anchor \"{0}\"! The sharing service was not ready.", anchor.name);
508  }
509 
510  return;
511  }
512 
513  if (!shouldExportAnchors)
514  {
515  if (ShowDetailedLogs)
516  {
517  Debug.LogWarningFormat("[SharingWorldAnchorManager] Attempting to export anchor \"{0}\".", anchor.name);
518  }
519 
520  if (AnchorDebugText != null)
521  {
522  AnchorDebugText.text += string.Format("\nAttempting to export anchor \"{0}\".", anchor.name);
523  }
524 
525  if (currentAnchorTransferBatch == null)
526  {
527  currentAnchorTransferBatch = new WorldAnchorTransferBatch();
528  if (AnchorDebugText != null)
529  {
530  AnchorDebugText.text += "\nCreating a new World Anchor Transfer Batch...";
531  }
532  }
533  else
534  {
535  Debug.LogWarning("[SharingWorldAnchorManager] We didn't properly cleanup our WorldAnchorTransferBatch!");
536  if (AnchorDebugText != null)
537  {
538  AnchorDebugText.text += "\nWe didn't properly cleanup our WorldAnchorTransferBatch!";
539  }
540  }
541 
542  currentAnchorTransferBatch.AddWorldAnchor(anchor.name, anchor);
543  shouldExportAnchors = true;
544  }
545  }
546 
551  private void ExportComplete(SerializationCompletionReason status)
552  {
553  if (status == SerializationCompletionReason.Succeeded &&
554  rawAnchorUploadData.Count > MinTrustworthySerializedAnchorDataSize)
555  {
556  if (ShowDetailedLogs)
557  {
558  Debug.LogFormat("[SharingWorldAnchorManager] Exporting {0} anchors with {1} bytes.", currentAnchorTransferBatch.anchorCount.ToString(), rawAnchorUploadData.ToArray().Length.ToString());
559  }
560 
561  if (AnchorDebugText != null)
562  {
563  AnchorDebugText.text += string.Format("\nExporting {0} anchors with {1} bytes.",
564  currentAnchorTransferBatch.anchorCount.ToString(),
565  rawAnchorUploadData.ToArray().Length.ToString());
566  }
567 
568  string[] anchorNames = currentAnchorTransferBatch.GetAllIds();
569 
570  for (var i = 0; i < anchorNames.Length; i++)
571  {
572  SharingStage.Instance.Manager.GetRoomManager().UploadAnchor(
573  SharingStage.Instance.CurrentRoom,
574  new XString(anchorNames[i]),
575  rawAnchorUploadData.ToArray(),
576  rawAnchorUploadData.Count);
577  }
578  }
579  else
580  {
581  Debug.LogWarning("[SharingWorldAnchorManager] Failed to upload anchor!");
582 
583  if (AnchorDebugText != null)
584  {
585  AnchorDebugText.text += "\nFailed to upload anchor!";
586  }
587 
588  if (rawAnchorUploadData.Count < MinTrustworthySerializedAnchorDataSize)
589  {
590  Debug.LogWarning("[SharingWorldAnchorManager] Anchor data was not valid. Try creating the anchor again.");
591 
592  if (AnchorDebugText != null)
593  {
594  AnchorDebugText.text += "\nAnchor data was not valid. Try creating the anchor again.";
595  }
596  }
597  }
598  }
599 
605  private void ImportComplete(SerializationCompletionReason status, WorldAnchorTransferBatch anchorBatch)
606  {
607  bool successful = status == SerializationCompletionReason.Succeeded;
608  GameObject objectToAnchor = null;
609 
610  if (successful)
611  {
612  if (ShowDetailedLogs)
613  {
614  Debug.LogFormat("[SharingWorldAnchorManager] Successfully imported \"{0}\" anchors.", anchorBatch.anchorCount.ToString());
615  }
616 
617  if (AnchorDebugText != null)
618  {
619  AnchorDebugText.text += string.Format("\nSuccessfully imported \"{0}\" anchors.", anchorBatch.anchorCount.ToString());
620  }
621 
622  string[] anchorNames = anchorBatch.GetAllIds();
623 
624  for (var i = 0; i < anchorNames.Length; i++)
625  {
626  if (AnchorGameObjectReferenceList.TryGetValue(anchorNames[i], out objectToAnchor))
627  {
628  AnchorStore.Save(anchorNames[i], anchorBatch.LockObject(anchorNames[i], objectToAnchor));
629  }
630  else
631  {
632  //TODO: Figure out how to get the GameObject reference from across the network. For now it's best to use unique GameObject names.
633  Debug.LogWarning("[SharingWorldAnchorManager] Unable to import anchor! We don't know which GameObject to anchor!");
634 
635  if (AnchorDebugText != null)
636  {
637  AnchorDebugText.text += "\nUnable to import anchor! We don\'t know which GameObject to anchor!";
638  }
639  }
640  }
641  }
642  else
643  {
644  Debug.LogError("[SharingWorldAnchorManager] Import failed!");
645 
646  if (AnchorDebugText != null)
647  {
648  AnchorDebugText.text += "\nImport failed!";
649  }
650  }
651 
652  if (AnchorDownloaded != null)
653  {
654  AnchorDownloaded(successful, objectToAnchor);
655  }
656 
657  anchorBatch.Dispose();
658  rawAnchorDownloadData = null;
659  isImportingAnchors = false;
660  }
661 
666  private void WriteBuffer(byte[] data)
667  {
668  rawAnchorUploadData.AddRange(data);
669  }
670 #endif
671  }
672 }
Wrapper around world anchor store to streamline some of the persistence API busy work.
Wrapper around world anchor store to streamline some of the persistence API busy work.
static T Instance
Returns the Singleton instance of the classes type. If no instance is found, then we search for an in...
Definition: Singleton.cs:26
virtual XString GetName()
Definition: Room.cs:43
virtual long GetID()
Definition: Room.cs:49
The SharingStage is in charge of managing the core networking layer for the application.
Definition: SharingStage.cs:14
virtual bool GetData(byte[] data, int dataSize)