关于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. [PHP] Laravel 联查中对不同表字段关系加条件的方式

    如果条件需要加在 where 条件里,使用 whereColumn,如下示例: whereColumn('A.b_id', '=', 'B.id'); 如果需要加载 join 的 on 之后作为多个条 ...

  2. 修复 WPF 安装 WindowsAppSDK 库构建失败 NETSDK1082 和 NETSDK1112 找不到 win10-arm 失败

    通过在 WPF 项目上安装 WindowsAppSDK 库,可以让 WPF 使用上 Win10 及以上版本提供的 Windows Runtime 强大的 API 集和使用上更多的黑科技.本文记录在安装 ...

  3. K8s集群中部署SpringCloud在线购物平台(一)

    一.安装k8s高可用集群 主机名 IP 配置 网络 master控制节点 192.168.10.10 centos 7.9 4核4G 桥接 node1工作节点 192.168.10.11 centos ...

  4. 羽夏闲谈——解决 MSI 安装包指定账户已存在

    序   前几天用VS2022,升级到17.1.0版本,发现模板用不了了,但能正常打开之前用它创建的项目.我重装试图修复该问题,解决雪上加霜,报错如下: 未能安装包"Microsoft.Vis ...

  5. 如何实现surging 多语言混合微服务异构

    1. 背景 作为微服务体系, 应该是不限语言的, 不管是.net.java, 都可以是一个微服务. 可以使用JAVA或者.NET 去实现业务模块,通过统一的消息模型进行传输调用因客户技术栈以多语言,多 ...

  6. 以对象的方式访问html中的标签,比正则表达式更好用的方式获取html中的内容,linq方式直接获取所有的链接,更加先进的c#版本爬虫开源库

    这是我本人自己写的一个开源库,现已经发布到nuget,可以直接在vs的nuget包管理中搜索到,或者可以到nuget官网下载:https://www.nuget.org/packages/ZmjCon ...

  7. ruby 定时器 rufus-scheduler

    安装 gem install rufus-scheduler ruby #!/usr/bin/env ruby require 'rubygems' require 'rufus-scheduler' ...

  8. 联想G470安装黑苹果

    macos10136 黑苹果usb无线网卡 1.系统下载: 下面是我自制的带clover 4596版本的u盘镜像: 链接: https://pan.baidu.com/s/1wRdVddwkei7bf ...

  9. iceoryx源码阅读(一)——全局概览

    一.什么是iceoryx iceoryx是一套基于共享内存实现的进程间通信组件. 二.源码结构 iceoryx源码包括若干工程,整理如下表所示: 下图展示了主要项目之间的依赖(FROM:iceoryx ...

  10. synchronized原理-字节码分析、对象内存结构、锁升级过程、Monitor

    本文分析的问题: synchronized 字节码文件分析之 monitorenter.monitorexit 指令 为什么任何一个Java对象都可以成为一把锁? 对象的内存结构 锁升级过程 Moni ...