HoloToolkit项目源码剖析 - Spatial Mapping功能实现

就像我之前所描述的,HoloToolkit项目是微软基于Unity内置的底层API封装的一套工具集合,帮助我们快速使用Unity集成开发HoloLens应用。

本文主要通过源码研究其中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;
            }
        }
时间: 2025-01-01 11:57:30

HoloToolkit项目源码剖析 - Spatial Mapping功能实现的相关文章

java crm 进销存 springmvc SSM 项目 源码 系统

系统介绍: 1.系统采用主流的 SSM 框架 jsp JSTL bootstrap html5 (PC浏览器使用) 2.springmvc +spring4.3.7+ mybaits3.3  SSM 普通java web(非maven, 附赠pom.xml文件)  数据库:mysql 3.开发工具:myeclipse  eclipse idea 均可, 没有限制. 我这边myeclipse 2014 导出来的项目源码 ---------------------------------------

下载BootStrap企业级应用培训课程(零基础、源码剖析,内部教材,项目实训)

全套500多课,附赠JS OOP编程,转一播放码.下载地址:http://pan.baidu.com/s/1kVLdZmf 第一季:基础篇,侧重于BootStrap 相关 API 详解.主要包含以下内容:Brackets前端开发工具详解.BootStrap框架三大核心-CSS.BootStrap框架三大核心-布局组件.BootStrap框架三大核心-JavaScript插件.附-BootStrap编码规范第二季:高级篇,侧重于BootStap源码解析与第三方扩展.主要包含以下内容:BootStr

一个Python开源项目-腾讯哈勃沙箱源码剖析(上)

前言 2019年来了,2020年还会远吗? 请把下一年的年终奖发一下,谢谢... 回顾逝去的2018年,最大的改变是从一名学生变成了一位工作者,不敢说自己多么的职业化,但是正在努力往那个方向走. 以前想的更多是尝试,现在需要考虑的更多是落地.学校和公司还是有很大的不一样,学到了很多东西. 2019年了,新年新气象,给大家宣布一下"七夜安全博客"今年的规划: 1. 2019年不再接任何商业广告(文末腾讯广告除外),纯粹输出安全技术干货. 2. 2019年每周至少两篇原创图文,也就是说每个

HashMap(2) 源码剖析(推荐)

今天看代码,想到去年发生的HashMap发生的CPU使用率100%的事件,转载下当时看的三个比较不错的博客(非常推荐) 参考:http://coolshell.cn/articles/9606.html   http://github.thinkingbar.com/hashmap-analysis/ http://developer.51cto.com/art/201102/246431.htm 在 Java 集合类中,使用最多的容器类恐怕就是 HashMap 和 ArrayList 了,所以

MongoDB的使用学习之(七)MongoDB的聚合查询(两种方式)附项目源码

先来张在路上-- 此项目是用Maven创建的,没有使用Maven的,自己百度.谷歌去:直接用Junit测试就行,先执行里面的save方法,添加10000条测试数据提供各种聚合查询等. 废话不多说,上干货-- 一.MongoDB数据库的配置(mongodb.xml) 以下是我自己的配置,红色字体请改为自己本机的东东,你说不懂设置端口,不会创建数据库名称,不会配置用户名密码,那有请查阅本系列的第4节(MongoDB的使用学习之(四)权限设置--用户名.密码.端口==),你说懒得设置,那就@#¥%--

Python源码剖析笔记6-函数机制

Python的函数机制是很重要的部分,很多时候用python写脚本,就是几个函数简单解决问题,不需要像java那样必须弄个class什么的. 本文简书地址:http://www.jianshu.com/p/d00108741a18 1 函数对象PyFunctionObject PyFunctionObject对象的定义如下: typedef struct { PyObject_HEAD PyObject *func_code; /* A code object */ PyObject *func

(升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课程会涵盖Scala编程详解.Spark核心编程.Spark SQL和Spark Streaming.Spark内核以及源码剖析.性能调优.企业级案例实战等部分.完全从零起步,让学员可以一站式精通Spark企业级大数据开发,提升自己的职场竞争力,实现更好的升职或者跳槽,或者从j2ee等传统软件开发工程

tomcat(12)org.apache.catalina.core.StandardContext源码剖析

[0]README 0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(12)StandardContext源码剖析" 的基础知识: 1)Context实例表示一个具体的web 应用程序,其中包含一个或多个Wrapper实例,每个Wrapper 表示一个具体的servlet定义: 2)Context容器还需要其他组件的支持,如载入器和Session 管理器.本章要intro 的 StandardContext是 catalina

JS魔法堂:mmDeferred源码剖析

一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见:[email protected] 二.API说明 {Deferred} Deferred({Function|Object} mixin?) ,创建一个Deferred实例,当mixin的类型为Object时,将mixin的属性和函数附加到Deferred实例的Promise实例上. {Stri