Skip to content

Instantly share code, notes, and snippets.

@WikkidEdd
Last active September 18, 2018 17:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WikkidEdd/d8f7edc474103aa8db16ff841e267ec9 to your computer and use it in GitHub Desktop.
Save WikkidEdd/d8f7edc474103aa8db16ff841e267ec9 to your computer and use it in GitHub Desktop.
Build process and asset to add 3d app launchers to Hololens apps. Add AppLauncher3DEditor.cs in to an "Editor" folder. Create a new 3DAppLauncher asset in your project and enter the path to your glb.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName="3DAppLauncher", menuName = "3D App Launcher")]
public class AppLauncher3D : ScriptableObject
{
[Tooltip("Path to glb relative to Assets folder. Include file extension")]
public string _Model;
public bool _OverrideBoundingBox = false;
public Vector3 _Center;
public Vector3 _Extents = Vector3.one;
}
using System.IO;
using System.Xml;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
[CustomEditor(typeof(AppLauncher3D))]
public class AppLauncher3DEditor : Editor
{
[PostProcessBuild(1)]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
if (target == BuildTarget.WSAPlayer)
{
// Find App Launcher Asset, if not we don't need to do anything
string[] applauncher3DAssets = AssetDatabase.FindAssets("t:AppLauncher3D");
if (applauncher3DAssets.Length > 0)
{
// Load in asset
string assetPath = AssetDatabase.GUIDToAssetPath(applauncher3DAssets[0]);
AppLauncher3D appLauncher = AssetDatabase.LoadAssetAtPath<AppLauncher3D>(assetPath);
// Add app launcher to project
Add3DAppLauncher(pathToBuiltProject, appLauncher);
}
}
}
// Define DEBUG_BUTTON to quickly iterate in editor without having to build.
#if DEBUG_BUTTON
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if(GUILayout.Button("Add 3D App Launcher"))
{
Add3DAppLauncher(AbsoluteBuildDirectory, target as AppLauncher3D);
}
}
public static string AbsoluteBuildDirectory
{
get { return Path.GetFullPath(Path.Combine(Path.Combine(Application.dataPath, ".."), "WindowsStoreApp")); }
}
#endif
private static void Add3DAppLauncher(string buildPath, AppLauncher3D settings)
{
string pathToProjectFiles = Path.Combine(buildPath, Application.productName);
AddToPackageManifest(pathToProjectFiles, settings);
if (PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup) == ScriptingImplementation.IL2CPP)
AddToVCXProj(pathToProjectFiles);
else
AddToCSProj(pathToProjectFiles);
CopyModel(pathToProjectFiles, settings);
}
private static void CopyModel(string buildPath, AppLauncher3D settings)
{
string launcherFileSourcePath = Path.Combine(Application.dataPath, settings._Model);
string launcherFileTargetPath = Path.Combine(buildPath, "Assets\\AppLauncher_3D.glb");
FileUtil.ReplaceFile(launcherFileSourcePath, launcherFileTargetPath);
}
private static void AddToVCXProj(string buildPath)
{
// Load project file xml
string projFilename = Path.Combine(buildPath, PlayerSettings.productName + ".vcxproj");
XmlDocument document = new XmlDocument();
document.Load(projFilename);
// Check if we've already added model to the project
bool alreadyAdded = false;
XmlNodeList nones = document.GetElementsByTagName("None");
foreach (var none in nones)
{
XmlElement element = none as XmlElement;
if (element.GetAttribute("Include") == "Assets\\AppLauncher_3D.glb")
{
alreadyAdded = true;
}
}
// If not add the content object
if (!alreadyAdded)
{
XmlElement newItemGroup = document.CreateElement("ItemGroup", document.DocumentElement.NamespaceURI);
XmlElement newNoneElement = document.CreateElement("None", document.DocumentElement.NamespaceURI);
XmlNode deploymentContentNode = document.CreateElement("DeploymentContent", document.DocumentElement.NamespaceURI);
newNoneElement.AppendChild(deploymentContentNode);
deploymentContentNode.AppendChild(document.CreateTextNode("true"));
newNoneElement.SetAttribute("Include", "Assets\\AppLauncher_3D.glb");
newItemGroup.AppendChild(newNoneElement);
document.DocumentElement.AppendChild(newItemGroup);
}
// Save project xml file
document.Save(projFilename);
}
private static void AddToCSProj(string buildPath)
{
ScriptingImplementation scriptingImplementation = PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup);
// Load project file xml
string projFilename = Path.Combine(buildPath, PlayerSettings.productName + ".csproj");
XmlDocument document = new XmlDocument();
document.Load(projFilename);
// Check if we've already added model to the project
bool alreadyAdded = false;
XmlNodeList contents = document.GetElementsByTagName("Content");
foreach (var content in contents)
{
XmlElement element = content as XmlElement;
if (element.GetAttribute("Include") == "Assets\\AppLauncher_3D.glb")
{
alreadyAdded = true;
}
}
// If not add the content object
if (!alreadyAdded)
{
XmlElement itemGroup = document.CreateElement("ItemGroup", document.DocumentElement.NamespaceURI);
XmlElement content = document.CreateElement("Content", document.DocumentElement.NamespaceURI);
content.SetAttribute("Include", "Assets\\AppLauncher_3D.glb");
itemGroup.AppendChild(content);
document.DocumentElement.AppendChild(itemGroup);
}
// Save project xml file
document.Save(projFilename);
}
private static void AddToPackageManifest(string buildPath, AppLauncher3D settings)
{
// Load package appxmanifest xml
string packageManifestPath = Path.Combine(buildPath, "Package.appxmanifest");
XmlDocument document = new XmlDocument();
document.Load(packageManifestPath);
// Find the package node
XmlNodeList packages = document.GetElementsByTagName("Package");
XmlElement package = packages.Item(0) as XmlElement;
// Set the require attributes
package.SetAttribute("xmlns:uap5", "http://schemas.microsoft.com/appx/manifest/uap/windows10/5");
package.SetAttribute("xmlns:uap6", "http://schemas.microsoft.com/appx/manifest/uap/windows10/6");
package.SetAttribute("IgnorableNamespaces", "uap uap2 uap5 uap6 mp");
// Check if we've already added the mixedl reality model node
XmlNodeList mixedRealityModels = document.GetElementsByTagName("uap5:MixedRealityModel");
XmlElement mixedRealityModel = null;
if (mixedRealityModels.Count == 0)
{
// Add mixed reality model node
XmlNodeList defaultTiles = document.GetElementsByTagName("uap:DefaultTile");
XmlNode defaultTile = defaultTiles.Item(0);
mixedRealityModel = document.CreateElement("uap5", "MixedRealityModel", "http://schemas.microsoft.com/appx/manifest/uap/windows10/5");
defaultTile.AppendChild(mixedRealityModel);
}
else
{
mixedRealityModel = mixedRealityModels.Item(0) as XmlElement;
}
// Set the path of the mixed reality model
mixedRealityModel.SetAttribute("Path", "Assets\\AppLauncher_3D.glb");
// Check if we've already got a bounding box and remove it
XmlNodeList boundingBoxes = document.GetElementsByTagName("uap6:SpatialBoundingBox");
if (boundingBoxes.Count == 1)
{
mixedRealityModel.RemoveChild(boundingBoxes.Item(0));
}
// Add it back in if we want to override bounding box
if (settings._OverrideBoundingBox)
{
// Add mixed reality model node
XmlElement boundingBox = document.CreateElement("uap6", "SpatialBoundingBox", "http://schemas.microsoft.com/appx/manifest/uap/windows10/6");
string center = settings._Center.x + "," + settings._Center.y + "," + settings._Center.z;
string extents = settings._Extents.x + "," + settings._Extents.y + "," + settings._Extents.z;
boundingBox.SetAttribute("Center", center);
boundingBox.SetAttribute("Extents", extents);
mixedRealityModel.AppendChild(boundingBox);
}
// Save xml
document.Save(packageManifestPath);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment