zoukankan      html  css  js  c++  java
  • HoloToolkit项目源码剖析

    就像我之前所描述的,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;
                }
            }
  • 相关阅读:
    【转】Geary's C
    ArcGIS中影像与影像,影像与点云之间的配准
    [转] EPSG CODE的含义
    地图投影
    【从翻译mos文章】oracle linux 和外部存储系统 关系
    找呀志_使用SQLiteDatabase增删改提供的搜索方法和事务
    [049] 微信公众平台视频公开课1说话-基础知识
    我看到西电通院考试——学生应该做的事情?
    使用 Eclipse 的 SVN 主要插件创建项目/支/标签
    【SSH三个框架】Hibernate第十篇基础:inverse属性具体解释
  • 原文地址:https://www.cnblogs.com/mantgh/p/5630127.html
Copyright © 2011-2022 走看看