前言

最近有新成员加入本团队,为了方便其开发HoloLens1 / HoloLens2,将不定时更新HoloLens相关开发相关内容。

软件需求:

HoloLens 1:VS2017 + Unity2017;HoloLens 2:VS2019 + Unity2019;

1.安装VS2017 / VS2019,HoloLens 1安装Win10 SDK 17134或者17763,HoloLens2要求至少18362;相关安装与配置请参考博文.

2.Unity2017 / Unity2019,安装UWP平台;

注:如果使用的软件为VS2019和Unity2019来开发HoloLens1,可参考HoloLens2的开发过程,修改MRTK的配置文件为HoloLens1即可!可参考博文1,博文2.


上一节介绍了HoloLens+Trilib插件综合开发-Part1,简单介绍了Trilib插件及示例工程Scene1,本节将开始Trilib插件在HoloLens端使用基础介绍。

一、主要代码说明

首先拷贝Trilib插件的示例工程Scene1到自己新建的文件夹下,自定义重命名,eg:HoloTrilib.

1.场景说明

打开场景HoloTrilib后看到左侧面板上如下图所示

主要功能脚本都在ForegroundCanvas下挂载:
1)AssetLoader下挂载AssetDownloader和AssetLoaderWindow组件。通过查看脚本内容可以看出,AssetDownloader组件为消息处理脚本;AssetLoaderWindow组件与AssetLoader下的界面UI绑定,主要为界面按钮得响应;

2)FileLoader下的FileOpenDialog为文件选取脚本,主要作用为调用系统的FileBrowser,显示文件目录提供给用户选取相应的fbx文件;

3)ErrorDialog如名字一般,其主要为错误反馈组件;

4)URIDialog没有用到,根据名字看是网络下载模型窗口;

5)LoadingTime显示加载模型用时;

我们在HoloLens上开发主要用到本地加载,因此着重于AssetLoader和FileLoader两个组件即可。

2.代码说明

1.AssetDownloader不需要修改,后期直接挂在我们的场景中即可

2.AssetLoaderWindow中最重要的部分如下:

private void LoadLocalAssetButtonClick()
{var fileOpenDialog = FileOpenDialog.Instance;fileOpenDialog.Title = "Please select a File";fileOpenDialog.Filter = AssetLoaderBase.GetSupportedFileExtensions() + "*.zip;";#if !UNITY_EDITOR && UNITY_WINRT && (NET_4_6 || NETFX_CORE || NET_STANDARD_2_0) && !ENABLE_IL2CPP && !ENABLE_MONOfileOpenDialog.ShowFileOpenDialog(delegate (byte[] fileBytes, string filename) {LoadInternal(filename, fileBytes);
#elsefileOpenDialog.ShowFileOpenDialog(delegate (string filename){LoadInternal(filename);
#endif});}private void LoadInternal(string filename, byte[] fileBytes = null)
{_loadingTimer.Reset();_loadingTimer.Start();PreLoadSetup();var assetLoaderOptions = GetAssetLoaderOptions();if (!Async){using (var assetLoader = new AssetLoader()){assetLoader.OnMetadataProcessed += AssetLoader_OnMetadataProcessed;try{#if !UNITY_EDITOR && UNITY_WINRT && (NET_4_6 || NETFX_CORE || NET_STANDARD_2_0) && !ENABLE_IL2CPP && !ENABLE_MONOvar extension = FileUtils.GetFileExtension(filename);_rootGameObject = assetLoader.LoadFromMemoryWithTextures(fileBytes, extension, assetLoaderOptions, _rootGameObject);
#elseif (fileBytes != null && fileBytes.Length > 0){var extension = FileUtils.GetFileExtension(filename);_rootGameObject = assetLoader.LoadFromMemoryWithTextures(fileBytes, extension, assetLoaderOptions, _rootGameObject);}else if (!string.IsNullOrEmpty(filename)){//获取obj_rootGameObject = assetLoader.LoadFromFileWithTextures(filename, assetLoaderOptions);}else{throw new System.Exception("File not selected");}
#endif}catch (Exception exception){ErrorDialog.Instance.ShowDialog(exception.ToString());}}if (_rootGameObject != null){PostLoadSetup();ShowLoadingTime();}else{HideLoadingTime();}}else{using (var assetLoader = new AssetLoaderAsync()){assetLoader.OnMetadataProcessed += AssetLoader_OnMetadataProcessed;try{if (fileBytes != null && fileBytes.Length > 0){var extension = FileUtils.GetFileExtension(filename);assetLoader.LoadFromMemoryWithTextures(fileBytes, extension, assetLoaderOptions, null, delegate (GameObject loadedGameObject){_rootGameObject = loadedGameObject;if (_rootGameObject != null){PostLoadSetup();ShowLoadingTime();}else{HideLoadingTime();}});}else if (!string.IsNullOrEmpty(filename)){assetLoader.LoadFromFileWithTextures(filename, assetLoaderOptions, null, delegate (GameObject loadedGameObject){_rootGameObject = loadedGameObject;if (_rootGameObject != null){PostLoadSetup();ShowLoadingTime();}else{HideLoadingTime();}});}else{throw new Exception("File not selected");}}catch (Exception exception){HideLoadingTime();ErrorDialog.Instance.ShowDialog(exception.ToString());}}}
}

这两个函数是底层实例化模型的接口函数,即用户在UI界面通过选择要加载的数模后,通过文件地址以及相关参数,Unity根据这两个函数实例化数模。

3.FileOpenDialog为文件选择脚本,我们将在HoloLens端开发使用时具体修改。

读者会注意到在LoadLocalAssetButtonClick函数中包含“#if…#else…#endif” 的结构,这是因为HoloLens所在的UWP平台和Unity所在的Unity在函数接口上的不一致,因此需要根据平台的不同,使用不同的接口用法。

3.HoloLens开发场景准备

经过以上介绍,我们在进行HoloLens+Trilib时,只需要保留需要的本地加载组件,同时还需要引入HoloLens交互组件,因此进行如下准备工作。

1.场景中,BackgroundCanvas删除,新建Canvas命名为“LoadCanvas”,取出ForegroundCanvas下的FileLoader组件,将其设为LoadCanvas的子物体,同时,把ForegroundCanvas剩余部分,如下所示

在LoadCanvas下新建UI-Button,并调整LoadCanvas的属性(Transform),将其调整到相机的可视范围内。这里为了后期上传HoloLens不需要再调整Canvas的比例,建议直接将LoadCanvas比例缩小,并保证相机可视。

2.为了方便HoloLens交互的舒适感,这里使用HoloToolKit的交互组件,因此需要导入HoloToolKit组件,如果苏读者有自己的交互设计,可忽略此步。

至此,准备工作完成,可以正式开始HoloLens+Trilib开发。

二、HoloLens+Trilib开发

1.场景及脚本修改

1.新建空物体,命名为Manager,挂载AssetDownloader脚本

2.Copy AssetLoaderWindow脚本至Scripts文件夹下,重命名,如myFBXLib,打开脚本修改脚本名,保持与文件名一致。

同时删除脚本中所有与UI有关的声明,以及所有URI加载数模的响应函数,最终如下

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
#if NETFX_CORE  //UWP下编译
using Windows.Storage;
#endif
using Debug = UnityEngine.Debug;namespace TriLib
{namespace Samples{[RequireComponent(typeof(AssetDownloader))]public class myFBXLib : MonoBehaviour{public static AssetLoaderWindow Instance { get; private set; }public bool Async;private GameObject _rootGameObject;/// <summary>/// Handles "Load local asset button" click event and tries to load an asset at chosen path./// </summary>public void LoadLocalAssetButtonClick(){var fileOpenDialog = FileOpenDialog.Instance;fileOpenDialog.Title = "Please select a File";fileOpenDialog.Filter = AssetLoaderBase.GetSupportedFileExtensions() + "*.zip;";#if !UNITY_EDITOR && UNITY_WINRT && (NET_4_6 || NETFX_CORE || NET_STANDARD_2_0) && !ENABLE_IL2CPP && !ENABLE_MONOfileOpenDialog.ShowFileOpenDialog(delegate (byte[] fileBytes, string filename) {LoadInternal(filename, fileBytes);
#elsefileOpenDialog.ShowFileOpenDialog(delegate (string filename){LoadInternal(filename);
#endif});}/// <summary>/// Loads a model from the given filename or given file bytes./// </summary>/// <param name="filename">Model filename.</param>/// <param name="fileBytes">Model file bytes.</param>private void LoadInternal(string filename, byte[] fileBytes = null){PreLoadSetup();var assetLoaderOptions = GetAssetLoaderOptions();if (!Async){using (var assetLoader = new AssetLoader()){assetLoader.OnMetadataProcessed += AssetLoader_OnMetadataProcessed;try{#if !UNITY_EDITOR && UNITY_WINRT && (NET_4_6 || NETFX_CORE || NET_STANDARD_2_0) && !ENABLE_IL2CPP && !ENABLE_MONOvar extension = FileUtils.GetFileExtension(filename);_rootGameObject = assetLoader.LoadFromMemoryWithTextures(fileBytes, extension, assetLoaderOptions, _rootGameObject);
#elseif (fileBytes != null && fileBytes.Length > 0){var extension = FileUtils.GetFileExtension(filename);_rootGameObject = assetLoader.LoadFromMemoryWithTextures(fileBytes, extension, assetLoaderOptions, _rootGameObject);}else if (!string.IsNullOrEmpty(filename)){//获取obj_rootGameObject = assetLoader.LoadFromFileWithTextures(filename, assetLoaderOptions);}else{throw new System.Exception("File not selected");}
#endif}catch (Exception exception){ErrorDialog.Instance.ShowDialog(exception.ToString());}}if (_rootGameObject != null){PostLoadSetup();}}else{using (var assetLoader = new AssetLoaderAsync()){assetLoader.OnMetadataProcessed += AssetLoader_OnMetadataProcessed;try{if (fileBytes != null && fileBytes.Length > 0){var extension = FileUtils.GetFileExtension(filename);assetLoader.LoadFromMemoryWithTextures(fileBytes, extension, assetLoaderOptions, null, delegate (GameObject loadedGameObject){_rootGameObject = loadedGameObject;if (_rootGameObject != null){PostLoadSetup();}});}else if (!string.IsNullOrEmpty(filename)){assetLoader.LoadFromFileWithTextures(filename, assetLoaderOptions, null, delegate (GameObject loadedGameObject){_rootGameObject = loadedGameObject;if (_rootGameObject != null){PostLoadSetup();}});}else{throw new Exception("File not selected");}}catch (Exception exception){ErrorDialog.Instance.ShowDialog(exception.ToString());}}}}/// <summary>/// Event assigned to FBX metadata loading. Editor debug purposes only./// </summary>/// <param name="metadataType">Type of loaded metadata</param>/// <param name="metadataIndex">Index of loaded metadata</param>/// <param name="metadataKey">Key of loaded metadata</param>/// <param name="metadataValue">Value of loaded metadata</param>private void AssetLoader_OnMetadataProcessed(AssimpMetadataType metadataType, uint metadataIndex, string metadataKey, object metadataValue){Debug.Log("Found metadata of type [" + metadataType + "] at index [" + metadataIndex + "] and key [" + metadataKey + "] with value [" + metadataValue + "]");}/// <summary>/// Gets the asset loader options./// </summary>/// <returns>The asset loader options.</returns>private AssetLoaderOptions GetAssetLoaderOptions(){var assetLoaderOptions = AssetLoaderOptions.CreateInstance();assetLoaderOptions.DontLoadCameras = false;assetLoaderOptions.DontLoadLights = false;assetLoaderOptions.AddAssetUnloader = true;return assetLoaderOptions;}/// <summary>/// Pre Load setup./// </summary>private void PreLoadSetup(){if (_rootGameObject != null){Destroy(_rootGameObject);_rootGameObject = null;}}/// <summary>/// Post load setup./// </summary>private void PostLoadSetup(){_rootGameObject.transform.eulerAngles = new Vector3(0, 0, 0);_rootGameObject.transform.position = new Vector3(0, 0, (float)0.5);_rootGameObject.transform.localScale = new Vector3((float)0.008, (float)0.008, (float)0.008);}}}
}

2.上传设备

1.把myFBXLib脚本挂在Manager上.

2.选中之前加入的Button,将其响应函数设定为myFBXLib中的LoadLocalAssetButtonClick.

3.先在PC上尝试一下效果,运行如下

4.为了在HoloLens上运行,需要加上HoloToolKit的交互组件,搜索并打开示例场景——InteractiveButtonComponents,复制其中的如下三个组件至我们的场景中,同时把Main Camera取消勾选,并调整Canvas到合适位置.

5.其他相关上传HoloLens的设置如常即可,导出部署,将数模放在HoloLens的3D Object目录下,运行效果如下

需要注意的是,PostLoadSetup函数中localScale的大小需要根据模型的大小自己设定,否则会出现加载后的模型过大/过小的问题,影响效果。

根据以上设定,如此便实现了Trilib插件在HoloLens上动态加载数模的功能。

3.后续内容

细心的读者会发现,当前的工程实现还存在一些问题,例如:

1.每次点击UI后,HoloLens主线程暂停,造成假死状态,且需要持续一段时间后才能正常打开FileBrowser,且每点一次按钮会有相同的情况,影响操作流畅感;

2.已加载模型后,再次点击按钮发现新的模型被加载,已加载模型消失;

3.对于已经加载的模型能否对其进行后续操作;

4.每次默认打开HoloLens的3D Object文件夹,能否选择其他文件夹内的数模;

5.单个按钮的UI过于单调,如何在复杂的UI场景中使用Trilib插件;

以上的问题的答案是肯定的,均可以通过修改相关脚本实现以上功能,笔者将在后续推出教程。


总结

以上为HoloLens+Trilib插件综合开发的Part2,介绍了Trilib插件在HoloLens中的简单使用,但该工程相对简单,还不能满足我们在正常工程中的使用,笔者将在后续继续更新相关内容。欢迎批评指正!

风和日暖,令人愿意永远活下去 .HDarker

HoloLens1开发(三):Trilib插件动态加载模型-Part2相关推荐

  1. HoloLens1开发(三):Trilib插件动态加载模型-Part1

    前言 最近有新成员加入本团队,为了方便其开发HoloLens1 / HoloLens2,将不定时更新HoloLens相关开发相关内容. 软件需求: HoloLens 1:VS2017 + Unity2 ...

  2. Unity3D的uniSWF插件动态加载SWF UI资源

    uniSWF能把Flash的素材像用AS3编程类似,只不过环境要在C#或者JS中编程.要是想创建一个类似Menu菜单或者像导航一样的菜单,在Unity中首先要选择摄像机,给摄像机添加MovieClip ...

  3. EntityFramework Core动态加载模型,我们要知道些什么呢?

    这篇文章源于一位问我的童鞋:在EntityFramework Core中如何动态加载模型呢?在学习EntityFramwork时关于这个问题已有对应童鞋给出答案,故没有过多研究,虽然最后解决了这位童鞋 ...

  4. 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用ApplicationPart动态加载控制器和视图

    目录 前言 什么是ApplicationPart? 创建项目 添加控制器和视图 如何动态加载插件中的控制器?# 如何加载组件的预编译Razor视图? 最终效果 总结 源代码:https://githu ...

  5. 在线客服系统源码开发实战总结:动态加载js文件实现粘贴一段js的sdk代码,直接引入插件效果...

    常见的在线客服系统中,或者是统计代码中,粘贴一段js代码,就能引入某个插件的效果.这个是怎么实现的呢? 原理非常的简单: 对于不同的加载文件类型创建不同的节点,然后添加各自的属性,最后扔到head 标 ...

  6. Android热修复技术初探(三):动态加载外部资源

    前面已经介绍了Android平台上的几种ClassLoader,这几种ClassLoader都有各自的使用场景,有了这些基础知识之后,才能更好地理解以及探究Android热修复技术.首先我们来探究怎么 ...

  7. 【jquery】Chosen.jquery.js 插件动态加载数据问题

    Chosen.jquery.js目前版本v1.6.2,官方文档链接https://harvesthq.github.io/chosen/ 插件有一个css文件和一个js文件,都命名为Chosen,引入 ...

  8. js插件动态加载js、css解决方案

    最近因为工作需要做了一个js自动导入的插件,一开始很天真的以为动态创建个script添加到head中就ok了,试了之后才发现了问题,就是如果同时引入了多个js文件,而且后一个文件中用到了前一个文件中的 ...

  9. 第十二章 软件壳(三)(动态加载型壳)

    文章目录 动态加载型壳 缓存脱壳法 内存 Dump 脱壳法 动态调试脱壳法 总结 Hook 脱壳法 系统定制脱壳法 动态加载型壳 即第一代壳 其发展时期正是从 Android 4.4 向 Androi ...

最新文章

  1. tf.variable和tf.get_Variable以及tf.name_scope和tf.variable_scope的区别
  2. 10474 - Where is the Marble?
  3. Lottie开源动画库
  4. python语言怎么用-这些小游戏是怎么用Python语言制作出来的……
  5. 云炬Android开发笔记 7登陆注册功能开发
  6. cgi与php的区别,fastcgi与cgi的区别
  7. python 之 前端初识 html
  8. JVM--类加载机制
  9. jzoj6275-[NOIP提高组模拟1]小L的数列【矩阵乘法,欧拉定理】
  10. ES6学习笔记04:Set与Map
  11. (十进制高速幂+矩阵优化)BZOJ 3240 3240: [Noi2013]矩阵游戏
  12. linux 占用缓存前10_MySQL基于linux的内存分析
  13. matlab 自动交易系统设计2
  14. 小米/VIVO/OPPO全系列救砖+解锁+工具+教程+激活账户技术
  15. c语言程序设计环境软件下载,c语言编程软件
  16. pythonobject转int_python – Pandas:将date’object’转换为int
  17. 多因子融合的实体识别与链指消歧
  18. php多进程兑换电影票,通过读写同一个文件锁来解决并发!
  19. Anaconda 环境克隆、迁移
  20. Day3 算法基本要素

热门文章

  1. 弱网络环境下最优调度和优化传输层协议方案
  2. mongodb的sharding架构搭建
  3. Linux SSH保持连接(解决Broken pipe)
  4. 工作方式~使用寄存的实现方式~
  5. css属性~(积少成多)
  6. 剑指 Offer JZ35 复杂链表的复制
  7. 力扣题目——25. K 个一组翻转链表
  8. 【Python】ModuleNotFoundError: No module named 'pandas.io.data'
  9. Spring velocity 中文乱码 解决方案
  10. Vmware虚拟机里面的linux系统ping不通宿主机的解决方法(注意同时安装vmware和vbox虚拟机)