HoloToolkit项目源码剖析 - Spatial Mapping功能实现
2016-07-01 08:37
267 查看
就像我之前所描述的,HoloToolkit项目是微软基于Unity内置的底层API封装的一套工具集合,帮助我们快速使用Unity集成开发HoloLens应用。
本文主要通过源码研究其中Spatial Mapping的实现,关于底层的API细节,请阅读我前一篇文章:HoloLens开发手记 - Unity之Spatial mapping 空间映射
本文重点研究SpatialMappingObserver.cs和SpatialMappingSource.cs,这是当前组件的核心内容。
我们先分析其属性,如下:
再分析其程序逻辑及流程:
Awake()方法最先被执行,这里对SurfaceObserver对象进行了初始化。
接下来是Start()方法,里面设定了扫描的空间范围。
Update()方法中则会根据当前状态来调用API请求空间表面信息或者生成mesh对象
SurfaceObserver_OnDataReady()事件方法用于处理使用SurfaceData请求到的mesh对象信息,用于后续的使用,比如处理其材质效果等。
SurfaceObserver_OnSurfaceChanged()事件方法用于处理SurfaceObserver获取到的空间表面数据,用于后续的请求mesh对象操作。
本文主要通过源码研究其中Spatial Mapping的实现,关于底层的API细节,请阅读我前一篇文章:HoloLens开发手记 - Unity之Spatial mapping 空间映射
0x00 组件结构
Spatial Mapping目录下有很多内容,其中Prefabs目录里有我们可以直接使用的预置组件,本文关注的重点是Scripts目录的脚本。组件目录结构如下:
本文重点研究SpatialMappingObserver.cs和SpatialMappingSource.cs,这是当前组件的核心内容。
0x01 SpatialMappingObserver.cs
源码地址:https://github.com/Microsoft/HoloToolkit-Unity/blob/master/Assets/HoloToolkit/SpatialMapping/Scripts/SpatialMappingObserver.cs
我们先分析其属性,如下:
//每立方米网格三角形数量,控制mesh质量 public float TrianglesPerCubicMeter = 500f; //当前Observer检测的空间范围 public Vector3 Extents = Vector3.one * 10.0f; //刷新时间间隔 public float TimeBetweenUpdates = 3.5f; //用于扫描空间平面的核心组件 private SurfaceObserver observer; //存储已构建的空间网格对象 private Dictionary<int, GameObject> surfaces = new Dictionary<int, GameObject>(); //SurfaceData队列,用于生成空间mesh private Queue<SurfaceData> surfaceWorkQueue = new Queue<SurfaceData>(); //为了避免同一时刻生成太多mesh,保证同一时刻只生成一个mesh,这个变量用于判断当前时刻是否有mesh正在创建 private bool surfaceWorkOutstanding = false; //用于追踪Observer对象上次更新时间 private float updateTime; //表示当前扫描状态,Running和Stopped两种状态 public ObserverStates ObserverState { get; private set; }
再分析其程序逻辑及流程:
Awake()方法最先被执行,这里对SurfaceObserver对象进行了初始化。
private void Awake() { observer = new SurfaceObserver(); ObserverState = ObserverStates.Stopped; }
接下来是Start()方法,里面设定了扫描的空间范围。
private void Start() { observer.SetVolumeAsAxisAlignedBox(Vector3.zero, Extents); }
Update()方法中则会根据当前状态来调用API请求空间表面信息或者生成mesh对象
private void Update() { if (ObserverState == ObserverStates.Running) { // 如果当前没有再生成mesh,且SurfaceData中有需要生成mesh的对象 if (surfaceWorkOutstanding == false && surfaceWorkQueue.Count > 0) { SurfaceData surfaceData = surfaceWorkQueue.Dequeue(); // 如果能成功请求到mesh对象,当前任务状态变为生成mesh中 surfaceWorkOutstanding = observer.RequestMeshAsync(surfaceData, SurfaceObserver_OnDataReady); } //如果当前没有任务运行且上次更新距现在大于时间间隔,则重新请求SurfaceData数据 else if (surfaceWorkOutstanding == false && (Time.time - updateTime) >= TimeBetweenUpdates) { observer.Update(SurfaceObserver_OnSurfaceChanged); updateTime = Time.time; } } }
SurfaceObserver_OnDataReady()事件方法用于处理使用SurfaceData请求到的mesh对象信息,用于后续的使用,比如处理其材质效果等。
private void SurfaceObserver_OnDataReady(SurfaceData cookedData, bool outputWritten, float elapsedCookTimeSeconds) { GameObject surface; if (surfaces.TryGetValue(cookedData.id.handle, out surface)) { // 设置 renderer组件的材质. MeshRenderer renderer = surface.GetComponent<MeshRenderer>(); renderer.sharedMaterial = SpatialMappingManager.Instance.SurfaceMaterial; //是否渲染mesh对象 renderer.enabled = SpatialMappingManager.Instance.DrawVisualMeshes; if (SpatialMappingManager.Instance.CastShadows == false) { renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } } surfaceWorkOutstanding = false; }
SurfaceObserver_OnSurfaceChanged()事件方法用于处理SurfaceObserver获取到的空间表面数据,用于后续的请求mesh对象操作。
private void SurfaceObserver_OnSurfaceChanged(SurfaceId id, SurfaceChange changeType, Bounds bounds, System.DateTime updateTime) { // 判断当前扫描状态 if (ObserverState != ObserverStates.Running) { return; } GameObject surface; switch (changeType) { case SurfaceChange.Added: case SurfaceChange.Updated: // 检测当前表面是否已被扫描过 if (!surfaces.TryGetValue(id.handle, out surface)) { // 创建一个和当前表面关联的mesh对象 surface = AddSurfaceObject(null, string.Format("Surface-{0}", id.handle), transform); surface.AddComponent<WorldAnchor>(); // 将surface对象加入已知空间表面字典 surfaces.Add(id.handle, surface); } // 请求生成或更新对应的mesh对象 QueueSurfaceDataRequest(id, surface); break; case SurfaceChange.Removed: // 移除关联的mesh对象 if (surfaces.TryGetValue(id.handle, out surface)) { surfaces.Remove(id.handle); Destroy(surface); } break; } }
相关文章推荐
- application和javaBean练习
- ImageLoader
- ViewPager的自动无限轮播
- iOS可执行文件瘦身方法
- iOS创建界面方法的讨论
- IOS应用通过UIWEBVIEW实现与JS交互
- android 自定义的小工具类(1)获取图片和视频的缩微图
- Android入门2_xml解析的3种方法
- "standard,singleTop,singleTask,singleInstance"-Android启动模式图文代码全解析
- Android入门1_传递数据的几种方式
- Android Studio导入太慢?修改项目配置文件再导入,飞一般的感觉
- Gradle version 2.10 is required. Current version is 2.8. If using the gradle wrapper 真正的解决方案
- 移动端Web开发调试之Weinre调试教程
- Unity3d 中显示 Spine 动画节点 以及设置 帧事件
- 常用iOS-oc工具方法总结
- 漫谈android系统(6)硬件抽象层kernel分析
- iOS下的并行开发
- 对RecyclerView Item做动画
- iOS 地图 MKWebView(在中国是使用高德地图)
- 自定义数据类型 --- 类全解(swift2.3)