关于UE引擎层面的东西:
  1. 在向场景重拖入一个NavMeshBoundsVolume时(或者修改时). 会调用
void UNavigationSystemV1::PerformNavigationBoundsUpdate(const TArray& UpdateRequests)
然后会 创建/更新 一个NavigationData Actor对象到场景中, 名字默认是RecastNavMesh-Default
  1. 在增加NavMeshBoundsVolume后的调用堆栈如下. 可以看到最后是把请求放到了PendingDirtyTiles里了. 走异步烘培
void UNavigationSystemV1::PerformNavigationBoundsUpdate
void ANavigationData::OnNavigationBoundsChanged
NavDataGenerator->OnNavigationBoundsChanged();
void FRecastNavMeshGenerator::OnNavigationBoundsChanged()
void FRecastNavMeshGenerator::MarkDirtyTiles -> PendingDirtyTiles
  1. 异步烘培的驱动堆栈如下, 可以看到是从World的Tick到ProcessTileTasksAsyncAndGetUpdatedTiles函数里. 这个函数里取出来PendingDirtyTiles里记录的异步请求. 执行
TArray FRecastNavMeshGenerator::ProcessTileTasksAsyncAndGetUpdatedTiles
TArray FRecastNavMeshGenerator::ProcessTileTasksAndGetUpdatedTiles
void FRecastNavMeshGenerator::TickAsyncBuild(float DeltaSeconds)
void UNavigationSystemV1::Tick
void UWorld::Tick
  1. 在ProcessTileTasksAsyncAndGetUpdatedTiles函数里创建了一个TileTask(调用CreateTileGenerator函数构造一个Generator初始化了Task)
上面这一行很关键,一点点的解释下;
1) 查看FRecastTileGeneratorTask的定义, 可以看到FRecastTileGeneratorTask其实是一个FAsyncTask
2) MakeUnique 是个模板函数, 功能是首先调用CreateTileGenerator(PendingElement.Coord, PendingElement.DirtyAreas)这个函数, 用它的返回值作为参数构造FRecastTileGeneratorTask
也就是构造FAsyncTask
 
3) FRecastNavMeshGenerator::CreateTileGenerator这个函数接受了coord和dirtyAreas作为参数, 调用了另外一个模板函数ConstuctTileGeneratorImpl. 返回一个TSharedRef 类型
4) 接着看ConstuctTileGeneratorImpl模板函数, 使用this指针, 也就是FRecastNavMeshGenerator和Coord参数构造了一个FRecastTileGenerator, 并调用了FRecastTileGenerator的Setup函数, 传入了this和DirtyAreas参数.
然后把完成初始化的FRecastTileGenerator返回出去.
5) 然后回到2)步里创建Task的过程里. 使用CreateTileGenerator返回的TSharedRef作为参数构造了FAsyncTask
然后看FAsyncTask的构造函数, 可以看到FAsyncTask是一个模板类, 含有一个TTask Task成员, 就是模板的类型FRecastTileGeneratorWrapper, 构造函数里把接收到的参数传递给了Task的构造函数
也就是FRecastTileGeneratorWrapper的构造函数, FRecastTileGeneratorWrapper里的TileGenerator成员就被初始化成4)里构造并调用过Setup的FRecastTileGenerator对象了.
6) 综上, 就完成了一个FAsyncTask的构建, 其中持有了一个FRecastTileGenerator的实例. 也就是TileGenerator成员.
7) 构造完成后, 通过调用TileTask.Release方法, 从智能指针处获得FRecastTileGeneratorTask* 指针. 赋值给RunningElement的AsyncTask对象.
然后就可以拿AsyncTask来调用同步或者异步接口. 调用完成后把RunningElement塞到RunningDirtyTiles里缓存. 并从PendingDiryTiles里移除.
 
8) 然后就是FAsyncTask的异步Work调度逻辑了
 
异步的话
9) 由于Task是FRecastTileGeneratorWrapper 所以是调用了这里.
然后是FRecastTileGenerator的这里
然后是这里. 调用GenerateNavigationData
 
具体的烘培逻辑
  1. UE的NavMesh烘培代码入口在 bool FRecastTileGenerator::GenerateTile()
  2. 主要烘培代码:
bool FRecastTileGenerator::GenerateNavigationData(FNavMeshBuildContext& BuildContext)
bool FRecastTileGenerator::GenerateNavigationDataLayer(FNavMeshBuildContext& BuildContext, FTileCacheCompressor& TileCompressor, FTileCacheAllocator& GenNavAllocator, FTileGenerationContext& GenerationContext, int32 LayerIdx)
  1. 烘培完成后数据存储:
  1. 逻辑流程参考Recast是走的Sample_TempObstacles流程, 烘培区域选择的算法是RC_REGION_WATERSHED
 
 
 
 

2024.4.3 补充

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
RebuildAll 逻辑流程.

  1. 在NavigationSystemV1 Build函数里调用NavData的RebuildAll, 实际会转发调用到FRecastNavMeshGenerator::RebuildAll()
  2. z在Generator的RebuildAll里先根据Bounds 标记所有的DirtyArea: MarkNavBoundsDirty()
  3. 然后通过EnsureBuildCompletion函数(图1, 图3)确保收集每次要处理的Tile任务和确保这些Tile任务执行EnsureCompletion, 完成Build (图3)
  4. 然后在Task的EnsureCompletion里, 调用的就是Task的DoWork, Task可以参考前面的分析.

 
 
 

过一遍后续的烘培流程 (以Tile为单位, 整个NavMesh是由n*m个tile组成. 多线程烘培. 同时最多进行MaxTileGeneratorTasks个Tile烘培任务):

从这里开始 bool FRecastTileGenerator::DoWork()   →  bool FRecastTileGenerator::GenerateTile()

具体:

  1. 先调用GenerateCompressedLayers 生成CompressedLayers,  这里有完整的烘培流程的前半部分
    1. 构造高度场 CreateHeightField
    2. 栅格化并标记NavModify. ComputeRasterizationMasks
    3. 栅格化网格, RasterizeTriangles → RasterizeGeometry / RasterizeGeometryRecast → rcRasterizeTriangles
    4. 过滤一些无效的span. ApplyVoxelFilter + GenerateRecastFilter → rcFilterLowHangingWalkableObstacles + rcFilterLedgeSpans + rcFilterWalkableLowHeightSpans
    5. 构造Compact高度场 BuildCompactHeightField
    6. Erode 一下寻路Span. RecastErodeWalkable → rcErodeWalkableAndLowAreas + rcErodeWalkableArea
    7. 重头戏, 烘培nav Layer: RecastBuildLayers → RC_REGION_WATERSHED → rcBuildDistanceField + rcBuildHeightfieldLayers → rcGatherRegionsNoFilter
    8. 构造dtBuildTileCacheLayer, 压缩Layer: RecastBuildTileCache
  2. 在得到CompressedLayers之后,  我们只是完成了NavMesh烘培的前半部分, 即完成BuildRegion阶段.  后续还需要做区域优化, 形成轮廓,  简化轮廓,  构建PolyMesh, 生成DetailPolyMesh
  3. 上面提到的步骤,紧接着GenerateCompressedLayers之后调用GenerateNavigationData实现.
    1. GenerateNavigationData()
    2. GenerateNavigationDataLayer
      1. 解压缩 dtDecompressTileCacheLayer
      2. Rasterize obstacles 处理动态阻挡. MarkDynamicAreas(*GenerationContext.Layer);
      3. 重新烘培区域, 类似上面的步骤g: dtBuildTileCacheDistanceField + dtBuildTileCacheRegions → filterSmallRegions
      4. 构建轮廓 dtBuildTileCacheContours
      5. 构建PolyMesh, dtBuildTileCachePolyMesh
      6. 构建PolyMeshDetail, dtBuildTileCachePolyMeshDetail
      7. 处理offmeshLinks
      8. 最后完成NavMeshData的构建: dtCreateNavMeshData(&Params, &NavData, &NavDataSize))
      9. 构建UE的NavMesh对象FNavMeshTileData: GenerationContext.NavigationData.Add(FNavMeshTileData(NavData, NavDataSize, LayerIdx, CompressedData.LayerBBox));

从上面可以看出来, Recast的Sample_TempObstacles和Sample_TileMesh关键的区别就在于. TempObstacles会先把烘培的前半部分数据Compress一下, 会有CompressedLayers

这部分数据是场景Mesh得到的原始数据,再加上动态阻挡的部分之后. 再重新烘培区域, 简化区域, 烘培细节网格. 得到最终的NavMesh.

这样做的原因不难理解: 场景不变的数据, 先烘培好压缩保存起来, 如果这里出现了动态阻挡, 则把场景不变的数据解压出来, 叠加动态阻挡, 再重新烘培一下区域. 形成更新的Navmesh.

所以这就是Sample_TempObstacles烘培的是支持动态阻挡的Dynamic NavMesh和Sample_TileMesh烘培的不支持动态阻挡的Static NavMesh 之间的区别. UE亦是如此.

 
 
 
 
 
 
 

UE 5 NavMesh 烘培 逻辑流程的更多相关文章

  1. 在Salesforce中通过 Debug Log 方式 跟踪逻辑流程

    在Salesforce中通过 Debug Log方式 跟踪逻辑流程 具体位置如下所示: Setup ---> Logs ---> Debug Logs ---> Monitored ...

  2. Unity3D研究院之动态修改烘培贴图的大小&脚本烘培场景

    Unity默认烘培场景以后每张烘培贴图的大小是1024.但是有可能你的场景比较简单,用1024会比较浪费.如下图所示,这是我的一个场景的烘培贴图,右上角一大部分完全是没有用到,但是它却占着空间.  有 ...

  3. 【翻译】CEDEC2012 SQUARE ENIX GPGPU实现高速GI烘培工具的方法

     虽然实时GI技术已经趋于成熟了,但出于对不同平台的性能和质量的考虑, 更倾向搭配一些预计算的渲染技术来实现,如给静态物体提供GI的LightMap, 给动态物体提供GI的Irradiance Vol ...

  4. Unity3D 5.1烘培 操作

    http://blog.csdn.net/asd237241291/article/details/48056575 原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 Unity3D引擎技 ...

  5. HDFS追本溯源:HDFS操作的逻辑流程与源码解析

    本文主要介绍5个典型的HDFS流程,这些流程充分体现了HDFS实体间IPC接口和stream接口之间的配合. 1. Client和NN Client到NN有大量的元数据操作,比如修改文件名,在给定目录 ...

  6. 3DMAX 烘培技术

    烘培是指,把光照信息渲染成贴图,而后把这个烘培后的贴图再贴回到场景中去的技术.烘培技术把光照计算的结果提前写入到了贴图中,因此在实时渲染中不需要进行耗时的光照计算,大大提高了实时渲染的效率. 烘培和渲 ...

  7. Unity3d 烘培lightingmap 注意的2点.

    1.在Qulity里面设置合适的灯光数量.否则,你会发现烘培出来的场景,有些灯光没有起作用. 2.在导入模型时候,注意勾选:Generate Lightingmap .  否则,模型没办法烘培. 3. ...

  8. Laravel5 快速认证逻辑流程分析

    Laravel5本身自带一套用户认证功能,只需在新项目下,使用命令行php artisan make:auth 和 php artisan migrate就可以使用自带的快速认证功能. 以下为分析登录 ...

  9. 【一起学源码-微服务】Hystrix 源码二:Hystrix核心流程:Hystix非降级逻辑流程梳理

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲了配置了feign.hystrix.enabl ...

  10. Nginx(二): worker 进程处理逻辑-流程框架

    Nginx 启动起来之后,会有几个进程运行:1. master 进程接收用户命令并做出响应; 2. worker 进程负责处理各网络事件,并同时接收来自master的处理协调命令: master 主要 ...

随机推荐

  1. 解决 System.Net.Sockets.SocketException 10106 无法加载或初始化请求的服务提供程序 无法联网

    本文收集 System.Net.Sockets.SocketException 异常错误码为 10106 导致无法联网的问题 这里的 10106 是 Win32 的 Socket 错误码,可以从 Wi ...

  2. 001_Cadence软件的安装与介绍

    001_Cadence软件的安装与介绍 软件版本16.6,软件下载:搜索PCB联盟; 安装步骤: 1)   把5个分卷的压缩包解压到同一文件夹; 2)   双击Setup.exe开始安装,先安装Lic ...

  3. ES_CCS/R(三):跨集群复制 Cross-cluster replication(CCR)

    跨集群复制(CCR)功能支持将远程集群中的索引复制到本地集群. 可以在一些常见的生产用例中使用此功能: 灾难恢复(DR)/高可用性(HA):如果主群集发生故障,则进行灾难恢复. 辅助群集可以用作热备份 ...

  4. Data Lake_理解数据湖

    Pentaho首席技术官James Dixon创造了"数据湖"一词.它把数据集市描述成一瓶水(清洗过的,包装过的和结构化易于使用的).而数据湖更像是在自然状态下的水,数据流从源系统 ...

  5. ITIL4之四维模型

    ITIL4的一个核心概念.它定义了四个维度(图中的1~4),这四个维度旨在确保组织能够在多方面考虑其服务提供,从而更有效地创造和交付价值. 四维模型的整合流程 确立价值载体:明确信息服务提供商的价值主 ...

  6. Kafka源码分析(四) - Server端-请求处理框架

    系列文章目录 https://zhuanlan.zhihu.com/p/367683572 一. 总体结构 先给一张概览图: 服务端请求处理过程涉及到两个模块:kafka.network和kafka. ...

  7. Notion API中Internal Notion integrations和Public Notion integrations的区别

    Internal Notion integrations Internal Notion integrations与一个单一的.特定的工作区相联系,只有该工作区的成员可以使用这个integration ...

  8. MindSpore反向传播配置关键字参数

    技术背景 在MindSpore深度学习框架中,我们可以向construct函数传输必备参数或者关键字参数,这跟普通的Python函数没有什么区别.但是对于MindSpore中的自定义反向传播bprop ...

  9. Django国际化与本地化指南

    title: Django国际化与本地化指南 date: 2024/5/12 16:51:04 updated: 2024/5/12 16:51:04 categories: 后端开发 tags: D ...

  10. C数据结构:二叉树的基本操作

    二叉树 树基本知识 二叉树的性质 满二叉树 完全二叉树 性质4.5的解释 顺序存储结构(利用性质4.5) 链式存储结构 结点结构体 建立二叉树 先序遍历 中序遍历 后序遍历 层次遍历 复制二叉树 计算 ...