LogoLogo
  • Mixed Reality Curriculum
  • What is the Metaverse?
    • Microsoft Mesh
  • Unity Lessons
    • 01 - Introduction to Mixed Reality
      • Concepts
        • What is Mixed Reality?
        • What is the difference between Augmented Reality, Virtual Reality and Mixed Reality?
        • Why is Mixed Reality important?
        • Will mixed reality replace our phones and Personal Computers?
        • How do I decide if I need to develop for Virtual Reality or Augmented Reality?
        • What are some use cases for Mixed Reality applications?
        • What are some examples of Mixed Reality Applications?
        • What is Mixed Reality Toolkit(MRTK)?
      • Project
        • What do I need to download for Mixed Reality development with Unity for HoloLens?
        • How to get started with Unity3D Editor interface?
        • What are some key concepts for working with Unity?
        • How to Get Started with Mixed Reality Development Using Unity?
        • How to get started with HoloLens Seed Project?
        • How to change preferences in Unity?
        • How to add Mixed Reality Toolkit(MRTK) to a project?
        • How to open MRTK example scenes?
        • How to enable Developer Mode in HoloLens?
        • How to enable Developer Mode on an Android Device?
        • How to build your project for HoloLens?
        • How to deploy your app to a HoloLens?
        • How to set up your project for iOS or Android[Experimental]?
        • How to build your scene for Android and iOS Devices?
        • How to build and deploy your project for Windows Mixed Reality Headset?
      • What could go wrong?
      • Resources
    • 02 - Mixed Reality Developer Tools and Concepts
      • Concepts
        • What is Debugging?
        • What makes a 3D model? What are Polygons, Splines, Vertices, Meshes and Materials?
        • How to choose performant 3D models for your application?
      • Project
        • How to simulate input interactions in Unity editor?
        • How to set-up HoloLens 2 development environment?
        • How to use MRTK Visual Profiler?
        • What is HoloLens Emulator?
        • How to set-up HoloLens 2 Emulator
        • How to deploy to HoloLens Emulator?
        • What is HoloLens Device Portal?
        • How to monitor performance of your app?
        • Working with 3D Objects
        • How to log for debugging purposes?
        • How to add MRTK(Mixed Reality Toolkit) Diagnostic System to your project?
        • Where to find pre-made 3D models?
        • How to upload 3D models to your project?
        • How to create your own models using Maquette?
        • How to create polygon models?
        • How to create 3D models with splines?
        • How to create 3D models using Autodesk 3dsMax?
      • What could go wrong?
      • Resources
    • 03 - Hand Interactions and Controllers
      • Concepts
        • Why the hand interaction is important?
        • What are Gestures?
      • Project
        • How to run the (Mixed Reality Toolkit)MRTK Hand Interaction examples in Unity Editor?
        • How to add hand interactions to an object?
        • How to add Manipulation Handler to your object?
        • How to organize your objects into a grid view?
        • How to grab and move an object?
        • How to rotate and scale an object?
        • How to make an object respond to input events?
        • How to style Bounding Box?
        • How to add visual feedback?
        • How to add audio feedback?
        • How to add button prefabs to your project?
        • How to organize your buttons into a grid view?
        • How to make your buttons follow your hand?
        • How to use simplified joint data access?
      • What could go wrong?
        • Mixing scaling and moving.
        • Having a small bounding box.
      • Resources
    • 04 - Eye and Head Gaze
      • Concepts
      • Project
        • How to get permission to use eye-tracking?
        • How to setup eye-tracking?
        • How to simulate eye-tracking in the Unity editor?
        • How to enable eye calibration?
        • How to use eye-tracking to select an object?
        • How to use eye-tracking for infinite scroll?
        • How to visualize eye tracking data?
        • How to setup head tracking?
      • What could go wrong?
        • Using eye movement without a delay to select an object.
        • Using eye gaze to influence the user.
      • Resources
    • 05 - Map Visualization
      • Concepts
        • Why is spatial data important?
        • What are some good spatial visualizations for Mixed Reality?
        • What is Bing Maps SDK?
      • Project
        • How to sign up for Bing Maps Developer Key?
        • How to add BingMaps SDK to your project?
        • How to create and configure your first map in unity?
        • How to style your map?
        • What is a Map Terrain Type?
        • How to add hand interactions for scaling and rotation?
        • How to style bounding box?
        • How to animate your map?
        • How to add pins to your map?
        • How to add pins using the MapPinLayer?
        • How to cluster map pins for larger data-sets?
        • What are the different considerations, settings you need for Virtual Reality vs Augmented Reality?
      • What could go wrong?
        • Does the dimensions of the map effect performance?
        • Does Map Terrain Type effect performance?
        • Can I customize the materials and shaders?
        • Can quality settings effect the performance?
        • Can adding pins would slow down my application?
      • Resources
    • 06 - REST APIs
      • Concepts
      • Project
        • What is UnityWebRequest?
      • What could go wrong?
      • Resources
    • 07 - Spatial Anchors
      • Concepts
        • What is a Spatial Anchor?
        • Why use Spatial Anchors?
        • Which devices does Azure Spatial Anchors support?
        • What do I need to do to make sure Android, iOS and HoloLens are using the same point as my anchor?
        • What is Anchor Relationships and what is it useful for?
        • What information about an environment is transmitted and stored on the ASA service?
      • Project
        • How to sign up for Azure Account?
        • How to create an Azure Spatial Anchor resources?
        • How to include Azure Spatial Anchors(ASA) SDK to your project?
        • How to create an Azure Spatial Anchor app and configure a scene?
        • How to add ASA script to your scene?
        • How to update the UI when a callback resolves?
        • How to initialize a CloudSpatialAnchorSession?
        • How to save the new CloudSpatialAnchor as a WorldAnchor on the local platform?
        • How to upload your local Anchor into the cloud?
        • How to build and use the ASA app for HoloLens?
        • How do I know my anchors are saved on Azure resources?
        • How to create a CosmosDB table to save and share the anchors between devices?
      • What could go wrong?
      • Resources
    • 08 - Spatial Anchor Visualization on Map
    • 09 - QR Codes
    • 10 - Spatial Awareness
      • Concepts
        • What is Spatial Mapping?
        • What is Scene Understanding?
        • What are SceneQuads?
        • How to decide to use Spatial Map or Scene Understanding?
      • Project
      • What could go wrong?
      • Resources
    • 11 - AI
    • 12 - Project Discussion and Case Studies
      • What are the things to consider before you decide on an idea?
      • What could go wrong?
  • WebXR Lessons
    • 3D on the Web
      • Concepts
        • What is WebGL?
        • What is Field of View?
        • What is Aspect Ratio of a Camera?
        • What is near and far clipping planes of a camera?
        • What does updating projection matrix do?
        • What are 3D primitive objects?
        • What is a Vertex?
        • What are 3D model loaders?
        • Materials
        • What is a Normal in 3D?
        • Environment Maps
        • Normal Maps
        • Subsurface Scattering
        • UV Mapping
        • Baking
        • Texturing
        • Animations and Rigging
        • 3D scene interactions
        • What is a Transformation Matrix
        • What are 3DOF or 6DOF?
      • Project
        • How to create a basic 3D scene?
        • BabylonJS Scene
        • ThreeJS Scene
        • AFrame Scene
        • How to create a globe visualization with ThreeJS
        • Lighting your scene
        • 3D Performance
      • What could go wrong?
      • Resources
    • WebXR Device APIs
      • Concepts
        • What are WebXR Device APIs?
        • Which Devices are Compatible with WebXR?
        • Which Browsers support WebXR?
        • What is the Lifecycle of a WebXR Application?
        • What is XRReferenceSpaceType?
      • Project
        • How to enable VR?
        • How to enable AR and Hit-test?
        • How to debug and test your WebXR Application with Chrome Dev Tools?
        • How to load a 3D Model
      • Resources
      • What could go wrong?
    • A-Frame
      • Concepts
      • Basic A-Frame Scene
      • Project
        • Inclusive Apps with WebXR & AI
        • Creating WebXR apps for Transportation with A-Frame
        • Transportation Game
      • What could go wrong?
      • Resources
    • Three.js
    • Babylon.js
      • Concepts
        • What is BabylonJS?
        • What is a scene?
        • What is Playground?
      • Project
        • How to create your first Scene in Playground
        • What is an Arc Camera?
        • What is an Hemispheric Light?
        • What is a Box Mesh?
        • Basic Scene Exercises
        • How to load a 3D model on Playground?
        • How to add user interactions?
        • How to add WebXR support?
          • How to add Virtual Reality capabilities to your app?
          • How to test your WebXR app on an Android phone or Google Cardboard?
          • How to add Augmented Reality capabilities to your app?
        • How to setup a BabylonJS local development environment and project?
        • How to Create a WebXR Augmented Reality App on Your Local Device
        • Babylon.js AR scene
        • Add Speech SDK
        • Server
        • Token Util
        • Create Speech Resources
      • Resources
    • WebXR Meetups
    • Resources
  • Unreal Engine Lessons
    • Blueprints
      • Concepts
        • What are Blueprints?
        • How Do Blueprints Work?
      • Project
        • How to add functionality to buttons using blueprints?
      • What could go wrong?
  • Artificial Intelligence(AI) Lessons
    • Introduction to AI Fundamental
      • Concepts
        • What is training data?
      • Project
        • How to create a no code AI application using Power Apps and Custom Vision?
          • What is Power Platform?
          • Is AI Builder the right choice?
          • What is Object Detection?
          • How to detect objects from images?
          • How to improve Model performance?
          • How to use your Custom Vision model in a Power App?
          • What's next?
      • What could go wrong?
      • Resources
    • Speech
      • What's the problem we are solving?
      • Where to get started?
      • Creating an Azure Resource
      • How to make the Speech API call?
      • How to transcribe Speech
      • How to Translate Speech
      • Tools
    • Cognitive Services Best Practices
      • Concepts
      • Project
      • What could go wrong?
      • Resources
    • AI Show Episode 46
      • What could go wrong?
    • AI Show Episode 48
      • Resources
  • Exercises
    • Creating your first app
    • Adding interactions
    • Working with Coordinate System
    • Working with Spatial Sound
    • Adding Voice Commands
    • Working with Speech services to create subtitles
    • Working with Translation Services
    • Detecting objects with Vision services
    • Creating IoT data visualizations
    • Working with Digital Twins to collect spatial data
    • Working with Azure Spatial Anchors for shared experiences
  • FAQ
  • Glossary
    • AI
    • XR
      • 3D Concepts
    • Computer Science
  • How to contribute?
Powered by GitBook
LogoLogo

Lessons

  • WebXR
  • AI
  • Unity
  • Unreal

Other Links

  • Home
  • FAQ
  • Glossary
  • Github

YouTube

  • WebXR
  • AysSomething
  • XR Dev
  • JavaScript

Social

  • Twitter
  • Instagram
  • Bluesky
  • Reddit

ARTistus LLC

On this page

Was this helpful?

Export as PDF
  1. Unity Lessons
  2. 07 - Spatial Anchors
  3. Project

How to build and use the ASA app for HoloLens?

At this point, your AzureSpatialAnchorsScript.cs should look like below code snippet:

using Microsoft.Azure.SpatialAnchors;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.XR.WSA;
using UnityEngine.XR.WSA.Input;

public class AzureSpatialAnchorsScript : MonoBehaviour
{   
    /// <summary>
    /// The sphere prefab.
    /// </summary>
    public GameObject anchorPrefab;

    /// <summary>
    /// Set this string to the Spatial Anchors account id provided in the Spatial Anchors resource.
    /// </summary>
    protected string SpatialAnchorsAccountId = "Set me";

    /// <summary>
    /// Set this string to the Spatial Anchors account key provided in the Spatial Anchors resource.
    /// </summary>
    protected string SpatialAnchorsAccountKey = "Set me";

    /// <summary>
    /// Our queue of actions that will be executed on the main thread.
    /// </summary>
    private readonly Queue<Action> dispatchQueue = new Queue<Action>();

    /// <summary>
    /// Use the recognizer to detect air taps.
    /// </summary>
    private GestureRecognizer recognizer;

    protected CloudSpatialAnchorSession cloudSpatialAnchorSession;

    /// <summary>
    /// The CloudSpatialAnchor that we either 1) placed and are saving or 2) just located.
    /// </summary>
    protected CloudSpatialAnchor currentCloudAnchor;

    /// <summary>
    /// True if we are 1) creating + saving an anchor or 2) looking for an anchor.
    /// </summary>
    protected bool tapExecuted = false;

    /// <summary>
    /// The ID of the CloudSpatialAnchor that was saved. Use it to find the CloudSpatialAnchor
    /// </summary>
    protected string cloudSpatialAnchorId = "";

    /// <summary>
    /// The sphere rendered to show the position of the CloudSpatialAnchor.
    /// </summary>
    protected GameObject anchor;
    protected Material anchorMaterial;

    /// <summary>
    /// Indicate if we are ready to save an anchor. We can save an anchor when value is greater than 1.
    /// </summary>
    protected float recommendedForCreate = 0;

    // Start is called before the first frame update
    void Start()
    {
        recognizer = new GestureRecognizer();

        recognizer.StartCapturingGestures();

        recognizer.SetRecognizableGestures(GestureSettings.Tap);

        recognizer.Tapped += HandleTap;

        InitializeSession();
    }

    // Update is called once per frame
    void Update()
    {
        lock (dispatchQueue)
        {
            if (dispatchQueue.Count > 0)
            {
                dispatchQueue.Dequeue()();
            }
        }
    }

    /// <summary>
    /// Queues the specified <see cref="Action"/> on update.
    /// </summary>
    /// <param name="updateAction">The update action.</param>
    protected void QueueOnUpdate(Action updateAction)
    {
        lock (dispatchQueue)
        {
            dispatchQueue.Enqueue(updateAction);
        }
    }

    /// <summary>
    /// Cleans up objects.
    /// </summary>
    public void CleanupObjects()
    {
        if (sphere != null)
        {
            Destroy(sphere);
            sphere = null;
        }

        if (sphereMaterial != null)
        {
            Destroy(sphereMaterial);
            sphereMaterial = null;
        }

        currentCloudAnchor = null;
    }

    /// <summary>
    /// Cleans up objects and stops the CloudSpatialAnchorSessions.
    /// </summary>
    public void ResetSession(Action completionRoutine = null)
    {
        Debug.Log("ASA Info: Resetting the session.");

        if (cloudSpatialAnchorSession.GetActiveWatchers().Count > 0)
        {
            Debug.LogError("ASA Error: We are resetting the session with active watchers, which is unexpected.");
        }

        CleanupObjects();

        this.cloudSpatialAnchorSession.Reset();

        lock (this.dispatchQueue)
        {
            this.dispatchQueue.Enqueue(() =>
            {
                if (cloudSpatialAnchorSession != null)
                {
                    cloudSpatialAnchorSession.Stop();
                    cloudSpatialAnchorSession.Dispose();
                    Debug.Log("ASA Info: Session was reset.");
                    completionRoutine?.Invoke();
                }
                else
                {
                    Debug.LogError("ASA Error: cloudSpatialAnchorSession was null, which is unexpected.");
                }
            });
        }
    }

    /// <summary>
    /// Initializes a new CloudSpatialAnchorSession.
    /// </summary>
    void InitializeSession()
    {
        Debug.Log("ASA Info: Initializing a CloudSpatialAnchorSession.");

        if (string.IsNullOrEmpty(SpatialAnchorsAccountId))
        {
            Debug.LogError("No account id set.");
            return;
        }

        if (string.IsNullOrEmpty(SpatialAnchorsAccountKey))
        {
            Debug.LogError("No account key set.");
            return;
        }

        cloudSpatialAnchorSession = new CloudSpatialAnchorSession();

        cloudSpatialAnchorSession.Configuration.AccountId = SpatialAnchorsAccountId.Trim();
        cloudSpatialAnchorSession.Configuration.AccountKey = SpatialAnchorsAccountKey.Trim();

        cloudSpatialAnchorSession.LogLevel = SessionLogLevel.All;

        cloudSpatialAnchorSession.Error += CloudSpatialAnchorSession_Error;
        cloudSpatialAnchorSession.OnLogDebug += CloudSpatialAnchorSession_OnLogDebug;
        cloudSpatialAnchorSession.SessionUpdated += CloudSpatialAnchorSession_SessionUpdated;
        cloudSpatialAnchorSession.AnchorLocated += CloudSpatialAnchorSession_AnchorLocated;
        cloudSpatialAnchorSession.LocateAnchorsCompleted += CloudSpatialAnchorSession_LocateAnchorsCompleted;

        cloudSpatialAnchorSession.Start();

        Debug.Log("ASA Info: Session was initialized.");
    }

    private void CloudSpatialAnchorSession_Error(object sender, SessionErrorEventArgs args)
    {
        Debug.LogError("ASA Error: " + args.ErrorMessage );
    }

    private void CloudSpatialAnchorSession_OnLogDebug(object sender, OnLogDebugEventArgs args)
    {
        Debug.Log("ASA Log: " + args.Message);
        System.Diagnostics.Debug.WriteLine("ASA Log: " + args.Message);
    }

    private void CloudSpatialAnchorSession_SessionUpdated(object sender, SessionUpdatedEventArgs args)
    {
        Debug.Log("ASA Log: recommendedForCreate: " + args.Status.RecommendedForCreateProgress);
        recommendedForCreate = args.Status.RecommendedForCreateProgress;
    }

    private void CloudSpatialAnchorSession_AnchorLocated(object sender, AnchorLocatedEventArgs args)
    {
        switch (args.Status)
        {
            case LocateAnchorStatus.Located:
                Debug.Log("ASA Info: Anchor located! Identifier: " + args.Identifier);
                QueueOnUpdate(() =>
                {
                    // Create a green sphere.
                    sphere = GameObject.InstantiatePrefab, Vector3.zero, Quaternion.identity) as GameObject;
                    sphere.AddComponent<WorldAnchor>();
                    sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
                    sphereMaterial.color = Color.green;

                    // Get the WorldAnchor from the CloudSpatialAnchor and use it to position the sphere.
                    sphere.GetComponent<UnityEngine.XR.WSA.WorldAnchor>().SetNativeSpatialAnchorPtr(args.Anchor.LocalAnchor);

                    // Clean up state so that we can start over and create a new anchor.
                    cloudSpatialAnchorId = "";
                    tapExecuted = false;
                });
                break;
            case LocateAnchorStatus.AlreadyTracked:
                Debug.Log("ASA Info: Anchor already tracked. Identifier: " + args.Identifier);
                break;
            case LocateAnchorStatus.NotLocated:
                Debug.Log("ASA Info: Anchor not located. Identifier: " + args.Identifier);
                break;
            case LocateAnchorStatus.NotLocatedAnchorDoesNotExist:
                Debug.LogError("ASA Error: Anchor not located does not exist. Identifier: " + args.Identifier);
                break;
        }
    }

    private void CloudSpatialAnchorSession_LocateAnchorsCompleted(object sender, LocateAnchorsCompletedEventArgs args)
    {
        Debug.Log("ASA Info: Locate anchors completed. Watcher identifier: " + args.Watcher.Identifier);
    }

    /// <summary>
    /// Called by GestureRecognizer when a tap is detected.
    /// </summary>
    /// <param name="tapEvent">The tap.</param>    
    public void HandleTap(TappedEventArgs tapEvent)
    {
        if (tapExecuted)
        {
            return;
        }
        tapExecuted = true;

        // We have saved an anchor, so we will now look for it.
        if (!String.IsNullOrEmpty(cloudSpatialAnchorId))
        {
            Debug.Log("ASA Info: We will look for a placed anchor.");
            tapExecuted = true;

            ResetSession(() =>
            {
                InitializeSession();

                // Create a Watcher to look for the anchor we created.
                AnchorLocateCriteria criteria = new AnchorLocateCriteria();
                criteria.Identifiers = new string[] { cloudSpatialAnchorId };
                cloudSpatialAnchorSession.CreateWatcher(criteria);

                Debug.Log("ASA Info: Watcher created. Number of active watchers: " + cloudSpatialAnchorSession.GetActiveWatchers().Count);
            });
            return;
        }

        Debug.Log("ASA Info: We will create a new anchor.");

        // Clean up any anchors that have been placed.
        CleanupObjects();

        // Construct a Ray using forward direction of the HoloLens.
        Ray GazeRay = new Ray(tapEvent.headPose.position, tapEvent.headPose.forward);

        // Raycast to get the hit point in the real world.
        RaycastHit hitInfo;
        Physics.Raycast(GazeRay, out hitInfo, float.MaxValue);

        this.CreateAndSaveSphere(hitInfo.point);
    }

    /// <summary>
    /// Creates a sphere at the hit point, and then saves a CloudSpatialAnchor there.
    /// </summary>
    /// <param name="hitPoint">The hit point.</param>
    protected virtual void CreateAndSaveSphere(Vector3 hitPoint)
    {
        // Create a white sphere.
        sphere = GameObject.Instantiate(spherePrefab, hitPoint, Quaternion.identity) as GameObject;
        sphere.AddComponent<WorldAnchor>();
        sphereMaterial = sphere.GetComponent<MeshRenderer>().material;
        sphereMaterial.color = Color.white;
        Debug.Log("ASA Info: Created a local anchor.");

        // Create the CloudSpatialAnchor.
        currentCloudAnchor = new CloudSpatialAnchor();

        // Set the LocalAnchor property of the CloudSpatialAnchor to the WorldAnchor component of our white sphere.
        WorldAnchor worldAnchor = sphere.GetComponent<WorldAnchor>();
        if (worldAnchor == null)
        {
            throw new Exception("ASA Error: Couldn't get the local anchor pointer.");
        }

        // Save the CloudSpatialAnchor to the cloud.
        currentCloudAnchor.LocalAnchor = worldAnchor.GetNativeSpatialAnchorPtr();
        Task.Run(async () =>
        {
            // Wait for enough data about the environment.
            while (recommendedForCreate < 1.0F)
            {
                await Task.Delay(330);
            }

            bool success = false;
            try
            {
                QueueOnUpdate(() =>
                {
                    // We are about to save the CloudSpatialAnchor to the Azure Spatial Anchors, turn it yellow.
                    sphereMaterial.color = Color.yellow;
                });

                await cloudSpatialAnchorSession.CreateAnchorAsync(currentCloudAnchor);
                success = currentCloudAnchor != null;

                if (success)
                {
                    // Allow the user to tap again to clear state and look for the anchor.
                    tapExecuted = false;

                    // Record the identifier to locate.
                    cloudSpatialAnchorId = currentCloudAnchor.Identifier;

                    QueueOnUpdate(() =>
                    {
                        // Turn the sphere blue.
                        sphereMaterial.color = Color.blue;
                    });

                    Debug.Log("ASA Info: Saved anchor to Azure Spatial Anchors! Identifier: " + cloudSpatialAnchorId);
                }
                else
                {
                    sphereMaterial.color = Color.red;
                    Debug.LogError("ASA Error: Failed to save, but no exception was thrown.");
                }
            }
            catch (Exception ex)
            {
                QueueOnUpdate(() =>
                {
                    sphereMaterial.color = Color.red;
                });
                Debug.LogError("ASA Error: " + ex.Message);
            }
        });
    }
}
PreviousHow to upload your local Anchor into the cloud?NextHow do I know my anchors are saved on Azure resources?

Last updated 5 years ago

Was this helpful?

Now you can follow the and start creating anchors in your environment.

build tutorial for HoloLens