从零开始学习导航网格#4 recast代码分析之solomesh(上)

温馨提示:
由于没有系统严谨的学习过相关理论,所以我在描述一些概念的时候可能会自己造一些名词,也不保证我的理解就是正确的。希望各位带哥海涵。
另外整个项目的代码量很大,算法细节也很多,全部展开分析是一个浩大的工程,我应该没有足够的时间精力来做这件事,只能记录下主流程以及我觉得需要拆解的点。
再有,为了能够顺畅的理解项目的思路和代码,可能需要学习一些基础的计算几何和图形学的知识,我在这篇文章中会有相应的整理

///正文开始
当一次寻路发生的时候,我们一般需要知道两个信息:
1.地图上哪些区域是可行走的
2.这些可行走区域之间的联通关系是什么样的
从这个角度来看,导航网格算是一种数据结构,用某种方式来描述地图的上述两种信息
在实际应用中,导航网格是以邻接的凸多边形集合来表示的
在独立的凸多边形内部,保证任意两点直线可达

而寻路关键是通过算法找到一组多边形,这组多边形满足这样的条件:
第一个和最后一个多边形包含了寻路的起始点和终点
中间的多边形负责所有多边形的连通性

因此导航网格寻路可以粗略的分成两大部分:
1.将3D场景转化为邻接的凸多边形集合
2.在凸多边形集合上寻路

在RecastNavigation项目中,
Recast工程对应第一部分
Detour工程对应第二部分
而RecastDemo是一个GUI程序,可以直观的验证导航网格的生成和寻路的过程

配置参数 struct rcConfig

如何输入一个3D场景 class InputGeom

生成导航网格可以从bool Sample_SoloMesh::handleBuild()这个函数开始看起(也就是在demo程序的界面上点击“build"之后的回调函数)

Step 1. Initialize build config.

根据配置初始化参数,并计算出网格的大小
其中最重要的参数是这几个:
场景尺寸(包围盒)
所有的的顶点(顶点个数&&顶点坐标集合)
所有的三角形面(三角形个数&&三角形集合)
另外这里的半径已经被转换为以体素大小作为单位
m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs);

Step 2. Rasterize input polygon soup.

体素化
体素化的概念在第一篇中已经有所讲述,这里再提一下,可以用2D图形的光栅化最类比理解,其实就是为了找到3维空间中的三角形面与那些体素盒子相交


光栅化

具体步骤:

a.标记所有三角形面是否可行走

rcMarkWalkableTriangles计算所有三角形的法线,与最大可行走角度做比较,并将结果存入m_triareas[]这个标记数组中

b.创建高度场

c.光栅化三角形面

rcRasterizeTriangles这个函数是重点,它主要做了这些事:
遍历所有三角形,调用rasterizeTri,将每个初始的三角形面转换成空间内的体素集
注意这里每个三角形之间是相互独立的,也许某一组三角形可以围成了一个多面体,但这里已经不再有"多面体"的概念,而是将地图的信息全部拆解到三角形面,再进一步拆解到体素盒子

1.找到三角形的AABB(注意是3维空间内的三角形)
2.计算三角形的高度(y轴)范围
3.按xz坐标网格切割三角形
俯视图如下(注意只是俯视图,实际上应该是3维空间的切割)


切割俯视图

在分割三角形时代码中有这样一个数组

float buf[7*3*4];
float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3;

解释一下7 3 4这三个数字
4:有4份对应的数据,用来存放输入的图形和分割后的图形
3:对应一个顶点的x y z 坐标值
7:一个三角形在分割时会被切4刀(即一个正方形格子的4条边),所以切完最多可能会变成一个7边形,也就是最多需要存7个顶点

这里有一个重要的函数:
用一条直线将一个凸多边形分成两个凸多边形

static void dividePoly(const float* in, int nin,
                      float* out1, int* nout1,
                      float* out2, int* nout2,
                      float x, int axis)

参数axis取值[0,2],对应表示x,y,z轴
枚举所有边,判断边的顶点坐标是在轴的同侧还是不同侧,
若是同侧则将两个顶点都加入对应的新多边形
若是在不同侧,则新生成一个分割点,将分割点(复制成两份)加入两个新多边形

4.找到切割后的多边形对应的span,根据对应的三角形面的可行走情况设置span的可行走属性,并把这个span根据xz的投影坐标添加入到高度场中

这里要特别说明两点
1.span在合并的时候,如果同一个span,有的面是可行走,有的是不可行走,在合并之后会变成可行走

// Merge flags.
if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
    s->area = rcMax(s->area, cur->area);

2.如果一个面是完全垂直于xz平面且与x轴或者z轴平行,在切割时会怎么样
不妨假设Xmax=Xmin,这时在x轴方向的切割只有一次即x=Xmax,并且dividePoly会判定所有三角形的边都在分割线的同一侧,分割的结果是一个原三角形和一个空集合

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353