11

3d icons for your Mixed Reality application

 3 years ago
source link: https://stevesspace.com/2020/09/mixed-reality-3d-app-launcher/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

The default app launchers for Mixed Reality applications built in Unity is a flat image - essentially a splash screen on a floating rectangle.

2D app launchers on a couch

I’m sure we can have better immersion than a bunch of 2d panels around the place.

The good news is, Windows Mixed Reality supports this, and have a guide on how to do it - so you can easily turn it into this:

3D app launchers on a couch

Isn’t that much nicer? Grab the gist and use it in Unity straight away, or read on for usage.

Status quo

To add a 3D app launcher to your Unity normally requires a few steps.

  1. Create your 3d model, export as GLB
  2. Build your UWP project
  3. Copy it into the Assets/ folder
  4. Update your project file and Package.appxmanifest to reference your new model.

I didn’t think this was very repeatable - especially since the guidance is to not check in your build folder (good advice, follow it).

Usage

I’ve automated the steps above into a little script, so now it’s simply

  1. Copy the MixedReality3dAppLauncher.cs script into an Editor/ folder of your scripts
  2. Save your app launcher to Assets/app-icon.glb
  3. Build your project

That’s it! Your app launcher will be included in the project, appxmanifest, and copied across when you build. Or you can manually patch it by going to UWP Tools/Patch manifest and project.

using System.IO; using System.Linq; using System.Text; using System.Xml; using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEngine;

namespace Project.Scripts.Editor { /// <summary> /// Adds a 3d icon to the project and appxmanifest. /// See https://docs.microsoft.com/en-us/windows/mixed-reality/implementing-3d-app-launchers /// </summary> public class MixedReality3dAppLauncher : IPostprocessBuildWithReport { private const string Uap5Namespace = "http://schemas.microsoft.com/appx/manifest/uap/windows10/5"; private const string AppIconSourcePath = "Assets/app-icon.glb"; private const string AppIconAssetPath = "Assets\\app-icon.glb";

public int callbackOrder => 0;

public void OnPostprocessBuild(BuildReport report) { if (report.summary.platform != BuildTarget.WSAPlayer) return; PatchProjectAndManifest(report.summary.outputPath, Application.productName); }

[MenuItem("UWP Tools/Patch Manifest and project")] public static void ManuallyPatch() { var buildLocation = EditorUserBuildSettings.GetBuildLocation(BuildTarget.WSAPlayer); if (!Directory.Exists(buildLocation)) { var defaultOptions = new BuildPlayerOptions(); var options = BuildPlayerWindow.DefaultBuildMethods.GetBuildPlayerOptions(defaultOptions); buildLocation = options.locationPathName; }

PatchProjectAndManifest(buildLocation, Application.productName); }

static void PatchProjectAndManifest(string path, string appName) { var modelPath = Path.Combine(Directory.GetCurrentDirectory(), AppIconSourcePath);

if (!File.Exists(modelPath)) { Debug.LogWarning(" MixedReality3dAppLauncher: Cannot add 3d model - please place one in Assets/app-icon.glb"); return; } var projectPath = Path.Combine(path, appName); CopyModelAndPatchXProj(appName, projectPath, modelPath); PatchAppxWithIcon(projectPath); Debug.Log(" MixedReality3dAppLauncher: Patched with 3d app launcher"); }

private static void CopyModelAndPatchXProj(string appName, string projectPath, string modelPath) { var destinationModelPath = Path.Combine(projectPath, AppIconAssetPath); if (File.GetLastWriteTimeUtc(modelPath) > File.GetLastWriteTimeUtc(destinationModelPath)) File.Copy(modelPath, destinationModelPath, true);

var projectXmlPath = Path.Combine(projectPath, $"{appName}.vcxproj"); var doc = new XmlDocument(); doc.LoadXml(File.ReadAllText(projectXmlPath));

var nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("ns", doc.DocumentElement.NamespaceURI);

var itemGroup = doc.SelectSingleNode("//ns:AppxManifest", nsmgr).ParentNode;

var hasExisting = itemGroup.ChildNodes.Cast<XmlNode>().Any(n => n.Attributes[0].Value == AppIconAssetPath); if (!hasExisting) { Debug.Log(" MixedReality3dAppLauncher: Adding icon include"); var includeNode = doc.CreateNode(XmlNodeType.Element, "None", doc.DocumentElement.NamespaceURI); var includeAttribute = doc.CreateAttribute("Include"); includeAttribute.Value = AppIconAssetPath; includeNode.Attributes.Append(includeAttribute); itemGroup.AppendChild(includeNode);

var deploymentContentNode = doc.CreateNode(XmlNodeType.Element, "DeploymentContent", doc.DocumentElement.NamespaceURI); deploymentContentNode.InnerText = "true"; includeNode.AppendChild(deploymentContentNode); }

using (var writer = new XmlTextWriter(projectXmlPath, Encoding.UTF8)) { writer.Formatting = Formatting.Indented; doc.WriteTo(writer); } }

private static void PatchAppxWithIcon(string projectPath) { var manifestPath = Path.Combine(projectPath, "package.appxmanifest"); var doc = new XmlDocument(); doc.LoadXml(File.ReadAllText(manifestPath));

var nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("ns", doc.DocumentElement.NamespaceURI);

var packageNode = doc.SelectSingleNode("/ns:Package", nsmgr);

var uapNamespace = packageNode.Attributes.Cast<XmlAttribute>().First(a => a.Name == "xmlns:uap").Value; nsmgr.AddNamespace("uap", uapNamespace); nsmgr.AddNamespace("uap5", Uap5Namespace);

if (!packageNode.Attributes.Cast<XmlAttribute>().Any(a => a.Name == "xmlns:uap5")) { var uap5Attribute = doc.CreateAttribute("xmlns:uap5"); uap5Attribute.Value = Uap5Namespace; packageNode.Attributes.Append(uap5Attribute); Debug.Log(" MixedReality3dAppLauncher: Added uap5 namspace to root node"); }

var ignoreNamespaces = packageNode.Attributes.Cast<XmlAttribute>().First(a => a.Name == "IgnorableNamespaces"); if (!ignoreNamespaces.Value.Contains("uap5")) { ignoreNamespaces.Value = ignoreNamespaces.Value + " uap5"; Debug.Log(" MixedReality3dAppLauncher: Added uap5 to IgnorableNamespaces"); }

var defaultTileNode = doc.SelectSingleNode("//uap:DefaultTile", nsmgr); if (!defaultTileNode.HasChildNodes) { var modelNode = doc.CreateNode(XmlNodeType.Element, "uap5:MixedRealityModel", Uap5Namespace); var pathAttribute = doc.CreateAttribute("Path"); pathAttribute.Value = AppIconAssetPath; modelNode.Attributes.Append(pathAttribute); defaultTileNode.AppendChild(modelNode); Debug.Log(" MixedReality3dAppLauncher: Added mixed reality model node"); } else { var modelNode = doc.SelectSingleNode("//uap5:MixedRealityModel", nsmgr); var pathAttribute = modelNode.Attributes.Cast<XmlAttribute>().First(a => a.Name == "Path"); if (pathAttribute.Value != AppIconAssetPath) { pathAttribute.Value = AppIconAssetPath; Debug.Log(" MixedReality3dAppLauncher: Updated model path"); } }

using (var writer = new XmlTextWriter(manifestPath, Encoding.UTF8)) { writer.Formatting = Formatting.Indented; doc.WriteTo(writer); } } } }

Enjoy!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK