NB: The usual blog disclaimer for this site applies to posts around HoloLens. I am not on the HoloLens team. I have no details on HoloLens other than what is on the public web and so what I post here is just from my own experience experimenting with pieces that are publicly available and you should always check out the official developer site for the product documentation.
One of the things that I wanted to understand a bit better when I wrote this post;
was the interchange between 2D and 3D views on the HoloLens as discussed in this document;
and I wanted to experiment with seeing if I could get an app up and running which switched between a 2D XAML based view and a 3D Unity based view.
To get going with that, I made a fairly blank Unity project in accordance with the steps here;
and then I added a cube into my scene so that I had something to look at;
and then made sure that I was exporting my project as a XAML based project as I mentioned in this previous post;
Windows 10, UWP, Unity, HoloLens– Small Explorations of D3D and XAML based Unity Projects
as I had a suspicion that the code that I was going to write might be dependent on having the initial view in the app come from the 2D/XAML world rather than the 3D/D3D world although I have yet to test that suspicion so apply a pinch of salt.
I placed a simple script onto my Cube in the scene above although the script is really a global handler so it didn’t need to be attached onto the cube but I needed something to hang my hat on and so I used the Cube;
and that script looks like this;
using UnityEngine; using System.Collections; using UnityEngine.VR.WSA.Input; public class TestScript : MonoBehaviour { GestureRecognizer recognizer; // Use this for initialization void Start() { this.recognizer = new GestureRecognizer(); this.recognizer.TappedEvent += OnTapped; this.recognizer.StartCapturingGestures(); } private void OnTapped(InteractionSourceKind source, int tapCount, Ray headRay) { #if !UNITY_EDITOR ViewLibrary.ViewManagement.SwitchTo2DViewAsync(); #endif } // Update is called once per frame void Update() { } }
and so it’s a very simple script and it’s just waiting for a tap (anywhere) before making a call into this SwitchTo2DViewAsync function and I’ve hidden that from the Unity editor so that it doesn’t have to think about it. The Tap isn’t specific to the Cube in any way hence my earlier comment about the script not really ‘belonging’ to the Cube.
That ViewLibrary code lives in a separate class library that I have tried to bring in to the Unity environment as a plugin;
and the way I did that came from this previous blog post;
Windows 10 UWP Unity and Adding a Reference to a (UWP) Class Library
The code inside that ViewManagement class looks like this and it’s a bit experimental at the time of writing but it “seems to work”;
namespace ViewLibrary { using System; using System.Threading.Tasks; using Windows.ApplicationModel.Core; using Windows.UI; using Windows.UI.Core; using Windows.UI.ViewManagement; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; public static class ViewManagement { public static async Task SwitchTo2DViewAsync() { if (coreView3d == null) { coreView3d = CoreApplication.MainView; } if (coreView2d == null) { coreView2d = CoreApplication.CreateNewView(); await RunOnDispatcherAsync( coreView2d, async () => { Window.Current.Content = Create2dUI(); } ); } await RunOnDispatcherAsync(coreView2d, SwitchViewsAsync); } static UIElement Create2dUI() { var button = new Button() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch, Content = "Back to 3D", Background = new SolidColorBrush(Colors.Red) }; button.Click += async (s, e) => { await SwitchTo3DViewAsync(); }; return (button); } static async Task RunOnDispatcherAsync(CoreApplicationView view, Func<Task> action) { await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()); } public static async Task SwitchTo3DViewAsync() { await RunOnDispatcherAsync(coreView3d, SwitchViewsAsync); } static async Task SwitchViewsAsync() { var view = ApplicationView.GetForCurrentView(); await ApplicationViewSwitcher.SwitchAsync(view.Id); Window.Current.Activate(); } static CoreApplicationView coreView3d; static CoreApplicationView coreView2d; } }
Mostly, that code came from this blog post about using multiple views in a regular UWP app but I manipulated it around a little here.
If I run this up on the emulator or an a device then I see my initial holographic view of the app containing my Cube;
and then if I tap I see;
and then if I Click I see;
I wouldn’t say that I have a 100% grip on this at the time of finishing this post but I think I understand it better than when I started writing it
I’d like to dig into whether this same approach works with a project that has been exported as D3D rather than as XAML and I’ll update the post as/when I figure that out.
nice work. going from 3D -> 2D seems to be infinitely easier than doing the reverse. What If i have a UWP app that I want on Hololens but want to make it awesomer by adding some 3D content/capabilities to it? This has been something I’ve had trouble figuring out.
I’m figuring things out too but I guess one aspect of that is whether you have a separate package for HoloLens that includes the 3D content as you wouldn’t want to ship that package to other devices where that content doesn’t get used. Beyond that you can use this technique to switch 2D->3D and show that content. The code I had here did go 3D->2D but it did start with 2D so I haven’t yet tried what happens here if you start in 3D.