当前位置: 首页 > news >正文

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;
            }
        }

 

相关文章:

  • html form post/get
  • 阿里云服务器Linux CentOS安装配置(四)yum安装tomcat
  • 3.saltstack模块介绍
  • Dedecms去掉URL中a目录的方法
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 查看进程命令
  • 『Java』Servlet 最简教程
  • 获取微信openid
  • Obsolete此API即将移除
  • Colorful Image Colorization 的环境配置
  • 上下左右滚动插件
  • TableViewCell重影问题
  • HDU1048 The Hardest Problem Ever
  • CentOS下配置Hadoop集群:java.net.NoRouteToHostException: No route to host问题的解决
  • Linux 下vsftp配置文件
  • 【面试系列】之二:关于js原型
  • Angularjs之国际化
  • CSS盒模型深入
  • dva中组件的懒加载
  • GraphQL学习过程应该是这样的
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JavaScript HTML DOM
  • js如何打印object对象
  • mysql_config not found
  • redis学习笔记(三):列表、集合、有序集合
  • web标准化(下)
  • Web设计流程优化:网页效果图设计新思路
  • XML已死 ?
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 坑!为什么View.startAnimation不起作用?
  • 使用parted解决大于2T的磁盘分区
  • 找一份好的前端工作,起点很重要
  • #Linux(make工具和makefile文件以及makefile语法)
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (力扣)1314.矩阵区域和
  • (转)Windows2003安全设置/维护
  • .jks文件(JAVA KeyStore)
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET Core中Emit的使用
  • .net FrameWork简介,数组,枚举
  • .NET 中什么样的类是可使用 await 异步等待的?
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)...
  • .NET6 命令行启动及发布单个Exe文件
  • .Net下的签名与混淆
  • .Net中wcf服务生成及调用
  • /etc/fstab 只读无法修改的解决办法
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @RequestBody与@ModelAttribute
  • [ vulhub漏洞复现篇 ] Apache APISIX 默认密钥漏洞 CVE-2020-13945
  • [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(十)RCE (远程代码/命令执行漏洞)相关面试题
  • [ 转载 ] SharePoint 资料